From 6cfe09b96d85152194e7020314a9330cba2f1bee Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:03:29 +0100 Subject: [PATCH] Add a WebRegistrationScreen (not included in the flow yet). (#3281) --- ElementX.xcodeproj/project.pbxproj | 54 ++++- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../Mocks/Generated/SDKGeneratedMocks.swift | 216 ++++++++++++++++++ .../LoginScreen/LoginHomeserver.swift | 2 + .../LoginScreen/LoginScreenCoordinator.swift | 1 + .../View/ServerConfirmationScreen.swift | 4 +- .../ServerSelectionScreenCoordinator.swift | 1 + .../SoftLogoutScreenCoordinator.swift | 1 + .../View/WebRegistrationScreen.swift | 139 +++++++++++ .../WebRegistrationScreenCoordinator.swift | 83 +++++++ .../WebRegistrationScreenModels.swift | 51 +++++ .../WebRegistrationScreenViewModel.swift | 34 +++ ...bRegistrationScreenViewModelProtocol.swift | 14 ++ .../AuthenticationService.swift | 33 ++- .../AuthenticationServiceProtocol.swift | 3 + ....swift => MockAuthenticationService.swift} | 6 +- ElementX/Sources/Services/Client/Client.swift | 32 +++ .../Sources/Services/Client/ClientProxy.swift | 15 +- .../Services/Client/ClientProxyProtocol.swift | 2 +- .../Services/Client/ElementWellKnown.swift | 4 +- .../UITests/UITestsAppCoordinator.swift | 6 +- UnitTests/Sources/ClientProtocolTests.swift | 70 ++++++ project.yml | 2 +- 23 files changed, 743 insertions(+), 34 deletions(-) create mode 100644 ElementX/Sources/Screens/Authentication/WebRegistrationScreen/View/WebRegistrationScreen.swift create mode 100644 ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenCoordinator.swift create mode 100644 ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenModels.swift create mode 100644 ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModel.swift create mode 100644 ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModelProtocol.swift rename ElementX/Sources/Services/Authentication/{MockAuthenticationServiceProxy.swift => MockAuthenticationService.swift} (90%) create mode 100644 ElementX/Sources/Services/Client/Client.swift create mode 100644 UnitTests/Sources/ClientProtocolTests.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 5cd795f40..dd524dc68 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -102,7 +102,6 @@ 149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */; }; 14E99D27628B1A6F0CB46FEA /* SeparatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */; }; 151D2477F75782C8702F2873 /* PollInteractionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC528B3764E3CF7FCFEF40E7 /* PollInteractionHandler.swift */; }; - 152AE2B8650FB23AFD2E28B9 /* MockAuthenticationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */; }; 155063E980E763D4910EA3CF /* Analytics+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4CFE236419E830E8946639C /* Analytics+SwiftUI.swift */; }; 1555A7643D85187D4851040C /* TemplateScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4549FCB53F43DB0B278374BC /* TemplateScreen.swift */; }; 1583E2D766E4485FF91662FC /* PermalinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3EB5B1848CF4F64E63C6B7 /* PermalinkTests.swift */; }; @@ -223,6 +222,7 @@ 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 */; }; + 32FC143630CE22A9E403370B /* MockAuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA38899517F08FE2AF34EB45 /* MockAuthenticationService.swift */; }; 339BC18777912E1989F2F17D /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584A61D9C459FAFEF038A7C0 /* Section.swift */; }; 33CAC1226DFB8B5D8447D286 /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; }; 33F1FB19F222BA9930AB1A00 /* RoomListFiltersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6372DD10DED30E7AD7BCE21 /* RoomListFiltersView.swift */; }; @@ -254,6 +254,7 @@ 38896D54D6D675534E606195 /* RoomTimelineControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6FCC416A3BFE73DF7B3E6BF /* RoomTimelineControllerFactory.swift */; }; 388D39ED9FE1122EA6D76BF2 /* Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BC84BA0AF11C2128D58ABD /* Common.swift */; }; 3895969759E68FAB90C63EF7 /* ElementCallServiceConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 406C90AF8C3E98DF5D4E5430 /* ElementCallServiceConstants.swift */; }; + 38CC67C7673FA97C21CCD5B5 /* WebRegistrationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B065EC39C99C1303A101C1C /* WebRegistrationScreen.swift */; }; 3982C505960006B341CFD0C6 /* UserDetailsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D0EA07BD545CC9F234DB8D /* UserDetailsEditScreenModels.swift */; }; 3982E60F9C126437D5E488A3 /* PillContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A6314FDC51DA25712D9A81 /* PillContextTests.swift */; }; 39A987B3E41B976D1DF944C6 /* CallScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A63A59BFDDC494B1C20119 /* CallScreenViewModel.swift */; }; @@ -446,6 +447,7 @@ 6586E1F1D5F0651D0638FFAF /* UserSessionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4469F6AE311BDC439B3A5EC /* UserSessionMock.swift */; }; 659E5B766F76FDEC1BF393A4 /* RoomDetailsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E413F4CBD7BF0588F394A9DD /* RoomDetailsEditScreenViewModel.swift */; }; 661EF50C1F7D4B0BC8A7AAE3 /* EmoteRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44ABA63DBE7F76C58260B43B /* EmoteRoomTimelineView.swift */; }; + 66357ECB73B1290E5490A012 /* WebRegistrationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F418426410F3823F3EB0828 /* WebRegistrationScreenViewModelProtocol.swift */; }; 663E198678778F7426A9B27D /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FAFE1C2149E6AC8156ED2B /* Collection.swift */; }; 67160204A8D362BB7D4AD259 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693E16574C6F7F9FA1015A8C /* Search.swift */; }; 6786C4B0936AC84D993B20BF /* NotificationSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.swift */; }; @@ -713,6 +715,7 @@ 9F19096BFA629C0AC282B1E4 /* CreateRoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */; }; 9FB41B0E8B2AA9B404E52C8B /* AppLockSetupBiometricsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CCC6C31102E1D8B9106DEDE /* AppLockSetupBiometricsScreenViewModelProtocol.swift */; }; 9FBE1FB20171012260A32492 /* TimelineSenderAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53FCCE44F96E0BC411A6CF0 /* TimelineSenderAvatarView.swift */; }; + 9FC820C410ED733CE6FC6616 /* WebRegistrationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6702BC84D3CC2421D78CD4E /* WebRegistrationScreenViewModel.swift */; }; A009BDFB0A6816D4C392ADCB /* SettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF715D4FD4710EBB637D661 /* SettingsScreenViewModelProtocol.swift */; }; A021827B528F1EDC9101CA58 /* AppCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */; }; A0601810597769B81C2358AF /* EncryptionResetPasswordScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2B5274C1D3D2999D643786 /* EncryptionResetPasswordScreenViewModelProtocol.swift */; }; @@ -723,6 +726,7 @@ A17FAD2EBC53E17B5FD384DB /* InviteUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22730A30C50AC2E3D5BA8642 /* InviteUsersScreenViewModelProtocol.swift */; }; A1BA8D6BABAFA9BAAEAA3FFD /* NotificationSettingsProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FDD775CFD72DD2D3C8A8390 /* NotificationSettingsProxyProtocol.swift */; }; A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */; }; + A20364EE08D902E647C11FB3 /* WebRegistrationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D851A10FDA55579960DC61 /* WebRegistrationScreenCoordinator.swift */; }; A216C83ADCF32BA5EF8A6FBC /* InviteUsersViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */; }; A2172B5A26976F9174228B8A /* AppHooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E4AB573FAEBB7B853DD04C /* AppHooks.swift */; }; A23B8B27A1436A1049EEF68E /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; }; @@ -740,6 +744,7 @@ A4B123C635F70DDD4BC2FAC9 /* BlockedUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76A706B3EEA32B882DA5E2D /* BlockedUsersScreenViewModelProtocol.swift */; }; A4C29D373986AFE4559696D5 /* SecureBackupKeyBackupScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4525E8C0FBDD27D1ACE90952 /* SecureBackupKeyBackupScreenViewModelProtocol.swift */; }; A4E885358D7DD5A072A06824 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = CCE5BF78B125320CBF3BB834 /* PostHog */; }; + A52090A4FE0DB826578DFC03 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0724EBDFE8BB4C9E5547C57D /* Client.swift */; }; A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9780389F8A53E4D26E23DD03 /* LoginScreenViewModelProtocol.swift */; }; A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */; }; A64B52D9F73F9A6B95AF24FE /* UserDetailsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CD503F5E0938FE53C7C6E7 /* UserDetailsEditScreenCoordinator.swift */; }; @@ -1058,6 +1063,7 @@ F07D88421A9BC4D03D4A5055 /* VideoRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */; }; F08F7BC07CA9AEF5CD157918 /* Snapshotting.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF17EA323AD0205A6AB621AA /* Snapshotting.swift */; }; F0A26CD502C3A5868353B0FA /* ServerConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24DEE0682C95F897B6C7CB0D /* ServerConfirmationScreenViewModel.swift */; }; + F0C2C49D707839F5273BFC6D /* WebRegistrationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C161B06F417CA5D1F1E088 /* WebRegistrationScreenModels.swift */; }; F0DACC95F24128A54CD537E4 /* GlobalSearchScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24B8177BD2AF45A286F5DA31 /* GlobalSearchScreen.swift */; }; F0F82C3C848C865C3098AA52 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 21C83087604B154AA30E9A8F /* SnapshotTesting */; }; F103924DED414ADFE398CE99 /* RoomPollsHistoryScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A130A2251A15A7AACC84FD37 /* RoomPollsHistoryScreenViewModelProtocol.swift */; }; @@ -1109,6 +1115,7 @@ FBD402E3170EB1ED0D1AA672 /* EncryptionKeyProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2355398E4A55DA5A89128AD1 /* EncryptionKeyProvider.swift */; }; FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; }; FC10228E73323BDC09526F97 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 4278261E147DB2DE5CFB7FC5 /* PostHog */; }; + FC8B95EC506E6BB5793D81CE /* ClientProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E34685D186453E429ADEE58E /* ClientProtocolTests.swift */; }; FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; }; FCDA202B246F75BA28E10C5F /* MapTilerAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = E062C1750EFC8627DE4CAB8E /* MapTilerAuthorization.swift */; }; FD29471C72872F8B7580E3E1 /* KeychainControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */; }; @@ -1214,6 +1221,7 @@ 06B098A612DCB5A7358EECD5 /* DeveloperOptionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenModels.swift; sourceTree = ""; }; 06F27F588F9059128E17C669 /* WindowManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManagerProtocol.swift; sourceTree = ""; }; 06FAE373A7F20780BA84B59C /* MessageForwardingScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenCoordinator.swift; sourceTree = ""; }; + 0724EBDFE8BB4C9E5547C57D /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; 07579F9C29001E40715F3014 /* NotificationSettingsChatType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsChatType.swift; sourceTree = ""; }; 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorStateMachine.swift; sourceTree = ""; }; 07C6B0B087FE6601C3F77816 /* JoinedRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinedRoomProxy.swift; sourceTree = ""; }; @@ -1287,6 +1295,7 @@ 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Strings+Untranslated.swift"; sourceTree = ""; }; 1A4D29F2683F5772AC72406F /* MapTilerStaticMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStaticMap.swift; sourceTree = ""; }; 1A7ED2EF5BDBAD2A7DBC4636 /* GeoURITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoURITests.swift; sourceTree = ""; }; + 1B065EC39C99C1303A101C1C /* WebRegistrationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreen.swift; sourceTree = ""; }; 1B2AC540DE619B36832A5DB5 /* LocationRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineItem.swift; sourceTree = ""; }; 1B53D6C5C0D14B04D3AB3F6E /* PillAttachmentViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillAttachmentViewProvider.swift; sourceTree = ""; }; 1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenModels.swift; sourceTree = ""; }; @@ -1621,7 +1630,6 @@ 653610CB5F9776EAAAB98155 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/Localizable.stringsdict; sourceTree = ""; }; 6569593FA36B22259E806A67 /* AudioRecorderState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecorderState.swift; sourceTree = ""; }; 65AAD845E53B0C8B5E0812C2 /* UserDiscoveryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoveryService.swift; sourceTree = ""; }; - 65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAuthenticationServiceProxy.swift; sourceTree = ""; }; 664ABD745A746C45CB842158 /* CallInviteRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInviteRoomTimelineView.swift; sourceTree = ""; }; 6654859746B0BE9611459391 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Localizable.stringsdict; sourceTree = ""; }; 6663BFB9FDB8752562CD12CA /* AuthenticationStartScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationStartScreenCoordinator.swift; sourceTree = ""; }; @@ -1655,6 +1663,7 @@ 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindableState.swift; sourceTree = ""; }; 6F1C3CBBC62C566DDF5E84C1 /* TimelineItemMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMenuAction.swift; sourceTree = ""; }; 6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateStoreViewModel.swift; sourceTree = ""; }; + 6F418426410F3823F3EB0828 /* WebRegistrationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreenViewModelProtocol.swift; sourceTree = ""; }; 6F6E6EDC4BBF962B2ED595A4 /* MessageForwardingScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModelTests.swift; sourceTree = ""; }; 6FA38E813BE14149F173F461 /* PinnedEventsBannerStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsBannerStateTests.swift; sourceTree = ""; }; 6FC5015B9634698BDB8701AF /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -1879,6 +1888,7 @@ A4A1003A0F7A1DFB47F4E2D0 /* TimelineItemMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMock.swift; sourceTree = ""; }; A54AAF72E821B4084B7E4298 /* PinnedEventsTimelineFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsTimelineFlowCoordinator.swift; sourceTree = ""; }; A58E93D91DE3288010390DEE /* EmojiDetectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiDetectionTests.swift; sourceTree = ""; }; + A6702BC84D3CC2421D78CD4E /* WebRegistrationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreenViewModel.swift; sourceTree = ""; }; A69869844D2B6F5BD9AABF85 /* OIDCConfigurationProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCConfigurationProxy.swift; sourceTree = ""; }; A6B19D10B102956066AF117B /* PollOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionView.swift; sourceTree = ""; }; A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = ""; }; @@ -2039,6 +2049,7 @@ C733D11B421CFE3A657EF230 /* test_image.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = test_image.png; sourceTree = ""; }; C75EF87651B00A176AB08E97 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomList.swift; sourceTree = ""; }; + C7D851A10FDA55579960DC61 /* WebRegistrationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreenCoordinator.swift; sourceTree = ""; }; C830A64609CBD152F06E0457 /* NotificationConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationConstants.swift; sourceTree = ""; }; C833673B334A0651AB46F30B /* StaticLocationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreenViewModelTests.swift; sourceTree = ""; }; C90514BE9B8ACCBCF0AD2489 /* ComposerToolbarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerToolbarViewModel.swift; sourceTree = ""; }; @@ -2115,6 +2126,7 @@ D95E8C0EFEC0C6F96EDAA71A /* PreviewTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = PreviewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DA14564EE143F73F7E4D1F79 /* RoomNotificationSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenModels.swift; sourceTree = ""; }; DA2AEC1AB349A341FE13DEC1 /* StartChatScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenUITests.swift; sourceTree = ""; }; + DA38899517F08FE2AF34EB45 /* MockAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAuthenticationService.swift; sourceTree = ""; }; DA3D82522494E78746B2214E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/SAS.strings; sourceTree = ""; }; DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageCache.swift; sourceTree = ""; }; DB06F22CFA34885B40976061 /* RoomDetailsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreen.swift; sourceTree = ""; }; @@ -2147,6 +2159,7 @@ E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProxyMock.swift; sourceTree = ""; }; E3059CFA00C67D8787273B20 /* ServerSelectionScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModel.swift; sourceTree = ""; }; E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallServiceMock.swift; sourceTree = ""; }; + E34685D186453E429ADEE58E /* ClientProtocolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProtocolTests.swift; sourceTree = ""; }; E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = ""; }; E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsClientProtocol.swift; sourceTree = ""; }; E4103AB4340F2974D690A12A /* CallScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallScreen.swift; sourceTree = ""; }; @@ -2242,6 +2255,7 @@ F733F135E6D67BBBEB76CC30 /* AppLockUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockUITests.swift; sourceTree = ""; }; F74532E01B317C56C1BE8FA8 /* RoomTimelineProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderMock.swift; sourceTree = ""; }; F7478623CECC9438014244BA /* ServerConfirmationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreen.swift; sourceTree = ""; }; + F7C161B06F417CA5D1F1E088 /* WebRegistrationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreenModels.swift; sourceTree = ""; }; F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinatorTests.swift; sourceTree = ""; }; F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionScreenTests.swift; sourceTree = ""; }; F8CCF9A924521DECA44778C4 /* AppLockSetupBiometricsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreen.swift; sourceTree = ""; }; @@ -2699,6 +2713,14 @@ path = Progress; sourceTree = ""; }; + 25A88085FB8D8227DCDB0C9C /* View */ = { + isa = PBXGroup; + children = ( + 1B065EC39C99C1303A101C1C /* WebRegistrationScreen.swift */, + ); + path = View; + sourceTree = ""; + }; 26C16326BCCCED74A85A0F48 /* View */ = { isa = PBXGroup; children = ( @@ -3742,6 +3764,7 @@ 7EECE8B331CD169790EF284F /* BugReportScreenViewModelTests.swift */, EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */, CAD9547E47C58930E2CE8306 /* CallScreenViewModelTests.swift */, + E34685D186453E429ADEE58E /* ClientProtocolTests.swift */, D5EA0312A6262484AA393AC9 /* CompletionSuggestionServiceTests.swift */, CA29952595B804DA221A0C1D /* ComposerToolbarViewModelTests.swift */, 69D42EE0102D2857933625DD /* CreateRoomViewModelTests.swift */, @@ -4033,6 +4056,7 @@ 8039515BAA53B7C3275AC64A /* Client */ = { isa = PBXGroup; children = ( + 0724EBDFE8BB4C9E5547C57D /* Client.swift */, D09A267106B9585D3D0CFC0D /* ClientError.swift */, 18F2958E6D247AE2516BEEE8 /* ClientProxy.swift */, 6033779EB37259F27F938937 /* ClientProxyProtocol.swift */, @@ -4596,7 +4620,7 @@ 0F569CFB77E0D40BD82203D9 /* AuthenticationClientBuilder.swift */, F3A1AB5A84D843B6AC8D5F1E /* AuthenticationService.swift */, 5E75948AA1FE1D1A7809931F /* AuthenticationServiceProtocol.swift */, - 65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */, + DA38899517F08FE2AF34EB45 /* MockAuthenticationService.swift */, A69869844D2B6F5BD9AABF85 /* OIDCConfigurationProxy.swift */, ); path = Authentication; @@ -5047,6 +5071,18 @@ path = RoomChangeRolesScreen; sourceTree = ""; }; + D847C12EC9B19A5FCDF2C815 /* WebRegistrationScreen */ = { + isa = PBXGroup; + children = ( + C7D851A10FDA55579960DC61 /* WebRegistrationScreenCoordinator.swift */, + F7C161B06F417CA5D1F1E088 /* WebRegistrationScreenModels.swift */, + A6702BC84D3CC2421D78CD4E /* WebRegistrationScreenViewModel.swift */, + 6F418426410F3823F3EB0828 /* WebRegistrationScreenViewModelProtocol.swift */, + 25A88085FB8D8227DCDB0C9C /* View */, + ); + path = WebRegistrationScreen; + sourceTree = ""; + }; D977D4E565C06D3F41C8F8FC /* Virtual */ = { isa = PBXGroup; children = ( @@ -5240,6 +5276,7 @@ BA1938A75D8C780F694CEB62 /* ServerConfirmationScreen */, 2D0D49B0533C4C2EB889BF3A /* ServerSelectionScreen */, 5B2C520AB9863B8CBC8EB3CA /* SoftLogoutScreen */, + D847C12EC9B19A5FCDF2C815 /* WebRegistrationScreen */, ); path = Authentication; sourceTree = ""; @@ -6007,6 +6044,7 @@ 1B2F9F368619FFF8C63C87CC /* BugReportScreenViewModelTests.swift in Sources */, 7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */, 366D5BFE52CB79E804C7D095 /* CallScreenViewModelTests.swift in Sources */, + FC8B95EC506E6BB5793D81CE /* ClientProtocolTests.swift in Sources */, B5321A1F5B26A0F3EC54909E /* CollapsibleFlowLayoutTests.swift in Sources */, 3A164187907DA43B7858F9EC /* CompletionSuggestionServiceTests.swift in Sources */, 0C932A5158C1D0604DFC5750 /* ComposerToolbarViewModelTests.swift in Sources */, @@ -6253,6 +6291,7 @@ BB6BF528BC7F5B87E08C4F18 /* CameraPicker.swift in Sources */, E14E469CD97550D0FC58F3CA /* CancellableTask.swift in Sources */, DF8F1211F2B0B56F0FCCA5C2 /* CertificateValidatorHook.swift in Sources */, + A52090A4FE0DB826578DFC03 /* Client.swift in Sources */, C80E06ED97CE52704A46C148 /* ClientBuilder.swift in Sources */, 87CEA3E07B602705BC2D2A20 /* ClientBuilderHook.swift in Sources */, 6A0E7551E0D1793245F34CDD /* ClientError.swift in Sources */, @@ -6483,7 +6522,7 @@ F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */, C13128AAA787A4C2CBE4EE82 /* MessageForwardingScreenViewModelProtocol.swift in Sources */, C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */, - 152AE2B8650FB23AFD2E28B9 /* MockAuthenticationServiceProxy.swift in Sources */, + 32FC143630CE22A9E403370B /* MockAuthenticationService.swift in Sources */, B659E3A49889E749E3239EA7 /* MockMediaProvider.swift in Sources */, 09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */, B721125D17A0BA86794F29FB /* MockServerSelectionScreenState.swift in Sources */, @@ -6925,6 +6964,11 @@ CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */, 63CDC201A5980F304F6D0A1C /* WaveformInteractionModifier.swift in Sources */, B773ACD8881DB18E876D950C /* WaveformSource.swift in Sources */, + 38CC67C7673FA97C21CCD5B5 /* WebRegistrationScreen.swift in Sources */, + A20364EE08D902E647C11FB3 /* WebRegistrationScreenCoordinator.swift in Sources */, + F0C2C49D707839F5273BFC6D /* WebRegistrationScreenModels.swift in Sources */, + 9FC820C410ED733CE6FC6616 /* WebRegistrationScreenViewModel.swift in Sources */, + 66357ECB73B1290E5490A012 /* WebRegistrationScreenViewModelProtocol.swift in Sources */, 08CB4BD12CEEDE6AAE4A18DD /* WindowManager.swift in Sources */, AE5AAD9E32511544FDFA5560 /* WindowManagerProtocol.swift in Sources */, ); @@ -7691,7 +7735,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.48; + version = 1.0.49; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 791e56312..13ef9fbfd 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "08f243c64b2b5104e431cf0d485c0c5a455073c6", - "version" : "1.0.48" + "revision" : "7e9a927b6ae4b0380fb403f54a55e52990af54b3", + "version" : "1.0.49" } }, { diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index 0be15af04..f7b9cff3c 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -341,6 +341,81 @@ open class ClientSDKMock: MatrixRustSDK.Client { } } + //MARK: - awaitRoomRemoteEcho + + open var awaitRoomRemoteEchoRoomIdThrowableError: Error? + var awaitRoomRemoteEchoRoomIdUnderlyingCallsCount = 0 + open var awaitRoomRemoteEchoRoomIdCallsCount: Int { + get { + if Thread.isMainThread { + return awaitRoomRemoteEchoRoomIdUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = awaitRoomRemoteEchoRoomIdUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + awaitRoomRemoteEchoRoomIdUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + awaitRoomRemoteEchoRoomIdUnderlyingCallsCount = newValue + } + } + } + } + open var awaitRoomRemoteEchoRoomIdCalled: Bool { + return awaitRoomRemoteEchoRoomIdCallsCount > 0 + } + open var awaitRoomRemoteEchoRoomIdReceivedRoomId: String? + open var awaitRoomRemoteEchoRoomIdReceivedInvocations: [String] = [] + + var awaitRoomRemoteEchoRoomIdUnderlyingReturnValue: Room! + open var awaitRoomRemoteEchoRoomIdReturnValue: Room! { + get { + if Thread.isMainThread { + return awaitRoomRemoteEchoRoomIdUnderlyingReturnValue + } else { + var returnValue: Room? = nil + DispatchQueue.main.sync { + returnValue = awaitRoomRemoteEchoRoomIdUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + awaitRoomRemoteEchoRoomIdUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + awaitRoomRemoteEchoRoomIdUnderlyingReturnValue = newValue + } + } + } + } + open var awaitRoomRemoteEchoRoomIdClosure: ((String) async throws -> Room)? + + open override func awaitRoomRemoteEcho(roomId: String) async throws -> Room { + if let error = awaitRoomRemoteEchoRoomIdThrowableError { + throw error + } + awaitRoomRemoteEchoRoomIdCallsCount += 1 + awaitRoomRemoteEchoRoomIdReceivedRoomId = roomId + DispatchQueue.main.async { + self.awaitRoomRemoteEchoRoomIdReceivedInvocations.append(roomId) + } + if let awaitRoomRemoteEchoRoomIdClosure = awaitRoomRemoteEchoRoomIdClosure { + return try await awaitRoomRemoteEchoRoomIdClosure(roomId) + } else { + return awaitRoomRemoteEchoRoomIdReturnValue + } + } + //MARK: - cachedAvatarUrl open var cachedAvatarUrlThrowableError: Error? @@ -2662,6 +2737,71 @@ open class ClientSDKMock: MatrixRustSDK.Client { } } + //MARK: - server + + var serverUnderlyingCallsCount = 0 + open var serverCallsCount: Int { + get { + if Thread.isMainThread { + return serverUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = serverUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + serverUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + serverUnderlyingCallsCount = newValue + } + } + } + } + open var serverCalled: Bool { + return serverCallsCount > 0 + } + + var serverUnderlyingReturnValue: String? + open var serverReturnValue: String? { + get { + if Thread.isMainThread { + return serverUnderlyingReturnValue + } else { + var returnValue: String?? = nil + DispatchQueue.main.sync { + returnValue = serverUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + serverUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + serverUnderlyingReturnValue = newValue + } + } + } + } + open var serverClosure: (() -> String?)? + + open override func server() -> String? { + serverCallsCount += 1 + if let serverClosure = serverClosure { + return serverClosure() + } else { + return serverReturnValue + } + } + //MARK: - session open var sessionThrowableError: Error? @@ -9974,6 +10114,82 @@ open class NotificationSettingsSDKMock: MatrixRustSDK.NotificationSettings { try await unmuteRoomRoomIdIsEncryptedIsOneToOneClosure?(roomId, isEncrypted, isOneToOne) } } +open class OidcAuthorizationDataSDKMock: MatrixRustSDK.OidcAuthorizationData { + init() { + super.init(noPointer: .init()) + } + + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") + } + + fileprivate var pointer: UnsafeMutableRawPointer! + + //MARK: - loginUrl + + var loginUrlUnderlyingCallsCount = 0 + open var loginUrlCallsCount: Int { + get { + if Thread.isMainThread { + return loginUrlUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = loginUrlUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loginUrlUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + loginUrlUnderlyingCallsCount = newValue + } + } + } + } + open var loginUrlCalled: Bool { + return loginUrlCallsCount > 0 + } + + var loginUrlUnderlyingReturnValue: String! + open var loginUrlReturnValue: String! { + get { + if Thread.isMainThread { + return loginUrlUnderlyingReturnValue + } else { + var returnValue: String? = nil + DispatchQueue.main.sync { + returnValue = loginUrlUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loginUrlUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + loginUrlUnderlyingReturnValue = newValue + } + } + } + } + open var loginUrlClosure: (() -> String)? + + open override func loginUrl() -> String { + loginUrlCallsCount += 1 + if let loginUrlClosure = loginUrlClosure { + return loginUrlClosure() + } else { + return loginUrlReturnValue + } + } +} open class QrCodeDataSDKMock: MatrixRustSDK.QrCodeData { init() { super.init(noPointer: .init()) diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift index eceb09fed..7f6695c8a 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift @@ -13,6 +13,8 @@ struct LoginHomeserver: Equatable { let address: String /// The types login supported by the homeserver. var loginMode: LoginMode + /// A temporary helper URL that can be used for registration. + var registrationHelperURL: URL? /// Creates a new homeserver value. init(address: String, loginMode: LoginMode) { diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift index 07b22953c..9c6d3e2be 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift @@ -23,6 +23,7 @@ enum LoginScreenCoordinatorAction { case signedIn(UserSessionProtocol) } +// Note: This code was brought over from Riot, we should move the authentication service logic into the view model. final class LoginScreenCoordinator: CoordinatorProtocol { private let parameters: LoginScreenCoordinatorParameters private var viewModel: LoginScreenViewModelProtocol diff --git a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift index 4d43d3130..53aa1c4de 100644 --- a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift +++ b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift @@ -66,9 +66,9 @@ struct ServerConfirmationScreen: View { // MARK: - Previews struct ServerConfirmationScreen_Previews: PreviewProvider, TestablePreview { - static let loginViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationServiceProxy(), + static let loginViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationService(), authenticationFlow: .login) - static let registerViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationServiceProxy(), + static let registerViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationService(), authenticationFlow: .register) static var previews: some View { diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift index f2f95bf01..6459f24a9 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift @@ -21,6 +21,7 @@ enum ServerSelectionScreenCoordinatorAction { case dismiss } +// Note: This code was brought over from Riot, we should move the authentication service logic into the view model. final class ServerSelectionScreenCoordinator: CoordinatorProtocol { private let parameters: ServerSelectionScreenCoordinatorParameters private let userIndicatorController: UserIndicatorControllerProtocol diff --git a/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift index 7a7175676..db748aa4f 100644 --- a/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift @@ -32,6 +32,7 @@ enum SoftLogoutScreenCoordinatorResult: CustomStringConvertible { } } +// Note: This code was brought over from Riot, we should move the authentication service logic into the view model. final class SoftLogoutScreenCoordinator: CoordinatorProtocol { private let parameters: SoftLogoutScreenCoordinatorParameters private var viewModel: SoftLogoutScreenViewModelProtocol diff --git a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/View/WebRegistrationScreen.swift b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/View/WebRegistrationScreen.swift new file mode 100644 index 000000000..cdb1768b5 --- /dev/null +++ b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/View/WebRegistrationScreen.swift @@ -0,0 +1,139 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Compound +import SwiftUI +import WebKit + +struct WebRegistrationScreen: View { + @ObservedObject var context: WebRegistrationScreenViewModel.Context + + var body: some View { + NavigationStack { + WebRegistrationWebView(url: context.viewState.url, viewModelContext: context) + .navigationTitle(L10n.screenCreateAccountTitle) + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button(L10n.actionCancel) { + context.send(viewAction: .cancel) + } + } + } + } + } +} + +struct WebRegistrationWebView: UIViewRepresentable { + let url: URL + let viewModelContext: WebRegistrationScreenViewModel.Context + + func makeUIView(context: Context) -> WKWebView { + context.coordinator.webView + } + + func updateUIView(_ webView: WKWebView, context: Context) { } + + func makeCoordinator() -> Coordinator { + Coordinator(url: url, viewModelContext: viewModelContext) + } + + class Coordinator: NSObject, WKUIDelegate { + private let url: URL + private let viewModelContext: WebRegistrationScreenViewModel.Context + + private(set) var webView: WKWebView! + + init(url: URL, viewModelContext: WebRegistrationScreenViewModel.Context) { + self.url = url + self.viewModelContext = viewModelContext + + super.init() + + let eventHandlerName = "elementx" + let userContentController = WKUserContentController() + userContentController.add(WKScriptMessageHandlerWrapper(self), name: eventHandlerName) + + let eventHandlerScript = """ + window.addEventListener( + "mobileregistrationresponse", + (event) => { + window.webkit.messageHandlers.\(eventHandlerName).postMessage(JSON.stringify(event.detail)); + }, + false, + ); + """ + + let userScript = WKUserScript(source: eventHandlerScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false) + userContentController.addUserScript(userScript) + + let configuration = WKWebViewConfiguration() + configuration.userContentController = userContentController + configuration.preferences.javaScriptCanOpenWindowsAutomatically = true + + webView = WKWebView(frame: .zero, configuration: configuration) + webView.uiDelegate = self + webView.load(URLRequest(url: url)) + } + + nonisolated func userContentController(_ userContentController: WKUserContentController, + didReceive message: WKScriptMessage) { + guard let jsonString = message.body as? String, let jsonData = jsonString.data(using: .utf8) else { + MXLog.error("Unexpected response.") + return + } + + guard let credentials = try? JSONDecoder().decode(WebRegistrationCredentials.self, from: jsonData) else { + MXLog.error("Invalid response.") + return + } + + MXLog.info("Received login credentials.") + Task { await viewModelContext.send(viewAction: .signedIn(credentials)) } + } + + // MARK: WKUIDelegate + + func webView(_ webView: WKWebView, + createWebViewWith configuration: WKWebViewConfiguration, + for navigationAction: WKNavigationAction, + windowFeatures: WKWindowFeatures) -> WKWebView? { + if let url = navigationAction.request.url, UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url) + } + return nil + } + } + + /// Avoids retain loops between the configuration and webView coordinator + private class WKScriptMessageHandlerWrapper: NSObject, WKScriptMessageHandler { + private weak var coordinator: Coordinator? + + init(_ coordinator: Coordinator) { + self.coordinator = coordinator + } + + // MARK: WKScriptMessageHandler + + nonisolated func userContentController(_ userContentController: WKUserContentController, + didReceive message: WKScriptMessage) { + coordinator?.userContentController(userContentController, didReceive: message) + } + } +} + +// MARK: - Previews + +struct WebRegistrationScreen_Previews: PreviewProvider { + static let viewModel = WebRegistrationScreenViewModel(registrationHelperURL: "https://develop.element.io/#/mobile_register") + static var previews: some View { + NavigationStack { + WebRegistrationScreen(context: viewModel.context) + } + } +} diff --git a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenCoordinator.swift new file mode 100644 index 000000000..444395957 --- /dev/null +++ b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenCoordinator.swift @@ -0,0 +1,83 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Combine +import MatrixRustSDK +import SwiftUI + +struct WebRegistrationScreenCoordinatorParameters { + /// The service used to authenticate the user. + let authenticationService: AuthenticationServiceProtocol + + let userIndicatorController: UserIndicatorControllerProtocol +} + +enum WebRegistrationScreenCoordinatorAction: CustomStringConvertible { + case cancel + case signedIn(UserSessionProtocol) + + var description: String { + switch self { + case .cancel: "cancel" + case .signedIn: "signedIn" + } + } +} + +// Note: This code was based on the LoginScreen, we should move the authentication service logic into the view model. +final class WebRegistrationScreenCoordinator: CoordinatorProtocol { + private let parameters: WebRegistrationScreenCoordinatorParameters + private let viewModel: WebRegistrationScreenViewModelProtocol + + private var cancellables = Set() + + private let actionsSubject: PassthroughSubject = .init() + var actionsPublisher: AnyPublisher { + actionsSubject.eraseToAnyPublisher() + } + + init(parameters: WebRegistrationScreenCoordinatorParameters) { + self.parameters = parameters + + guard let registrationHelperURL = parameters.authenticationService.homeserver.value.registrationHelperURL else { + MXLog.error("Attempted registration without a helper URL.") + fatalError("A helper URL is required.") + } + viewModel = WebRegistrationScreenViewModel(registrationHelperURL: registrationHelperURL) + } + + func start() { + viewModel.actionsPublisher.sink { [weak self] action in + MXLog.info("Coordinator: received view model action: \(action)") + + guard let self else { return } + switch action { + case .cancel: + actionsSubject.send(.cancel) + case .signedIn(let credentials): + Task { await self.completeRegistration(using: credentials) } + } + } + .store(in: &cancellables) + } + + func toPresentable() -> AnyView { + AnyView(WebRegistrationScreen(context: viewModel.context)) + } + + // MARK: - Private + + private func completeRegistration(using credentials: WebRegistrationCredentials) async { + switch await parameters.authenticationService.completeWebRegistration(using: credentials) { + case .success(let userSession): + actionsSubject.send(.signedIn(userSession)) + case .failure(let error): + MXLog.error("Failed registration: \(error)") + parameters.userIndicatorController.alertInfo = .init(id: UUID(), title: L10n.errorUnknown, message: String(describing: error)) + } + } +} diff --git a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenModels.swift b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenModels.swift new file mode 100644 index 000000000..324ae0c87 --- /dev/null +++ b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenModels.swift @@ -0,0 +1,51 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation + +enum WebRegistrationScreenViewModelAction: CustomStringConvertible { + case cancel + case signedIn(WebRegistrationCredentials) + + var description: String { + switch self { + case .cancel: "cancel" + case .signedIn: "signedIn" + } + } +} + +struct WebRegistrationScreenViewState: BindableState { + var url: URL + var bindings = WebRegistrationScreenViewStateBindings() +} + +struct WebRegistrationScreenViewStateBindings { } + +enum WebRegistrationScreenViewAction: CustomStringConvertible { + case cancel + case signedIn(WebRegistrationCredentials) + + var description: String { + switch self { + case .cancel: "cancel" + case .signedIn: "signedIn" + } + } +} + +struct WebRegistrationCredentials: Decodable { + let userID: String + let accessToken: String + let deviceID: String + + enum CodingKeys: String, CodingKey { + case userID = "user_id" + case accessToken = "access_token" + case deviceID = "device_id" + } +} diff --git a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModel.swift b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModel.swift new file mode 100644 index 000000000..7c8cacc33 --- /dev/null +++ b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModel.swift @@ -0,0 +1,34 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Combine +import MatrixRustSDK +import SwiftUI + +typealias WebRegistrationScreenViewModelType = StateStoreViewModel + +class WebRegistrationScreenViewModel: WebRegistrationScreenViewModelType, WebRegistrationScreenViewModelProtocol { + private let actionsSubject: PassthroughSubject = .init() + var actionsPublisher: AnyPublisher { + actionsSubject.eraseToAnyPublisher() + } + + init(registrationHelperURL: URL) { + super.init(initialViewState: WebRegistrationScreenViewState(url: registrationHelperURL)) + } + + override func process(viewAction: WebRegistrationScreenViewAction) { + MXLog.info("View model: received view action: \(viewAction)") + + switch viewAction { + case .cancel: + actionsSubject.send(.cancel) + case .signedIn(let credentials): + actionsSubject.send(.signedIn(credentials)) + } + } +} diff --git a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModelProtocol.swift b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModelProtocol.swift new file mode 100644 index 000000000..2a5a6112b --- /dev/null +++ b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModelProtocol.swift @@ -0,0 +1,14 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Combine + +@MainActor +protocol WebRegistrationScreenViewModelProtocol { + var actionsPublisher: AnyPublisher { get } + var context: WebRegistrationScreenViewModelType.Context { get } +} diff --git a/ElementX/Sources/Services/Authentication/AuthenticationService.swift b/ElementX/Sources/Services/Authentication/AuthenticationService.swift index 5036ac022..c799a8f31 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationService.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationService.swift @@ -40,15 +40,21 @@ class AuthenticationService: AuthenticationServiceProtocol { let client = try await makeClientBuilder().build(homeserverAddress: homeserverAddress) let loginDetails = await client.homeserverLoginDetails() + let elementWellKnown = await client.getElementWellKnown() MXLog.info("Sliding sync: \(client.slidingSyncVersion())") - if loginDetails.supportsOidcLogin() { - homeserver.loginMode = .oidc + homeserver.loginMode = if loginDetails.supportsOidcLogin() { + .oidc } else if loginDetails.supportsPasswordLogin() { - homeserver.loginMode = .password + .password } else { - homeserver.loginMode = .unsupported + .unsupported + } + + homeserver.registrationHelperURL = switch elementWellKnown { + case .success(let wellKnown): wellKnown.registrationHelperUrl.flatMap(URL.init) + case .failure: nil } self.client = client @@ -125,6 +131,25 @@ class AuthenticationService: AuthenticationServiceProtocol { } } + func completeWebRegistration(using credentials: WebRegistrationCredentials) async -> Result { + guard let client else { return .failure(.failedLoggingIn) } + let session = Session(accessToken: credentials.accessToken, + refreshToken: nil, + userId: credentials.userID, + deviceId: credentials.deviceID, + homeserverUrl: client.homeserver(), + oidcData: nil, + slidingSyncVersion: client.slidingSyncVersion()) + + do { + try await client.restoreSession(session: session) + return await userSession(for: client) + } catch { + MXLog.error("Failed restoring the client using the provided credentials.") + return .failure(.failedUsingWebCredentials) + } + } + // MARK: - Private private func makeClientBuilder() -> AuthenticationClientBuilder { diff --git a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift index ee6b97ca8..e347bc7b9 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift @@ -27,6 +27,7 @@ enum AuthenticationServiceError: Error { case accountDeactivated case failedLoggingIn case sessionTokenRefreshNotSupported + case failedUsingWebCredentials } protocol AuthenticationServiceProtocol { @@ -43,6 +44,8 @@ protocol AuthenticationServiceProtocol { func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthorizationDataProxy) async -> Result /// Performs a password login using the current homeserver. func login(username: String, password: String, initialDeviceName: String?, deviceID: String?) async -> Result + /// Completes registration using the credentials obtained via the helper URL. + func completeWebRegistration(using credentials: WebRegistrationCredentials) async -> Result } // MARK: - OIDC diff --git a/ElementX/Sources/Services/Authentication/MockAuthenticationServiceProxy.swift b/ElementX/Sources/Services/Authentication/MockAuthenticationService.swift similarity index 90% rename from ElementX/Sources/Services/Authentication/MockAuthenticationServiceProxy.swift rename to ElementX/Sources/Services/Authentication/MockAuthenticationService.swift index 1d6485065..3cd72631a 100644 --- a/ElementX/Sources/Services/Authentication/MockAuthenticationServiceProxy.swift +++ b/ElementX/Sources/Services/Authentication/MockAuthenticationService.swift @@ -9,7 +9,7 @@ import Combine import Foundation import MatrixRustSDK -class MockAuthenticationServiceProxy: AuthenticationServiceProtocol { +class MockAuthenticationService: AuthenticationServiceProtocol { let validCredentials = (username: "alice", password: "12345678") private let homeserverSubject: CurrentValueSubject @@ -58,4 +58,8 @@ class MockAuthenticationServiceProxy: AuthenticationServiceProtocol { let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: username)))) return .success(userSession) } + + func completeWebRegistration(using credentials: WebRegistrationCredentials) async -> Result { + .failure(.failedLoggingIn) + } } diff --git a/ElementX/Sources/Services/Client/Client.swift b/ElementX/Sources/Services/Client/Client.swift new file mode 100644 index 000000000..d149c6aea --- /dev/null +++ b/ElementX/Sources/Services/Client/Client.swift @@ -0,0 +1,32 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +extension ClientProtocol { + func getElementWellKnown() async -> Result { + do { + let serverName = if let userIDServerName = try? userIdServerName() { + "https://\(userIDServerName)" + } else { + server() + } + + guard let serverName, + let url = URL(string: serverName)?.appending(path: "/.well-known/element/element.json") else { + return .failure(.invalidServerName) + } + + let response = try await getUrl(url: url.absoluteString) + let wellKnown = try makeElementWellKnown(string: response) + return .success(wellKnown) + } catch { + return .failure(.sdkError(error)) + } + } +} diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 1c8ba316f..c1fedbb38 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -594,20 +594,7 @@ class ClientProxy: ClientProxyProtocol { } func getElementWellKnown() async -> Result { - guard let userIDServerName, - var url = URL(string: "https://\(userIDServerName)") else { - return .failure(.invalidUserIDServerName) - } - - url.append(path: "/.well-known/element/element.json") - - do { - let response = try await client.getUrl(url: url.absoluteString) - let sdkWellKnown = try makeElementWellKnown(string: response) - return .success(ElementWellKnown(sdkWellKnown)) - } catch { - return .failure(.sdkError(error)) - } + await client.getElementWellKnown().map(ElementWellKnown.init) } // MARK: Ignored users diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index 1a7baf5db..eba44ddaf 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -32,7 +32,7 @@ enum ClientProxyError: Error { case sdkError(Error) case invalidMedia - case invalidUserIDServerName + case invalidServerName case failedUploadingMedia(Error, MatrixErrorCode) case roomPreviewIsPrivate } diff --git a/ElementX/Sources/Services/Client/ElementWellKnown.swift b/ElementX/Sources/Services/Client/ElementWellKnown.swift index b369d3fae..187d74234 100644 --- a/ElementX/Sources/Services/Client/ElementWellKnown.swift +++ b/ElementX/Sources/Services/Client/ElementWellKnown.swift @@ -19,8 +19,10 @@ struct ElementWellKnown { } let call: Call? + let registrationHelperURL: URL? init?(_ wellKnown: MatrixRustSDK.ElementWellKnown) { - call = Call(wellKnown.call) + call = wellKnown.call.flatMap(Call.init) + registrationHelperURL = wellKnown.registrationHelperUrl.flatMap(URL.init) } } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 3b398793a..ac29d717a 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -111,20 +111,20 @@ class MockScreen: Identifiable { switch id { case .login: let navigationStackCoordinator = NavigationStackCoordinator() - let coordinator = LoginScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(), + let coordinator = LoginScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationService(), analytics: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController)) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .serverSelection: let navigationStackCoordinator = NavigationStackCoordinator() - let coordinator = ServerSelectionScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(), + let coordinator = ServerSelectionScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationService(), userIndicatorController: ServiceLocator.shared.userIndicatorController, isModallyPresented: true)) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .authenticationFlow: - let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: MockAuthenticationServiceProxy(), + let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: MockAuthenticationService(), qrCodeLoginService: QRCodeLoginServiceMock(), bugReportService: BugReportServiceMock(), navigationRootCoordinator: navigationRootCoordinator, diff --git a/UnitTests/Sources/ClientProtocolTests.swift b/UnitTests/Sources/ClientProtocolTests.swift new file mode 100644 index 000000000..a4e03855c --- /dev/null +++ b/UnitTests/Sources/ClientProtocolTests.swift @@ -0,0 +1,70 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import XCTest + +@testable import ElementX +@testable import MatrixRustSDK + +class ClientProtocolTests: XCTestCase { + let server = "https://matrix.org" + let userIDServerName = "matrix.org" + let wellKnownURL = "https://matrix.org/.well-known/element/element.json" + + var client: ClientProtocol! + + func testWellKnownLoggedOut() async { + // Given a client that is logged out but has discovered a server. + let client = ClientSDKMock() + client.userIdServerNameThrowableError = MockError.notAvailable + client.serverReturnValue = server + + // When discovering a server that contains the registration helper URL. + client.getUrlUrlClosure = { [wellKnownURL] url in + guard url == wellKnownURL else { + XCTFail("An unexpected URL was used.") + throw MockError.notAvailable + } + return "{\"registration_helper_url\":\"https://develop.element.io/#/mobile_register\"}" + } + + guard case let .success(wellKnown) = await client.getElementWellKnown() else { + XCTFail("The request should succeed.") + return + } + + // Then the well-known should include that URL. + XCTAssertEqual(wellKnown, .init(call: nil, registrationHelperUrl: "https://develop.element.io/#/mobile_register")) + } + + func testWellKnownLoggedIn() async { + // Given a client that is logged in. + let client = ClientSDKMock() + client.userIdServerNameReturnValue = userIDServerName + + // When discovering a server that contains a custom call widget URL. + client.getUrlUrlClosure = { [wellKnownURL] url in + guard url == wellKnownURL else { + XCTFail("An unexpected URL was used.") + throw MockError.notAvailable + } + return "{\"call\":{\"widget_url\":\"https://call.element.dev\"}}" + } + + guard case let .success(wellKnown) = await client.getElementWellKnown() else { + XCTFail("The request should succeed.") + return + } + + // Then the well-known should include that URL. + XCTAssertEqual(wellKnown, .init(call: .init(widgetUrl: "https://call.element.dev"), registrationHelperUrl: nil)) + } + + enum MockError: Error { + case notAvailable + } +} diff --git a/project.yml b/project.yml index 930447218..b3d7ec2c1 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.48 + exactVersion: 1.0.49 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios