mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-11 13:59:13 +00:00
QuickLook media. (#447)
* Use QL previews for video and present full screen. * Use URL(staticString:) in more places. * Fix DesignKit issues.
This commit is contained in:
parent
3153c0f0d9
commit
c383029203
@ -40,6 +40,7 @@ public struct ElementCapsuleButtonStyle: ButtonStyle {
|
||||
.multilineTextAlignment(.center)
|
||||
.background(background)
|
||||
.opacity(configuration.isPressed ? 0.6 : 1)
|
||||
.contentShape(Capsule())
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -62,6 +62,7 @@ public struct ElementTextFieldStyle: TextFieldStyle {
|
||||
configuration
|
||||
.textFieldStyle(BorderedInputFieldStyle(isEditing: isFocused, isError: isError, returnKey: nil))
|
||||
.focused($isFocused)
|
||||
.onTapGesture { isFocused = true } // Set focus with taps in the space between the border and text field.
|
||||
|
||||
if let footerText {
|
||||
Text(footerText)
|
||||
|
@ -47,7 +47,6 @@
|
||||
13C77FDF17C4C6627CFFC205 /* RoomTimelineItemFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */; };
|
||||
14132418A748C988B85B025E /* OnboardingPageIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09199C43BAB209C0BD89A836 /* OnboardingPageIndicator.swift */; };
|
||||
149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */; };
|
||||
1504CE9A609A348D90B69E47 /* VideoPlayerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3004DFA1B10951962787D90 /* VideoPlayerViewModelTests.swift */; };
|
||||
152AE2B8650FB23AFD2E28B9 /* MockAuthenticationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */; };
|
||||
1555A7643D85187D4851040C /* TemplateScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4549FCB53F43DB0B278374BC /* TemplateScreen.swift */; };
|
||||
157E5FDDF419C0B2CA7E2C28 /* TimelineItemBubbledStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98A2932515EA11D3DD8A3506 /* TimelineItemBubbledStylerView.swift */; };
|
||||
@ -61,7 +60,6 @@
|
||||
191161FE9E0DA89704301F37 /* Untranslated.strings in Resources */ = {isa = PBXBuildFile; fileRef = D2F7194F440375338F8E2487 /* Untranslated.strings */; };
|
||||
1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2958E6D247AE2516BEEE8 /* ClientProxy.swift */; };
|
||||
19839F3526CE8C35AAF241AD /* ServerSelectionViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F52BF30D12BA3BD3D3DBB8F /* ServerSelectionViewModelProtocol.swift */; };
|
||||
19ED6CF7FDBB1158692D101C /* VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D783758EAE6A88C93564EB /* VideoPlayerViewModel.swift */; };
|
||||
1A70A2199394B5EC660934A5 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = A678E40E917620059695F067 /* MatrixRustSDK */; };
|
||||
1A8BDEB96C3B2F033FA563F8 /* EmojiPickerHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB785716B9212C093704E767 /* EmojiPickerHeaderView.swift */; };
|
||||
1AE4AEA0FA8DEF52671832E0 /* RoomTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */; };
|
||||
@ -183,7 +181,6 @@
|
||||
5D9F0695DC6C0057F85C12B6 /* UserNotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1113CA0A67B4AA227AAFB63B /* UserNotificationController.swift */; };
|
||||
5E0F2E612718BB4397A6D40A /* TextRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */; };
|
||||
5E25568E1CDAD983517E58B5 /* MediaSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179423E34EE846E048E49CBF /* MediaSourceProxy.swift */; };
|
||||
5E540CAEF764D7FBD8D80776 /* VideoPlayerModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3FC45B7643298BF361CEB1 /* VideoPlayerModels.swift */; };
|
||||
5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */; };
|
||||
5F1FDE49DFD0C680386E48F9 /* TemplateViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B80895CE021B49847BD7D74 /* TemplateViewModelProtocol.swift */; };
|
||||
5F5488FBC9CFEB6F433D74A4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7109E709A7738E6BCC4553E6 /* Localizable.strings */; };
|
||||
@ -388,12 +385,10 @@
|
||||
C7B251DC896C0867C51B616D /* AnalyticsPrompt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541542F5AC323709D8563458 /* AnalyticsPrompt.swift */; };
|
||||
C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */; };
|
||||
C8E82786DE1B6A400DA9BA25 /* RoomTimelineItemProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 289FA233E896FBC5956C67E0 /* RoomTimelineItemProperties.swift */; };
|
||||
C94A6048C654B01163AE1BF1 /* VideoPlayerViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5282B7A2DCD076AD2CF27F46 /* VideoPlayerViewModelProtocol.swift */; };
|
||||
CA45758F08DF42D41D8A4B29 /* FilePreviewViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF38B69D2C331A499276F400 /* FilePreviewViewModelTests.swift */; };
|
||||
CB137BFB3E083C33E398A6CB /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 0DD568A494247444A4B56031 /* Kingfisher */; };
|
||||
CB498F4E27AA0545DCEF0F6F /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4003BC24B24C9E63D3304177 /* DeviceKit */; };
|
||||
CB99B0FA38A4AC596F38CC13 /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E94DCFEE803E5ABAE8ACCE /* KeychainControllerProtocol.swift */; };
|
||||
CBF64DE774298D773DBD5354 /* VideoPlayerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB634B42CFE667112369D57 /* VideoPlayerScreen.swift */; };
|
||||
CC2A6B71E12DDF1EE6ECD299 /* RoomMembersCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F118CF7C5548099AACF7E90C /* RoomMembersCoordinator.swift */; };
|
||||
CC736DA1AA8F8B9FD8785009 /* ScreenshotDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5C4AF6E3885730CD560311C /* ScreenshotDetector.swift */; };
|
||||
CCAA0671B46EAFD0BB528E2C /* apple_emojis_data.json in Resources */ = {isa = PBXBuildFile; fileRef = 8FC26871038FB0E4AAE22605 /* apple_emojis_data.json */; };
|
||||
@ -409,7 +404,6 @@
|
||||
D05A193AE63030F2CFCE2E9C /* UITestScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6FE34A0A47D010BBB4D4D4 /* UITestScreenIdentifier.swift */; };
|
||||
D0619D2E6B9C511190FBEB95 /* RoomMessageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607974D08BD2AF83725D817A /* RoomMessageProtocol.swift */; };
|
||||
D2D70B5DB1A5E4AF0CD88330 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 033DB41C51865A2E83174E87 /* target.yml */; };
|
||||
D3E603A5E9D529CF293E1BF9 /* VideoPlayerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1651A532305027D3F605E2B /* VideoPlayerCoordinator.swift */; };
|
||||
D59F046B15AA8E971053C1A6 /* RoomDetailsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813B198AE8833FD12E5A9C78 /* RoomDetailsCoordinator.swift */; };
|
||||
D5C805F49B2C75DC3793E780 /* EmojiItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */; };
|
||||
D5EA4C6C80579279770D5804 /* ImageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */; };
|
||||
@ -475,7 +469,7 @@
|
||||
FA9C427FFB11B1AA2DCC5602 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */; };
|
||||
FBCD77D557AACBE9B445133A /* MediaProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C9E0B61A77C7F0EE7918C /* MediaProxy.swift */; };
|
||||
FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; };
|
||||
FCB9B475F908765531335859 /* NavigationSplitCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A63A32A0627A03F00A2900FE /* NavigationSplitCoordinator.swift */; };
|
||||
FCB9B475F908765531335859 /* NavigationSplitCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A63A32A0627A03F00A2900FE /* NavigationSplitCoordinatorTests.swift */; };
|
||||
FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; };
|
||||
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; };
|
||||
FE79E2BCCF69E8BF4D21E15A /* RoomMessageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA154570F693D93513E584C1 /* RoomMessageFactory.swift */; };
|
||||
@ -557,7 +551,6 @@
|
||||
0C88046D6A070D9827181C4D /* OnboardingUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingUITests.swift; sourceTree = "<group>"; };
|
||||
0CB569EAA5017B5B23970655 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
0D8F620C8B314840D8602E3F /* NSE.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = NSE.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0DB634B42CFE667112369D57 /* VideoPlayerScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerScreen.swift; sourceTree = "<group>"; };
|
||||
0DD16CE9A66C9040B066AD60 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
0DE6C5C756E1393202BA95CD /* UserNotificationControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationControllerTests.swift; sourceTree = "<group>"; };
|
||||
0E7062F88E9D5F79C8A80524 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = th; path = th.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
@ -590,7 +583,6 @@
|
||||
18FE0CDF1FFA92EA7EE17B0B /* RoomTimelineControllerFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerFactoryProtocol.swift; sourceTree = "<group>"; };
|
||||
1941C8817E6B6971BA4415F5 /* VideoRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Strings+Untranslated.swift"; sourceTree = "<group>"; };
|
||||
1A3FC45B7643298BF361CEB1 /* VideoPlayerModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerModels.swift; sourceTree = "<group>"; };
|
||||
1A63815AD6A5C306453342F2 /* ImageRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
1BC4437C107D52ED19357DFC /* OnboardingViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
1C429043E986008B97736636 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
@ -712,7 +704,6 @@
|
||||
505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
51DF91C374901E94D93276F1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-MX"; path = "es-MX.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreen.swift; sourceTree = "<group>"; };
|
||||
5282B7A2DCD076AD2CF27F46 /* VideoPlayerViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
529513218340CC8419273165 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
52D7074991B3267B26D89B22 /* MockRoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineController.swift; sourceTree = "<group>"; };
|
||||
53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewAdapter.swift; sourceTree = "<group>"; };
|
||||
@ -859,7 +850,6 @@
|
||||
A1ED7E89865201EE7D53E6DA /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
A2AC3C656E960E15B5905E05 /* UnsupportedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
A2B6433F516F1E6DFA0E2D89 /* vls */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vls; path = vls.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
A3004DFA1B10951962787D90 /* VideoPlayerViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModelTests.swift; sourceTree = "<group>"; };
|
||||
A30A1758E2B73EF38E7C42F8 /* ServerSelectionModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionModels.swift; sourceTree = "<group>"; };
|
||||
A34A814CBD56230BC74FFCF4 /* MXLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXLogger.swift; sourceTree = "<group>"; };
|
||||
A40C19719687984FD9478FBE /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = "<group>"; };
|
||||
@ -869,7 +859,7 @@
|
||||
A4B5B19A10D3F7C2BC5315DF /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
A4CF2FC815D26B337E78DA45 /* RoomMembersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersViewModel.swift; sourceTree = "<group>"; };
|
||||
A5B0B1226DA8DB55918B34CD /* FileCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileCache.swift; sourceTree = "<group>"; };
|
||||
A63A32A0627A03F00A2900FE /* NavigationSplitCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitCoordinator.swift; sourceTree = "<group>"; };
|
||||
A63A32A0627A03F00A2900FE /* NavigationSplitCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitCoordinatorTests.swift; sourceTree = "<group>"; };
|
||||
A64F0DB78E0AC23C91AD89EF /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mk; path = mk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
A65F140F9FE5E8D4DAEFF354 /* RoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomProxy.swift; sourceTree = "<group>"; };
|
||||
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = "<group>"; };
|
||||
@ -968,10 +958,8 @@
|
||||
D06DFD894157A4C93A02D8B5 /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
D09A267106B9585D3D0CFC0D /* ClientError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientError.swift; sourceTree = "<group>"; };
|
||||
D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
D1651A532305027D3F605E2B /* VideoPlayerCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerCoordinator.swift; sourceTree = "<group>"; };
|
||||
D1A9CCCF53495CF3D7B19FCE /* MockSessionVerificationControllerProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSessionVerificationControllerProxy.swift; sourceTree = "<group>"; };
|
||||
D263254AFE5B7993FFBBF324 /* NSE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NSE.entitlements; sourceTree = "<group>"; };
|
||||
D2D783758EAE6A88C93564EB /* VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModel.swift; sourceTree = "<group>"; };
|
||||
D31DC8105C6233E5FFD9B84C /* element-x-ios */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "element-x-ios"; path = .; sourceTree = SOURCE_ROOT; };
|
||||
D33116993D54FADC0C721C1F /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
||||
D3D455BC2423D911A62ACFB2 /* NSELogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSELogger.swift; sourceTree = "<group>"; };
|
||||
@ -1252,18 +1240,6 @@
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
285079C24A5189C48284CC47 /* VideoPlayer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D1651A532305027D3F605E2B /* VideoPlayerCoordinator.swift */,
|
||||
1A3FC45B7643298BF361CEB1 /* VideoPlayerModels.swift */,
|
||||
D2D783758EAE6A88C93564EB /* VideoPlayerViewModel.swift */,
|
||||
5282B7A2DCD076AD2CF27F46 /* VideoPlayerViewModelProtocol.swift */,
|
||||
5E01022071DDDC48EF453374 /* View */,
|
||||
);
|
||||
path = VideoPlayer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D6DC9871FD7173E51D67C73 /* Cache */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1601,14 +1577,6 @@
|
||||
path = EmojiMart;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5E01022071DDDC48EF453374 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DB634B42CFE667112369D57 /* VideoPlayerScreen.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
605F8221E52991786397FCC9 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1723,7 +1691,7 @@
|
||||
A05707BF550D770168A406DB /* LoginViewModelTests.swift */,
|
||||
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */,
|
||||
F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */,
|
||||
A63A32A0627A03F00A2900FE /* NavigationSplitCoordinator.swift */,
|
||||
A63A32A0627A03F00A2900FE /* NavigationSplitCoordinatorTests.swift */,
|
||||
9C698E30698EC59302A8EEBD /* NavigationStackCoordinatorTests.swift */,
|
||||
00A941F289F6AB876BA3361A /* OnboardingViewModelTests.swift */,
|
||||
6FB31A32C93D94930B253FBF /* PermalinkBuilderTests.swift */,
|
||||
@ -1740,7 +1708,6 @@
|
||||
1734A445A58ED855B977A0A8 /* TracingConfigurationTests.swift */,
|
||||
EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */,
|
||||
0DE6C5C756E1393202BA95CD /* UserNotificationControllerTests.swift */,
|
||||
A3004DFA1B10951962787D90 /* VideoPlayerViewModelTests.swift */,
|
||||
53280D2292E6C9C7821773FD /* UserSession */,
|
||||
7583EAC171059A86B767209F /* MediaProvider */,
|
||||
7DBC911559934065993A5FF4 /* NotificationManager */,
|
||||
@ -2361,7 +2328,6 @@
|
||||
679E9837ECA8D6776079D16E /* RoomScreen */,
|
||||
D958761758AA1110476DE6A3 /* SessionVerification */,
|
||||
70B74A432C241E56A7ACE610 /* Settings */,
|
||||
285079C24A5189C48284CC47 /* VideoPlayer */,
|
||||
);
|
||||
path = Screens;
|
||||
sourceTree = "<group>";
|
||||
@ -2923,7 +2889,7 @@
|
||||
DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */,
|
||||
E9631F628251F77A24AA4BB4 /* MockMediaProxy.swift in Sources */,
|
||||
981853650217B6C8ECDD998C /* NavigationRootCoordinatorTests.swift in Sources */,
|
||||
FCB9B475F908765531335859 /* NavigationSplitCoordinator.swift in Sources */,
|
||||
FCB9B475F908765531335859 /* NavigationSplitCoordinatorTests.swift in Sources */,
|
||||
4BB282209EA82015D0DF8F89 /* NavigationStackCoordinatorTests.swift in Sources */,
|
||||
1B2DADC008EE211AF1DA5292 /* NotificationManagerTests.swift in Sources */,
|
||||
F9F6D2883BBEBB9A3789A137 /* OnboardingViewModelTests.swift in Sources */,
|
||||
@ -2946,7 +2912,6 @@
|
||||
08248D02BACA75CDC3B39A96 /* UserNotificationCenterSpy.swift in Sources */,
|
||||
8196A2E71ACC902DD69F24EE /* UserNotificationControllerTests.swift in Sources */,
|
||||
81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */,
|
||||
1504CE9A609A348D90B69E47 /* VideoPlayerViewModelTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -3249,11 +3214,6 @@
|
||||
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */,
|
||||
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */,
|
||||
AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */,
|
||||
D3E603A5E9D529CF293E1BF9 /* VideoPlayerCoordinator.swift in Sources */,
|
||||
5E540CAEF764D7FBD8D80776 /* VideoPlayerModels.swift in Sources */,
|
||||
CBF64DE774298D773DBD5354 /* VideoPlayerScreen.swift in Sources */,
|
||||
19ED6CF7FDBB1158692D101C /* VideoPlayerViewModel.swift in Sources */,
|
||||
C94A6048C654B01163AE1BF1 /* VideoPlayerViewModelProtocol.swift in Sources */,
|
||||
36C10EDEDC0466E3A9D63132 /* VideoRoomTimelineItem.swift in Sources */,
|
||||
64F43D7390DA2A0AFD6BA911 /* VideoRoomTimelineView.swift in Sources */,
|
||||
6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */,
|
||||
|
@ -90,6 +90,28 @@ class NavigationSplitCoordinator: CoordinatorProtocol, ObservableObject, CustomS
|
||||
sheetModule?.coordinator
|
||||
}
|
||||
|
||||
@Published fileprivate var fullScreenCoverModule: NavigationModule? {
|
||||
didSet {
|
||||
if let oldValue {
|
||||
logPresentationChange("Remove fullscreen cover", oldValue)
|
||||
oldValue.coordinator.stop()
|
||||
oldValue.dismissalCallback?()
|
||||
}
|
||||
|
||||
if let fullScreenCoverModule {
|
||||
logPresentationChange("Set fullscreen cover", fullScreenCoverModule)
|
||||
fullScreenCoverModule.coordinator.start()
|
||||
}
|
||||
|
||||
updateCompactLayoutComponents()
|
||||
}
|
||||
}
|
||||
|
||||
/// The currently displayed fullscreen cover coordinator
|
||||
var fullScreenCoverCoordinator: (any CoordinatorProtocol)? {
|
||||
fullScreenCoverModule?.coordinator
|
||||
}
|
||||
|
||||
@Published fileprivate var compactLayoutRootModule: NavigationModule?
|
||||
|
||||
var compactLayoutRootCoordinator: (any CoordinatorProtocol)? {
|
||||
@ -149,6 +171,19 @@ class NavigationSplitCoordinator: CoordinatorProtocol, ObservableObject, CustomS
|
||||
sheetModule = NavigationModule(coordinator, dismissalCallback: dismissalCallback)
|
||||
}
|
||||
|
||||
/// Present a fullscreen cover on top of the split view
|
||||
/// - Parameters:
|
||||
/// - coordinator: the coordinator to display
|
||||
/// - dismissalCallback: called when the fullscreen cover has been dismissed, programatically or otherwise
|
||||
func setFullScreenCoverCoordinator(_ coordinator: (any CoordinatorProtocol)?, dismissalCallback: (() -> Void)? = nil) {
|
||||
guard let coordinator else {
|
||||
fullScreenCoverModule = nil
|
||||
return
|
||||
}
|
||||
|
||||
fullScreenCoverModule = NavigationModule(coordinator, dismissalCallback: dismissalCallback)
|
||||
}
|
||||
|
||||
// MARK: - CoordinatorProtocol
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
@ -301,6 +336,11 @@ private struct NavigationSplitCoordinatorView: View {
|
||||
// through the NavigationSplitCoordinator as well.
|
||||
.sheet(item: $navigationSplitCoordinator.sheetModule) { module in
|
||||
module.coordinator.toPresentable()
|
||||
.tint(.element.accent)
|
||||
}
|
||||
.fullScreenCover(item: $navigationSplitCoordinator.fullScreenCoverModule) { module in
|
||||
module.coordinator.toPresentable()
|
||||
.tint(.element.accent)
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,6 +429,31 @@ class NavigationStackCoordinator: ObservableObject, CoordinatorProtocol, CustomS
|
||||
return sheetModule?.coordinator
|
||||
}
|
||||
|
||||
@Published fileprivate var fullScreenCoverModule: NavigationModule? {
|
||||
didSet {
|
||||
if let oldValue {
|
||||
logPresentationChange("Remove fullscreen cover", oldValue)
|
||||
oldValue.coordinator.stop()
|
||||
oldValue.dismissalCallback?()
|
||||
}
|
||||
|
||||
if let fullScreenCoverModule {
|
||||
logPresentationChange("Set fullscreen cover", fullScreenCoverModule)
|
||||
fullScreenCoverModule.coordinator.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The currently presented fullscreen cover coordinator
|
||||
// Fullscreen covers will be presented through the NavigationSplitCoordinator if provided
|
||||
var fullScreenCoverCoordinator: (any CoordinatorProtocol)? {
|
||||
if let navigationSplitCoordinator {
|
||||
return navigationSplitCoordinator.fullScreenCoverCoordinator
|
||||
}
|
||||
|
||||
return fullScreenCoverModule?.coordinator
|
||||
}
|
||||
|
||||
@Published fileprivate var stackModules = [NavigationModule]() {
|
||||
didSet {
|
||||
let diffs = stackModules.difference(from: oldValue)
|
||||
@ -488,6 +553,25 @@ class NavigationStackCoordinator: ObservableObject, CoordinatorProtocol, CustomS
|
||||
sheetModule = NavigationModule(coordinator, dismissalCallback: dismissalCallback)
|
||||
}
|
||||
|
||||
/// Present a fullscreen cover on top of the stack. If this NavigationStackCoordinator is embedded within a NavigationSplitCoordinator
|
||||
/// then the presentation will be proxied to the split
|
||||
/// - Parameters:
|
||||
/// - coordinator: the coordinator to display
|
||||
/// - dismissalCallback: called when the fullscreen cover has been dismissed, programatically or otherwise
|
||||
func setFullScreenCoverCoordinator(_ coordinator: (any CoordinatorProtocol)?, dismissalCallback: (() -> Void)? = nil) {
|
||||
if let navigationSplitCoordinator {
|
||||
navigationSplitCoordinator.setFullScreenCoverCoordinator(coordinator, dismissalCallback: dismissalCallback)
|
||||
return
|
||||
}
|
||||
|
||||
guard let coordinator else {
|
||||
fullScreenCoverModule = nil
|
||||
return
|
||||
}
|
||||
|
||||
fullScreenCoverModule = NavigationModule(coordinator, dismissalCallback: dismissalCallback)
|
||||
}
|
||||
|
||||
// MARK: - CoordinatorProtocol
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
@ -524,6 +608,11 @@ private struct NavigationStackCoordinatorView: View {
|
||||
}
|
||||
.sheet(item: $navigationStackCoordinator.sheetModule) { module in
|
||||
module.coordinator.toPresentable()
|
||||
.tint(.element.accent)
|
||||
}
|
||||
.fullScreenCover(item: $navigationStackCoordinator.fullScreenCoverModule) { module in
|
||||
module.coordinator.toPresentable()
|
||||
.tint(.element.accent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,10 +64,7 @@ extension LoginHomeserver {
|
||||
|
||||
/// A mock homeserver that supports only supports authentication via a single SSO provider.
|
||||
static var mockOIDC: LoginHomeserver {
|
||||
guard let issuerURL = URL(string: "https://auth.company.com") else {
|
||||
fatalError("This shoud never fail parsing")
|
||||
}
|
||||
|
||||
let issuerURL = URL(staticString: "https://auth.company.com")
|
||||
return LoginHomeserver(address: "company.com", loginMode: .oidc(issuerURL))
|
||||
}
|
||||
|
||||
|
@ -22,13 +22,15 @@ struct FilePreviewScreen: View {
|
||||
@ObservedObject var context: FilePreviewViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
PreviewController(fileURL: context.viewState.fileURL, title: context.viewState.title)
|
||||
.ignoresSafeArea(.all, edges: [.horizontal, .bottom])
|
||||
.navigationTitle(ElementL10n.attachmentTypeFile)
|
||||
PreviewView(context: context,
|
||||
fileURL: context.viewState.fileURL,
|
||||
title: context.viewState.title)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
|
||||
private struct PreviewController: UIViewControllerRepresentable {
|
||||
private struct PreviewView: UIViewControllerRepresentable {
|
||||
let context: FilePreviewViewModel.Context
|
||||
let fileURL: URL
|
||||
let title: String?
|
||||
|
||||
@ -36,28 +38,40 @@ private struct PreviewController: UIViewControllerRepresentable {
|
||||
let controller = QLPreviewController()
|
||||
controller.dataSource = context.coordinator
|
||||
|
||||
let doneButton = UIBarButtonItem(title: "Done",
|
||||
style: .done,
|
||||
target: context.coordinator,
|
||||
action: #selector(Coordinator.done))
|
||||
controller.navigationItem.rightBarButtonItem = doneButton
|
||||
|
||||
return UINavigationController(rootViewController: controller)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) { }
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(parent: self)
|
||||
Coordinator(view: self)
|
||||
}
|
||||
|
||||
class Coordinator: QLPreviewControllerDataSource {
|
||||
let parent: PreviewController
|
||||
class Coordinator: NSObject, QLPreviewControllerDataSource {
|
||||
let view: PreviewView
|
||||
|
||||
init(parent: PreviewController) {
|
||||
self.parent = parent
|
||||
init(view: PreviewView) {
|
||||
self.view = view
|
||||
}
|
||||
|
||||
@objc func done() {
|
||||
Task { await view.context.send(viewAction: .cancel) }
|
||||
}
|
||||
|
||||
// MARK: - QLPreviewControllerDataSource
|
||||
|
||||
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
|
||||
1
|
||||
}
|
||||
|
||||
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
||||
PreviewItem(previewItemURL: parent.fileURL, previewItemTitle: parent.title)
|
||||
PreviewItem(previewItemURL: view.fileURL, previewItemTitle: view.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,9 +55,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
switch result {
|
||||
case .displayRoomDetails:
|
||||
self.displayRoomDetails()
|
||||
case .displayVideo(let videoURL):
|
||||
self.displayVideo(for: videoURL)
|
||||
case .displayFile(let fileURL, let title):
|
||||
case .displayVideo(let fileURL, let title), .displayFile(let fileURL, let title):
|
||||
self.displayFile(for: fileURL, with: title)
|
||||
case .displayEmojiPicker(let itemId):
|
||||
self.displayEmojiPickerScreen(for: itemId)
|
||||
@ -81,25 +79,14 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func displayVideo(for videoURL: URL) {
|
||||
let params = VideoPlayerCoordinatorParameters(videoURL: videoURL)
|
||||
let coordinator = VideoPlayerCoordinator(parameters: params)
|
||||
|
||||
coordinator.callback = { [weak self] _ in
|
||||
self?.navigationStackCoordinator.pop()
|
||||
}
|
||||
|
||||
navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
|
||||
private func displayFile(for fileURL: URL, with title: String?) {
|
||||
let params = FilePreviewCoordinatorParameters(fileURL: fileURL, title: title)
|
||||
let coordinator = FilePreviewCoordinator(parameters: params)
|
||||
coordinator.callback = { [weak self] _ in
|
||||
self?.navigationStackCoordinator.pop()
|
||||
self?.navigationStackCoordinator.setFullScreenCoverCoordinator(nil)
|
||||
}
|
||||
|
||||
navigationStackCoordinator.push(coordinator)
|
||||
navigationStackCoordinator.setFullScreenCoverCoordinator(coordinator)
|
||||
}
|
||||
|
||||
private func displayEmojiPickerScreen(for itemId: String) {
|
||||
|
@ -19,7 +19,7 @@ import UIKit
|
||||
|
||||
enum RoomScreenViewModelAction {
|
||||
case displayRoomDetails
|
||||
case displayVideo(videoURL: URL)
|
||||
case displayVideo(videoURL: URL, title: String?)
|
||||
case displayFile(fileURL: URL, title: String?)
|
||||
case displayEmojiPicker(itemId: String)
|
||||
}
|
||||
|
@ -140,8 +140,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
let action = await timelineController.processItemTap(itemId)
|
||||
|
||||
switch action {
|
||||
case .displayVideo(let videoURL):
|
||||
callback?(.displayVideo(videoURL: videoURL))
|
||||
case .displayVideo(let videoURL, let title):
|
||||
callback?(.displayVideo(videoURL: videoURL, title: title))
|
||||
case .displayFile(let fileURL, let title):
|
||||
callback?(.displayFile(fileURL: fileURL, title: title))
|
||||
case .none:
|
||||
|
@ -1,83 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import AVKit
|
||||
import SwiftUI
|
||||
|
||||
struct VideoPlayerCoordinatorParameters {
|
||||
let videoURL: URL
|
||||
}
|
||||
|
||||
enum VideoPlayerCoordinatorAction {
|
||||
case cancel
|
||||
}
|
||||
|
||||
final class VideoPlayerCoordinator: CoordinatorProtocol {
|
||||
private let parameters: VideoPlayerCoordinatorParameters
|
||||
private var viewModel: VideoPlayerViewModelProtocol
|
||||
|
||||
var callback: ((VideoPlayerCoordinatorAction) -> Void)?
|
||||
|
||||
init(parameters: VideoPlayerCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = VideoPlayerViewModel(videoURL: parameters.videoURL,
|
||||
autoplay: UIApplication.shared.applicationState == .active)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
configureAudioSession(.sharedInstance())
|
||||
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
MXLog.debug("VideoPlayerViewModel did complete with result: \(action).")
|
||||
switch action {
|
||||
case .cancel:
|
||||
self.callback?(.cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
deconfigureAudioSession(.sharedInstance())
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(VideoPlayerScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func configureAudioSession(_ session: AVAudioSession) {
|
||||
do {
|
||||
try session.setCategory(.playback,
|
||||
mode: .default)
|
||||
try session.setActive(true)
|
||||
} catch {
|
||||
MXLog.debug("Configure audio session failed: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func deconfigureAudioSession(_ session: AVAudioSession) {
|
||||
do {
|
||||
try session.setActive(false, options: .notifyOthersOnDeactivation)
|
||||
} catch {
|
||||
MXLog.debug("Deconfigure audio session failed: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum VideoPlayerViewModelAction {
|
||||
case cancel
|
||||
}
|
||||
|
||||
struct VideoPlayerViewState: BindableState {
|
||||
let videoURL: URL
|
||||
let autoplay: Bool
|
||||
}
|
||||
|
||||
enum VideoPlayerViewAction {
|
||||
case cancel
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
typealias VideoPlayerViewModelType = StateStoreViewModel<VideoPlayerViewState, VideoPlayerViewAction>
|
||||
|
||||
class VideoPlayerViewModel: VideoPlayerViewModelType, VideoPlayerViewModelProtocol {
|
||||
var callback: ((VideoPlayerViewModelAction) -> Void)?
|
||||
|
||||
init(videoURL: URL, autoplay: Bool = true) {
|
||||
super.init(initialViewState: VideoPlayerViewState(videoURL: videoURL,
|
||||
autoplay: autoplay))
|
||||
}
|
||||
|
||||
override func process(viewAction: VideoPlayerViewAction) async {
|
||||
switch viewAction {
|
||||
case .cancel:
|
||||
callback?(.cancel)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
protocol VideoPlayerViewModelProtocol {
|
||||
var callback: ((VideoPlayerViewModelAction) -> Void)? { get set }
|
||||
var context: VideoPlayerViewModelType.Context { get }
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import AVKit
|
||||
import SwiftUI
|
||||
|
||||
struct VideoPlayerScreen: View {
|
||||
@ObservedObject var context: VideoPlayerViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
VideoPlayer(player: player())
|
||||
.ignoresSafeArea(.all, edges: [.horizontal, .bottom])
|
||||
.navigationTitle(ElementL10n.a11yVideo)
|
||||
}
|
||||
|
||||
private func player() -> AVPlayer {
|
||||
let player = AVPlayer(url: context.viewState.videoURL)
|
||||
if context.viewState.autoplay {
|
||||
player.play()
|
||||
}
|
||||
return player
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct VideoPlayer_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
let viewModel = VideoPlayerViewModel(videoURL: URL(staticString: "https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"),
|
||||
autoplay: false)
|
||||
VideoPlayerScreen(context: viewModel.context)
|
||||
}
|
||||
.tint(.element.accent)
|
||||
}
|
||||
}
|
@ -127,7 +127,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
return .none
|
||||
}
|
||||
if let videoURL = item.cachedVideoURL {
|
||||
return .displayVideo(videoURL: videoURL)
|
||||
return .displayVideo(videoURL: videoURL, title: item.text)
|
||||
}
|
||||
return .none
|
||||
case let item as FileRoomTimelineItem:
|
||||
|
@ -26,7 +26,7 @@ enum RoomTimelineControllerCallback {
|
||||
}
|
||||
|
||||
enum RoomTimelineControllerAction {
|
||||
case displayVideo(videoURL: URL)
|
||||
case displayVideo(videoURL: URL, title: String?)
|
||||
case displayFile(fileURL: URL, title: String?)
|
||||
case none
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ final class MediaProviderTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_whenFileFromSourceWithSource_correctValuesAreReturned() throws {
|
||||
let expectedURL = try XCTUnwrap(URL(string: "some_url"))
|
||||
let expectedURL = URL(filePath: "/some/file/path")
|
||||
fileCache.fileURLToReturn = expectedURL
|
||||
let url = mediaProvider.fileFromSource(MediaSourceProxy(urlString: "test/test1"), fileExtension: "png")
|
||||
XCTAssertEqual(fileCache.fileKey, "test1")
|
||||
@ -177,7 +177,7 @@ final class MediaProviderTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_whenLoadFileFromSourceAndFileFromSourceExists_urlIsReturned() async throws {
|
||||
let expectedURL = try XCTUnwrap(URL(string: "some_url"))
|
||||
let expectedURL = URL(filePath: "/some/file/path")
|
||||
let expectedResult: Result<URL, MediaProviderError> = .success(expectedURL)
|
||||
fileCache.fileURLToReturn = expectedURL
|
||||
let result = await mediaProvider.loadFileFromSource(MediaSourceProxy(urlString: "test/test1"), fileExtension: "png")
|
||||
@ -185,7 +185,7 @@ final class MediaProviderTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_whenLoadFileFromSourceAndNoFileFromSourceExists_mediaLoadedFromSource() async throws {
|
||||
let expectedURL = try XCTUnwrap(URL(string: "some_url"))
|
||||
let expectedURL = URL(filePath: "/some/file/path")
|
||||
let expectedResult: Result<URL, MediaProviderError> = .success(expectedURL)
|
||||
mediaProxy.mediaContentData = try loadTestImage().pngData()
|
||||
fileCache.storeURLToReturn = expectedURL
|
||||
@ -217,14 +217,14 @@ final class MediaProviderTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_whenFileFromURLString_correctURLIsReturned() throws {
|
||||
let expectedURL = try XCTUnwrap(URL(string: "some_url"))
|
||||
let expectedURL = URL(filePath: "/some/file/path")
|
||||
fileCache.fileURLToReturn = expectedURL
|
||||
let url = mediaProvider.fileFromURLString("test/test1", fileExtension: "png")
|
||||
XCTAssertEqual(url?.absoluteString, expectedURL.absoluteString)
|
||||
}
|
||||
|
||||
func test_whenLoadFileFromURLString_correctURLIsReturned() async throws {
|
||||
let expectedURL = try XCTUnwrap(URL(string: "some_url"))
|
||||
let expectedURL = URL(filePath: "/some/file/path")
|
||||
let expectedResult: Result<URL, MediaProviderError> = .success(expectedURL)
|
||||
fileCache.fileURLToReturn = expectedURL
|
||||
let result = await mediaProvider.loadFileFromURLString("test/test1", fileExtension: "png")
|
||||
|
@ -1,44 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable import ElementX
|
||||
|
||||
@MainActor
|
||||
class VideoPlayerScreenViewModelTests: XCTestCase {
|
||||
var viewModel: VideoPlayerViewModelProtocol!
|
||||
var context: VideoPlayerViewModelType.Context!
|
||||
|
||||
@MainActor override func setUpWithError() throws {
|
||||
viewModel = VideoPlayerViewModel(videoURL: URL(staticString: "https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"), autoplay: true)
|
||||
context = viewModel.context
|
||||
}
|
||||
|
||||
@MainActor func testCancel() async throws {
|
||||
var correctResult = false
|
||||
viewModel.callback = { result in
|
||||
switch result {
|
||||
case .cancel:
|
||||
correctResult = true
|
||||
}
|
||||
}
|
||||
|
||||
context.send(viewAction: .cancel)
|
||||
await Task.yield()
|
||||
XCTAssert(correctResult)
|
||||
}
|
||||
}
|
1
changelog.d/418.change
Normal file
1
changelog.d/418.change
Normal file
@ -0,0 +1 @@
|
||||
Use QuickLook previews for video and present previews full screen (doesn't address gestures yet).
|
Loading…
x
Reference in New Issue
Block a user