mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Remove GenericCallLinkCoordinator, merging it into CallScreen. (#3181)
* Remove incorrect message send call and fix typo. * Add overlay coordinator presentation to the NavigationRootCoordinator. * Remove GenericCallLinkCoordinator, merging it into CallScreen. This will allow for picture in picture on call links when available.
This commit is contained in:
parent
6616d1799f
commit
e0ba9925e7
@ -218,6 +218,7 @@
|
||||
30E5628F74AD3C27A061BF25 /* QRCodeLoginScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC1B7CB061C9865B2B91B56 /* QRCodeLoginScreenViewModel.swift */; };
|
||||
3113065AABBC14CEAE6843FA /* UserSessionFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8774CF614849664B5B3C2A1 /* UserSessionFlowCoordinatorStateMachine.swift */; };
|
||||
3116693C5EB476E028990416 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74611A4182DCF5F4D42696EC /* XCTestCase.swift */; };
|
||||
3118D9ABFD4BE5A3492FF88A /* ElementCallConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC437C491EA6996513B1CEAB /* ElementCallConfiguration.swift */; };
|
||||
32B7891D937377A59606EDFC /* UserFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8599815136EFF5B73F38 /* UserFlowTests.swift */; };
|
||||
339BC18777912E1989F2F17D /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584A61D9C459FAFEF038A7C0 /* Section.swift */; };
|
||||
33CAC1226DFB8B5D8447D286 /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; };
|
||||
@ -425,6 +426,7 @@
|
||||
6298AB0906DDD3525CD78C6B /* LoremSwiftum in Frameworks */ = {isa = PBXBuildFile; productRef = 1A6B622CCFDEFB92D9CF1CA5 /* LoremSwiftum */; };
|
||||
62A7FC3A0191BC7181AA432B /* AudioRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 907FA4DE17DEA1A3738EFB83 /* AudioRecorder.swift */; };
|
||||
62C5876C4254C58C2086F0DE /* HomeScreenContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3B4B58B79A6FA250B24A1EC /* HomeScreenContent.swift */; };
|
||||
63780F9DA06573E38A471ECA /* GenericCallLinkWidgetDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C202C1C7E330F124981A31 /* GenericCallLinkWidgetDriver.swift */; };
|
||||
63CDC201A5980F304F6D0A1C /* WaveformInteractionModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEE91FB8ABB5F5884B6D940 /* WaveformInteractionModifier.swift */; };
|
||||
63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */; };
|
||||
6409CE10CFF4DCB68C4C3872 /* ScaledPaddingModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26C69EC1157D71CC61ADAE4 /* ScaledPaddingModifier.swift */; };
|
||||
@ -792,7 +794,6 @@
|
||||
B4A0C69370E6008A971463E7 /* BugReportScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C89820BB2B88D4EA28131C /* BugReportScreenViewModelProtocol.swift */; };
|
||||
B4AAB3257A83B73F53FB2689 /* StateStoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */; };
|
||||
B5321A1F5B26A0F3EC54909E /* CollapsibleFlowLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC5F5209279A752D98AAC4B2 /* CollapsibleFlowLayoutTests.swift */; };
|
||||
B53D292A5CA61E371C4CD785 /* GenericCallLinkCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514923AA9640C34F39E0500A /* GenericCallLinkCoordinator.swift */; };
|
||||
B5479997ECC516C121E6625E /* LocationMarkerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFECCE59967018204876D0A5 /* LocationMarkerView.swift */; };
|
||||
B5618E3C948584E5C1F67033 /* DTHTMLElement+AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E508AB0EDEE017FF4F6F8D1 /* DTHTMLElement+AttributedStringBuilder.swift */; };
|
||||
B5899F18AD6C56CE08FE532B /* RoomSummaryProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC83F47D2173B7538AA72E0E /* RoomSummaryProviderMock.swift */; };
|
||||
@ -1357,6 +1358,7 @@
|
||||
284FEEB0789B8894E52A7F34 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; 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>"; };
|
||||
28C202C1C7E330F124981A31 /* GenericCallLinkWidgetDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericCallLinkWidgetDriver.swift; sourceTree = "<group>"; };
|
||||
28EA8BE9EEDBD17555141C7E /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = el; path = el.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
2910422CB628D3B2BBE47449 /* SeparatorRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
295E28C3B9EAADF519BF2F44 /* AuthenticationFlowCoordinatorUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationFlowCoordinatorUITests.swift; sourceTree = "<group>"; };
|
||||
@ -1364,6 +1366,7 @@
|
||||
2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilder.swift; sourceTree = "<group>"; };
|
||||
2AB2C848BB9A7A9B618B7B89 /* TextBasedRoomTimelineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBasedRoomTimelineTests.swift; sourceTree = "<group>"; };
|
||||
2AE807361805463F5AEDD1CA /* VoiceMessagePreviewComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessagePreviewComposer.swift; sourceTree = "<group>"; };
|
||||
2AE83A3DD63BCFBB956FE5CB /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nl; path = nl.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
2AF715D4FD4710EBB637D661 /* SettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
2BA894BC09972DC45E497D37 /* TimelineInteractionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineInteractionHandler.swift; sourceTree = "<group>"; };
|
||||
2BB385E148DE55C85C0A02D6 /* SoftLogoutScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenModels.swift; sourceTree = "<group>"; };
|
||||
@ -1376,6 +1379,7 @@
|
||||
2D0946F77B696176E062D037 /* RoomMembersListScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenModels.swift; sourceTree = "<group>"; };
|
||||
2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemProxy.swift; sourceTree = "<group>"; };
|
||||
2D7A2C4A3A74F0D2FFE9356A /* MediaPlayerProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProviderTests.swift; sourceTree = "<group>"; };
|
||||
2DA3DBE1A42EAFF93889FA04 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||
2DA4F09CB613C54FDC73AE6A /* ThreadDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadDecorator.swift; sourceTree = "<group>"; };
|
||||
2DB0E533508094156D8024C3 /* TimelineStyler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyler.swift; sourceTree = "<group>"; };
|
||||
2E11E7C396ED06A154CF6DF3 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||
@ -1458,6 +1462,7 @@
|
||||
40B21E611DADDEF00307E7AC /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
4100DDE6BF3C566AB66B80CC /* MentionSuggestionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionSuggestionItemView.swift; sourceTree = "<group>"; };
|
||||
4176C3E20C772DE8D182863C /* LegalInformationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreen.swift; sourceTree = "<group>"; };
|
||||
419957D7B1C983D7B3B93678 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
41BB37D96C3EA18F3CE8675D /* RoomDirectorySearchScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenModels.swift; sourceTree = "<group>"; };
|
||||
41D041A857614A9AE13C7795 /* RoomChangePermissionsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
421E716C521F96D24ECE69B3 /* NoticeRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
@ -1488,6 +1493,7 @@
|
||||
47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomProxyProtocol.swift; sourceTree = "<group>"; };
|
||||
471BB7276C97AF60B3A5463B /* RoomDirectorySearchProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxy.swift; sourceTree = "<group>"; };
|
||||
475D47D0BFE961B02BAC5D49 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
475EB595D7527E9A8A14043E /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
47873756E45B46683D97DC32 /* LegalInformationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenModels.swift; sourceTree = "<group>"; };
|
||||
47EBB5D698CE9A25BB553A2D /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
||||
47F29139BC2A804CE5E0757E /* MediaUploadPreviewScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
@ -1528,7 +1534,6 @@
|
||||
50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixUserShareLink.swift; sourceTree = "<group>"; };
|
||||
510E89B989477E5EE8E503C0 /* PinnedEventsTimelineScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsTimelineScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
514923AA9640C34F39E0500A /* GenericCallLinkCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericCallLinkCoordinator.swift; sourceTree = "<group>"; };
|
||||
51C2BCE0BC1FC69C1B36E688 /* BugReportScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportScreenModels.swift; sourceTree = "<group>"; };
|
||||
51C454AE59914B551A6D02C0 /* UserProfileProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileProxy.swift; sourceTree = "<group>"; };
|
||||
52135BD9E0E7A091688F627A /* MessageForwardingScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenModels.swift; sourceTree = "<group>"; };
|
||||
@ -2041,6 +2046,7 @@
|
||||
CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JoinedRoomSize+MemberCount.swift"; sourceTree = "<group>"; };
|
||||
CC03209FDE8CE0810617BFFF /* RoomMembersListScreenMemberCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenMemberCell.swift; sourceTree = "<group>"; };
|
||||
CC1DDB2293A51EA4C2739351 /* RoomListFiltersEmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFiltersEmptyStateView.swift; sourceTree = "<group>"; };
|
||||
CC437C491EA6996513B1CEAB /* ElementCallConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallConfiguration.swift; sourceTree = "<group>"; };
|
||||
CC680E0E79D818706CB28CF8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
CC743C7A85E3171BCBF0A653 /* AvatarHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarHeaderView.swift; sourceTree = "<group>"; };
|
||||
CCACD75595C40EACD6AD4A74 /* AuthenticationTextFieldStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationTextFieldStyle.swift; sourceTree = "<group>"; };
|
||||
@ -2049,6 +2055,7 @@
|
||||
CD6613DE16AD26B3A74DA1F5 /* LocationRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineItemContent.swift; sourceTree = "<group>"; };
|
||||
CD700E035C85738EE4B97129 /* PerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = "<group>"; };
|
||||
CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProvider.swift; sourceTree = "<group>"; };
|
||||
CDE3F3911FF7CC639BDE5844 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
||||
D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = "<group>"; };
|
||||
@ -2110,6 +2117,7 @@
|
||||
DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaFileHandleProxy.swift; sourceTree = "<group>"; };
|
||||
DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationViewModelTests.swift; sourceTree = "<group>"; };
|
||||
DF3D25B3EDB283B5807EADCF /* ReadMarkerRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
DFFB0E7C6D8E190AFA0176DC /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uz; path = uz.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
E062C1750EFC8627DE4CAB8E /* MapTilerAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerAuthorization.swift; sourceTree = "<group>"; };
|
||||
E06AAD6D9D3F5833E7A5A2F9 /* RoomListFilterModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFilterModels.swift; sourceTree = "<group>"; };
|
||||
E0F7CCC4A9D1927223F559D5 /* AuthenticationStartScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationStartScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
@ -2138,6 +2146,7 @@
|
||||
E55B5EA766E89FF1F87C3ACB /* RoomNotificationSettingsProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsProxyProtocol.swift; sourceTree = "<group>"; };
|
||||
E5E94DCFEE803E5ABAE8ACCE /* KeychainControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerProtocol.swift; sourceTree = "<group>"; };
|
||||
E5F2B6443D1ED8602F328539 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
E60757AFE04391B43EA568B8 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E6372DD10DED30E7AD7BCE21 /* RoomListFiltersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFiltersView.swift; sourceTree = "<group>"; };
|
||||
E65DA46BD5CA83747AE144F3 /* secrets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = secrets.xcconfig; sourceTree = "<group>"; };
|
||||
E66763BD54A3A1D9C6E6F2F1 /* PinnedItemsIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedItemsIndicatorView.swift; sourceTree = "<group>"; };
|
||||
@ -3919,7 +3928,6 @@
|
||||
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */,
|
||||
4552D3466B1453F287223ADA /* SwipeRightAction.swift */,
|
||||
464C6BFAA853DC755B9C1F60 /* PinnedItemsBanner */,
|
||||
B7D3886505ECC85A06DA8258 /* Timeline */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
@ -4240,11 +4248,13 @@
|
||||
92E99C57D7F92ED16F73282C /* ElementCall */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CC437C491EA6996513B1CEAB /* ElementCallConfiguration.swift */,
|
||||
33AE897D86784CCA5E4E9227 /* ElementCallService.swift */,
|
||||
406C90AF8C3E98DF5D4E5430 /* ElementCallServiceConstants.swift */,
|
||||
6FC8B21E86B137BE4E91F82A /* ElementCallServiceProtocol.swift */,
|
||||
309AD8BAE6437C31BA7157BF /* ElementCallWidgetDriver.swift */,
|
||||
A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */,
|
||||
28C202C1C7E330F124981A31 /* GenericCallLinkWidgetDriver.swift */,
|
||||
);
|
||||
path = ElementCall;
|
||||
sourceTree = "<group>";
|
||||
@ -4482,7 +4492,6 @@
|
||||
A448A3A8F764174C60CD0CA1 /* Other */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
514923AA9640C34F39E0500A /* GenericCallLinkCoordinator.swift */,
|
||||
BF34A2FD6797535C95AC918D /* PlaceholderScreenCoordinator.swift */,
|
||||
854BCEAF2A832176FAACD2CB /* SplashScreenCoordinator.swift */,
|
||||
);
|
||||
@ -4699,13 +4708,6 @@
|
||||
path = UserIndicator;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B7D3886505ECC85A06DA8258 /* Timeline */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = Timeline;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B86CF59E083C82C2A842E4AD /* RoomMemberDetailsScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -5644,6 +5646,7 @@
|
||||
id,
|
||||
it,
|
||||
ka,
|
||||
nl,
|
||||
pl,
|
||||
pt,
|
||||
"pt-BR",
|
||||
@ -5652,6 +5655,7 @@
|
||||
sk,
|
||||
sv,
|
||||
uk,
|
||||
uz,
|
||||
"zh-Hans",
|
||||
"zh-Hant-TW",
|
||||
);
|
||||
@ -6252,6 +6256,7 @@
|
||||
2955F4C160CFD7794D819C64 /* EffectsScene.swift in Sources */,
|
||||
AE1160076F663BF14E0E893A /* EffectsView.swift in Sources */,
|
||||
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */,
|
||||
3118D9ABFD4BE5A3492FF88A /* ElementCallConfiguration.swift in Sources */,
|
||||
5732395A4F71F51F9C754C5A /* ElementCallService.swift in Sources */,
|
||||
3895969759E68FAB90C63EF7 /* ElementCallServiceConstants.swift in Sources */,
|
||||
8E7A902CA16E24928F83646C /* ElementCallServiceMock.swift in Sources */,
|
||||
@ -6305,7 +6310,7 @@
|
||||
7807B1DEE32617896886A8E5 /* FormattingToolbar.swift in Sources */,
|
||||
46BA7F4B4D3A7164DED44B88 /* FullscreenDialog.swift in Sources */,
|
||||
F18CA61A58C77C84F551B8E7 /* GeneratedMocks.swift in Sources */,
|
||||
B53D292A5CA61E371C4CD785 /* GenericCallLinkCoordinator.swift in Sources */,
|
||||
63780F9DA06573E38A471ECA /* GenericCallLinkWidgetDriver.swift in Sources */,
|
||||
4295E5F850897710A51AE114 /* GeoURI.swift in Sources */,
|
||||
F0DACC95F24128A54CD537E4 /* GlobalSearchScreen.swift in Sources */,
|
||||
9F11E743EA01482E78A438B0 /* GlobalSearchScreenCell.swift in Sources */,
|
||||
@ -6966,6 +6971,7 @@
|
||||
D196116D2DD3F2757D45FCB7 /* hu */,
|
||||
330AF4D121C3396F7A14B21D /* id */,
|
||||
61B33F23681660E940BA57F4 /* it */,
|
||||
2DA3DBE1A42EAFF93889FA04 /* nl */,
|
||||
BEE365C5A4E90ACBE398EFFE /* pt */,
|
||||
FABAC5C4373B0EC24D399663 /* pt-BR */,
|
||||
105429F29096729EDD3152CF /* ru */,
|
||||
@ -6993,6 +6999,7 @@
|
||||
475D47D0BFE961B02BAC5D49 /* id */,
|
||||
6FC5015B9634698BDB8701AF /* it */,
|
||||
D1896F6288D80E1F3EFB3DF8 /* ka */,
|
||||
2AE83A3DD63BCFBB956FE5CB /* nl */,
|
||||
4C8D988E82A8DFA13BE46F7C /* pl */,
|
||||
8166F121C79C7B62BF01D508 /* pt */,
|
||||
21BA866267F84BF4350B0CB7 /* pt-BR */,
|
||||
@ -7001,6 +7008,7 @@
|
||||
667DD3A9D932D7D9EB380CAA /* sk */,
|
||||
0EE9EAF0309A2A1D67D8FAF5 /* sv */,
|
||||
5F12E996BFBEB43815189ABF /* uk */,
|
||||
DFFB0E7C6D8E190AFA0176DC /* uz */,
|
||||
AB26D5444A4A7E095222DE8B /* zh-Hans */,
|
||||
49E6066092ED45E36BB306F7 /* zh-Hant-TW */,
|
||||
);
|
||||
@ -7023,6 +7031,7 @@
|
||||
EF98A02DED04075F7CF0C721 /* id */,
|
||||
7B04BD3874D736127A8156B8 /* it */,
|
||||
4629710C0337ADD9C8909542 /* ka */,
|
||||
CDE3F3911FF7CC639BDE5844 /* nl */,
|
||||
8140010A796DB2C7977B6643 /* pl */,
|
||||
0CB569EAA5017B5B23970655 /* pt */,
|
||||
8A9AE4967817E9608E22EB44 /* pt-BR */,
|
||||
@ -7031,6 +7040,7 @@
|
||||
AD378D580A41E42560C60E9C /* sk */,
|
||||
ACA11F7F50A4A3887A18CA5A /* sv */,
|
||||
ADCB8A232D3A8FB3E16A7303 /* uk */,
|
||||
475EB595D7527E9A8A14043E /* uz */,
|
||||
284FEEB0789B8894E52A7F34 /* zh-Hans */,
|
||||
91CF6F7D08228D16BA69B63B /* zh-Hant-TW */,
|
||||
);
|
||||
@ -7052,8 +7062,10 @@
|
||||
1D652E78832289CD9EB64488 /* hu */,
|
||||
7199693797B66245EF97BCF5 /* id */,
|
||||
44C314C00533E2C297796B60 /* it */,
|
||||
E60757AFE04391B43EA568B8 /* nl */,
|
||||
997BF045585AF6DB2EBC5755 /* pl */,
|
||||
A8DF55467ED4CE76B7AE9A33 /* pt */,
|
||||
419957D7B1C983D7B3B93678 /* pt-BR */,
|
||||
86C8CE2630F54D5FE1591786 /* ro */,
|
||||
9B7D8D3638864B7482E148CC /* ru */,
|
||||
7D39AF1F659923D77778511E /* sk */,
|
||||
|
@ -212,7 +212,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
if let userSessionFlowCoordinator {
|
||||
userSessionFlowCoordinator.handleAppRoute(route, animated: true)
|
||||
} else {
|
||||
navigationRootCoordinator.setSheetCoordinator(GenericCallLinkCoordinator(parameters: .init(url: url)))
|
||||
presentCallScreen(genericCallLink: url)
|
||||
}
|
||||
case .userProfile(let userID):
|
||||
if isExternalURL {
|
||||
@ -648,6 +648,31 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
|
||||
elementCallService.setClientProxy(userSession.clientProxy)
|
||||
}
|
||||
|
||||
private func presentCallScreen(genericCallLink url: URL) {
|
||||
let configuration = ElementCallConfiguration(genericCallLink: url)
|
||||
|
||||
let callScreenCoordinator = CallScreenCoordinator(parameters: .init(elementCallService: elementCallService,
|
||||
configuration: configuration,
|
||||
elementCallPictureInPictureEnabled: false,
|
||||
appHooks: appHooks))
|
||||
|
||||
callScreenCoordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .pictureInPictureStarted, .pictureInPictureStopped:
|
||||
// Don't allow PiP when signed out - the user could login at which point we'd
|
||||
// need to hand over the call from here to the user session flow coordinator.
|
||||
MXLog.error("Picture in Picture not supported before login.")
|
||||
case .dismiss:
|
||||
navigationRootCoordinator.setOverlayCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationRootCoordinator.setOverlayCoordinator(callScreenCoordinator, animated: false)
|
||||
}
|
||||
|
||||
private func configureNotificationManager() {
|
||||
notificationManager.setUserSession(userSession)
|
||||
|
@ -457,6 +457,7 @@ private struct NavigationSplitCoordinatorView: View {
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: navigationSplitCoordinator.overlayPresentationMode)
|
||||
.animation(.elementDefault, value: navigationSplitCoordinator.overlayModule)
|
||||
}
|
||||
// Handle `horizontalSizeClass` changes breaking the navigation bar
|
||||
// https://github.com/element-hq/element-x-ios/issues/617
|
||||
|
@ -56,6 +56,25 @@ class NavigationRootCoordinator: ObservableObject, CoordinatorProtocol, CustomSt
|
||||
sheetModule?.coordinator
|
||||
}
|
||||
|
||||
@Published fileprivate var overlayModule: NavigationModule? {
|
||||
didSet {
|
||||
if let oldValue {
|
||||
logPresentationChange("Remove overlay", oldValue)
|
||||
oldValue.tearDown()
|
||||
}
|
||||
|
||||
if let overlayModule {
|
||||
logPresentationChange("Set overlay", overlayModule)
|
||||
overlayModule.coordinator?.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The currently displayed overlay coordinator
|
||||
var overlayCoordinator: (any CoordinatorProtocol)? {
|
||||
overlayModule?.coordinator
|
||||
}
|
||||
|
||||
/// Sets or replaces the presented coordinator
|
||||
/// - Parameter coordinator: the coordinator to display
|
||||
func setRootCoordinator(_ coordinator: (any CoordinatorProtocol)?, animated: Bool = true, dismissalCallback: (() -> Void)? = nil) {
|
||||
@ -90,6 +109,31 @@ class NavigationRootCoordinator: ObservableObject, CoordinatorProtocol, CustomSt
|
||||
sheetModule = NavigationModule(coordinator, dismissalCallback: dismissalCallback)
|
||||
}
|
||||
}
|
||||
|
||||
/// Present an overlay on top of the split view
|
||||
/// - Parameters:
|
||||
/// - coordinator: the coordinator to display
|
||||
/// - animated: whether the transition should be animated
|
||||
/// - dismissalCallback: called when the overlay has been dismissed, programatically or otherwise
|
||||
func setOverlayCoordinator(_ coordinator: (any CoordinatorProtocol)?,
|
||||
animated: Bool = true,
|
||||
dismissalCallback: (() -> Void)? = nil) {
|
||||
guard let coordinator else {
|
||||
overlayModule = nil
|
||||
return
|
||||
}
|
||||
|
||||
if overlayModule?.coordinator === coordinator {
|
||||
fatalError("Cannot use the same coordinator more than once")
|
||||
}
|
||||
|
||||
var transaction = Transaction()
|
||||
transaction.disablesAnimations = !animated
|
||||
|
||||
withTransaction(transaction) {
|
||||
overlayModule = NavigationModule(coordinator, dismissalCallback: dismissalCallback)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CoordinatorProtocol
|
||||
|
||||
@ -127,5 +171,14 @@ private struct NavigationRootCoordinatorView: View {
|
||||
.sheet(item: $rootCoordinator.sheetModule) { module in
|
||||
module.coordinator?.toPresentable()
|
||||
}
|
||||
.overlay {
|
||||
Group {
|
||||
if let coordinator = rootCoordinator.overlayModule?.coordinator {
|
||||
coordinator.toPresentable()
|
||||
.transition(.opacity)
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: rootCoordinator.overlayModule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -268,11 +268,9 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
case .userProfile(let userID):
|
||||
stateMachine.processEvent(.showUserProfileScreen(userID: userID), userInfo: .init(animated: animated))
|
||||
case .call(let roomID):
|
||||
Task {
|
||||
await presentCallScreen(roomID: roomID)
|
||||
}
|
||||
Task { await presentCallScreen(roomID: roomID) }
|
||||
case .genericCallLink(let url):
|
||||
navigationSplitCoordinator.setSheetCoordinator(GenericCallLinkCoordinator(parameters: .init(url: url)), animated: animated)
|
||||
presentCallScreen(genericCallLink: url)
|
||||
case .settings, .chatBackupSettings:
|
||||
settingsFlowCoordinator.handleAppRoute(appRoute, animated: animated)
|
||||
case .oidcCallback:
|
||||
@ -558,23 +556,39 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
// MARK: Calls
|
||||
|
||||
private var callScreenPictureInPictureController: AVPictureInPictureController?
|
||||
private func presentCallScreen(genericCallLink url: URL) {
|
||||
presentCallScreen(configuration: .init(genericCallLink: url))
|
||||
}
|
||||
|
||||
private func presentCallScreen(roomID: String) async {
|
||||
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) else {
|
||||
return
|
||||
}
|
||||
|
||||
presentCallScreen(roomProxy: roomProxy)
|
||||
}
|
||||
|
||||
private func presentCallScreen(roomProxy: RoomProxyProtocol) {
|
||||
guard elementCallService.ongoingCallRoomID != roomProxy.id else {
|
||||
let colorScheme: ColorScheme = appMediator.windowManager.mainWindow.traitCollection.userInterfaceStyle == .light ? .light : .dark
|
||||
presentCallScreen(configuration: .init(roomProxy: roomProxy,
|
||||
clientProxy: userSession.clientProxy,
|
||||
clientID: InfoPlistReader.main.bundleIdentifier,
|
||||
elementCallBaseURL: appSettings.elementCallBaseURL,
|
||||
elementCallBaseURLOverride: appSettings.elementCallBaseURLOverride,
|
||||
colorScheme: colorScheme))
|
||||
}
|
||||
|
||||
private var callScreenPictureInPictureController: AVPictureInPictureController?
|
||||
private func presentCallScreen(configuration: ElementCallConfiguration) {
|
||||
guard elementCallService.ongoingCallRoomID != configuration.callID else {
|
||||
MXLog.info("Returning to existing call.")
|
||||
callScreenPictureInPictureController?.stopPictureInPicture()
|
||||
return
|
||||
}
|
||||
|
||||
let colorScheme: ColorScheme = appMediator.windowManager.mainWindow.traitCollection.userInterfaceStyle == .light ? .light : .dark
|
||||
let callScreenCoordinator = CallScreenCoordinator(parameters: .init(elementCallService: elementCallService,
|
||||
clientProxy: userSession.clientProxy,
|
||||
roomProxy: roomProxy,
|
||||
clientID: InfoPlistReader.main.bundleIdentifier,
|
||||
elementCallBaseURL: appSettings.elementCallBaseURL,
|
||||
elementCallBaseURLOverride: appSettings.elementCallBaseURLOverride,
|
||||
configuration: configuration,
|
||||
elementCallPictureInPictureEnabled: appSettings.elementCallPictureInPictureEnabled,
|
||||
colorScheme: colorScheme,
|
||||
appHooks: appHooks))
|
||||
|
||||
callScreenCoordinator.actions
|
||||
@ -600,14 +614,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
analytics.track(screen: .RoomCall)
|
||||
}
|
||||
|
||||
private func presentCallScreen(roomID: String) async {
|
||||
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) else {
|
||||
return
|
||||
}
|
||||
|
||||
presentCallScreen(roomProxy: roomProxy)
|
||||
}
|
||||
|
||||
private func dismissCallScreenIfNeeded() {
|
||||
guard navigationSplitCoordinator.sheetCoordinator is CallScreenCoordinator else {
|
||||
return
|
||||
|
@ -5142,17 +5142,17 @@ class ElementCallWidgetDriverMock: ElementCallWidgetDriverProtocol {
|
||||
return startBaseURLClientIDColorSchemeReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendMessage
|
||||
//MARK: - handleMessage
|
||||
|
||||
var sendMessageUnderlyingCallsCount = 0
|
||||
var sendMessageCallsCount: Int {
|
||||
var handleMessageUnderlyingCallsCount = 0
|
||||
var handleMessageCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return sendMessageUnderlyingCallsCount
|
||||
return handleMessageUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = sendMessageUnderlyingCallsCount
|
||||
returnValue = handleMessageUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@ -5160,29 +5160,29 @@ class ElementCallWidgetDriverMock: ElementCallWidgetDriverProtocol {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
sendMessageUnderlyingCallsCount = newValue
|
||||
handleMessageUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
sendMessageUnderlyingCallsCount = newValue
|
||||
handleMessageUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var sendMessageCalled: Bool {
|
||||
return sendMessageCallsCount > 0
|
||||
var handleMessageCalled: Bool {
|
||||
return handleMessageCallsCount > 0
|
||||
}
|
||||
var sendMessageReceivedMessage: String?
|
||||
var sendMessageReceivedInvocations: [String] = []
|
||||
var handleMessageReceivedMessage: String?
|
||||
var handleMessageReceivedInvocations: [String] = []
|
||||
|
||||
var sendMessageUnderlyingReturnValue: Result<Bool, ElementCallWidgetDriverError>!
|
||||
var sendMessageReturnValue: Result<Bool, ElementCallWidgetDriverError>! {
|
||||
var handleMessageUnderlyingReturnValue: Result<Bool, ElementCallWidgetDriverError>!
|
||||
var handleMessageReturnValue: Result<Bool, ElementCallWidgetDriverError>! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return sendMessageUnderlyingReturnValue
|
||||
return handleMessageUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: Result<Bool, ElementCallWidgetDriverError>? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = sendMessageUnderlyingReturnValue
|
||||
returnValue = handleMessageUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@ -5190,26 +5190,26 @@ class ElementCallWidgetDriverMock: ElementCallWidgetDriverProtocol {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
sendMessageUnderlyingReturnValue = newValue
|
||||
handleMessageUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
sendMessageUnderlyingReturnValue = newValue
|
||||
handleMessageUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var sendMessageClosure: ((String) async -> Result<Bool, ElementCallWidgetDriverError>)?
|
||||
var handleMessageClosure: ((String) async -> Result<Bool, ElementCallWidgetDriverError>)?
|
||||
|
||||
func sendMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError> {
|
||||
sendMessageCallsCount += 1
|
||||
sendMessageReceivedMessage = message
|
||||
func handleMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError> {
|
||||
handleMessageCallsCount += 1
|
||||
handleMessageReceivedMessage = message
|
||||
DispatchQueue.main.async {
|
||||
self.sendMessageReceivedInvocations.append(message)
|
||||
self.handleMessageReceivedInvocations.append(message)
|
||||
}
|
||||
if let sendMessageClosure = sendMessageClosure {
|
||||
return await sendMessageClosure(message)
|
||||
if let handleMessageClosure = handleMessageClosure {
|
||||
return await handleMessageClosure(message)
|
||||
} else {
|
||||
return sendMessageReturnValue
|
||||
return handleMessageReturnValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11129,17 +11129,17 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
return elementCallWidgetDriverDeviceIDReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - sendCallNotificationIfNeeeded
|
||||
//MARK: - sendCallNotificationIfNeeded
|
||||
|
||||
var sendCallNotificationIfNeeededUnderlyingCallsCount = 0
|
||||
var sendCallNotificationIfNeeededCallsCount: Int {
|
||||
var sendCallNotificationIfNeededUnderlyingCallsCount = 0
|
||||
var sendCallNotificationIfNeededCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return sendCallNotificationIfNeeededUnderlyingCallsCount
|
||||
return sendCallNotificationIfNeededUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = sendCallNotificationIfNeeededUnderlyingCallsCount
|
||||
returnValue = sendCallNotificationIfNeededUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@ -11147,27 +11147,27 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
sendCallNotificationIfNeeededUnderlyingCallsCount = newValue
|
||||
sendCallNotificationIfNeededUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
sendCallNotificationIfNeeededUnderlyingCallsCount = newValue
|
||||
sendCallNotificationIfNeededUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var sendCallNotificationIfNeeededCalled: Bool {
|
||||
return sendCallNotificationIfNeeededCallsCount > 0
|
||||
var sendCallNotificationIfNeededCalled: Bool {
|
||||
return sendCallNotificationIfNeededCallsCount > 0
|
||||
}
|
||||
|
||||
var sendCallNotificationIfNeeededUnderlyingReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendCallNotificationIfNeeededReturnValue: Result<Void, RoomProxyError>! {
|
||||
var sendCallNotificationIfNeededUnderlyingReturnValue: Result<Void, RoomProxyError>!
|
||||
var sendCallNotificationIfNeededReturnValue: Result<Void, RoomProxyError>! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return sendCallNotificationIfNeeededUnderlyingReturnValue
|
||||
return sendCallNotificationIfNeededUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: Result<Void, RoomProxyError>? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = sendCallNotificationIfNeeededUnderlyingReturnValue
|
||||
returnValue = sendCallNotificationIfNeededUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@ -11175,22 +11175,22 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
sendCallNotificationIfNeeededUnderlyingReturnValue = newValue
|
||||
sendCallNotificationIfNeededUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
sendCallNotificationIfNeeededUnderlyingReturnValue = newValue
|
||||
sendCallNotificationIfNeededUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var sendCallNotificationIfNeeededClosure: (() async -> Result<Void, RoomProxyError>)?
|
||||
var sendCallNotificationIfNeededClosure: (() async -> Result<Void, RoomProxyError>)?
|
||||
|
||||
func sendCallNotificationIfNeeeded() async -> Result<Void, RoomProxyError> {
|
||||
sendCallNotificationIfNeeededCallsCount += 1
|
||||
if let sendCallNotificationIfNeeededClosure = sendCallNotificationIfNeeededClosure {
|
||||
return await sendCallNotificationIfNeeededClosure()
|
||||
func sendCallNotificationIfNeeded() async -> Result<Void, RoomProxyError> {
|
||||
sendCallNotificationIfNeededCallsCount += 1
|
||||
if let sendCallNotificationIfNeededClosure = sendCallNotificationIfNeededClosure {
|
||||
return await sendCallNotificationIfNeededClosure()
|
||||
} else {
|
||||
return sendCallNotificationIfNeeededReturnValue
|
||||
return sendCallNotificationIfNeededReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - matrixToPermalink
|
||||
|
@ -153,7 +153,7 @@ extension RoomProxyMock {
|
||||
widgetDriver.startBaseURLClientIDColorSchemeReturnValue = .success(url)
|
||||
|
||||
elementCallWidgetDriverDeviceIDReturnValue = widgetDriver
|
||||
sendCallNotificationIfNeeededReturnValue = .success(())
|
||||
sendCallNotificationIfNeededReturnValue = .success(())
|
||||
|
||||
matrixToPermalinkReturnValue = .success(.homeDirectory)
|
||||
matrixToEventPermalinkReturnValue = .success(.homeDirectory)
|
||||
|
@ -20,13 +20,8 @@ import SwiftUI
|
||||
|
||||
struct CallScreenCoordinatorParameters {
|
||||
let elementCallService: ElementCallServiceProtocol
|
||||
let clientProxy: ClientProxyProtocol
|
||||
let roomProxy: RoomProxyProtocol
|
||||
let clientID: String
|
||||
let elementCallBaseURL: URL
|
||||
let elementCallBaseURLOverride: URL?
|
||||
let configuration: ElementCallConfiguration
|
||||
let elementCallPictureInPictureEnabled: Bool
|
||||
let colorScheme: ColorScheme
|
||||
let appHooks: AppHooks
|
||||
}
|
||||
|
||||
@ -50,13 +45,8 @@ final class CallScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
init(parameters: CallScreenCoordinatorParameters) {
|
||||
viewModel = CallScreenViewModel(elementCallService: parameters.elementCallService,
|
||||
clientProxy: parameters.clientProxy,
|
||||
roomProxy: parameters.roomProxy,
|
||||
clientID: parameters.clientID,
|
||||
elementCallBaseURL: parameters.elementCallBaseURL,
|
||||
elementCallBaseURLOverride: parameters.elementCallBaseURLOverride,
|
||||
configuration: parameters.configuration,
|
||||
elementCallPictureInPictureEnabled: parameters.elementCallPictureInPictureEnabled,
|
||||
colorScheme: parameters.colorScheme,
|
||||
appHooks: parameters.appHooks)
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ typealias CallScreenViewModelType = StateStoreViewModel<CallScreenViewState, Cal
|
||||
|
||||
class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol {
|
||||
private let elementCallService: ElementCallServiceProtocol
|
||||
private let roomProxy: RoomProxyProtocol
|
||||
private let configuration: ElementCallConfiguration
|
||||
private let isPictureInPictureEnabled: Bool
|
||||
|
||||
private let widgetDriver: ElementCallWidgetDriverProtocol
|
||||
@ -42,35 +42,28 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
/// - callBaseURL: Which Element Call instance should be used
|
||||
/// - clientID: Something to identify the current client on the Element Call side
|
||||
init(elementCallService: ElementCallServiceProtocol,
|
||||
clientProxy: ClientProxyProtocol,
|
||||
roomProxy: RoomProxyProtocol,
|
||||
clientID: String,
|
||||
elementCallBaseURL: URL,
|
||||
elementCallBaseURLOverride: URL?,
|
||||
configuration: ElementCallConfiguration,
|
||||
elementCallPictureInPictureEnabled: Bool,
|
||||
colorScheme: ColorScheme,
|
||||
appHooks: AppHooks) {
|
||||
guard let deviceID = clientProxy.deviceID else { fatalError("Missing device ID for the call.") }
|
||||
|
||||
self.elementCallService = elementCallService
|
||||
self.roomProxy = roomProxy
|
||||
self.configuration = configuration
|
||||
isPictureInPictureEnabled = elementCallPictureInPictureEnabled
|
||||
|
||||
widgetDriver = roomProxy.elementCallWidgetDriver(deviceID: deviceID)
|
||||
switch configuration.kind {
|
||||
case .genericCallLink(let url):
|
||||
widgetDriver = GenericCallLinkWidgetDriver(url: url)
|
||||
case .roomCall(let roomProxy, let clientProxy, _, _, _, _):
|
||||
guard let deviceID = clientProxy.deviceID else { fatalError("Missing device ID for the call.") }
|
||||
widgetDriver = roomProxy.elementCallWidgetDriver(deviceID: deviceID)
|
||||
}
|
||||
|
||||
super.init(initialViewState: CallScreenViewState(messageHandler: Self.eventHandlerName,
|
||||
script: Self.eventHandlerInjectionScript,
|
||||
certificateValidator: appHooks.certificateValidatorHook))
|
||||
|
||||
state.bindings.javaScriptMessageHandler = { [weak self] message in
|
||||
guard let self,
|
||||
let message = message as? String else {
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
await self.widgetDriver.sendMessage(message)
|
||||
}
|
||||
guard let self, let message = message as? String else { return }
|
||||
Task { await self.widgetDriver.handleMessage(message) }
|
||||
}
|
||||
|
||||
elementCallService.actions
|
||||
@ -80,8 +73,8 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
|
||||
switch action {
|
||||
case let .setAudioEnabled(enabled, roomID):
|
||||
guard roomID == roomProxy.id else {
|
||||
MXLog.error("Received mute request for a different room: \(roomID) != \(roomProxy.id)")
|
||||
guard roomID == configuration.callID else {
|
||||
MXLog.error("Received mute request for a different room: \(roomID) != \(configuration.callID)")
|
||||
return
|
||||
}
|
||||
|
||||
@ -114,48 +107,11 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
case .callEnded:
|
||||
actionsSubject.send(.dismiss)
|
||||
case .mediaStateChanged(let audioEnabled, _):
|
||||
elementCallService.setAudioEnabled(audioEnabled, roomID: roomProxy.id)
|
||||
elementCallService.setAudioEnabled(audioEnabled, roomID: configuration.callID)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
// Wait for room states to be up to date before starting the call and notifying others
|
||||
syncUpdateCancellable = clientProxy.actionsPublisher
|
||||
.filter(\.isSyncUpdate)
|
||||
.timeout(.seconds(5), scheduler: DispatchQueue.main)
|
||||
.first() // Timeout will make the publisher complete, use first to handle both branches in the same place
|
||||
.sink(receiveCompletion: { [weak self] _ in
|
||||
Task { [weak self] in
|
||||
guard let self else { return }
|
||||
|
||||
let baseURL = if let elementCallBaseURLOverride {
|
||||
elementCallBaseURLOverride
|
||||
} else if case .success(let wellKnown) = await clientProxy.getElementWellKnown(), let wellKnownCall = wellKnown?.call {
|
||||
wellKnownCall.widgetURL
|
||||
} else {
|
||||
elementCallBaseURL
|
||||
}
|
||||
|
||||
switch await widgetDriver.start(baseURL: baseURL, clientID: clientID, colorScheme: colorScheme) {
|
||||
case .success(let url):
|
||||
state.url = url
|
||||
case .failure(let error):
|
||||
MXLog.error("Failed starting ElementCall Widget Driver with error: \(error)")
|
||||
state.bindings.alertInfo = .init(id: UUID(), title: L10n.errorUnknown, primaryButton: .init(title: L10n.actionOk, action: { [weak self] in
|
||||
self?.actionsSubject.send(.dismiss)
|
||||
}))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await elementCallService.setupCallSession(roomID: roomProxy.id, roomDisplayName: roomProxy.roomTitle)
|
||||
|
||||
_ = await roomProxy.sendCallNotificationIfNeeeded()
|
||||
|
||||
syncUpdateCancellable = nil
|
||||
}
|
||||
}, receiveValue: { _ in })
|
||||
|
||||
// Use did start otherwise there's a black box left on the screen during the pip controller animation.
|
||||
NotificationCenter.default.publisher(for: .init("AVPictureInPictureControllerDidStartNotification"))
|
||||
.sink { [weak self] notification in
|
||||
@ -172,6 +128,8 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
Task { try await self.state.bindings.javaScriptEvaluator?("controls.disableCompatPip()") }
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
setupCall()
|
||||
}
|
||||
|
||||
override func process(viewAction: CallScreenViewAction) {
|
||||
@ -194,6 +152,54 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupCall() {
|
||||
switch configuration.kind {
|
||||
case .genericCallLink(let url):
|
||||
state.url = url
|
||||
// We need widget messaging to work before enabling CallKit, otherwise mute, hangup etc do nothing.
|
||||
|
||||
case .roomCall(let roomProxy, let clientProxy, let clientID, let elementCallBaseURL, let elementCallBaseURLOverride, let colorScheme):
|
||||
// Wait for room states to be up to date before starting the call and notifying others
|
||||
syncUpdateCancellable = clientProxy.actionsPublisher
|
||||
.filter(\.isSyncUpdate)
|
||||
.timeout(.seconds(5), scheduler: DispatchQueue.main)
|
||||
.first() // Timeout will make the publisher complete, use first to handle both branches in the same place
|
||||
.sink(receiveCompletion: { [weak self] _ in
|
||||
Task { [weak self] in
|
||||
guard let self else { return }
|
||||
|
||||
let baseURL = if let elementCallBaseURLOverride {
|
||||
elementCallBaseURLOverride
|
||||
} else if case .success(let wellKnown) = await clientProxy.getElementWellKnown(), let wellKnownCall = wellKnown?.call {
|
||||
wellKnownCall.widgetURL
|
||||
} else {
|
||||
elementCallBaseURL
|
||||
}
|
||||
|
||||
switch await widgetDriver.start(baseURL: baseURL, clientID: clientID, colorScheme: colorScheme) {
|
||||
case .success(let url):
|
||||
state.url = url
|
||||
case .failure(let error):
|
||||
MXLog.error("Failed starting ElementCall Widget Driver with error: \(error)")
|
||||
state.bindings.alertInfo = .init(id: UUID(),
|
||||
title: L10n.errorUnknown,
|
||||
primaryButton: .init(title: L10n.actionOk) {
|
||||
self.actionsSubject.send(.dismiss)
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await elementCallService.setupCallSession(roomID: roomProxy.id, roomDisplayName: roomProxy.roomTitle)
|
||||
|
||||
_ = await roomProxy.sendCallNotificationIfNeeded()
|
||||
|
||||
syncUpdateCancellable = nil
|
||||
}
|
||||
}, receiveValue: { _ in })
|
||||
}
|
||||
}
|
||||
|
||||
private func handleBackwardsNavigation() {
|
||||
#if targetEnvironment(simulator)
|
||||
if UIDevice.current.isPhone {
|
||||
@ -237,7 +243,6 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
do {
|
||||
let data = try JSONEncoder().encode(message)
|
||||
let json = String(decoding: data, as: UTF8.self)
|
||||
_ = await widgetDriver.sendMessage(json)
|
||||
|
||||
await postJSONToWidget(json)
|
||||
} catch {
|
||||
|
@ -34,11 +34,11 @@ struct CallScreen: View {
|
||||
Image(systemSymbol: .chevronBackward)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
.offset(y: -8)
|
||||
// .padding(.leading, -8) // Fixes the button alignment, but harder to tap.
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(item: $context.alertInfo)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
@ -50,8 +50,6 @@ struct CallScreen: View {
|
||||
// This URL is stable, forces view reloads if this representable is ever reused for another url
|
||||
.id(context.viewState.url)
|
||||
.ignoresSafeArea(edges: .bottom)
|
||||
.presentationDragIndicator(.visible)
|
||||
.alert(item: $context.alertInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -211,7 +209,7 @@ struct CallScreen_Previews: PreviewProvider {
|
||||
clientProxy.deviceID = "call-device-id"
|
||||
|
||||
let roomProxy = RoomProxyMock()
|
||||
roomProxy.sendCallNotificationIfNeeededReturnValue = .success(())
|
||||
roomProxy.sendCallNotificationIfNeededReturnValue = .success(())
|
||||
|
||||
let widgetDriver = ElementCallWidgetDriverMock()
|
||||
widgetDriver.underlyingMessagePublisher = .init()
|
||||
@ -221,13 +219,13 @@ struct CallScreen_Previews: PreviewProvider {
|
||||
roomProxy.elementCallWidgetDriverDeviceIDReturnValue = widgetDriver
|
||||
|
||||
return CallScreenViewModel(elementCallService: ElementCallServiceMock(.init()),
|
||||
clientProxy: clientProxy,
|
||||
roomProxy: roomProxy,
|
||||
clientID: "io.element.elementx",
|
||||
elementCallBaseURL: "https://call.element.io",
|
||||
elementCallBaseURLOverride: nil,
|
||||
configuration: .init(roomProxy: roomProxy,
|
||||
clientProxy: clientProxy,
|
||||
clientID: "io.element.elementx",
|
||||
elementCallBaseURL: "https://call.element.io",
|
||||
elementCallBaseURLOverride: nil,
|
||||
colorScheme: .light),
|
||||
elementCallPictureInPictureEnabled: false,
|
||||
colorScheme: .light,
|
||||
appHooks: AppHooks())
|
||||
}()
|
||||
|
||||
|
@ -1,127 +0,0 @@
|
||||
//
|
||||
// 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 SwiftUI
|
||||
import WebKit
|
||||
|
||||
struct GenericCallLinkCoordinatorParameters {
|
||||
let url: URL
|
||||
}
|
||||
|
||||
private enum GenericCallLinkQueryParameters {
|
||||
static let appPrompt = "appPrompt"
|
||||
static let confineToRoom = "confineToRoom"
|
||||
}
|
||||
|
||||
class GenericCallLinkCoordinator: CoordinatorProtocol {
|
||||
private let parameters: GenericCallLinkCoordinatorParameters
|
||||
|
||||
init(parameters: GenericCallLinkCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(WebView(url: parameters.url)
|
||||
// This URL is stable, forces view reloads if this representable is ever reused for another url
|
||||
.id(parameters.url)
|
||||
.ignoresSafeArea(edges: .bottom)
|
||||
.presentationDragIndicator(.visible))
|
||||
}
|
||||
}
|
||||
|
||||
private struct WebView: UIViewRepresentable {
|
||||
let url: URL
|
||||
|
||||
func makeUIView(context: Context) -> WKWebView {
|
||||
context.coordinator.webView
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(url: url)
|
||||
}
|
||||
|
||||
func updateUIView(_ webView: WKWebView, context: Context) {
|
||||
webView.load(URLRequest(url: context.coordinator.url))
|
||||
}
|
||||
|
||||
@MainActor
|
||||
class Coordinator: NSObject, WKUIDelegate, WKNavigationDelegate {
|
||||
let url: URL
|
||||
private(set) var webView: WKWebView!
|
||||
|
||||
init(url: URL) {
|
||||
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) {
|
||||
var fragmentQueryItems = urlComponents.fragmentQueryItems ?? []
|
||||
|
||||
fragmentQueryItems.removeAll { $0.name == GenericCallLinkQueryParameters.appPrompt }
|
||||
fragmentQueryItems.removeAll { $0.name == GenericCallLinkQueryParameters.confineToRoom }
|
||||
|
||||
fragmentQueryItems.append(.init(name: GenericCallLinkQueryParameters.appPrompt, value: "false"))
|
||||
fragmentQueryItems.append(.init(name: GenericCallLinkQueryParameters.confineToRoom, value: "true"))
|
||||
|
||||
urlComponents.fragmentQueryItems = fragmentQueryItems
|
||||
|
||||
if let adjustedURL = urlComponents.url {
|
||||
self.url = adjustedURL
|
||||
} else {
|
||||
MXLog.error("Failed adjusting URL with components: \(urlComponents)")
|
||||
self.url = url
|
||||
}
|
||||
} else {
|
||||
MXLog.error("Failed constructing URL components for url: \(url)")
|
||||
self.url = url
|
||||
}
|
||||
|
||||
super.init()
|
||||
|
||||
let configuration = WKWebViewConfiguration()
|
||||
|
||||
configuration.allowsInlineMediaPlayback = true
|
||||
configuration.allowsPictureInPictureMediaPlayback = true
|
||||
|
||||
webView = WKWebView(frame: .zero, configuration: configuration)
|
||||
webView.uiDelegate = self
|
||||
}
|
||||
|
||||
// MARK: - WKUIDelegate
|
||||
|
||||
func webView(_ webView: WKWebView, decideMediaCapturePermissionsFor origin: WKSecurityOrigin, initiatedBy frame: WKFrameInfo, type: WKMediaCaptureType) async -> WKPermissionDecision {
|
||||
// Don't allow permissions for domains different than what the call was started on
|
||||
guard origin.host == url.host else {
|
||||
return .deny
|
||||
}
|
||||
|
||||
return .grant
|
||||
}
|
||||
|
||||
// MARK: - WKNavigationDelegate
|
||||
|
||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction) async -> WKNavigationActionPolicy {
|
||||
// Allow any content from the main URL.
|
||||
if navigationAction.request.url?.host == url.host {
|
||||
return .allow
|
||||
}
|
||||
|
||||
// Additionally allow any embedded content such as captchas.
|
||||
if let targetFrame = navigationAction.targetFrame, !targetFrame.isMainFrame {
|
||||
return .allow
|
||||
}
|
||||
|
||||
// Otherwise the request is invalid.
|
||||
return .cancel
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
private enum GenericCallLinkQueryParameters {
|
||||
static let appPrompt = "appPrompt"
|
||||
static let confineToRoom = "confineToRoom"
|
||||
}
|
||||
|
||||
/// Information about how a call should be configured.
|
||||
struct ElementCallConfiguration {
|
||||
enum Kind {
|
||||
case genericCallLink(URL)
|
||||
case roomCall(roomProxy: RoomProxyProtocol,
|
||||
clientProxy: ClientProxyProtocol,
|
||||
clientID: String,
|
||||
elementCallBaseURL: URL,
|
||||
elementCallBaseURLOverride: URL?,
|
||||
colorScheme: ColorScheme)
|
||||
}
|
||||
|
||||
/// The type of call being configured i.e. whether it's an external URL or an internal room call.
|
||||
let kind: Kind
|
||||
|
||||
/// Creates a configuration for an external call URL.
|
||||
init(genericCallLink url: URL) {
|
||||
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) {
|
||||
var fragmentQueryItems = urlComponents.fragmentQueryItems ?? []
|
||||
|
||||
fragmentQueryItems.removeAll { $0.name == GenericCallLinkQueryParameters.appPrompt }
|
||||
fragmentQueryItems.removeAll { $0.name == GenericCallLinkQueryParameters.confineToRoom }
|
||||
|
||||
fragmentQueryItems.append(.init(name: GenericCallLinkQueryParameters.appPrompt, value: "false"))
|
||||
fragmentQueryItems.append(.init(name: GenericCallLinkQueryParameters.confineToRoom, value: "true"))
|
||||
|
||||
urlComponents.fragmentQueryItems = fragmentQueryItems
|
||||
|
||||
if let adjustedURL = urlComponents.url {
|
||||
kind = .genericCallLink(adjustedURL)
|
||||
} else {
|
||||
MXLog.error("Failed adjusting URL with components: \(urlComponents)")
|
||||
kind = .genericCallLink(url)
|
||||
}
|
||||
} else {
|
||||
MXLog.error("Failed constructing URL components for url: \(url)")
|
||||
kind = .genericCallLink(url)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a configuration for an internal room call.
|
||||
init(roomProxy: RoomProxyProtocol,
|
||||
clientProxy: ClientProxyProtocol,
|
||||
clientID: String,
|
||||
elementCallBaseURL: URL,
|
||||
elementCallBaseURLOverride: URL?,
|
||||
colorScheme: ColorScheme) {
|
||||
kind = .roomCall(roomProxy: roomProxy,
|
||||
clientProxy: clientProxy,
|
||||
clientID: clientID,
|
||||
elementCallBaseURL: elementCallBaseURL,
|
||||
elementCallBaseURLOverride: elementCallBaseURLOverride,
|
||||
colorScheme: colorScheme)
|
||||
}
|
||||
|
||||
/// A string representing the call being configured.
|
||||
var callID: String {
|
||||
switch kind {
|
||||
case .genericCallLink(let url):
|
||||
url.absoluteString
|
||||
case .roomCall(let roomProxy, _, _, _, _, _):
|
||||
roomProxy.id
|
||||
}
|
||||
}
|
||||
}
|
@ -147,7 +147,7 @@ class ElementCallWidgetDriver: WidgetCapabilitiesProvider, ElementCallWidgetDriv
|
||||
return .success(url)
|
||||
}
|
||||
|
||||
func sendMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError> {
|
||||
func handleMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError> {
|
||||
guard let widgetDriver else {
|
||||
return .failure(.driverNotSetup)
|
||||
}
|
||||
|
@ -40,5 +40,6 @@ protocol ElementCallWidgetDriverProtocol {
|
||||
|
||||
func start(baseURL: URL, clientID: String, colorScheme: ColorScheme) async -> Result<URL, ElementCallWidgetDriverError>
|
||||
|
||||
func sendMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError>
|
||||
/// Passes a message from the Widget to the SDK to handle, returning a Bool that represents whether or not the widget driver is still running.
|
||||
func handleMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError>
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright 2024 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
|
||||
import SwiftUI
|
||||
|
||||
class GenericCallLinkWidgetDriver: ElementCallWidgetDriverProtocol {
|
||||
private let url: URL
|
||||
|
||||
let widgetID = UUID().uuidString
|
||||
let messagePublisher = PassthroughSubject<String, Never>()
|
||||
|
||||
private let actionsSubject: PassthroughSubject<ElementCallWidgetDriverAction, Never> = .init()
|
||||
var actions: AnyPublisher<ElementCallWidgetDriverAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(url: URL) {
|
||||
self.url = url
|
||||
}
|
||||
|
||||
func start(baseURL: URL, clientID: String, colorScheme: ColorScheme) async -> Result<URL, ElementCallWidgetDriverError> {
|
||||
MXLog.error("Nothing to start, use the configuration's URL directly instead.")
|
||||
return .success(url)
|
||||
}
|
||||
|
||||
func handleMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError> {
|
||||
// The web view doesn't send us messages through the Widget API, so nothing to implement (yet?).
|
||||
.failure(.driverNotSetup)
|
||||
}
|
||||
}
|
@ -609,7 +609,7 @@ class RoomProxy: RoomProxyProtocol {
|
||||
ElementCallWidgetDriver(room: room, deviceID: deviceID)
|
||||
}
|
||||
|
||||
func sendCallNotificationIfNeeeded() async -> Result<Void, RoomProxyError> {
|
||||
func sendCallNotificationIfNeeded() async -> Result<Void, RoomProxyError> {
|
||||
do {
|
||||
try await room.sendCallNotificationIfNeeded()
|
||||
return .success(())
|
||||
|
@ -141,7 +141,7 @@ protocol RoomProxyProtocol {
|
||||
func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError>
|
||||
func elementCallWidgetDriver(deviceID: String) -> ElementCallWidgetDriverProtocol
|
||||
|
||||
func sendCallNotificationIfNeeeded() async -> Result<Void, RoomProxyError>
|
||||
func sendCallNotificationIfNeeded() async -> Result<Void, RoomProxyError>
|
||||
|
||||
// MARK: - Permalinks
|
||||
|
||||
|
@ -40,6 +40,22 @@ class NavigationRootCoordinatorTests: XCTestCase {
|
||||
assertCoordinatorsEqual(secondRootCoordinator, navigationRootCoordinator.rootCoordinator)
|
||||
}
|
||||
|
||||
func testOverlay() {
|
||||
let rootCoordinator = SomeTestCoordinator()
|
||||
navigationRootCoordinator.setRootCoordinator(rootCoordinator)
|
||||
|
||||
let overlayCoordinator = SomeTestCoordinator()
|
||||
navigationRootCoordinator.setOverlayCoordinator(overlayCoordinator)
|
||||
|
||||
assertCoordinatorsEqual(rootCoordinator, navigationRootCoordinator.rootCoordinator)
|
||||
assertCoordinatorsEqual(overlayCoordinator, navigationRootCoordinator.overlayCoordinator)
|
||||
|
||||
navigationRootCoordinator.setOverlayCoordinator(nil)
|
||||
|
||||
assertCoordinatorsEqual(rootCoordinator, navigationRootCoordinator.rootCoordinator)
|
||||
XCTAssertNil(navigationRootCoordinator.overlayCoordinator)
|
||||
}
|
||||
|
||||
func testReplacementDismissalCallbacks() {
|
||||
XCTAssertNil(navigationRootCoordinator.rootCoordinator)
|
||||
|
||||
@ -54,6 +70,18 @@ class NavigationRootCoordinatorTests: XCTestCase {
|
||||
waitForExpectations(timeout: 1.0)
|
||||
}
|
||||
|
||||
func testOverlayDismissalCallback() {
|
||||
let overlayCoordinator = SomeTestCoordinator()
|
||||
|
||||
let expectation = expectation(description: "Wait for callback")
|
||||
navigationRootCoordinator.setOverlayCoordinator(overlayCoordinator) {
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
navigationRootCoordinator.setOverlayCoordinator(nil)
|
||||
waitForExpectations(timeout: 1.0)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func assertCoordinatorsEqual(_ lhs: CoordinatorProtocol?, _ rhs: CoordinatorProtocol?) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user