mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-06 04:18:31 +00:00
Prevent multiple conversion of the same voice message audio file. (#1887)
This commit is contained in:
@@ -106,6 +106,7 @@
|
|||||||
208C19811613F9A10F8A7B75 /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */; };
|
208C19811613F9A10F8A7B75 /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */; };
|
||||||
20C16A3F718802B0E4A19C83 /* URLComponentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76310030C831D4610A705603 /* URLComponentsTests.swift */; };
|
20C16A3F718802B0E4A19C83 /* URLComponentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76310030C831D4610A705603 /* URLComponentsTests.swift */; };
|
||||||
2185C1F6724C78FFF355D6FA /* WelcomeScreenScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB10FA6570DD08B3966C159 /* WelcomeScreenScreenUITests.swift */; };
|
2185C1F6724C78FFF355D6FA /* WelcomeScreenScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB10FA6570DD08B3966C159 /* WelcomeScreenScreenUITests.swift */; };
|
||||||
|
21AFEFB8CEFE56A3811A1F5B /* VoiceMessageCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 283974987DA7EC61D2AB57D9 /* VoiceMessageCacheTests.swift */; };
|
||||||
21BF2B7CEDFE3CA67C5355AD /* test_image.png in Resources */ = {isa = PBXBuildFile; fileRef = C733D11B421CFE3A657EF230 /* test_image.png */; };
|
21BF2B7CEDFE3CA67C5355AD /* test_image.png in Resources */ = {isa = PBXBuildFile; fileRef = C733D11B421CFE3A657EF230 /* test_image.png */; };
|
||||||
22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; };
|
22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; };
|
||||||
234E2C782981003971ABE96E /* PermalinkBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */; };
|
234E2C782981003971ABE96E /* PermalinkBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */; };
|
||||||
@@ -221,6 +222,7 @@
|
|||||||
43F35A7E5703D64DB0519C59 /* ServerSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD469F7513574341181F7EAA /* ServerSelectionScreen.swift */; };
|
43F35A7E5703D64DB0519C59 /* ServerSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD469F7513574341181F7EAA /* ServerSelectionScreen.swift */; };
|
||||||
440123E29E2F9B001A775BBE /* TimelineItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */; };
|
440123E29E2F9B001A775BBE /* TimelineItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */; };
|
||||||
44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */; };
|
44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */; };
|
||||||
|
44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */; };
|
||||||
44F0E1B576C7599DF8022071 /* SwiftOGG in Frameworks */ = {isa = PBXBuildFile; productRef = 391D11F92DFC91666AA1503F /* SwiftOGG */; };
|
44F0E1B576C7599DF8022071 /* SwiftOGG in Frameworks */ = {isa = PBXBuildFile; productRef = 391D11F92DFC91666AA1503F /* SwiftOGG */; };
|
||||||
4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E492690C8B27A892C194CC4 /* AdvancedSettingsScreenCoordinator.swift */; };
|
4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E492690C8B27A892C194CC4 /* AdvancedSettingsScreenCoordinator.swift */; };
|
||||||
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; };
|
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; };
|
||||||
@@ -251,6 +253,7 @@
|
|||||||
4E945AD6862C403F74E57755 /* RoomTimelineItemFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */; };
|
4E945AD6862C403F74E57755 /* RoomTimelineItemFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */; };
|
||||||
4EA1CE0E88EA68E862FF0EA2 /* NotificationSettingsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */; };
|
4EA1CE0E88EA68E862FF0EA2 /* NotificationSettingsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */; };
|
||||||
4EB1B717C1EFE3A7ABFBC0A8 /* CreatePollScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A22A3A4109959414EBC6113 /* CreatePollScreenViewModelProtocol.swift */; };
|
4EB1B717C1EFE3A7ABFBC0A8 /* CreatePollScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A22A3A4109959414EBC6113 /* CreatePollScreenViewModelProtocol.swift */; };
|
||||||
|
4F2DF6138E87A4B8C2488CA3 /* VoiceMessageCacheProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A84EE187D0C772E18A4E39 /* VoiceMessageCacheProtocol.swift */; };
|
||||||
4FC085B1E5D1EB804495E2F4 /* MockMediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD6E621CC5E6D4830D96D2D /* MockMediaProvider.swift */; };
|
4FC085B1E5D1EB804495E2F4 /* MockMediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD6E621CC5E6D4830D96D2D /* MockMediaProvider.swift */; };
|
||||||
4FC1EFE4968A259CBBACFAFB /* RoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A65F140F9FE5E8D4DAEFF354 /* RoomProxy.swift */; };
|
4FC1EFE4968A259CBBACFAFB /* RoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A65F140F9FE5E8D4DAEFF354 /* RoomProxy.swift */; };
|
||||||
4FDC8A9764CFDA90CE035725 /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB2253D36E81E045E1CB432 /* Duration.swift */; };
|
4FDC8A9764CFDA90CE035725 /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB2253D36E81E045E1CB432 /* Duration.swift */; };
|
||||||
@@ -429,6 +432,7 @@
|
|||||||
829062DD3C3F7016FE1A6476 /* RoomDetailsScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BFDAF6918BB096C44788FC9 /* RoomDetailsScreenUITests.swift */; };
|
829062DD3C3F7016FE1A6476 /* RoomDetailsScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BFDAF6918BB096C44788FC9 /* RoomDetailsScreenUITests.swift */; };
|
||||||
8317E1314C00DCCC99D30DA8 /* TextBasedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9227F7495DA43324050A863 /* TextBasedRoomTimelineItem.swift */; };
|
8317E1314C00DCCC99D30DA8 /* TextBasedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9227F7495DA43324050A863 /* TextBasedRoomTimelineItem.swift */; };
|
||||||
83A4DAB181C56987C3E804FF /* MapTilerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B9F5BC4C80543DE7228B9D /* MapTilerStyle.swift */; };
|
83A4DAB181C56987C3E804FF /* MapTilerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B9F5BC4C80543DE7228B9D /* MapTilerStyle.swift */; };
|
||||||
|
8418688282763F4B9DDC42FB /* AudioConverterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB4D7B3FBCA261927536FE64 /* AudioConverterProtocol.swift */; };
|
||||||
84226AD2E1F1FBC965F3B09E /* UnitTestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A8E19C4645D3F5F9FB02355 /* UnitTestsAppCoordinator.swift */; };
|
84226AD2E1F1FBC965F3B09E /* UnitTestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A8E19C4645D3F5F9FB02355 /* UnitTestsAppCoordinator.swift */; };
|
||||||
84EFCB95F9DA2979C8042B26 /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */; };
|
84EFCB95F9DA2979C8042B26 /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */; };
|
||||||
8544657DEEE717ED2E22E382 /* RoomNotificationSettingsProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D1BAA90F3A073D91B4F16B /* RoomNotificationSettingsProxyMock.swift */; };
|
8544657DEEE717ED2E22E382 /* RoomNotificationSettingsProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D1BAA90F3A073D91B4F16B /* RoomNotificationSettingsProxyMock.swift */; };
|
||||||
@@ -1055,6 +1059,7 @@
|
|||||||
27B8315A340B46F98B9C5AF0 /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
|
27B8315A340B46F98B9C5AF0 /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
|
||||||
27D0EA07BD545CC9F234DB8D /* UserDetailsEditScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDetailsEditScreenModels.swift; sourceTree = "<group>"; };
|
27D0EA07BD545CC9F234DB8D /* UserDetailsEditScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDetailsEditScreenModels.swift; sourceTree = "<group>"; };
|
||||||
27EA0F71A3A400A202E15318 /* CreatePollScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatePollScreen.swift; sourceTree = "<group>"; };
|
27EA0F71A3A400A202E15318 /* CreatePollScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatePollScreen.swift; sourceTree = "<group>"; };
|
||||||
|
283974987DA7EC61D2AB57D9 /* VoiceMessageCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageCacheTests.swift; sourceTree = "<group>"; };
|
||||||
287FC98AF2664EAD79C0D902 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
|
287FC98AF2664EAD79C0D902 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
|
||||||
28C19F54A0C4FC9AB7ABD583 /* TextRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItemContent.swift; sourceTree = "<group>"; };
|
28C19F54A0C4FC9AB7ABD583 /* TextRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItemContent.swift; sourceTree = "<group>"; };
|
||||||
2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilder.swift; sourceTree = "<group>"; };
|
2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilder.swift; sourceTree = "<group>"; };
|
||||||
@@ -1134,6 +1139,7 @@
|
|||||||
42ADEA322D2089391E049535 /* InvitesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreen.swift; sourceTree = "<group>"; };
|
42ADEA322D2089391E049535 /* InvitesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreen.swift; sourceTree = "<group>"; };
|
||||||
42C64A14EE89928207E3B42B /* AnalyticsSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenModels.swift; sourceTree = "<group>"; };
|
42C64A14EE89928207E3B42B /* AnalyticsSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenModels.swift; sourceTree = "<group>"; };
|
||||||
42EEA67A6796BDC2761619C5 /* PaginationIndicatorRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIndicatorRoomTimelineView.swift; sourceTree = "<group>"; };
|
42EEA67A6796BDC2761619C5 /* PaginationIndicatorRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIndicatorRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
|
43A84EE187D0C772E18A4E39 /* VoiceMessageCacheProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageCacheProtocol.swift; sourceTree = "<group>"; };
|
||||||
4481799F455B3DA243BDA2AC /* ShareToMapsAppActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareToMapsAppActivity.swift; sourceTree = "<group>"; };
|
4481799F455B3DA243BDA2AC /* ShareToMapsAppActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareToMapsAppActivity.swift; sourceTree = "<group>"; };
|
||||||
450E04B2A976CC4C8CC1807C /* EmoteRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItem.swift; sourceTree = "<group>"; };
|
450E04B2A976CC4C8CC1807C /* EmoteRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
4549FCB53F43DB0B278374BC /* TemplateScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreen.swift; sourceTree = "<group>"; };
|
4549FCB53F43DB0B278374BC /* TemplateScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreen.swift; sourceTree = "<group>"; };
|
||||||
@@ -1440,6 +1446,7 @@
|
|||||||
ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockImageCache.swift; sourceTree = "<group>"; };
|
AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockImageCache.swift; sourceTree = "<group>"; };
|
||||||
AC3F82523D6F48B926D6AF68 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
AC3F82523D6F48B926D6AF68 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
||||||
|
AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageMediaManagerTests.swift; sourceTree = "<group>"; };
|
||||||
AC5F5209279A752D98AAC4B2 /* CollapsibleFlowLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleFlowLayoutTests.swift; sourceTree = "<group>"; };
|
AC5F5209279A752D98AAC4B2 /* CollapsibleFlowLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleFlowLayoutTests.swift; sourceTree = "<group>"; };
|
||||||
ACCC1874C122E2BBE648B8F5 /* LegalInformationScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenUITests.swift; sourceTree = "<group>"; };
|
ACCC1874C122E2BBE648B8F5 /* LegalInformationScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenUITests.swift; sourceTree = "<group>"; };
|
||||||
AD378D580A41E42560C60E9C /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
AD378D580A41E42560C60E9C /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
@@ -1702,6 +1709,7 @@
|
|||||||
FA7BB497B2F539C17E88F6B7 /* NotificationSettingsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
FA7BB497B2F539C17E88F6B7 /* NotificationSettingsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
FA88C615D8BFCCEF0D2FEAC9 /* AudioPlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerState.swift; sourceTree = "<group>"; };
|
FA88C615D8BFCCEF0D2FEAC9 /* AudioPlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerState.swift; sourceTree = "<group>"; };
|
||||||
FB0D6CB491777E7FC6B5BA12 /* CreateRoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreen.swift; sourceTree = "<group>"; };
|
FB0D6CB491777E7FC6B5BA12 /* CreateRoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreen.swift; sourceTree = "<group>"; };
|
||||||
|
FB4D7B3FBCA261927536FE64 /* AudioConverterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioConverterProtocol.swift; sourceTree = "<group>"; };
|
||||||
FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenViewModel.swift; sourceTree = "<group>"; };
|
FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
FBB0328F2887BF0A65BC5D49 /* NotificationSettingsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreen.swift; sourceTree = "<group>"; };
|
FBB0328F2887BF0A65BC5D49 /* NotificationSettingsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreen.swift; sourceTree = "<group>"; };
|
||||||
FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorProtocol.swift; sourceTree = "<group>"; };
|
FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorProtocol.swift; sourceTree = "<group>"; };
|
||||||
@@ -1821,6 +1829,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */,
|
DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */,
|
||||||
|
43A84EE187D0C772E18A4E39 /* VoiceMessageCacheProtocol.swift */,
|
||||||
40076C770A5FB83325252973 /* VoiceMessageMediaManager.swift */,
|
40076C770A5FB83325252973 /* VoiceMessageMediaManager.swift */,
|
||||||
889DEDD63C68ABDA8AD29812 /* VoiceMessageMediaManagerProtocol.swift */,
|
889DEDD63C68ABDA8AD29812 /* VoiceMessageMediaManagerProtocol.swift */,
|
||||||
);
|
);
|
||||||
@@ -2870,6 +2879,8 @@
|
|||||||
EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */,
|
EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */,
|
||||||
2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */,
|
2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */,
|
||||||
BA241DEEF7C8A7181C0AEDC9 /* UserPreferenceTests.swift */,
|
BA241DEEF7C8A7181C0AEDC9 /* UserPreferenceTests.swift */,
|
||||||
|
283974987DA7EC61D2AB57D9 /* VoiceMessageCacheTests.swift */,
|
||||||
|
AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */,
|
||||||
C796FC1DFDBCDD5573D0360F /* WaitlistScreenViewModelTests.swift */,
|
C796FC1DFDBCDD5573D0360F /* WaitlistScreenViewModelTests.swift */,
|
||||||
851EF6258DF8B7EF129DC3AC /* WelcomeScreenScreenViewModelTests.swift */,
|
851EF6258DF8B7EF129DC3AC /* WelcomeScreenScreenViewModelTests.swift */,
|
||||||
53280D2292E6C9C7821773FD /* UserSession */,
|
53280D2292E6C9C7821773FD /* UserSession */,
|
||||||
@@ -3328,6 +3339,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
CE1CD5EC6265A09315772DB7 /* AudioConverter.swift */,
|
CE1CD5EC6265A09315772DB7 /* AudioConverter.swift */,
|
||||||
|
FB4D7B3FBCA261927536FE64 /* AudioConverterProtocol.swift */,
|
||||||
2EDDE503C9BAC82B661E4164 /* AudioPlayer.swift */,
|
2EDDE503C9BAC82B661E4164 /* AudioPlayer.swift */,
|
||||||
048A3590E8379BCED4D30D5C /* AudioPlayerProtocol.swift */,
|
048A3590E8379BCED4D30D5C /* AudioPlayerProtocol.swift */,
|
||||||
FA88C615D8BFCCEF0D2FEAC9 /* AudioPlayerState.swift */,
|
FA88C615D8BFCCEF0D2FEAC9 /* AudioPlayerState.swift */,
|
||||||
@@ -4695,6 +4707,8 @@
|
|||||||
A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */,
|
A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */,
|
||||||
04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */,
|
04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */,
|
||||||
81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */,
|
81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */,
|
||||||
|
21AFEFB8CEFE56A3811A1F5B /* VoiceMessageCacheTests.swift in Sources */,
|
||||||
|
44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */,
|
||||||
FB9A1DD83EF641A75ABBCE69 /* WaitlistScreenViewModelTests.swift in Sources */,
|
FB9A1DD83EF641A75ABBCE69 /* WaitlistScreenViewModelTests.swift in Sources */,
|
||||||
7F02063FB3D1C3E5601471A1 /* WelcomeScreenScreenViewModelTests.swift in Sources */,
|
7F02063FB3D1C3E5601471A1 /* WelcomeScreenScreenViewModelTests.swift in Sources */,
|
||||||
3116693C5EB476E028990416 /* XCTestCase.swift in Sources */,
|
3116693C5EB476E028990416 /* XCTestCase.swift in Sources */,
|
||||||
@@ -4764,6 +4778,7 @@
|
|||||||
3ED2725734568F6B8CC87544 /* AttributedStringBuilder.swift in Sources */,
|
3ED2725734568F6B8CC87544 /* AttributedStringBuilder.swift in Sources */,
|
||||||
A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */,
|
A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */,
|
||||||
5F8E96263497FFB7D3254EB2 /* AudioConverter.swift in Sources */,
|
5F8E96263497FFB7D3254EB2 /* AudioConverter.swift in Sources */,
|
||||||
|
8418688282763F4B9DDC42FB /* AudioConverterProtocol.swift in Sources */,
|
||||||
CD1C6943F42F29079E5E7511 /* AudioPlayer.swift in Sources */,
|
CD1C6943F42F29079E5E7511 /* AudioPlayer.swift in Sources */,
|
||||||
F0B196905CD23E3B4505CB7B /* AudioPlayerProtocol.swift in Sources */,
|
F0B196905CD23E3B4505CB7B /* AudioPlayerProtocol.swift in Sources */,
|
||||||
C19085A284D54A166A64A86C /* AudioPlayerState.swift in Sources */,
|
C19085A284D54A166A64A86C /* AudioPlayerState.swift in Sources */,
|
||||||
@@ -5286,6 +5301,7 @@
|
|||||||
64F43D7390DA2A0AFD6BA911 /* VideoRoomTimelineView.swift in Sources */,
|
64F43D7390DA2A0AFD6BA911 /* VideoRoomTimelineView.swift in Sources */,
|
||||||
6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */,
|
6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */,
|
||||||
4681820102DAC8BA586357D4 /* VoiceMessageCache.swift in Sources */,
|
4681820102DAC8BA586357D4 /* VoiceMessageCache.swift in Sources */,
|
||||||
|
4F2DF6138E87A4B8C2488CA3 /* VoiceMessageCacheProtocol.swift in Sources */,
|
||||||
386720B603F87D156DB01FB2 /* VoiceMessageMediaManager.swift in Sources */,
|
386720B603F87D156DB01FB2 /* VoiceMessageMediaManager.swift in Sources */,
|
||||||
9DE801D278AC34737467F937 /* VoiceMessageMediaManagerProtocol.swift in Sources */,
|
9DE801D278AC34737467F937 /* VoiceMessageMediaManagerProtocol.swift in Sources */,
|
||||||
A9482B967FC85DA611514D35 /* VoiceMessageRoomPlaybackView.swift in Sources */,
|
A9482B967FC85DA611514D35 /* VoiceMessageRoomPlaybackView.swift in Sources */,
|
||||||
|
|||||||
@@ -329,13 +329,11 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID),
|
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID),
|
||||||
appSettings: appSettings)
|
appSettings: appSettings)
|
||||||
|
|
||||||
let voiceMesssageMediaManager = VoiceMessageMediaManager(mediaProvider: userSession.mediaProvider)
|
|
||||||
|
|
||||||
let timelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy,
|
let timelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy,
|
||||||
timelineItemFactory: timelineItemFactory,
|
timelineItemFactory: timelineItemFactory,
|
||||||
mediaProvider: userSession.mediaProvider,
|
mediaProvider: userSession.mediaProvider,
|
||||||
mediaPlayerProvider: mediaPlayerProvider,
|
mediaPlayerProvider: mediaPlayerProvider,
|
||||||
voiceMessageMediaManager: voiceMesssageMediaManager)
|
voiceMessageMediaManager: userSession.voiceMessageMediaManager)
|
||||||
self.timelineController = timelineController
|
self.timelineController = timelineController
|
||||||
|
|
||||||
analytics.trackViewRoom(isDM: roomProxy.isDirect, isSpace: roomProxy.isSpace)
|
analytics.trackViewRoom(isDM: roomProxy.isDirect, isSpace: roomProxy.isSpace)
|
||||||
|
|||||||
@@ -115,6 +115,49 @@ class AnalyticsClientMock: AnalyticsClientProtocol {
|
|||||||
updateUserPropertiesClosure?(userProperties)
|
updateUserPropertiesClosure?(userProperties)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class AudioConverterMock: AudioConverterProtocol {
|
||||||
|
|
||||||
|
//MARK: - convertToOpusOgg
|
||||||
|
|
||||||
|
var convertToOpusOggSourceURLDestinationURLThrowableError: Error?
|
||||||
|
var convertToOpusOggSourceURLDestinationURLCallsCount = 0
|
||||||
|
var convertToOpusOggSourceURLDestinationURLCalled: Bool {
|
||||||
|
return convertToOpusOggSourceURLDestinationURLCallsCount > 0
|
||||||
|
}
|
||||||
|
var convertToOpusOggSourceURLDestinationURLReceivedArguments: (sourceURL: URL, destinationURL: URL)?
|
||||||
|
var convertToOpusOggSourceURLDestinationURLReceivedInvocations: [(sourceURL: URL, destinationURL: URL)] = []
|
||||||
|
var convertToOpusOggSourceURLDestinationURLClosure: ((URL, URL) throws -> Void)?
|
||||||
|
|
||||||
|
func convertToOpusOgg(sourceURL: URL, destinationURL: URL) throws {
|
||||||
|
if let error = convertToOpusOggSourceURLDestinationURLThrowableError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
convertToOpusOggSourceURLDestinationURLCallsCount += 1
|
||||||
|
convertToOpusOggSourceURLDestinationURLReceivedArguments = (sourceURL: sourceURL, destinationURL: destinationURL)
|
||||||
|
convertToOpusOggSourceURLDestinationURLReceivedInvocations.append((sourceURL: sourceURL, destinationURL: destinationURL))
|
||||||
|
try convertToOpusOggSourceURLDestinationURLClosure?(sourceURL, destinationURL)
|
||||||
|
}
|
||||||
|
//MARK: - convertToMPEG4AAC
|
||||||
|
|
||||||
|
var convertToMPEG4AACSourceURLDestinationURLThrowableError: Error?
|
||||||
|
var convertToMPEG4AACSourceURLDestinationURLCallsCount = 0
|
||||||
|
var convertToMPEG4AACSourceURLDestinationURLCalled: Bool {
|
||||||
|
return convertToMPEG4AACSourceURLDestinationURLCallsCount > 0
|
||||||
|
}
|
||||||
|
var convertToMPEG4AACSourceURLDestinationURLReceivedArguments: (sourceURL: URL, destinationURL: URL)?
|
||||||
|
var convertToMPEG4AACSourceURLDestinationURLReceivedInvocations: [(sourceURL: URL, destinationURL: URL)] = []
|
||||||
|
var convertToMPEG4AACSourceURLDestinationURLClosure: ((URL, URL) throws -> Void)?
|
||||||
|
|
||||||
|
func convertToMPEG4AAC(sourceURL: URL, destinationURL: URL) throws {
|
||||||
|
if let error = convertToMPEG4AACSourceURLDestinationURLThrowableError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
convertToMPEG4AACSourceURLDestinationURLCallsCount += 1
|
||||||
|
convertToMPEG4AACSourceURLDestinationURLReceivedArguments = (sourceURL: sourceURL, destinationURL: destinationURL)
|
||||||
|
convertToMPEG4AACSourceURLDestinationURLReceivedInvocations.append((sourceURL: sourceURL, destinationURL: destinationURL))
|
||||||
|
try convertToMPEG4AACSourceURLDestinationURLClosure?(sourceURL, destinationURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
class AudioPlayerMock: AudioPlayerProtocol {
|
class AudioPlayerMock: AudioPlayerProtocol {
|
||||||
var actions: AnyPublisher<AudioPlayerAction, Never> {
|
var actions: AnyPublisher<AudioPlayerAction, Never> {
|
||||||
get { return underlyingActions }
|
get { return underlyingActions }
|
||||||
@@ -2191,6 +2234,67 @@ class UserNotificationCenterMock: UserNotificationCenterProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class VoiceMessageCacheMock: VoiceMessageCacheProtocol {
|
||||||
|
|
||||||
|
//MARK: - fileURL
|
||||||
|
|
||||||
|
var fileURLForCallsCount = 0
|
||||||
|
var fileURLForCalled: Bool {
|
||||||
|
return fileURLForCallsCount > 0
|
||||||
|
}
|
||||||
|
var fileURLForReceivedMediaSource: MediaSourceProxy?
|
||||||
|
var fileURLForReceivedInvocations: [MediaSourceProxy] = []
|
||||||
|
var fileURLForReturnValue: URL?
|
||||||
|
var fileURLForClosure: ((MediaSourceProxy) -> URL?)?
|
||||||
|
|
||||||
|
func fileURL(for mediaSource: MediaSourceProxy) -> URL? {
|
||||||
|
fileURLForCallsCount += 1
|
||||||
|
fileURLForReceivedMediaSource = mediaSource
|
||||||
|
fileURLForReceivedInvocations.append(mediaSource)
|
||||||
|
if let fileURLForClosure = fileURLForClosure {
|
||||||
|
return fileURLForClosure(mediaSource)
|
||||||
|
} else {
|
||||||
|
return fileURLForReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - cache
|
||||||
|
|
||||||
|
var cacheMediaSourceUsingMoveThrowableError: Error?
|
||||||
|
var cacheMediaSourceUsingMoveCallsCount = 0
|
||||||
|
var cacheMediaSourceUsingMoveCalled: Bool {
|
||||||
|
return cacheMediaSourceUsingMoveCallsCount > 0
|
||||||
|
}
|
||||||
|
var cacheMediaSourceUsingMoveReceivedArguments: (mediaSource: MediaSourceProxy, fileURL: URL, move: Bool)?
|
||||||
|
var cacheMediaSourceUsingMoveReceivedInvocations: [(mediaSource: MediaSourceProxy, fileURL: URL, move: Bool)] = []
|
||||||
|
var cacheMediaSourceUsingMoveReturnValue: URL!
|
||||||
|
var cacheMediaSourceUsingMoveClosure: ((MediaSourceProxy, URL, Bool) throws -> URL)?
|
||||||
|
|
||||||
|
func cache(mediaSource: MediaSourceProxy, using fileURL: URL, move: Bool) throws -> URL {
|
||||||
|
if let error = cacheMediaSourceUsingMoveThrowableError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
cacheMediaSourceUsingMoveCallsCount += 1
|
||||||
|
cacheMediaSourceUsingMoveReceivedArguments = (mediaSource: mediaSource, fileURL: fileURL, move: move)
|
||||||
|
cacheMediaSourceUsingMoveReceivedInvocations.append((mediaSource: mediaSource, fileURL: fileURL, move: move))
|
||||||
|
if let cacheMediaSourceUsingMoveClosure = cacheMediaSourceUsingMoveClosure {
|
||||||
|
return try cacheMediaSourceUsingMoveClosure(mediaSource, fileURL, move)
|
||||||
|
} else {
|
||||||
|
return cacheMediaSourceUsingMoveReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - clearCache
|
||||||
|
|
||||||
|
var clearCacheCallsCount = 0
|
||||||
|
var clearCacheCalled: Bool {
|
||||||
|
return clearCacheCallsCount > 0
|
||||||
|
}
|
||||||
|
var clearCacheClosure: (() -> Void)?
|
||||||
|
|
||||||
|
func clearCache() {
|
||||||
|
clearCacheCallsCount += 1
|
||||||
|
clearCacheClosure?()
|
||||||
|
}
|
||||||
|
}
|
||||||
class VoiceMessageMediaManagerMock: VoiceMessageMediaManagerProtocol {
|
class VoiceMessageMediaManagerMock: VoiceMessageMediaManagerProtocol {
|
||||||
|
|
||||||
//MARK: - loadVoiceMessageFromSource
|
//MARK: - loadVoiceMessageFromSource
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ extension View {
|
|||||||
|
|
||||||
struct ShimmerOverlay_Previews: PreviewProvider, TestablePreview {
|
struct ShimmerOverlay_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: MockClientProxy(userID: ""),
|
static let viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: MockClientProxy(userID: ""),
|
||||||
mediaProvider: MockMediaProvider()),
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock()),
|
||||||
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
|
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
|
||||||
mentionBuilder: MentionBuilder(mentionsEnabled: ServiceLocator.shared.settings.mentionsEnabled)),
|
mentionBuilder: MentionBuilder(mentionsEnabled: ServiceLocator.shared.settings.mentionsEnabled)),
|
||||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ struct WaitlistScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
static let successViewModel = {
|
static let successViewModel = {
|
||||||
let viewModel = WaitlistScreenViewModel(homeserver: .mockMatrixDotOrg)
|
let viewModel = WaitlistScreenViewModel(homeserver: .mockMatrixDotOrg)
|
||||||
viewModel.update(userSession: MockUserSession(clientProxy: MockClientProxy(userID: "@alice:matrix.org"),
|
viewModel.update(userSession: MockUserSession(clientProxy: MockClientProxy(userID: "@alice:matrix.org"),
|
||||||
mediaProvider: MockMediaProvider()))
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock()))
|
||||||
return viewModel
|
return viewModel
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,8 @@ struct CreateRoomScreen: View {
|
|||||||
struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = {
|
static let viewModel = {
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let parameters = CreateRoomFlowParameters()
|
let parameters = CreateRoomFlowParameters()
|
||||||
let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie]
|
let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie]
|
||||||
|
|
||||||
@@ -220,7 +221,8 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
|||||||
|
|
||||||
static let emtpyViewModel = {
|
static let emtpyViewModel = {
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let parameters = CreateRoomFlowParameters()
|
let parameters = CreateRoomFlowParameters()
|
||||||
return CreateRoomViewModel(userSession: userSession,
|
return CreateRoomViewModel(userSession: userSession,
|
||||||
createRoomParameters: .init(parameters),
|
createRoomParameters: .init(parameters),
|
||||||
|
|||||||
@@ -335,7 +335,8 @@ struct HomeScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
static func viewModel(_ state: MockRoomSummaryProviderState) -> HomeScreenViewModel {
|
static func viewModel(_ state: MockRoomSummaryProviderState) -> HomeScreenViewModel {
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@alice:example.com",
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@alice:example.com",
|
||||||
roomSummaryProvider: MockRoomSummaryProvider(state: state)),
|
roomSummaryProvider: MockRoomSummaryProvider(state: state)),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
|
|
||||||
return HomeScreenViewModel(userSession: userSession,
|
return HomeScreenViewModel(userSession: userSession,
|
||||||
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder(mentionsEnabled: ServiceLocator.shared.settings.mentionsEnabled)),
|
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder(mentionsEnabled: ServiceLocator.shared.settings.mentionsEnabled)),
|
||||||
|
|||||||
@@ -151,7 +151,8 @@ struct HomeScreenEmptyStateView_Previews: PreviewProvider, TestablePreview {
|
|||||||
static let viewModel = {
|
static let viewModel = {
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@user:example.com",
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@user:example.com",
|
||||||
roomSummaryProvider: MockRoomSummaryProvider(state: .loaded([]))),
|
roomSummaryProvider: MockRoomSummaryProvider(state: .loaded([]))),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
|
|
||||||
return HomeScreenViewModel(userSession: userSession,
|
return HomeScreenViewModel(userSession: userSession,
|
||||||
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder(mentionsEnabled: ServiceLocator.shared.settings.mentionsEnabled)),
|
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder(mentionsEnabled: ServiceLocator.shared.settings.mentionsEnabled)),
|
||||||
|
|||||||
@@ -185,7 +185,8 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
|
|||||||
let summaryProvider = MockRoomSummaryProvider(state: .loaded(.mockRooms))
|
let summaryProvider = MockRoomSummaryProvider(state: .loaded(.mockRooms))
|
||||||
|
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe", roomSummaryProvider: summaryProvider),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe", roomSummaryProvider: summaryProvider),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
|
|
||||||
let viewModel = HomeScreenViewModel(userSession: userSession,
|
let viewModel = HomeScreenViewModel(userSession: userSession,
|
||||||
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
|
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ struct InvitesScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
private extension InvitesScreenViewModel {
|
private extension InvitesScreenViewModel {
|
||||||
static let noInvites: InvitesScreenViewModel = {
|
static let noInvites: InvitesScreenViewModel = {
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let regularViewModel = InvitesScreenViewModel(userSession: userSession,
|
let regularViewModel = InvitesScreenViewModel(userSession: userSession,
|
||||||
appSettings: ServiceLocator.shared.settings,
|
appSettings: ServiceLocator.shared.settings,
|
||||||
analytics: ServiceLocator.shared.analytics,
|
analytics: ServiceLocator.shared.analytics,
|
||||||
@@ -93,7 +94,8 @@ private extension InvitesScreenViewModel {
|
|||||||
clientProxy.inviteSummaryProvider = MockRoomSummaryProvider(state: .loaded(.mockInvites))
|
clientProxy.inviteSummaryProvider = MockRoomSummaryProvider(state: .loaded(.mockInvites))
|
||||||
clientProxy.roomSummaryProvider = MockRoomSummaryProvider(state: .loaded(.mockInvites))
|
clientProxy.roomSummaryProvider = MockRoomSummaryProvider(state: .loaded(.mockInvites))
|
||||||
let userSession = MockUserSession(clientProxy: clientProxy,
|
let userSession = MockUserSession(clientProxy: clientProxy,
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let regularViewModel = InvitesScreenViewModel(userSession: userSession,
|
let regularViewModel = InvitesScreenViewModel(userSession: userSession,
|
||||||
appSettings: ServiceLocator.shared.settings,
|
appSettings: ServiceLocator.shared.settings,
|
||||||
analytics: ServiceLocator.shared.analytics,
|
analytics: ServiceLocator.shared.analytics,
|
||||||
|
|||||||
+5
-3
@@ -76,7 +76,8 @@ struct NotificationSettingsEditScreen_Previews: PreviewProvider, TestablePreview
|
|||||||
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.compactMap(\.id)
|
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.compactMap(\.id)
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@alice:example.com",
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@alice:example.com",
|
||||||
roomSummaryProvider: MockRoomSummaryProvider(state: .loaded(.mockRooms))),
|
roomSummaryProvider: MockRoomSummaryProvider(state: .loaded(.mockRooms))),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
|
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
notificationSettingsProxy: notificationSettingsProxy)
|
notificationSettingsProxy: notificationSettingsProxy)
|
||||||
@@ -90,7 +91,8 @@ struct NotificationSettingsEditScreen_Previews: PreviewProvider, TestablePreview
|
|||||||
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = []
|
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = []
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@alice:example.com",
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@alice:example.com",
|
||||||
roomSummaryProvider: MockRoomSummaryProvider(state: .loaded(.mockRooms))),
|
roomSummaryProvider: MockRoomSummaryProvider(state: .loaded(.mockRooms))),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
notificationSettingsProxy: notificationSettingsProxy)
|
notificationSettingsProxy: notificationSettingsProxy)
|
||||||
@@ -102,7 +104,7 @@ struct NotificationSettingsEditScreen_Previews: PreviewProvider, TestablePreview
|
|||||||
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
|
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
|
||||||
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
|
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
|
||||||
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = []
|
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = []
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe"), mediaProvider: MockMediaProvider())
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe"), mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
|
|
||||||
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
|
|||||||
+2
-1
@@ -62,7 +62,8 @@ struct NotificationSettingsEditScreenRoomCell_Previews: PreviewProvider, Testabl
|
|||||||
let summaryProvider = MockRoomSummaryProvider(state: .loaded(.mockRooms))
|
let summaryProvider = MockRoomSummaryProvider(state: .loaded(.mockRooms))
|
||||||
|
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe", roomSummaryProvider: summaryProvider),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe", roomSummaryProvider: summaryProvider),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
|
|
||||||
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
|
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
|
||||||
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = []
|
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = []
|
||||||
|
|||||||
+3
-2
@@ -199,7 +199,8 @@ struct NotificationSettingsScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
notificationSettingsProxy.isRoomMentionEnabledReturnValue = true
|
notificationSettingsProxy.isRoomMentionEnabledReturnValue = true
|
||||||
notificationSettingsProxy.isCallEnabledReturnValue = false
|
notificationSettingsProxy.isCallEnabledReturnValue = false
|
||||||
|
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe"), mediaProvider: MockMediaProvider())
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe"), mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
|
|
||||||
var viewModel = NotificationSettingsScreenViewModel(userSession: userSession,
|
var viewModel = NotificationSettingsScreenViewModel(userSession: userSession,
|
||||||
appSettings: appSettings,
|
appSettings: appSettings,
|
||||||
@@ -228,7 +229,7 @@ struct NotificationSettingsScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
notificationSettingsProxy.isRoomMentionEnabledReturnValue = true
|
notificationSettingsProxy.isRoomMentionEnabledReturnValue = true
|
||||||
notificationSettingsProxy.isCallEnabledReturnValue = false
|
notificationSettingsProxy.isCallEnabledReturnValue = false
|
||||||
|
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe"), mediaProvider: MockMediaProvider())
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "John Doe"), mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
|
|
||||||
var viewModel = NotificationSettingsScreenViewModel(userSession: userSession,
|
var viewModel = NotificationSettingsScreenViewModel(userSession: userSession,
|
||||||
appSettings: appSettings,
|
appSettings: appSettings,
|
||||||
|
|||||||
@@ -235,7 +235,8 @@ struct SettingsScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
let userSession = MockUserSession(sessionVerificationController: verificationController,
|
let userSession = MockUserSession(sessionVerificationController: verificationController,
|
||||||
clientProxy: MockClientProxy(userID: "@userid:example.com",
|
clientProxy: MockClientProxy(userID: "@userid:example.com",
|
||||||
deviceID: "AAAAAAAAAAA"),
|
deviceID: "AAAAAAAAAAA"),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
return SettingsScreenViewModel(userSession: userSession,
|
return SettingsScreenViewModel(userSession: userSession,
|
||||||
appSettings: ServiceLocator.shared.settings)
|
appSettings: ServiceLocator.shared.settings)
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ struct StartChatScreen: View {
|
|||||||
struct StartChatScreen_Previews: PreviewProvider, TestablePreview {
|
struct StartChatScreen_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = {
|
static let viewModel = {
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let userDiscoveryService = UserDiscoveryServiceMock()
|
let userDiscoveryService = UserDiscoveryServiceMock()
|
||||||
userDiscoveryService.fetchSuggestionsReturnValue = .success([.mockAlice])
|
userDiscoveryService.fetchSuggestionsReturnValue = .success([.mockAlice])
|
||||||
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
|
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
|
||||||
|
|||||||
@@ -22,7 +22,12 @@ enum AudioConverterError: Error {
|
|||||||
case conversionFailed(Error?)
|
case conversionFailed(Error?)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AudioConverter {
|
enum AudioConverterPreferredFileExtension: String {
|
||||||
|
case mpeg4aac = "m4a"
|
||||||
|
case ogg
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AudioConverter: AudioConverterProtocol {
|
||||||
func convertToOpusOgg(sourceURL: URL, destinationURL: URL) throws {
|
func convertToOpusOgg(sourceURL: URL, destinationURL: URL) throws {
|
||||||
do {
|
do {
|
||||||
try OGGConverter.convertM4aFileToOpusOGG(src: sourceURL, dest: destinationURL)
|
try OGGConverter.convertM4aFileToOpusOGG(src: sourceURL, dest: destinationURL)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 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
|
||||||
|
|
||||||
|
protocol AudioConverterProtocol {
|
||||||
|
func convertToOpusOgg(sourceURL: URL, destinationURL: URL) throws
|
||||||
|
func convertToMPEG4AAC(sourceURL: URL, destinationURL: URL) throws
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcery: AutoMockable
|
||||||
|
extension AudioConverterProtocol { }
|
||||||
@@ -63,7 +63,8 @@ class MockAuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: username),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: username),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
return .success(userSession)
|
return .success(userSession)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,11 @@ struct MockMediaProvider: MediaProviderProtocol {
|
|||||||
return .success(data)
|
return .success(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var loadFileFromSourceReturnValue: MediaFileHandleProxy?
|
||||||
func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result<MediaFileHandleProxy, MediaProviderError> {
|
func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result<MediaFileHandleProxy, MediaProviderError> {
|
||||||
.failure(.failedRetrievingFile)
|
if let loadFileFromSourceReturnValue {
|
||||||
|
return .success(loadFileFromSourceReturnValue)
|
||||||
|
}
|
||||||
|
return .failure(.failedRetrievingFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,4 +24,5 @@ struct MockUserSession: UserSessionProtocol {
|
|||||||
var homeserver: String { clientProxy.homeserver }
|
var homeserver: String { clientProxy.homeserver }
|
||||||
let clientProxy: ClientProxyProtocol
|
let clientProxy: ClientProxyProtocol
|
||||||
let mediaProvider: MediaProviderProtocol
|
let mediaProvider: MediaProviderProtocol
|
||||||
|
let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,14 @@ class UserSession: UserSessionProtocol {
|
|||||||
|
|
||||||
let clientProxy: ClientProxyProtocol
|
let clientProxy: ClientProxyProtocol
|
||||||
let mediaProvider: MediaProviderProtocol
|
let mediaProvider: MediaProviderProtocol
|
||||||
|
let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol
|
||||||
let callbacks = PassthroughSubject<UserSessionCallback, Never>()
|
let callbacks = PassthroughSubject<UserSessionCallback, Never>()
|
||||||
private(set) var sessionVerificationController: SessionVerificationControllerProxyProtocol?
|
private(set) var sessionVerificationController: SessionVerificationControllerProxyProtocol?
|
||||||
|
|
||||||
init(clientProxy: ClientProxyProtocol, mediaProvider: MediaProviderProtocol) {
|
init(clientProxy: ClientProxyProtocol, mediaProvider: MediaProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol) {
|
||||||
self.clientProxy = clientProxy
|
self.clientProxy = clientProxy
|
||||||
self.mediaProvider = mediaProvider
|
self.mediaProvider = mediaProvider
|
||||||
|
self.voiceMessageMediaManager = voiceMessageMediaManager
|
||||||
|
|
||||||
setupSessionVerificationWatchdog()
|
setupSessionVerificationWatchdog()
|
||||||
setupAuthErrorWatchdog()
|
setupAuthErrorWatchdog()
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ protocol UserSessionProtocol {
|
|||||||
|
|
||||||
var clientProxy: ClientProxyProtocol { get }
|
var clientProxy: ClientProxyProtocol { get }
|
||||||
var mediaProvider: MediaProviderProtocol { get }
|
var mediaProvider: MediaProviderProtocol { get }
|
||||||
|
var voiceMessageMediaManager: VoiceMessageMediaManagerProtocol { get }
|
||||||
|
|
||||||
var sessionVerificationController: SessionVerificationControllerProxyProtocol? { get }
|
var sessionVerificationController: SessionVerificationControllerProxyProtocol? { get }
|
||||||
|
|
||||||
|
|||||||
@@ -94,10 +94,16 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
let imageCache = ImageCache.onlyInMemory
|
let imageCache = ImageCache.onlyInMemory
|
||||||
imageCache.memoryStorage.config.keepWhenEnteringBackground = true
|
imageCache.memoryStorage.config.keepWhenEnteringBackground = true
|
||||||
|
|
||||||
return UserSession(clientProxy: clientProxy,
|
let mediaProvider = MediaProvider(mediaLoader: clientProxy,
|
||||||
mediaProvider: MediaProvider(mediaLoader: clientProxy,
|
|
||||||
imageCache: imageCache,
|
imageCache: imageCache,
|
||||||
backgroundTaskService: backgroundTaskService))
|
backgroundTaskService: backgroundTaskService)
|
||||||
|
|
||||||
|
let voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider,
|
||||||
|
backgroundTaskService: backgroundTaskService)
|
||||||
|
|
||||||
|
return UserSession(clientProxy: clientProxy,
|
||||||
|
mediaProvider: mediaProvider,
|
||||||
|
voiceMessageMediaManager: voiceMessageMediaManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func restorePreviousLogin(_ credentials: KeychainCredentials) async -> Result<ClientProxyProtocol, UserSessionStoreError> {
|
private func restorePreviousLogin(_ credentials: KeychainCredentials) async -> Result<ClientProxyProtocol, UserSessionStoreError> {
|
||||||
|
|||||||
@@ -16,32 +16,33 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class VoiceMessageCache {
|
enum VoiceMessageCacheError: Error {
|
||||||
var temporaryFilesFolderURL: URL {
|
case invalidFileExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
class VoiceMessageCache: VoiceMessageCacheProtocol {
|
||||||
|
private let preferredFileExtension = "m4a"
|
||||||
|
private var temporaryFilesFolderURL: URL {
|
||||||
FileManager.default.temporaryDirectory.appendingPathComponent("media/voice-message")
|
FileManager.default.temporaryDirectory.appendingPathComponent("media/voice-message")
|
||||||
}
|
}
|
||||||
|
|
||||||
func cacheURL(for mediaSource: MediaSourceProxy, replacingExtension newExtension: String? = nil) -> URL {
|
func fileURL(for mediaSource: MediaSourceProxy) -> URL? {
|
||||||
var newURL = temporaryFilesFolderURL.appendingPathComponent(mediaSource.url.lastPathComponent)
|
let url = cacheURL(for: mediaSource)
|
||||||
if let newExtension {
|
|
||||||
newURL = newURL.deletingPathExtension().appendingPathExtension(newExtension)
|
|
||||||
}
|
|
||||||
return newURL
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileURL(for mediaSource: MediaSourceProxy, withExtension fileExtension: String? = nil) -> URL? {
|
|
||||||
var url = temporaryFilesFolderURL.appendingPathComponent(mediaSource.url.lastPathComponent)
|
|
||||||
if let fileExtension {
|
|
||||||
url = url.deletingPathExtension().appendingPathExtension(fileExtension)
|
|
||||||
}
|
|
||||||
return FileManager.default.fileExists(atPath: url.path()) ? url : nil
|
return FileManager.default.fileExists(atPath: url.path()) ? url : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cache(mediaSource: MediaSourceProxy, using fileURL: URL) throws -> URL {
|
func cache(mediaSource: MediaSourceProxy, using fileURL: URL, move: Bool = false) throws -> URL {
|
||||||
|
guard fileURL.pathExtension == preferredFileExtension else {
|
||||||
|
throw VoiceMessageCacheError.invalidFileExtension
|
||||||
|
}
|
||||||
setupTemporaryFilesFolder()
|
setupTemporaryFilesFolder()
|
||||||
let url = cacheURL(for: mediaSource)
|
let url = cacheURL(for: mediaSource)
|
||||||
try? FileManager.default.removeItem(at: url)
|
try? FileManager.default.removeItem(at: url)
|
||||||
|
if move {
|
||||||
|
try FileManager.default.moveItem(at: fileURL, to: url)
|
||||||
|
} else {
|
||||||
try FileManager.default.copyItem(at: fileURL, to: url)
|
try FileManager.default.copyItem(at: fileURL, to: url)
|
||||||
|
}
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,4 +65,8 @@ class VoiceMessageCache {
|
|||||||
MXLog.error("Failed to setup audio cache manager.")
|
MXLog.error("Failed to setup audio cache manager.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func cacheURL(for mediaSource: MediaSourceProxy) -> URL {
|
||||||
|
temporaryFilesFolderURL.appendingPathComponent(mediaSource.url.lastPathComponent).appendingPathExtension(preferredFileExtension)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 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
|
||||||
|
|
||||||
|
protocol VoiceMessageCacheProtocol {
|
||||||
|
func fileURL(for mediaSource: MediaSourceProxy) -> URL?
|
||||||
|
func cache(mediaSource: MediaSourceProxy, using fileURL: URL, move: Bool) throws -> URL
|
||||||
|
func clearCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourcery: AutoMockable
|
||||||
|
extension VoiceMessageCacheProtocol { }
|
||||||
@@ -20,31 +20,47 @@ enum VoiceMessageMediaManagerError: Error {
|
|||||||
case unsupportedMimeTye
|
case unsupportedMimeTye
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class VoiceMessageConversionRequest {
|
||||||
|
var continuations: [CheckedContinuation<URL, Error>] = []
|
||||||
|
}
|
||||||
|
|
||||||
class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol {
|
class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol {
|
||||||
private let mediaProvider: MediaProviderProtocol
|
private let mediaProvider: MediaProviderProtocol
|
||||||
private let cache: VoiceMessageCache
|
private let voiceMessageCache: VoiceMessageCacheProtocol
|
||||||
|
private let audioConverter: AudioConverterProtocol
|
||||||
|
|
||||||
|
private let backgroundTaskService: BackgroundTaskServiceProtocol?
|
||||||
|
private let processingQueue: DispatchQueue
|
||||||
|
private var conversionRequests = [MediaSourceProxy: VoiceMessageConversionRequest]()
|
||||||
|
|
||||||
private let supportedVoiceMessageMimeType = "audio/ogg"
|
private let supportedVoiceMessageMimeType = "audio/ogg"
|
||||||
|
|
||||||
/// Preferred audio file extension after conversion
|
init(mediaProvider: MediaProviderProtocol,
|
||||||
private let preferredAudioExtension = "m4a"
|
voiceMessageCache: VoiceMessageCacheProtocol = VoiceMessageCache(),
|
||||||
|
audioConverter: AudioConverterProtocol = AudioConverter(),
|
||||||
init(mediaProvider: MediaProviderProtocol) {
|
processingQueue: DispatchQueue = .global(),
|
||||||
|
backgroundTaskService: BackgroundTaskServiceProtocol?) {
|
||||||
self.mediaProvider = mediaProvider
|
self.mediaProvider = mediaProvider
|
||||||
cache = VoiceMessageCache()
|
self.voiceMessageCache = voiceMessageCache
|
||||||
|
self.audioConverter = audioConverter
|
||||||
|
self.processingQueue = processingQueue
|
||||||
|
self.backgroundTaskService = backgroundTaskService
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
cache.clearCache()
|
voiceMessageCache.clearCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadVoiceMessageFromSource(_ source: MediaSourceProxy, body: String?) async throws -> URL {
|
func loadVoiceMessageFromSource(_ source: MediaSourceProxy, body: String?) async throws -> URL {
|
||||||
|
let loadFileBgTask = await backgroundTaskService?.startBackgroundTask(withName: "LoadFile: \(source.url.hashValue)")
|
||||||
|
defer { loadFileBgTask?.stop() }
|
||||||
|
|
||||||
guard let mimeType = source.mimeType, mimeType == supportedVoiceMessageMimeType else {
|
guard let mimeType = source.mimeType, mimeType == supportedVoiceMessageMimeType else {
|
||||||
throw VoiceMessageMediaManagerError.unsupportedMimeTye
|
throw VoiceMessageMediaManagerError.unsupportedMimeTye
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we already have a converted version?
|
// Do we already have a converted version?
|
||||||
if let fileURL = cache.fileURL(for: source, withExtension: preferredAudioExtension) {
|
if let fileURL = voiceMessageCache.fileURL(for: source) {
|
||||||
return fileURL
|
return fileURL
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,16 +68,50 @@ class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol {
|
|||||||
guard case .success(let fileHandle) = await mediaProvider.loadFileFromSource(source, body: body) else {
|
guard case .success(let fileHandle) = await mediaProvider.loadFileFromSource(source, body: body) else {
|
||||||
throw MediaProviderError.failedRetrievingFile
|
throw MediaProviderError.failedRetrievingFile
|
||||||
}
|
}
|
||||||
let fileURL = try cache.cache(mediaSource: source, using: fileHandle.url)
|
|
||||||
|
return try await enqueueVoiceMessageConversionRequest(forSource: source) { [audioConverter, voiceMessageCache] in
|
||||||
|
// Do we already have a converted version?
|
||||||
|
if let fileURL = voiceMessageCache.fileURL(for: source) {
|
||||||
|
return fileURL
|
||||||
|
}
|
||||||
|
|
||||||
// Convert from ogg
|
// Convert from ogg
|
||||||
let audioConverter = AudioConverter()
|
let convertedFileURL = URL.temporaryDirectory.appendingPathComponent(fileHandle.url.deletingPathExtension().lastPathComponent).appendingPathExtension(AudioConverterPreferredFileExtension.mpeg4aac.rawValue)
|
||||||
let convertedFileURL = cache.cacheURL(for: source, replacingExtension: preferredAudioExtension)
|
try audioConverter.convertToMPEG4AAC(sourceURL: fileHandle.url, destinationURL: convertedFileURL)
|
||||||
try audioConverter.convertToMPEG4AAC(sourceURL: fileURL, destinationURL: convertedFileURL)
|
|
||||||
|
|
||||||
// we don't need the original file anymore
|
// Cache the file and return the url
|
||||||
try? FileManager.default.removeItem(at: fileURL)
|
return try voiceMessageCache.cache(mediaSource: source, using: convertedFileURL, move: true)
|
||||||
|
}
|
||||||
return convertedFileURL
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func enqueueVoiceMessageConversionRequest(forSource source: MediaSourceProxy, operation: @escaping () throws -> URL) async throws -> URL {
|
||||||
|
if let conversionRequests = conversionRequests[source] {
|
||||||
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
|
conversionRequests.continuations.append(continuation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let conversionRequest = VoiceMessageConversionRequest()
|
||||||
|
conversionRequests[source] = conversionRequest
|
||||||
|
|
||||||
|
defer {
|
||||||
|
conversionRequests[source] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let result = try await Task.dispatch(on: processingQueue) {
|
||||||
|
try operation()
|
||||||
|
}
|
||||||
|
|
||||||
|
conversionRequest.continuations.forEach { $0.resume(returning: result) }
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
conversionRequest.continuations.forEach { $0.resume(throwing: error) }
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,7 +164,8 @@ class MockScreen: Identifiable {
|
|||||||
case .home:
|
case .home:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
let session = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:matrix.org"),
|
let session = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:matrix.org"),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let coordinator = HomeScreenCoordinator(parameters: .init(userSession: session,
|
let coordinator = HomeScreenCoordinator(parameters: .init(userSession: session,
|
||||||
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder(mentionsEnabled: true)),
|
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder(mentionsEnabled: true)),
|
||||||
bugReportService: BugReportServiceMock(),
|
bugReportService: BugReportServiceMock(),
|
||||||
@@ -177,7 +178,7 @@ class MockScreen: Identifiable {
|
|||||||
let clientProxy = MockClientProxy(userID: "@mock:client.com")
|
let clientProxy = MockClientProxy(userID: "@mock:client.com")
|
||||||
let coordinator = SettingsScreenCoordinator(parameters: .init(navigationStackCoordinator: navigationStackCoordinator,
|
let coordinator = SettingsScreenCoordinator(parameters: .init(navigationStackCoordinator: navigationStackCoordinator,
|
||||||
userIndicatorController: nil,
|
userIndicatorController: nil,
|
||||||
userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()),
|
userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()),
|
||||||
bugReportService: BugReportServiceMock(),
|
bugReportService: BugReportServiceMock(),
|
||||||
notificationSettings: NotificationSettingsProxyMock(with: .init()),
|
notificationSettings: NotificationSettingsProxyMock(with: .init()),
|
||||||
appSettings: ServiceLocator.shared.settings))
|
appSettings: ServiceLocator.shared.settings))
|
||||||
@@ -207,7 +208,8 @@ class MockScreen: Identifiable {
|
|||||||
let userNotificationCenter = UserNotificationCenterMock()
|
let userNotificationCenter = UserNotificationCenterMock()
|
||||||
userNotificationCenter.authorizationStatusReturnValue = .denied
|
userNotificationCenter.authorizationStatusReturnValue = .denied
|
||||||
let session = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:matrix.org"),
|
let session = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:matrix.org"),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let parameters = NotificationSettingsScreenCoordinatorParameters(userSession: session,
|
let parameters = NotificationSettingsScreenCoordinatorParameters(userSession: session,
|
||||||
userNotificationCenter: userNotificationCenter,
|
userNotificationCenter: userNotificationCenter,
|
||||||
notificationSettings: NotificationSettingsProxyMock(with: .init()),
|
notificationSettings: NotificationSettingsProxyMock(with: .init()),
|
||||||
@@ -217,7 +219,8 @@ class MockScreen: Identifiable {
|
|||||||
let userNotificationCenter = UserNotificationCenterMock()
|
let userNotificationCenter = UserNotificationCenterMock()
|
||||||
userNotificationCenter.authorizationStatusReturnValue = .denied
|
userNotificationCenter.authorizationStatusReturnValue = .denied
|
||||||
let session = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:matrix.org"),
|
let session = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:matrix.org"),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let notificationSettings = NotificationSettingsProxyMock(with: .init())
|
let notificationSettings = NotificationSettingsProxyMock(with: .init())
|
||||||
notificationSettings.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
notificationSettings.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
||||||
switch (isEncrypted, isOneToOne) {
|
switch (isEncrypted, isOneToOne) {
|
||||||
@@ -439,7 +442,7 @@ class MockScreen: Identifiable {
|
|||||||
ServiceLocator.shared.settings.migratedAccounts[clientProxy.userID] = true
|
ServiceLocator.shared.settings.migratedAccounts[clientProxy.userID] = true
|
||||||
ServiceLocator.shared.settings.hasShownWelcomeScreen = true
|
ServiceLocator.shared.settings.hasShownWelcomeScreen = true
|
||||||
|
|
||||||
let coordinator = UserSessionFlowCoordinator(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()),
|
let coordinator = UserSessionFlowCoordinator(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()),
|
||||||
navigationSplitCoordinator: navigationSplitCoordinator,
|
navigationSplitCoordinator: navigationSplitCoordinator,
|
||||||
bugReportService: BugReportServiceMock(),
|
bugReportService: BugReportServiceMock(),
|
||||||
roomTimelineControllerFactory: MockRoomTimelineControllerFactory(),
|
roomTimelineControllerFactory: MockRoomTimelineControllerFactory(),
|
||||||
@@ -614,7 +617,7 @@ class MockScreen: Identifiable {
|
|||||||
let userDiscoveryMock = UserDiscoveryServiceMock()
|
let userDiscoveryMock = UserDiscoveryServiceMock()
|
||||||
userDiscoveryMock.fetchSuggestionsReturnValue = .success([.mockAlice, .mockBob, .mockCharlie])
|
userDiscoveryMock.fetchSuggestionsReturnValue = .success([.mockAlice, .mockBob, .mockCharlie])
|
||||||
userDiscoveryMock.searchProfilesWithReturnValue = .success([])
|
userDiscoveryMock.searchProfilesWithReturnValue = .success([])
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:client.com"), mediaProvider: MockMediaProvider())
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:client.com"), mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let parameters: StartChatScreenCoordinatorParameters = .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock)
|
let parameters: StartChatScreenCoordinatorParameters = .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock)
|
||||||
let coordinator = StartChatScreenCoordinator(parameters: parameters)
|
let coordinator = StartChatScreenCoordinator(parameters: parameters)
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
@@ -625,7 +628,7 @@ class MockScreen: Identifiable {
|
|||||||
let userDiscoveryMock = UserDiscoveryServiceMock()
|
let userDiscoveryMock = UserDiscoveryServiceMock()
|
||||||
userDiscoveryMock.fetchSuggestionsReturnValue = .success([])
|
userDiscoveryMock.fetchSuggestionsReturnValue = .success([])
|
||||||
userDiscoveryMock.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby])
|
userDiscoveryMock.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby])
|
||||||
let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let coordinator = StartChatScreenCoordinator(parameters: .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock))
|
let coordinator = StartChatScreenCoordinator(parameters: .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock))
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
@@ -662,7 +665,7 @@ class MockScreen: Identifiable {
|
|||||||
let summaryProvider = MockRoomSummaryProvider(state: .loaded(.mockInvites))
|
let summaryProvider = MockRoomSummaryProvider(state: .loaded(.mockInvites))
|
||||||
clientProxy.inviteSummaryProvider = summaryProvider
|
clientProxy.inviteSummaryProvider = summaryProvider
|
||||||
|
|
||||||
let coordinator = InvitesScreenCoordinator(parameters: .init(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())))
|
let coordinator = InvitesScreenCoordinator(parameters: .init(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock())))
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
case .invites:
|
case .invites:
|
||||||
@@ -674,13 +677,13 @@ class MockScreen: Identifiable {
|
|||||||
let summaryProvider = MockRoomSummaryProvider(state: .loaded(.mockInvites))
|
let summaryProvider = MockRoomSummaryProvider(state: .loaded(.mockInvites))
|
||||||
clientProxy.inviteSummaryProvider = summaryProvider
|
clientProxy.inviteSummaryProvider = summaryProvider
|
||||||
|
|
||||||
let coordinator = InvitesScreenCoordinator(parameters: .init(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())))
|
let coordinator = InvitesScreenCoordinator(parameters: .init(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock())))
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
case .invitesNoInvites:
|
case .invitesNoInvites:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
let clientProxy = MockClientProxy(userID: "@mock:client.com")
|
let clientProxy = MockClientProxy(userID: "@mock:client.com")
|
||||||
let coordinator = InvitesScreenCoordinator(parameters: .init(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())))
|
let coordinator = InvitesScreenCoordinator(parameters: .init(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock())))
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
case .inviteUsers, .inviteUsersInRoom, .inviteUsersInRoomExistingMembers:
|
case .inviteUsers, .inviteUsersInRoom, .inviteUsersInRoomExistingMembers:
|
||||||
@@ -715,7 +718,7 @@ class MockScreen: Identifiable {
|
|||||||
case .createRoom:
|
case .createRoom:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
let clientProxy = MockClientProxy(userID: "@mock:client.com")
|
let clientProxy = MockClientProxy(userID: "@mock:client.com")
|
||||||
let mockUserSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
let mockUserSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let createRoomParameters = CreateRoomFlowParameters()
|
let createRoomParameters = CreateRoomFlowParameters()
|
||||||
let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie]
|
let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie]
|
||||||
let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, userIndicatorController: nil, createRoomParameters: .init(createRoomParameters), selectedUsers: .init(selectedUsers))
|
let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, userIndicatorController: nil, createRoomParameters: .init(createRoomParameters), selectedUsers: .init(selectedUsers))
|
||||||
@@ -725,7 +728,7 @@ class MockScreen: Identifiable {
|
|||||||
case .createRoomNoUsers:
|
case .createRoomNoUsers:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
let clientProxy = MockClientProxy(userID: "@mock:client.com")
|
let clientProxy = MockClientProxy(userID: "@mock:client.com")
|
||||||
let mockUserSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
let mockUserSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let createRoomParameters = CreateRoomFlowParameters()
|
let createRoomParameters = CreateRoomFlowParameters()
|
||||||
let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, userIndicatorController: nil, createRoomParameters: .init(createRoomParameters), selectedUsers: .init([]))
|
let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, userIndicatorController: nil, createRoomParameters: .init(createRoomParameters), selectedUsers: .init([]))
|
||||||
let coordinator = CreateRoomCoordinator(parameters: parameters)
|
let coordinator = CreateRoomCoordinator(parameters: parameters)
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ class CreateRoomScreenViewModelTests: XCTestCase {
|
|||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
clientProxy = MockClientProxy(userID: "@a:b.com")
|
clientProxy = MockClientProxy(userID: "@a:b.com")
|
||||||
userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
userSession = MockUserSession(clientProxy: clientProxy,
|
||||||
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
let parameters = CreateRoomFlowParameters()
|
let parameters = CreateRoomFlowParameters()
|
||||||
usersSubject.send([.mockAlice, .mockBob, .mockCharlie])
|
usersSubject.send([.mockAlice, .mockBob, .mockCharlie])
|
||||||
let viewModel = CreateRoomViewModel(userSession: userSession,
|
let viewModel = CreateRoomViewModel(userSession: userSession,
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ class HomeScreenViewModelTests: XCTestCase {
|
|||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
clientProxy = MockClientProxy(userID: "@mock:client.com")
|
clientProxy = MockClientProxy(userID: "@mock:client.com")
|
||||||
viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: clientProxy,
|
viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: clientProxy,
|
||||||
mediaProvider: MockMediaProvider()),
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock()),
|
||||||
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder(mentionsEnabled: true)),
|
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, mentionBuilder: MentionBuilder(mentionsEnabled: true)),
|
||||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
||||||
appSettings: ServiceLocator.shared.settings,
|
appSettings: ServiceLocator.shared.settings,
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ class InvitesScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
clientProxy = MockClientProxy(userID: "@a:b.com")
|
clientProxy = MockClientProxy(userID: "@a:b.com")
|
||||||
userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
userSession = MockUserSession(clientProxy: clientProxy,
|
||||||
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
mockNotificationCenter = NotificationCenterMock()
|
mockNotificationCenter = NotificationCenterMock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ import XCTest
|
|||||||
final class NotificationManagerTests: XCTestCase {
|
final class NotificationManagerTests: XCTestCase {
|
||||||
var notificationManager: NotificationManager!
|
var notificationManager: NotificationManager!
|
||||||
private let clientProxy = MockClientProxy(userID: "@test:user.net")
|
private let clientProxy = MockClientProxy(userID: "@test:user.net")
|
||||||
private lazy var mockUserSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
private lazy var mockUserSession = MockUserSession(clientProxy: clientProxy,
|
||||||
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
private var notificationCenter: UserNotificationCenterMock!
|
private var notificationCenter: UserNotificationCenterMock!
|
||||||
private var authorizationStatusWasGranted = false
|
private var authorizationStatusWasGranted = false
|
||||||
private var shouldDisplayInAppNotificationReturnValue = false
|
private var shouldDisplayInAppNotificationReturnValue = false
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
@MainActor override func setUpWithError() throws {
|
@MainActor override func setUpWithError() throws {
|
||||||
let clientProxy = MockClientProxy(userID: "@a:b.com")
|
let clientProxy = MockClientProxy(userID: "@a:b.com")
|
||||||
userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
userSession = MockUserSession(clientProxy: clientProxy,
|
||||||
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
notificationSettingsProxy = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration())
|
notificationSettingsProxy = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration())
|
||||||
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .allMessages
|
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .allMessages
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
notificationSettingsProxy.isCallEnabledReturnValue = true
|
notificationSettingsProxy.isCallEnabledReturnValue = true
|
||||||
|
|
||||||
let clientProxy = MockClientProxy(userID: "@a:b.com")
|
let clientProxy = MockClientProxy(userID: "@a:b.com")
|
||||||
userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
userSession = MockUserSession(clientProxy: clientProxy,
|
||||||
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
|
|
||||||
viewModel = NotificationSettingsScreenViewModel(userSession: userSession,
|
viewModel = NotificationSettingsScreenViewModel(userSession: userSession,
|
||||||
appSettings: appSettings,
|
appSettings: appSettings,
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
|||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
let clientProxy = MockClientProxy(userID: "hi@bob", roomSummaryProvider: MockRoomSummaryProvider(state: .loaded(.mockRooms)))
|
let clientProxy = MockClientProxy(userID: "hi@bob", roomSummaryProvider: MockRoomSummaryProvider(state: .loaded(.mockRooms)))
|
||||||
let mediaProvider = MockMediaProvider()
|
let mediaProvider = MockMediaProvider()
|
||||||
let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: mediaProvider)
|
let voiceMessageMediaManager = VoiceMessageMediaManagerMock()
|
||||||
|
let userSession = MockUserSession(clientProxy: clientProxy,
|
||||||
|
mediaProvider: mediaProvider,
|
||||||
|
voiceMessageMediaManager: voiceMessageMediaManager)
|
||||||
|
|
||||||
let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator())
|
let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator())
|
||||||
navigationStackCoordinator = NavigationStackCoordinator()
|
navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class SettingsScreenViewModelTests: XCTestCase {
|
|||||||
@MainActor override func setUpWithError() throws {
|
@MainActor override func setUpWithError() throws {
|
||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: ""),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: ""),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
viewModel = SettingsScreenViewModel(userSession: userSession, appSettings: ServiceLocator.shared.settings)
|
viewModel = SettingsScreenViewModel(userSession: userSession, appSettings: ServiceLocator.shared.settings)
|
||||||
context = viewModel.context
|
context = viewModel.context
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ class StartChatScreenViewModelTests: XCTestCase {
|
|||||||
userDiscoveryService = UserDiscoveryServiceMock()
|
userDiscoveryService = UserDiscoveryServiceMock()
|
||||||
userDiscoveryService.fetchSuggestionsReturnValue = .success([])
|
userDiscoveryService.fetchSuggestionsReturnValue = .success([])
|
||||||
userDiscoveryService.searchProfilesWithReturnValue = .success([])
|
userDiscoveryService.searchProfilesWithReturnValue = .success([])
|
||||||
let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
let userSession = MockUserSession(clientProxy: clientProxy,
|
||||||
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
viewModel = StartChatScreenViewModel(userSession: userSession,
|
viewModel = StartChatScreenViewModel(userSession: userSession,
|
||||||
appSettings: ServiceLocator.shared.settings,
|
appSettings: ServiceLocator.shared.settings,
|
||||||
analytics: ServiceLocator.shared.analytics,
|
analytics: ServiceLocator.shared.analytics,
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ final class UserSessionTests: XCTestCase {
|
|||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
userSession = UserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
userSession = UserSession(clientProxy: clientProxy,
|
||||||
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock())
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenUserSessionReceivesSyncUpdateAndSessionControllerRetrievedAndSessionNotVerified_sessionVerificationNeededEventReceived() throws {
|
func test_whenUserSessionReceivesSyncUpdateAndSessionControllerRetrievedAndSessionNotVerified_sessionVerificationNeededEventReceived() throws {
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 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 Combine
|
||||||
|
@testable import ElementX
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
class VoiceMessageCacheTests: XCTestCase {
|
||||||
|
private var voiceMessageCache: VoiceMessageCache!
|
||||||
|
private var mediaSource: MediaSourceProxy!
|
||||||
|
private var fileManager: FileManager!
|
||||||
|
|
||||||
|
private let someURL = URL("/some/url")
|
||||||
|
private let cachedFileURL = URL("/cache/file/url")
|
||||||
|
private let audioOGGMimeType = "audio/ogg"
|
||||||
|
private let testFilename = "test-file"
|
||||||
|
private let mpeg4aacFileExtension = "m4a"
|
||||||
|
private let testTemporaryDirectory = URL.temporaryDirectory.appendingPathComponent("test-voice-messsage-cache")
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
voiceMessageCache = VoiceMessageCache()
|
||||||
|
voiceMessageCache.clearCache()
|
||||||
|
|
||||||
|
fileManager = FileManager.default
|
||||||
|
mediaSource = MediaSourceProxy(url: someURL, mimeType: "audio/ogg")
|
||||||
|
|
||||||
|
// Create the temporary directory we will use
|
||||||
|
try fileManager.createDirectory(at: testTemporaryDirectory, withIntermediateDirectories: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
voiceMessageCache.clearCache()
|
||||||
|
|
||||||
|
// clear the test temporary directory
|
||||||
|
try fileManager.removeItem(at: testTemporaryDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFileURL() async throws {
|
||||||
|
// If the file is not already in the cache, no URL is expected
|
||||||
|
XCTAssertNil(voiceMessageCache.fileURL(for: mediaSource))
|
||||||
|
|
||||||
|
// If the file is present in the cache, its URL must be returned
|
||||||
|
let temporaryFileURL = try createTemporaryFile(named: testFilename, withExtension: mpeg4aacFileExtension)
|
||||||
|
let cachedURL = try voiceMessageCache.cache(mediaSource: mediaSource, using: temporaryFileURL, move: true)
|
||||||
|
|
||||||
|
XCTAssertEqual(cachedURL, voiceMessageCache.fileURL(for: mediaSource))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCacheInvalidFileExtension() async throws {
|
||||||
|
// An error should be raised if the file extension is not "m4a"
|
||||||
|
let mpegFileURL = try createTemporaryFile(named: testFilename, withExtension: "mpg")
|
||||||
|
do {
|
||||||
|
_ = try voiceMessageCache.cache(mediaSource: mediaSource, using: mpegFileURL, move: true)
|
||||||
|
XCTFail("An error is expected")
|
||||||
|
} catch {
|
||||||
|
switch error as? VoiceMessageCacheError {
|
||||||
|
case .invalidFileExtension:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
XCTFail("A VoiceMessageCacheError.invalidFileExtension is expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCacheCopy() async throws {
|
||||||
|
let fileURL = try createTemporaryFile(named: testFilename, withExtension: mpeg4aacFileExtension)
|
||||||
|
let cacheURL = try voiceMessageCache.cache(mediaSource: mediaSource, using: fileURL, move: false)
|
||||||
|
|
||||||
|
// The source file must remain in its original location
|
||||||
|
XCTAssertTrue(fileManager.fileExists(atPath: fileURL.path()))
|
||||||
|
// A copy must be present in the cache
|
||||||
|
XCTAssertTrue(fileManager.fileExists(atPath: cacheURL.path()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCacheMove() async throws {
|
||||||
|
let fileURL = try createTemporaryFile(named: testFilename, withExtension: mpeg4aacFileExtension)
|
||||||
|
let cacheURL = try voiceMessageCache.cache(mediaSource: mediaSource, using: fileURL, move: true)
|
||||||
|
|
||||||
|
// The file must have been moved
|
||||||
|
XCTAssertFalse(fileManager.fileExists(atPath: fileURL.path()))
|
||||||
|
XCTAssertTrue(fileManager.fileExists(atPath: cacheURL.path()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createTemporaryFile(named filename: String, withExtension fileExtension: String) throws -> URL {
|
||||||
|
let temporaryFileURL = testTemporaryDirectory.appendingPathComponent(filename).appendingPathExtension(fileExtension)
|
||||||
|
try Data().write(to: temporaryFileURL)
|
||||||
|
return temporaryFileURL
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 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 Combine
|
||||||
|
@testable import ElementX
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
class VoiceMessageMediaManagerTests: XCTestCase {
|
||||||
|
private var voiceMessageMediaManager: VoiceMessageMediaManager!
|
||||||
|
private var voiceMessageCache: VoiceMessageCacheMock!
|
||||||
|
private var mediaProvider: MockMediaProvider!
|
||||||
|
|
||||||
|
private let someURL = URL("/some/url")
|
||||||
|
private let audioOGGMimeType = "audio/ogg"
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
voiceMessageCache = VoiceMessageCacheMock()
|
||||||
|
mediaProvider = MockMediaProvider()
|
||||||
|
voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider,
|
||||||
|
voiceMessageCache: voiceMessageCache,
|
||||||
|
backgroundTaskService: MockBackgroundTaskService())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLoadVoiceMessageFromSourceUnsupportedMedia() async throws {
|
||||||
|
// Only "audio/ogg" file are supported
|
||||||
|
let unsupportedMediaSource = MediaSourceProxy(url: someURL, mimeType: "audio/wav")
|
||||||
|
do {
|
||||||
|
_ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(unsupportedMediaSource, body: nil)
|
||||||
|
XCTFail("A `VoiceMessageMediaManagerError.unsupportedMimeTye` error is expected")
|
||||||
|
} catch {
|
||||||
|
switch error as? VoiceMessageMediaManagerError {
|
||||||
|
case .unsupportedMimeTye:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
XCTFail("A `VoiceMessageMediaManagerError.unsupportedMimeTye` error is expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLoadVoiceMessageFromSourceAlreadyCached() async throws {
|
||||||
|
// Check if the file is already present in cache
|
||||||
|
voiceMessageCache.fileURLForReturnValue = URL("/converted_file/url")
|
||||||
|
let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
|
||||||
|
let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
|
||||||
|
XCTAssertEqual(url, URL("/converted_file/url"))
|
||||||
|
// The file must have be search in the cache
|
||||||
|
XCTAssertTrue(voiceMessageCache.fileURLForCalled)
|
||||||
|
XCTAssertEqual(voiceMessageCache.fileURLForReceivedMediaSource, mediaSource)
|
||||||
|
// The file must not have been cached again
|
||||||
|
XCTAssertFalse(voiceMessageCache.cacheMediaSourceUsingMoveCalled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLoadVoiceMessageFromSourceMediaProviderError() async throws {
|
||||||
|
// An error must be reported if the file cannot be retrieved
|
||||||
|
do {
|
||||||
|
voiceMessageCache.fileURLForReturnValue = nil
|
||||||
|
let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
|
||||||
|
_ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
|
||||||
|
XCTFail("A `MediaProviderError.failedRetrievingFile` error is expected")
|
||||||
|
} catch {
|
||||||
|
switch error as? MediaProviderError {
|
||||||
|
case .failedRetrievingFile:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
XCTFail("A `MediaProviderError.failedRetrievingFile` error is expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLoadVoiceMessageFromSourceSingleCall() async throws {
|
||||||
|
// URL representing the file loaded by the media provider
|
||||||
|
let loadedFile = URL("/some/url/loaded_file")
|
||||||
|
// URL representing the final cached file
|
||||||
|
let cachedConvertedFileURL = URL("/some/url/cached_converted_file")
|
||||||
|
|
||||||
|
// Check if the file is not already present in cache
|
||||||
|
voiceMessageCache.fileURLForReturnValue = nil
|
||||||
|
let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
|
||||||
|
mediaProvider.loadFileFromSourceReturnValue = MediaFileHandleProxy.unmanaged(url: loadedFile)
|
||||||
|
let audioConverter = AudioConverterMock()
|
||||||
|
voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = cachedConvertedFileURL
|
||||||
|
voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider,
|
||||||
|
voiceMessageCache: voiceMessageCache,
|
||||||
|
audioConverter: audioConverter,
|
||||||
|
backgroundTaskService: MockBackgroundTaskService())
|
||||||
|
let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
|
||||||
|
|
||||||
|
// The file must have been converted
|
||||||
|
XCTAssertTrue(audioConverter.convertToMPEG4AACSourceURLDestinationURLCalled)
|
||||||
|
// The converted file must have been cached
|
||||||
|
XCTAssert(voiceMessageCache.cacheMediaSourceUsingMoveCalled)
|
||||||
|
XCTAssertEqual(voiceMessageCache.cacheMediaSourceUsingMoveReceivedArguments?.mediaSource, mediaSource)
|
||||||
|
XCTAssertEqual(voiceMessageCache.cacheMediaSourceUsingMoveReceivedArguments?.fileURL.pathExtension, "m4a")
|
||||||
|
XCTAssertTrue(voiceMessageCache.cacheMediaSourceUsingMoveReceivedArguments?.move ?? false)
|
||||||
|
// The returned URL must point to the cached converted file
|
||||||
|
XCTAssertEqual(url, cachedConvertedFileURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLoadVoiceMessageFromSourceMultipleCalls() async throws {
|
||||||
|
// URL representing the file loaded by the media provider
|
||||||
|
let loadedFile = URL("/some/url/loaded_file")
|
||||||
|
// URL representing the final cached file
|
||||||
|
let cachedConvertedFileURL = URL("/some/url/cached_converted_file")
|
||||||
|
|
||||||
|
// Multiple calls
|
||||||
|
var cachedURL: URL?
|
||||||
|
voiceMessageCache.fileURLForClosure = { _ in
|
||||||
|
cachedURL
|
||||||
|
}
|
||||||
|
voiceMessageCache.cacheMediaSourceUsingMoveClosure = { _, _, _ in
|
||||||
|
cachedURL = cachedConvertedFileURL
|
||||||
|
return cachedConvertedFileURL
|
||||||
|
}
|
||||||
|
|
||||||
|
let audioConverter = AudioConverterMock()
|
||||||
|
mediaProvider.loadFileFromSourceReturnValue = MediaFileHandleProxy.unmanaged(url: loadedFile)
|
||||||
|
|
||||||
|
voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider,
|
||||||
|
voiceMessageCache: voiceMessageCache,
|
||||||
|
audioConverter: audioConverter,
|
||||||
|
backgroundTaskService: MockBackgroundTaskService())
|
||||||
|
|
||||||
|
let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
|
||||||
|
for _ in 0..<10 {
|
||||||
|
let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
|
||||||
|
XCTAssertEqual(url, cachedConvertedFileURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The file must have been converted only once
|
||||||
|
XCTAssertEqual(audioConverter.convertToMPEG4AACSourceURLDestinationURLCallsCount, 1)
|
||||||
|
|
||||||
|
// The converted file must have been cached only once
|
||||||
|
XCTAssertEqual(voiceMessageCache.cacheMediaSourceUsingMoveCallsCount, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,7 +32,8 @@ class WaitlistScreenViewModelTests: XCTestCase {
|
|||||||
XCTAssertTrue(context.viewState.isWaiting, "The view should start off in the waiting state.")
|
XCTAssertTrue(context.viewState.isWaiting, "The view should start off in the waiting state.")
|
||||||
|
|
||||||
viewModel.update(userSession: MockUserSession(clientProxy: MockClientProxy(userID: "@alice:matrix.org"),
|
viewModel.update(userSession: MockUserSession(clientProxy: MockClientProxy(userID: "@alice:matrix.org"),
|
||||||
mediaProvider: MockMediaProvider()))
|
mediaProvider: MockMediaProvider(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock()))
|
||||||
|
|
||||||
XCTAssertNotNil(context.viewState.userSession, "The user session should have been updated.")
|
XCTAssertNotNil(context.viewState.userSession, "The user session should have been updated.")
|
||||||
XCTAssertFalse(context.viewState.isWaiting, "The view should not be in the waiting state after setting a user session.")
|
XCTAssertFalse(context.viewState.isWaiting, "The view should not be in the waiting state after setting a user session.")
|
||||||
|
|||||||
Reference in New Issue
Block a user