diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index e397f3c4b..d8f131bd0 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -295,7 +295,6 @@ 386720B603F87D156DB01FB2 /* VoiceMessageMediaManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40076C770A5FB83325252973 /* VoiceMessageMediaManager.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 */; }; @@ -326,7 +325,6 @@ 3F2148F11164C7C5609984EB /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 2B788C81F6369D164ADEB917 /* GZIP */; }; 3F327A62D233933F54F0F33A /* SwiftOGG in Frameworks */ = {isa = PBXBuildFile; productRef = 3FE40E79C36E7903121E6E3B /* SwiftOGG */; }; 3F70E237CE4C3FAB02FC227F /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; }; - 3F997171C3C79A45E92BF9EF /* ElementWellKnown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FAC366FF299BCC555D756E /* ElementWellKnown.swift */; }; 401BB28CD6B7DD6B4E7863E7 /* ServerConfirmationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9342F5D6729627B6393AF853 /* ServerConfirmationScreenModels.swift */; }; 407DCE030E0F9B7C9861D38A /* LoremSwiftum in Frameworks */ = {isa = PBXBuildFile; productRef = 1A6B622CCFDEFB92D9CF1CA5 /* LoremSwiftum */; }; 40B79D20A873620F7F128A2C /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FA991289149D31F4286747 /* UserPreference.swift */; }; @@ -524,7 +522,6 @@ 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 */; }; 6681D6D3ADF69EBD2625F29A /* KnockedRoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */; }; 66832DE7B5C2E861045265DC /* RoomSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D879DC5515B1D42577F96C94 /* RoomSelectionScreen.swift */; }; @@ -821,7 +818,6 @@ 9F11E743EA01482E78A438B0 /* GlobalSearchScreenCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22DB19219E6CC4D002E15D48 /* GlobalSearchScreenCell.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 */; }; @@ -834,7 +830,6 @@ 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 */; }; A2357AA4A188BC37085BC6F0 /* EditRoomAddressScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5FDFAA04174CC99FB66391C /* EditRoomAddressScreenViewModel.swift */; }; @@ -855,7 +850,6 @@ A4C29D373986AFE4559696D5 /* SecureBackupKeyBackupScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4525E8C0FBDD27D1ACE90952 /* SecureBackupKeyBackupScreenViewModelProtocol.swift */; }; A4E885358D7DD5A072A06824 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = CCE5BF78B125320CBF3BB834 /* PostHog */; }; A51C65E5A3C9F2464A91A380 /* AuthenticationClientBuilderFactoryMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */; }; - A52090A4FE0DB826578DFC03 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0724EBDFE8BB4C9E5547C57D /* Client.swift */; }; A588572ED0EB18D947B32A5E /* SendInviteConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F276F31C1AEC19E52B951B62 /* SendInviteConfirmationView.swift */; }; A5B455D1A6DADF7476F7B417 /* EmojiProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BCCE3D12B0A9C6E559B5B5A /* EmojiProviderProtocol.swift */; }; A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9780389F8A53E4D26E23DD03 /* LoginScreenViewModelProtocol.swift */; }; @@ -1207,7 +1201,6 @@ 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 */; }; @@ -1272,7 +1265,6 @@ FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; }; FC0EEFF630F34899953BB950 /* BigIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01FD1171FF40E34D707FD00 /* BigIcon.swift */; }; FC10228E73323BDC09526F97 /* Mapbox in Frameworks */ = {isa = PBXBuildFile; productRef = C1BF15833233CD3BDB7E2B1D /* Mapbox */; }; - FC8B95EC506E6BB5793D81CE /* ClientProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E34685D186453E429ADEE58E /* ClientProtocolTests.swift */; }; FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; }; FD29471C72872F8B7580E3E1 /* KeychainControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */; }; FD4C21F8DA1E273DE94FCD1A /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; }; @@ -1404,7 +1396,6 @@ 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 = ""; }; 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessInfo.swift; sourceTree = ""; }; 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorStateMachine.swift; sourceTree = ""; }; @@ -1491,7 +1482,6 @@ 1A13364350970987B93F6018 /* JoinRoomByAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomByAddressView.swift; sourceTree = ""; }; 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Strings+Untranslated.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 = ""; }; 1B30CD19ED6243FEDFBA8400 /* ManageRoomMemberSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageRoomMemberSheetView.swift; sourceTree = ""; }; 1B53D6C5C0D14B04D3AB3F6E /* PillAttachmentViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillAttachmentViewProvider.swift; sourceTree = ""; }; @@ -1897,7 +1887,6 @@ 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 = ""; }; 6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderMock.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 = ""; }; @@ -1941,7 +1930,6 @@ 7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorControllerMock.swift; sourceTree = ""; }; 78BBDF7A05CF53B5CDC13682 /* landscape_test_video.mov */ = {isa = PBXFileReference; lastKnownFileType = video.quicktime; path = landscape_test_video.mov; sourceTree = ""; }; 796CBD0C56FA0D3AEDAB255B /* SessionVerificationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenCoordinator.swift; sourceTree = ""; }; - 79FAC366FF299BCC555D756E /* ElementWellKnown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementWellKnown.swift; sourceTree = ""; }; 7A03E073077D92AA19C43DCF /* IdentityConfirmationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreenCoordinator.swift; sourceTree = ""; }; 7AAD8C633AA57948B34EDCF7 /* RoomChangeRolesScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenViewModelProtocol.swift; sourceTree = ""; }; 7AC0CD1CAFD3F8B057F9AEA5 /* ClientBuilderHook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientBuilderHook.swift; sourceTree = ""; }; @@ -2159,7 +2147,6 @@ A436057DBEA1A23CA8CB1FD7 /* UIFont+AttributedStringBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIFont+AttributedStringBuilder.h"; sourceTree = ""; }; A443FAE2EE820A5790C35C8D /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = ""; }; A54AAF72E821B4084B7E4298 /* PinnedEventsTimelineFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsTimelineFlowCoordinator.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 = ""; }; @@ -2340,7 +2327,6 @@ C75EF87651B00A176AB08E97 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C75FE3F524B575D53787868C /* TimelineMediaPreviewRedactConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaPreviewRedactConfirmationView.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 = ""; }; @@ -2465,7 +2451,6 @@ 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 = ""; }; @@ -2567,7 +2552,6 @@ F64A8582F65567AC38C2976A /* PollFormScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenViewModel.swift; sourceTree = ""; }; F72EFC8C634469F9262659C7 /* NSItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSItemProvider.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 = ""; }; @@ -3063,14 +3047,6 @@ path = Progress; sourceTree = ""; }; - 25A88085FB8D8227DCDB0C9C /* View */ = { - isa = PBXGroup; - children = ( - 1B065EC39C99C1303A101C1C /* WebRegistrationScreen.swift */, - ); - path = View; - sourceTree = ""; - }; 26397A1EDB867FD573821532 /* MediaEventsTimelineScreen */ = { isa = PBXGroup; children = ( @@ -4290,7 +4266,6 @@ 7EECE8B331CD169790EF284F /* BugReportScreenViewModelTests.swift */, EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */, CAD9547E47C58930E2CE8306 /* CallScreenViewModelTests.swift */, - E34685D186453E429ADEE58E /* ClientProtocolTests.swift */, D5EA0312A6262484AA393AC9 /* CompletionSuggestionServiceTests.swift */, CA29952595B804DA221A0C1D /* ComposerToolbarViewModelTests.swift */, 69D42EE0102D2857933625DD /* CreateRoomViewModelTests.swift */, @@ -4606,10 +4581,8 @@ 8039515BAA53B7C3275AC64A /* Client */ = { isa = PBXGroup; children = ( - 0724EBDFE8BB4C9E5547C57D /* Client.swift */, 18F2958E6D247AE2516BEEE8 /* ClientProxy.swift */, 6033779EB37259F27F938937 /* ClientProxyProtocol.swift */, - 79FAC366FF299BCC555D756E /* ElementWellKnown.swift */, ); path = Client; sourceTree = ""; @@ -5647,18 +5620,6 @@ 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 = ( @@ -5906,7 +5867,6 @@ BA1938A75D8C780F694CEB62 /* ServerConfirmationScreen */, 2D0D49B0533C4C2EB889BF3A /* ServerSelectionScreen */, 5B2C520AB9863B8CBC8EB3CA /* SoftLogoutScreen */, - D847C12EC9B19A5FCDF2C815 /* WebRegistrationScreen */, ); path = Authentication; sourceTree = ""; @@ -6778,7 +6738,6 @@ 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 */, @@ -7069,7 +7028,6 @@ E14E469CD97550D0FC58F3CA /* CancellableTask.swift in Sources */, DF8F1211F2B0B56F0FCCA5C2 /* CertificateValidatorHook.swift in Sources */, D885B783B95AD7832D4EF5DD /* CharacterSet.swift in Sources */, - A52090A4FE0DB826578DFC03 /* Client.swift in Sources */, C80E06ED97CE52704A46C148 /* ClientBuilder.swift in Sources */, 87CEA3E07B602705BC2D2A20 /* ClientBuilderHook.swift in Sources */, 1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */, @@ -7144,7 +7102,6 @@ 07CC13C5729C24255348CBBD /* ElementCallWidgetDriver.swift in Sources */, 370AF5BFCD4384DD455479B6 /* ElementCallWidgetDriverProtocol.swift in Sources */, A87DC550659C5176AC1829DE /* ElementTextFieldStyle.swift in Sources */, - 3F997171C3C79A45E92BF9EF /* ElementWellKnown.swift in Sources */, 7C1A7B594B2F8143F0DD0005 /* ElementXAttributeScope.swift in Sources */, 3A08584ECDD4A4541DBF21F8 /* EmojiLoaderProtocol.swift in Sources */, 340D39DB87F3800D53A6A621 /* EmojiPickerScreen.swift in Sources */, @@ -7828,11 +7785,6 @@ 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 */, 80F1B442DB5E2C362ACDD8E2 /* ZoomTransition.swift in Sources */, diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index d9877439e..5c942372d 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -210,8 +210,10 @@ final class AppSettings { contacts: [supportEmailAddress], staticRegistrations: oidcStaticRegistrations.mapKeys { $0.absoluteString }) - /// A temporary hack to allow registration on matrix.org until MAS is deployed. - let webRegistrationEnabled = true + /// Whether or not the Create Account button is shown on the start screen. + /// + /// **Note:** Setting this to false doesn't prevent someone from creating an account when the selected homeserver's MAS allows registration. + let showCreateAccountButton = true // MARK: - Notifications diff --git a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift index e54333b1a..93b6a1bbd 100644 --- a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift @@ -68,7 +68,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { // MARK: - Private private func showStartScreen() { - let parameters = AuthenticationStartScreenParameters(webRegistrationEnabled: appSettings.webRegistrationEnabled, + let parameters = AuthenticationStartScreenParameters(showCreateAccountButton: appSettings.showCreateAccountButton, isBugReportServiceEnabled: bugReportService.isEnabled) let coordinator = AuthenticationStartScreenCoordinator(parameters: parameters) @@ -145,10 +145,8 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { switch action { case .continue(let window): - if authenticationService.homeserver.value.loginMode == .oidc, let window { + if authenticationService.homeserver.value.loginMode.supportsOIDCFlow, let window { showOIDCAuthentication(presentationAnchor: window) - } else if authenticationFlow == .register { - showWebRegistration() } else { showLoginScreen() } @@ -187,26 +185,6 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { navigationStackCoordinator.setSheetCoordinator(navigationCoordinator) } - private func showWebRegistration() { - let parameters = WebRegistrationScreenCoordinatorParameters(authenticationService: authenticationService, - userIndicatorController: userIndicatorController) - let coordinator = WebRegistrationScreenCoordinator(parameters: parameters) - - coordinator.actionsPublisher.sink { [weak self] action in - guard let self else { return } - - switch action { - case .cancel: - navigationStackCoordinator.setSheetCoordinator(nil) - case .signedIn(let userSession): - userHasSignedIn(userSession: userSession) - } - } - .store(in: &cancellables) - - navigationStackCoordinator.setSheetCoordinator(coordinator) - } - private func showOIDCAuthentication(presentationAnchor: UIWindow) { startLoading() diff --git a/ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift b/ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift index fed76b08d..d98f3e359 100644 --- a/ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift +++ b/ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift @@ -15,19 +15,21 @@ extension AuthenticationClientBuilderMock { "example.com": ClientSDKMock(configuration: .init(serverAddress: "example.com", homeserverURL: "https://matrix.example.com", slidingSyncVersion: .native, - supportsPasswordLogin: true, - elementWellKnown: "")), + oidcLoginURL: nil, + supportsOIDCCreatePrompt: false, + supportsPasswordLogin: true)), "company.com": ClientSDKMock(configuration: .init(serverAddress: "company.com", homeserverURL: "https://matrix.company.com", slidingSyncVersion: .native, oidcLoginURL: "https://auth.company.com/oidc", - supportsPasswordLogin: false, - elementWellKnown: "")), + supportsOIDCCreatePrompt: false, + supportsPasswordLogin: false)), "server.net": ClientSDKMock(configuration: .init(serverAddress: "server.net", homeserverURL: "https://matrix.example.com", slidingSyncVersion: .native, - supportsPasswordLogin: false, - elementWellKnown: "")) + oidcLoginURL: nil, + supportsOIDCCreatePrompt: false, + supportsPasswordLogin: false)) ] var qrCodeClient = ClientSDKMock(configuration: .init()) } diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index 907029451..31bd7bad5 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -76,8 +76,6 @@ extension ClientProxyMock { recentlyVisitedRoomsReturnValue = .success([]) recentConversationCounterpartsReturnValue = [] - getElementWellKnownReturnValue = .success(nil) - loadMediaContentForSourceThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic) loadMediaThumbnailForSourceWidthHeightThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic) loadMediaFileForSourceFilenameThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 3cf60c159..d56e74f34 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -4281,70 +4281,6 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable { return isAliasAvailableReturnValue } } - //MARK: - getElementWellKnown - - var getElementWellKnownUnderlyingCallsCount = 0 - var getElementWellKnownCallsCount: Int { - get { - if Thread.isMainThread { - return getElementWellKnownUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = getElementWellKnownUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getElementWellKnownUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - getElementWellKnownUnderlyingCallsCount = newValue - } - } - } - } - var getElementWellKnownCalled: Bool { - return getElementWellKnownCallsCount > 0 - } - - var getElementWellKnownUnderlyingReturnValue: Result! - var getElementWellKnownReturnValue: Result! { - get { - if Thread.isMainThread { - return getElementWellKnownUnderlyingReturnValue - } else { - var returnValue: Result? = nil - DispatchQueue.main.sync { - returnValue = getElementWellKnownUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getElementWellKnownUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - getElementWellKnownUnderlyingReturnValue = newValue - } - } - } - } - var getElementWellKnownClosure: (() async -> Result)? - - func getElementWellKnown() async -> Result { - getElementWellKnownCallsCount += 1 - if let getElementWellKnownClosure = getElementWellKnownClosure { - return await getElementWellKnownClosure() - } else { - return getElementWellKnownReturnValue - } - } //MARK: - clearCaches var clearCachesUnderlyingCallsCount = 0 diff --git a/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift b/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift index 9c694db81..d796825fe 100644 --- a/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift +++ b/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift @@ -15,9 +15,9 @@ extension ClientSDKMock { var serverAddress = "matrix.org" var homeserverURL = "https://matrix-client.matrix.org" var slidingSyncVersion = SlidingSyncVersion.native - var oidcLoginURL: String? + var oidcLoginURL: String? = "https://account.matrix.org/authorize" + var supportsOIDCCreatePrompt = true var supportsPasswordLogin = true - var elementWellKnown = "{\"registration_helper_url\":\"https://develop.element.io/#/mobile_register\"}" var validCredentials = (username: "alice", password: "12345678") // MARK: Session @@ -41,7 +41,6 @@ extension ClientSDKMock { slidingSyncVersionReturnValue = configuration.slidingSyncVersion userIdServerNameThrowableError = MockError.generic serverReturnValue = "https://\(configuration.serverAddress)" - getUrlUrlReturnValue = configuration.elementWellKnown urlForOidcOidcConfigurationPromptReturnValue = OAuthAuthorizationDataSDKMock(configuration: configuration) loginUsernamePasswordInitialDeviceNameDeviceIdClosure = { username, password, _, _ in guard username == configuration.validCredentials.username, @@ -62,6 +61,11 @@ extension HomeserverLoginDetailsSDKMock { slidingSyncVersionReturnValue = configuration.slidingSyncVersion supportsPasswordLoginReturnValue = configuration.supportsPasswordLogin supportsOidcLoginReturnValue = configuration.oidcLoginURL != nil + supportedOidcPromptsReturnValue = switch (configuration.oidcLoginURL, configuration.supportsOIDCCreatePrompt) { + case (.none, _): [] + case (.some, true): [.consent, .create] + case (.some, false): [.consent] + } urlReturnValue = configuration.homeserverURL } } diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift index 87b137b4a..e1f6efa51 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift @@ -13,21 +13,13 @@ 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, registrationHelperURL: URL? = nil) { + init(address: String, loginMode: LoginMode) { let address = Self.sanitized(address).components(separatedBy: "://").last ?? address self.address = address self.loginMode = loginMode - self.registrationHelperURL = registrationHelperURL - } - - /// Whether or not the app is able to register on this homeserver. - var supportsRegistration: Bool { - loginMode == .oidc || (address == "matrix.org" && registrationHelperURL != nil) } /// Sanitizes a user entered homeserver address with the following rules @@ -53,7 +45,7 @@ struct LoginHomeserver: Equatable { extension LoginHomeserver { /// A mock homeserver that is configured just like matrix.org. static var mockMatrixDotOrg: LoginHomeserver { - LoginHomeserver(address: "matrix.org", loginMode: .password, registrationHelperURL: "https://develop.element.io/#/mobile_register") + LoginHomeserver(address: "matrix.org", loginMode: .oidc(supportsCreatePrompt: true)) } /// A mock homeserver that supports login and registration via a password but has no SSO providers. @@ -63,7 +55,7 @@ extension LoginHomeserver { /// A mock homeserver that supports only supports authentication via a single SSO provider. static var mockOIDC: LoginHomeserver { - LoginHomeserver(address: "company.com", loginMode: .oidc) + LoginHomeserver(address: "company.com", loginMode: .oidc(supportsCreatePrompt: false)) } /// A mock homeserver that only with no supported login flows. diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginMode.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginMode.swift index c4edcda57..6163f6c8b 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginMode.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginMode.swift @@ -12,7 +12,7 @@ enum LoginMode: Equatable { /// The login mode hasn't been determined yet. case unknown /// The homeserver supports login via OpenID Connect. - case oidc + case oidc(supportsCreatePrompt: Bool) /// The homeserver supports login with a password. case password /// The homeserver only allows login with unsupported mechanisms. Use fallback instead. diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModel.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModel.swift index b3ed156d8..280836ed8 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModel.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModel.swift @@ -69,7 +69,7 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc Task { switch await authenticationService.configure(for: homeserverDomain, flow: .login) { case .success: - if authenticationService.homeserver.value.loginMode == .oidc { + if authenticationService.homeserver.value.loginMode.supportsOIDCFlow { actionsSubject.send(.configuredForOIDC) } stopLoading() diff --git a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/View/WebRegistrationScreen.swift b/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/View/WebRegistrationScreen.swift deleted file mode 100644 index a45e9e808..000000000 --- a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/View/WebRegistrationScreen.swift +++ /dev/null @@ -1,139 +0,0 @@ -// -// Copyright 2022-2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files 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)) - } - - 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.") - 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 - - 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 deleted file mode 100644 index b8c294cf8..000000000 --- a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenCoordinator.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright 2022-2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files 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 deleted file mode 100644 index c107fbc4a..000000000 --- a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenModels.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright 2022-2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files 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 deleted file mode 100644 index e87476b07..000000000 --- a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModel.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright 2022-2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files 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 deleted file mode 100644 index 6d3af5a6c..000000000 --- a/ElementX/Sources/Screens/Authentication/WebRegistrationScreen/WebRegistrationScreenViewModelProtocol.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Copyright 2022-2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files 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/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift index 5dbe1b76d..61e112876 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift @@ -9,7 +9,7 @@ import Combine import SwiftUI struct AuthenticationStartScreenParameters { - let webRegistrationEnabled: Bool + let showCreateAccountButton: Bool let isBugReportServiceEnabled: Bool } @@ -23,7 +23,7 @@ final class AuthenticationStartScreenCoordinator: CoordinatorProtocol { } init(parameters: AuthenticationStartScreenParameters) { - viewModel = AuthenticationStartScreenViewModel(webRegistrationEnabled: parameters.webRegistrationEnabled, + viewModel = AuthenticationStartScreenViewModel(showCreateAccountButton: parameters.showCreateAccountButton, isBugReportServiceEnabled: parameters.isBugReportServiceEnabled) } diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift index 1624c752a..bc7b12468 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift @@ -24,7 +24,7 @@ enum AuthenticationStartScreenViewModelAction { } struct AuthenticationStartScreenViewState: BindableState { - let isWebRegistrationEnabled: Bool + let showCreateAccountButton: Bool let isQRCodeLoginEnabled: Bool let isBugReportServiceEnabled: Bool } diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift index 0fba37741..463115010 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift @@ -17,8 +17,8 @@ class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType actionsSubject.eraseToAnyPublisher() } - init(webRegistrationEnabled: Bool, isBugReportServiceEnabled: Bool) { - super.init(initialViewState: AuthenticationStartScreenViewState(isWebRegistrationEnabled: webRegistrationEnabled, + init(showCreateAccountButton: Bool, isBugReportServiceEnabled: Bool) { + super.init(initialViewState: AuthenticationStartScreenViewState(showCreateAccountButton: showCreateAccountButton, isQRCodeLoginEnabled: !ProcessInfo.processInfo.isiOSAppOnMac, isBugReportServiceEnabled: isBugReportServiceEnabled)) } diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift index 110d8c22c..1da2eb0bc 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift @@ -102,7 +102,7 @@ struct AuthenticationStartScreen: View { .buttonStyle(.compound(.primary)) .accessibilityIdentifier(A11yIdentifiers.authenticationStartScreen.signIn) - if context.viewState.isWebRegistrationEnabled { + if context.viewState.showCreateAccountButton { Button { context.send(viewAction: .register) } label: { Text(L10n.screenCreateAccountTitle) .padding(14) @@ -129,7 +129,7 @@ struct AuthenticationStartScreen_Previews: PreviewProvider, TestablePreview { } static func makeViewModel(isBugReportServiceEnabled: Bool = true) -> AuthenticationStartScreenViewModel { - AuthenticationStartScreenViewModel(webRegistrationEnabled: true, + AuthenticationStartScreenViewModel(showCreateAccountButton: true, isBugReportServiceEnabled: isBugReportServiceEnabled) } } diff --git a/ElementX/Sources/Screens/CallScreen/View/CallScreen.swift b/ElementX/Sources/Screens/CallScreen/View/CallScreen.swift index d9767e64b..472ec45df 100644 --- a/ElementX/Sources/Screens/CallScreen/View/CallScreen.swift +++ b/ElementX/Sources/Screens/CallScreen/View/CallScreen.swift @@ -292,7 +292,6 @@ private struct CallView: UIViewRepresentable { struct CallScreen_Previews: PreviewProvider { static let viewModel = { let clientProxy = ClientProxyMock() - clientProxy.getElementWellKnownReturnValue = .success(nil) clientProxy.deviceID = "call-device-id" let roomProxy = JoinedRoomProxyMock() diff --git a/ElementX/Sources/Services/Authentication/AuthenticationService.swift b/ElementX/Sources/Services/Authentication/AuthenticationService.swift index f8489b809..a4aa9faae 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationService.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationService.swift @@ -48,27 +48,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())") homeserver.loginMode = if loginDetails.supportsOidcLogin() { - .oidc + .oidc(supportsCreatePrompt: loginDetails.supportedOidcPrompts().contains(.create)) } else if loginDetails.supportsPasswordLogin() { .password } else { .unsupported } - homeserver.registrationHelperURL = switch elementWellKnown { - case .success(let wellKnown): wellKnown.registrationHelperUrl.flatMap(URL.init) - case .failure: nil - } - if flow == .login, homeserver.loginMode == .unsupported { return .failure(.loginNotSupported) } - if flow == .register, !homeserver.supportsRegistration { + if flow == .register, !homeserver.loginMode.supportsOIDCFlow { return .failure(.registrationNotSupported) } @@ -91,6 +85,8 @@ class AuthenticationService: AuthenticationServiceProtocol { func urlForOIDCLogin() async -> Result { guard let client else { return .failure(.oidcError(.urlFailure)) } do { + // The create prompt is broken: https://github.com/element-hq/matrix-authentication-service/issues/3429 + // let prompt: OidcPrompt = flow == .register ? .create : .consent let oidcData = try await client.urlForOidc(oidcConfiguration: appSettings.oidcConfiguration.rustValue, prompt: .consent) return .success(OIDCAuthorizationDataProxy(underlyingData: oidcData)) @@ -148,25 +144,6 @@ 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) - } - } - func reset() { homeserverSubject.send(LoginHomeserver(address: appSettings.defaultHomeserverAddress, loginMode: .unknown)) flow = .login diff --git a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift index 278a86a92..54b6611dd 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift @@ -48,8 +48,6 @@ protocol AuthenticationServiceProtocol { func loginWithOIDCCallback(_ callbackURL: URL) 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 /// Resets the current configuration requiring `configure(for:flow:)` to be called again. func reset() diff --git a/ElementX/Sources/Services/Client/Client.swift b/ElementX/Sources/Services/Client/Client.swift deleted file mode 100644 index 8b5b3e82c..000000000 --- a/ElementX/Sources/Services/Client/Client.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright 2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files 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 667fea153..8b43e9ddb 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -703,10 +703,6 @@ class ClientProxy: ClientProxyProtocol { } } - func getElementWellKnown() async -> Result { - await client.getElementWellKnown().map(ElementWellKnown.init) - } - func clearCaches() async -> Result { do { return try await .success(client.clearCaches()) diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index 980127fdd..97450350e 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -182,8 +182,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func isAliasAvailable(_ alias: String) async -> Result - func getElementWellKnown() async -> Result - @discardableResult func clearCaches() async -> Result // MARK: - Ignored users diff --git a/ElementX/Sources/Services/Client/ElementWellKnown.swift b/ElementX/Sources/Services/Client/ElementWellKnown.swift deleted file mode 100644 index 6be07d98a..000000000 --- a/ElementX/Sources/Services/Client/ElementWellKnown.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright 2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files in the repository root for full details. -// - -import Foundation -import MatrixRustSDK - -struct ElementWellKnown { - let registrationHelperURL: URL? - - init?(_ wellKnown: MatrixRustSDK.ElementWellKnown) { - registrationHelperURL = wellKnown.registrationHelperUrl.flatMap(URL.init) - } -} diff --git a/UnitTests/Sources/AuthenticationServiceTests.swift b/UnitTests/Sources/AuthenticationServiceTests.swift index a612cf84a..8644c5e3e 100644 --- a/UnitTests/Sources/AuthenticationServiceTests.swift +++ b/UnitTests/Sources/AuthenticationServiceTests.swift @@ -16,10 +16,10 @@ class AuthenticationServiceTests: XCTestCase { var service: AuthenticationService! - func testLogin() async { - setupMocks() + func testPasswordLogin() async { + setupMocks(serverAddress: "example.com") - switch await service.configure(for: "matrix.org", flow: .login) { + switch await service.configure(for: "example.com", flow: .login) { case .success: break case .failure(let error): @@ -27,7 +27,7 @@ class AuthenticationServiceTests: XCTestCase { } XCTAssertEqual(service.flow, .login) - XCTAssertEqual(service.homeserver.value, .mockMatrixDotOrg) + XCTAssertEqual(service.homeserver.value, .mockBasicServer) switch await service.login(username: "alice", password: "12345678", initialDeviceName: nil, deviceID: nil) { case .success: @@ -40,7 +40,21 @@ class AuthenticationServiceTests: XCTestCase { } } - func testConfigureRegister() async { + func testConfigureLoginWithOIDC() async { + setupMocks() + + switch await service.configure(for: "matrix.org", flow: .login) { + case .success: + break + case .failure(let error): + XCTFail("Unexpected failure: \(error)") + } + + XCTAssertEqual(service.flow, .login) + XCTAssertEqual(service.homeserver.value, .mockMatrixDotOrg) + } + + func testConfigureRegisterWithOIDC() async { setupMocks() switch await service.configure(for: "matrix.org", flow: .register) { diff --git a/UnitTests/Sources/ClientProtocolTests.swift b/UnitTests/Sources/ClientProtocolTests.swift deleted file mode 100644 index 6bd66a5bd..000000000 --- a/UnitTests/Sources/ClientProtocolTests.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// Copyright 2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files 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/UnitTests/Sources/LoginScreenViewModelTests.swift b/UnitTests/Sources/LoginScreenViewModelTests.swift index b2109e07f..63a3d78f4 100644 --- a/UnitTests/Sources/LoginScreenViewModelTests.swift +++ b/UnitTests/Sources/LoginScreenViewModelTests.swift @@ -17,7 +17,7 @@ class LoginScreenViewModelTests: XCTestCase { var clientBuilderFactory: AuthenticationClientBuilderFactoryMock! var service: AuthenticationServiceProtocol! - private func setupViewModel(homeserverAddress: String = "matrix.org") async { + private func setupViewModel(homeserverAddress: String = "example.com") async { clientBuilderFactory = AuthenticationClientBuilderFactoryMock(configuration: .init()) service = AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()), encryptionKeyProvider: EncryptionKeyProvider(), @@ -36,18 +36,9 @@ class LoginScreenViewModelTests: XCTestCase { analytics: ServiceLocator.shared.analytics) } - func testMatrixDotOrg() async { - // Given the initial view model configured for matrix.org. - await setupViewModel() - - // Then the view state should contain a homeserver that matches matrix.org and show the login form. - XCTAssertEqual(context.viewState.homeserver, .mockMatrixDotOrg, "The homeserver data should match the default homeserver.") - XCTAssertEqual(context.viewState.loginMode, .password, "The login form should be shown.") - } - func testBasicServer() async { // Given the view model configured for a basic server example.com that only supports password authentication. - await setupViewModel(homeserverAddress: "example.com") + await setupViewModel() // Then the view state should be updated with the homeserver and show the login form. XCTAssertEqual(context.viewState.homeserver, .mockBasicServer, "The homeserver data should should match the new homeserver.") diff --git a/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift b/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift index 920bc90ed..75a1a3220 100644 --- a/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift +++ b/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift @@ -89,9 +89,9 @@ class ServerConfirmationScreenViewModelTests: XCTestCase { func testRegistrationNotSupportedAlert() async throws { // Given a view model for registration using a service that hasn't been configured and the default server doesn't support registration. - setupViewModel(authenticationFlow: .register, supportsRegistrationHelper: false) + // Note: We don't currently take the create prompt into account when determining registration support. + setupViewModel(authenticationFlow: .register, supportsOIDC: false, supportsOIDCCreatePrompt: false) XCTAssertEqual(service.homeserver.value.loginMode, .unknown) - XCTAssertFalse(service.homeserver.value.supportsRegistration) XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) XCTAssertNil(context.alertInfo) @@ -100,16 +100,15 @@ class ServerConfirmationScreenViewModelTests: XCTestCase { context.send(viewAction: .confirm) try await deferred.fulfill() - // Then the configured homeserver should be used and no additional call should be made to the service. + // Then the configuration should fail with an alert about not supporting registration. XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) XCTAssertEqual(context.alertInfo?.id, .registration) } func testLoginNotSupportedAlert() async throws { // Given a view model for login using a service that hasn't been configured and the default server doesn't support login. - setupViewModel(authenticationFlow: .login, supportsRegistrationHelper: false, supportsPasswordLogin: false) + setupViewModel(authenticationFlow: .login, supportsOIDC: false, supportsOIDCCreatePrompt: false, supportsPasswordLogin: false) XCTAssertEqual(service.homeserver.value.loginMode, .unknown) - XCTAssertFalse(service.homeserver.value.supportsRegistration) XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) XCTAssertNil(context.alertInfo) @@ -125,14 +124,11 @@ class ServerConfirmationScreenViewModelTests: XCTestCase { // MARK: - Helpers - private func setupViewModel(authenticationFlow: AuthenticationFlow, supportsRegistrationHelper: Bool = true, supportsPasswordLogin: Bool = true) { + private func setupViewModel(authenticationFlow: AuthenticationFlow, supportsOIDC: Bool = true, supportsOIDCCreatePrompt: Bool = true, supportsPasswordLogin: Bool = true) { // Manually create a configuration as the default homeserver address setting is immutable. - let clientConfiguration: ClientSDKMock.Configuration = if supportsRegistrationHelper { - .init(supportsPasswordLogin: supportsPasswordLogin) - } else { - .init(supportsPasswordLogin: supportsPasswordLogin, elementWellKnown: "") - } - let client = ClientSDKMock(configuration: clientConfiguration) + let client = ClientSDKMock(configuration: .init(oidcLoginURL: supportsOIDC ? "https://account.matrix.org/authorize" : nil, + supportsOIDCCreatePrompt: supportsOIDCCreatePrompt, + supportsPasswordLogin: supportsPasswordLogin)) let configuration = AuthenticationClientBuilderMock.Configuration(homeserverClients: ["matrix.org": client], qrCodeClient: client) diff --git a/UnitTests/Sources/ServerSelectionScreenViewModelTests.swift b/UnitTests/Sources/ServerSelectionScreenViewModelTests.swift index e8747a4a4..06ee2feb1 100644 --- a/UnitTests/Sources/ServerSelectionScreenViewModelTests.swift +++ b/UnitTests/Sources/ServerSelectionScreenViewModelTests.swift @@ -21,7 +21,6 @@ class ServerSelectionScreenViewModelTests: XCTestCase { // Given a view model for login. setupViewModel(authenticationFlow: .login) XCTAssertEqual(service.homeserver.value.loginMode, .unknown) - XCTAssertFalse(service.homeserver.value.supportsRegistration) XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) // When selecting matrix.org. @@ -39,7 +38,6 @@ class ServerSelectionScreenViewModelTests: XCTestCase { // Given a view model for login. setupViewModel(authenticationFlow: .login) XCTAssertEqual(service.homeserver.value.loginMode, .unknown) - XCTAssertFalse(service.homeserver.value.supportsRegistration) XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) XCTAssertNil(context.alertInfo) @@ -58,7 +56,6 @@ class ServerSelectionScreenViewModelTests: XCTestCase { // Given a view model for registration. setupViewModel(authenticationFlow: .register) XCTAssertEqual(service.homeserver.value.loginMode, .unknown) - XCTAssertFalse(service.homeserver.value.supportsRegistration) XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) // When selecting matrix.org. @@ -76,7 +73,6 @@ class ServerSelectionScreenViewModelTests: XCTestCase { // Given a view model for registration. setupViewModel(authenticationFlow: .register) XCTAssertEqual(service.homeserver.value.loginMode, .unknown) - XCTAssertFalse(service.homeserver.value.supportsRegistration) XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) XCTAssertNil(context.alertInfo) diff --git a/UnitTests/Sources/SoftLogoutViewModelTests.swift b/UnitTests/Sources/SoftLogoutViewModelTests.swift index 4840b94c5..8837b038a 100644 --- a/UnitTests/Sources/SoftLogoutViewModelTests.swift +++ b/UnitTests/Sources/SoftLogoutViewModelTests.swift @@ -9,39 +9,14 @@ import XCTest @testable import ElementX +@MainActor class SoftLogoutViewModelTests: XCTestCase { let credentials = SoftLogoutScreenCredentials(userID: "mock_user_id", - homeserverName: "https://matrix.org", + homeserverName: "https://example.com", userDisplayName: "mock_username", deviceID: "ABCDEFGH") - @MainActor func testInitialStateForMatrixOrg() { - let viewModel = SoftLogoutScreenViewModel(credentials: credentials, - homeserver: .mockMatrixDotOrg, - keyBackupNeeded: true) - let context = viewModel.context - - // Given a view model where the user hasn't yet sent the verification email. - XCTAssert(context.password.isEmpty, "The view model should start with an empty password.") - XCTAssertFalse(context.viewState.canSubmit, "The view model should start with an invalid password.") - XCTAssertEqual(context.viewState.loginMode, .password, "The view model should show login form for the given homeserver.") - XCTAssert(context.viewState.showRecoverEncryptionKeysMessage, "The view model should show recover encryption keys message.") - } - - @MainActor func testInitialStateForMatrixOrgPasswordEntered() { - let viewModel = SoftLogoutScreenViewModel(credentials: credentials, - homeserver: .mockMatrixDotOrg, - keyBackupNeeded: true, - password: "12345678") - let context = viewModel.context - - // Given a view model where the user hasn't yet sent the verification email. - XCTAssertTrue(context.viewState.canSubmit, "The view model should start with a valid password.") - XCTAssertEqual(context.viewState.loginMode, .password, "The view model should show login form for the given homeserver.") - XCTAssert(context.viewState.showRecoverEncryptionKeysMessage, "The view model should show recover encryption keys message.") - } - - @MainActor func testInitialStateForBasicServer() { + func testInitialStateForBasicServer() { let viewModel = SoftLogoutScreenViewModel(credentials: credentials, homeserver: .mockBasicServer, keyBackupNeeded: false) @@ -53,10 +28,23 @@ class SoftLogoutViewModelTests: XCTestCase { XCTAssertEqual(context.viewState.loginMode, .password, "The view model should show login form for the given homeserver.") XCTAssertFalse(context.viewState.showRecoverEncryptionKeysMessage, "The view model should not show recover encryption keys message.") } - - @MainActor func testInitialStateForOIDC() { + + func testInitialStateForBasicServerPasswordEntered() { let viewModel = SoftLogoutScreenViewModel(credentials: credentials, - homeserver: .mockOIDC, + homeserver: .mockBasicServer, + keyBackupNeeded: true, + password: "12345678") + let context = viewModel.context + + // Given a view model where the user hasn't yet sent the verification email. + XCTAssertTrue(context.viewState.canSubmit, "The view model should start with a valid password.") + XCTAssertEqual(context.viewState.loginMode, .password, "The view model should show login form for the given homeserver.") + XCTAssert(context.viewState.showRecoverEncryptionKeysMessage, "The view model should show recover encryption keys message.") + } + + func testInitialStateForOIDC() { + let viewModel = SoftLogoutScreenViewModel(credentials: credentials, + homeserver: .mockMatrixDotOrg, keyBackupNeeded: false) let context = viewModel.context @@ -67,7 +55,7 @@ class SoftLogoutViewModelTests: XCTestCase { XCTAssertFalse(context.viewState.showRecoverEncryptionKeysMessage, "The view model should not show recover encryption keys message.") } - @MainActor func testInitialStateForUnsupported() { + func testInitialStateForUnsupported() { let viewModel = SoftLogoutScreenViewModel(credentials: credentials, homeserver: .mockUnsupported, keyBackupNeeded: false)