Server Confirmation Screen (#959)

* Add ServerConfirmationScreen.
This commit is contained in:
Doug 2023-05-30 10:31:18 +01:00 committed by GitHub
parent 088ca622a1
commit 74b36ce870
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 643 additions and 201 deletions

View File

@ -118,6 +118,7 @@
2CA6ABBC9A88EB89EA52FCCB /* ConfettiScene.scn in Resources */ = {isa = PBXBuildFile; fileRef = B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */; };
2CB6787E25B11711518E9588 /* OnboardingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6281B199D8A8F0892490C2E /* OnboardingCoordinator.swift */; };
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */; };
2E8C6672D0EE7D5B1BEDB8E2 /* ServerConfirmationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7478623CECC9438014244BA /* ServerConfirmationScreen.swift */; };
2F1CF90A3460C153154427F0 /* RoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086B997409328F091EBA43CE /* RoomScreenUITests.swift */; };
2F94054F50E312AF30BE07F3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B21E611DADDEF00307E7AC /* String.swift */; };
308BD9343B95657FAA583FB7 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 19CD5B074D7DD44AF4C58BB6 /* SwiftState */; };
@ -152,6 +153,7 @@
3F1893D73EEBF6ED4FCF6747 /* AnalyticsSettingsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85890C78055B786CCABC9194 /* AnalyticsSettingsScreenModels.swift */; };
3F2148F11164C7C5609984EB /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 2B788C81F6369D164ADEB917 /* GZIP */; };
3F70E237CE4C3FAB02FC227F /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; };
401BB28CD6B7DD6B4E7863E7 /* ServerConfirmationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9342F5D6729627B6393AF853 /* ServerConfirmationScreenModels.swift */; };
407DCE030E0F9B7C9861D38A /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 4278261E147DB2DE5CFB7FC5 /* PostHog */; };
40B79D20A873620F7F128A2C /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FA991289149D31F4286747 /* UserPreference.swift */; };
414F50CFCFEEE2611127DCFB /* RestorationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3558A15CFB934F9229301527 /* RestorationToken.swift */; };
@ -194,6 +196,7 @@
518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */; };
520EEDAFBC778AB0B41F2F53 /* ClientMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE6170EFE6A161B0A68AB61 /* ClientMock.swift */; };
5375902175B2FEA2949D7D74 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */; };
53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */; };
53DEF39F0C4DE02E3FC56D91 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 800631D7250B7F93195035F1 /* KeychainAccess */; };
53F1196F9C69512306A2693F /* TextRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C19F54A0C4FC9AB7ABD583 /* TextRoomTimelineItemContent.swift */; };
5455147CAC63F71E48F7D699 /* NSELogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D455BC2423D911A62ACFB2 /* NSELogger.swift */; };
@ -204,6 +207,7 @@
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */; };
5770C4906668C6D3008A2AC9 /* SessionVerificationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5046BB295AEAFA6FB81655 /* SessionVerificationScreenModels.swift */; };
588411C8FD72B2A2DFE5F7DE /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = E992D7B8BE54B2AB454613AF /* XCUIElement.swift */; };
5894C2514400A4FBC9327632 /* ServerConfirmationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03277E40D0E0DE0712021A71 /* ServerConfirmationScreenCoordinator.swift */; };
5897A59DDBD3592282092223 /* MediaSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49B9785E3AD7D1C15A29F2F /* MediaSourceProxy.swift */; };
595965CAD43B4E6CD615C17D /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D116D4633E177BE1AC0E71 /* AnalyticsSettingsScreenViewModel.swift */; };
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2245243369B99216C7D84E /* ImageCache.swift */; };
@ -330,6 +334,7 @@
890F0D453FE388756479AC97 /* AnalyticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C687844F60BFF532D49A994C /* AnalyticsTests.swift */; };
8922219C5C934C4155E8CA50 /* SharedUserDefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA8DC95C079805B0B56E8A9 /* SharedUserDefaultsKeys.swift */; };
8944548A684F1C837CEC47F4 /* RoomMembersListScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0946F77B696176E062D037 /* RoomMembersListScreenModels.swift */; };
89658A44C9FC19B58FD1C226 /* ServerConfirmationScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */; };
899793EFC63DF93C3E0141E7 /* RoomMemberDetailsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FA60F848D1C14F873F9621A /* RoomMemberDetailsScreenCoordinator.swift */; };
8A0BD60CA4A6004DB06B5403 /* MediaUploadingPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 669F35C505ACE1110589F875 /* MediaUploadingPreprocessor.swift */; };
8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E5E9C044BEB7C70B1378E91 /* UserSession.swift */; };
@ -400,6 +405,7 @@
A14A9419105A1CD42F0511C4 /* UserIndicatorModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43005941B3A2C9671E23C85 /* UserIndicatorModalView.swift */; };
A17FAD2EBC53E17B5FD384DB /* InviteUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22730A30C50AC2E3D5BA8642 /* InviteUsersScreenViewModelProtocol.swift */; };
A182920710146E5BEAA1A705 /* AnalyticsSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7DBA101D643B31E813F3AC1 /* AnalyticsSettingsScreen.swift */; };
A1D4033881320C9EB88196E6 /* ServerConfirmationScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE846DDA83BFD7EC5C03760B /* ServerConfirmationScreenUITests.swift */; };
A216C83ADCF32BA5EF8A6FBC /* InviteUsersViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */; };
A23B8B27A1436A1049EEF68E /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; };
A2434D4DFB49A68E5CD0F53C /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; };
@ -478,7 +484,6 @@
BB6BF528BC7F5B87E08C4F18 /* CameraPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A3B7637DDBD6AA97AC2545 /* CameraPicker.swift */; };
BB784A02BADB03C820617A46 /* TextRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A55430639712CFACA34F43 /* TextRoomTimelineItem.swift */; };
BCC864190651B3A3CF51E4DF /* MediaFileHandleProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */; };
BCEC41FB1F2BB663183863E4 /* LoginServerInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D379E13DD9D987470A3C70C /* LoginServerInfoSection.swift */; };
BD203FC6A7AE7637EA003643 /* RoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABDE6F66532CBEB0E016F94 /* RoomProxyMock.swift */; };
BD6D98676111DA8FC2BE4908 /* InvitesScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86873A768B13069BB5CAECF6 /* InvitesScreenViewModelProtocol.swift */; };
BD782053BE4C3D2F0BDE5699 /* ServiceLocator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */; };
@ -605,6 +610,7 @@
EF7924005216B8189898F370 /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA028DCD4157F9A1F999827 /* BackgroundTaskProtocol.swift */; };
F06CE9132855E81EBB6DDC32 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 940C605265DD82DA0C655E23 /* Kingfisher */; };
F07D88421A9BC4D03D4A5055 /* VideoRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */; };
F0A26CD502C3A5868353B0FA /* ServerConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24DEE0682C95F897B6C7CB0D /* ServerConfirmationScreenViewModel.swift */; };
F0F82C3C848C865C3098AA52 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 21C83087604B154AA30E9A8F /* SnapshotTesting */; };
F118DD449066E594F63C697D /* RoomMemberProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B5E17028C02DFA7DDA3931 /* RoomMemberProxyProtocol.swift */; };
F16109A6F6DF03DA26D59233 /* RoomDetailsEditScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122186B7CD1BC46A9C629DD9 /* RoomDetailsEditScreenUITests.swift */; };
@ -640,6 +646,7 @@
FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; };
FC10228E73323BDC09526F97 /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 997C7385E1A07E061D7E2100 /* GZIP */; };
FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; };
FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; };
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; };
FF149F0A3550A54C50ECBE7A /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C843CF833BF6485B64AC87E1 /* AppRouter.swift */; };
FFD3E4FF948E06C7585317FC /* TimelineStyler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 892E29C98C4E8182C9037F84 /* TimelineStyler.swift */; };
@ -699,6 +706,7 @@
024F7398C5FC12586FB10E9D /* EffectsScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EffectsScene.swift; sourceTree = "<group>"; };
0287793F11C480E242B03DF5 /* UserDiscoveryServiceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoveryServiceTest.swift; sourceTree = "<group>"; };
02D155E09BF961BBA8F85263 /* InviteUsersScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenViewModel.swift; sourceTree = "<group>"; };
03277E40D0E0DE0712021A71 /* ServerConfirmationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenCoordinator.swift; sourceTree = "<group>"; };
033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = "<group>"; };
03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = "<group>"; };
@ -783,9 +791,11 @@
227AC5D71A4CE43512062243 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
24227FF9A2797F6EA7F69CDD /* HomeScreenInvitesButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenInvitesButton.swift; sourceTree = "<group>"; };
248649EBA5BC33DB93698734 /* SessionVerificationControllerProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationControllerProxyMock.swift; sourceTree = "<group>"; };
24DEE0682C95F897B6C7CB0D /* ServerConfirmationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModel.swift; sourceTree = "<group>"; };
24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelProtocol.swift; sourceTree = "<group>"; };
25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxy.swift; sourceTree = "<group>"; };
260004737C573A56FA01E86E /* Encodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encodable.swift; sourceTree = "<group>"; };
277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfigurationScreenViewStateTests.swift; sourceTree = "<group>"; };
27A1AD6389A4659AF0CEAE62 /* NotificationServiceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtension.swift; sourceTree = "<group>"; };
287FC98AF2664EAD79C0D902 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
28C19F54A0C4FC9AB7ABD583 /* TextRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItemContent.swift; sourceTree = "<group>"; };
@ -969,7 +979,6 @@
7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowCoordinatorProtocol.swift; sourceTree = "<group>"; };
7D0CBC76C80E04345E11F2DB /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFactoryProtocol.swift; sourceTree = "<group>"; };
7D379E13DD9D987470A3C70C /* LoginServerInfoSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginServerInfoSection.swift; sourceTree = "<group>"; };
7DDBF99755A9008CF8C8499E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
7DDF49CEBC0DFC59C308335F /* RoomMemberDetailsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
7E0ADE4FAA5A4DB91CB07737 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; };
@ -1016,6 +1025,7 @@
92B45A6B13D32A131FCA4EFF /* FilePreviewScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewScreenViewModelProtocol.swift; sourceTree = "<group>"; };
92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTask.swift; sourceTree = "<group>"; };
9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = "<group>"; };
9342F5D6729627B6393AF853 /* ServerConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenModels.swift; sourceTree = "<group>"; };
9349F590E35CE514A71E6764 /* LoginHomeserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginHomeserver.swift; sourceTree = "<group>"; };
935C2FB18EFB8EEE96B26330 /* CreateRoomFlowParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomFlowParameters.swift; sourceTree = "<group>"; };
93B3513E60591237A49EE102 /* AnalyticsSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenCoordinator.swift; sourceTree = "<group>"; };
@ -1075,6 +1085,7 @@
AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserProfile+Mock.swift"; sourceTree = "<group>"; };
AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskServiceProtocol.swift; sourceTree = "<group>"; };
AB8E75B9CB6C78BE8D09B1AF /* OnboardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreen.swift; sourceTree = "<group>"; };
ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockImageCache.swift; sourceTree = "<group>"; };
AC3F82523D6F48B926D6AF68 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
ACB6C5E4950B6C9842F35A38 /* RoomTimelineViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineViewProvider.swift; sourceTree = "<group>"; };
@ -1194,6 +1205,7 @@
DBA8DC95C079805B0B56E8A9 /* SharedUserDefaultsKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedUserDefaultsKeys.swift; sourceTree = "<group>"; };
DBFEAC3AC691CBB84983E275 /* ElementXTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementXTests.swift; sourceTree = "<group>"; };
DC0AEA686E425F86F6BA0404 /* UNNotification+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNNotification+Creator.swift"; sourceTree = "<group>"; };
DE846DDA83BFD7EC5C03760B /* ServerConfirmationScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenUITests.swift; sourceTree = "<group>"; };
DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaFileHandleProxy.swift; sourceTree = "<group>"; };
DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationViewModelTests.swift; sourceTree = "<group>"; };
DF38B69D2C331A499276F400 /* FilePreviewViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewViewModelTests.swift; sourceTree = "<group>"; };
@ -1240,6 +1252,7 @@
EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewFrameReader.swift; sourceTree = "<group>"; };
EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceTests.swift; sourceTree = "<group>"; };
F012CB5EE3F2B67359F6CC52 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; };
F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenModels.swift; sourceTree = "<group>"; };
F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = "<group>"; };
F1964EE08550BEDBD0B0F5FD /* FilePreviewScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewScreenViewModel.swift; sourceTree = "<group>"; };
@ -1255,6 +1268,7 @@
F57C8022B8A871A1DCD1750A /* UserIndicatorToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorToastView.swift; sourceTree = "<group>"; };
F72EFC8C634469F9262659C7 /* NSItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSItemProvider.swift; sourceTree = "<group>"; };
F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattedBodyText.swift; sourceTree = "<group>"; };
F7478623CECC9438014244BA /* ServerConfirmationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreen.swift; sourceTree = "<group>"; };
F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermalinkBuilder.swift; sourceTree = "<group>"; };
F7E8A8047B50E3607ACD354E /* ImageProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProviderProtocol.swift; sourceTree = "<group>"; };
F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinatorTests.swift; sourceTree = "<group>"; };
@ -1560,6 +1574,14 @@
path = RoomMember;
sourceTree = "<group>";
};
2CAAA563083EAC0FA6BDC4F3 /* View */ = {
isa = PBXGroup;
children = (
F7478623CECC9438014244BA /* ServerConfirmationScreen.swift */,
);
path = View;
sourceTree = "<group>";
};
2D0D49B0533C4C2EB889BF3A /* ServerSelectionScreen */ = {
isa = PBXGroup;
children = (
@ -2056,7 +2078,6 @@
isa = PBXGroup;
children = (
4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */,
7D379E13DD9D987470A3C70C /* LoginServerInfoSection.swift */,
);
path = View;
sourceTree = "<group>";
@ -2231,6 +2252,8 @@
69B63F817FE305548DB4B512 /* RoomMembersListViewModelTests.swift */,
93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */,
AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */,
277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */,
F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */,
EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */,
A1C22B1B5FA3A765EADB2CC9 /* SessionVerificationStateMachineTests.swift */,
DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */,
@ -2546,6 +2569,7 @@
0F19DBE940499D3E3DD405D8 /* RoomMemberDetailsScreenUITests.swift */,
C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */,
086B997409328F091EBA43CE /* RoomScreenUITests.swift */,
DE846DDA83BFD7EC5C03760B /* ServerConfirmationScreenUITests.swift */,
054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */,
6D4777F0142E330A75C46FE4 /* SessionVerificationUITests.swift */,
8EC57A32ABC80D774CC663DB /* SettingsScreenUITests.swift */,
@ -2793,6 +2817,18 @@
path = RoomMemberDetailsScreen;
sourceTree = "<group>";
};
BA1938A75D8C780F694CEB62 /* ServerConfirmationScreen */ = {
isa = PBXGroup;
children = (
03277E40D0E0DE0712021A71 /* ServerConfirmationScreenCoordinator.swift */,
9342F5D6729627B6393AF853 /* ServerConfirmationScreenModels.swift */,
24DEE0682C95F897B6C7CB0D /* ServerConfirmationScreenViewModel.swift */,
ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */,
2CAAA563083EAC0FA6BDC4F3 /* View */,
);
path = ServerConfirmationScreen;
sourceTree = "<group>";
};
C0937E3B06A8F0E2DB7C8241 /* Other */ = {
isa = PBXGroup;
children = (
@ -3067,6 +3103,7 @@
92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */,
9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */,
90F48FEF84016ED42A94BA24 /* LoginScreen */,
BA1938A75D8C780F694CEB62 /* ServerConfirmationScreen */,
2D0D49B0533C4C2EB889BF3A /* ServerSelectionScreen */,
5B2C520AB9863B8CBC8EB3CA /* SoftLogoutScreen */,
);
@ -3640,6 +3677,8 @@
CAF8755E152204F55F8D6B5B /* RoomMembersListViewModelTests.swift in Sources */,
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */,
CC0D088F505F33A20DC5590F /* RoomStateEventStringBuilderTests.swift in Sources */,
53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */,
89658A44C9FC19B58FD1C226 /* ServerConfirmationScreenViewModelTests.swift in Sources */,
93875ADD456142D20823ED24 /* ServerSelectionViewModelTests.swift in Sources */,
86675910612A12409262DFBD /* SessionVerificationStateMachineTests.swift in Sources */,
755727E0B756430DFFEC4732 /* SessionVerificationViewModelTests.swift in Sources */,
@ -3845,7 +3884,6 @@
C5A07E2D88BE7D51DCECD166 /* LoginScreenModels.swift in Sources */,
BDA68E8D95B2B24B28825B8B /* LoginScreenViewModel.swift in Sources */,
A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */,
BCEC41FB1F2BB663183863E4 /* LoginServerInfoSection.swift in Sources */,
B94368839BDB69172E28E245 /* MXLog.swift in Sources */,
B66757D0254843162595B25D /* MXLogger.swift in Sources */,
67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */,
@ -3979,6 +4017,11 @@
0BFA67AFD757EE2BA569836A /* ScrollViewAdapter.swift in Sources */,
14E99D27628B1A6F0CB46FEA /* SeparatorRoomTimelineItem.swift in Sources */,
49F2E7DD8CAACE09CEECE3E6 /* SeparatorRoomTimelineView.swift in Sources */,
2E8C6672D0EE7D5B1BEDB8E2 /* ServerConfirmationScreen.swift in Sources */,
5894C2514400A4FBC9327632 /* ServerConfirmationScreenCoordinator.swift in Sources */,
401BB28CD6B7DD6B4E7863E7 /* ServerConfirmationScreenModels.swift in Sources */,
F0A26CD502C3A5868353B0FA /* ServerConfirmationScreenViewModel.swift in Sources */,
FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */,
43F35A7E5703D64DB0519C59 /* ServerSelectionScreen.swift in Sources */,
E5F4C992845388B50BABACAA /* ServerSelectionScreenCoordinator.swift in Sources */,
6530865EB9A8C0F0AF0216DA /* ServerSelectionScreenModels.swift in Sources */,
@ -4123,6 +4166,7 @@
A8771F5975A82759FA5138AE /* RoomMemberDetailsScreenUITests.swift in Sources */,
44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */,
2F1CF90A3460C153154427F0 /* RoomScreenUITests.swift in Sources */,
A1D4033881320C9EB88196E6 /* ServerConfirmationScreenUITests.swift in Sources */,
77FACC29F98FE2E65BBB6A5F /* ServerSelectionUITests.swift in Sources */,
05EC896A4B9AF4A56670C0BB /* SessionVerificationUITests.swift in Sources */,
9C5A07E7C33F3F40287D7861 /* SettingsScreenUITests.swift in Sources */,

View File

@ -6,7 +6,6 @@
// MARK: - Soft logout
"soft_logout_forgot_password" = "Forgot password";
"soft_logout_signin_title" = "Sign in";
"soft_logout_signin_notice" = "Your homeserver (%1$s) admin has signed you out of your account %2$s (%3$s).";
"soft_logout_signin_e2e_warning_notice" = "Sign in to recover encryption keys stored exclusively on this device. You need them to read all of your secure messages on any device.";

View File

@ -23,8 +23,6 @@ public enum UntranslatedL10n {
public static var softLogoutClearDataSubmit: String { return UntranslatedL10n.tr("Untranslated", "soft_logout_clear_data_submit") }
/// Clear personal data
public static var softLogoutClearDataTitle: String { return UntranslatedL10n.tr("Untranslated", "soft_logout_clear_data_title") }
/// Forgot password
public static var softLogoutForgotPassword: String { return UntranslatedL10n.tr("Untranslated", "soft_logout_forgot_password") }
/// Sign in to recover encryption keys stored exclusively on this device. You need them to read all of your secure messages on any device.
public static var softLogoutSigninE2eWarningNotice: String { return UntranslatedL10n.tr("Untranslated", "soft_logout_signin_e2e_warning_notice") }
/// Your homeserver (%1$s) admin has signed you out of your account %2$s (%3$s).

View File

@ -26,6 +26,7 @@ struct A11yIdentifiers {
static let reportContent = ReportContent()
static let roomScreen = RoomScreen()
static let roomDetailsScreen = RoomDetailsScreen()
static let serverConfirmationScreen = ServerConfirmationScreen()
static let sessionVerificationScreen = SessionVerificationScreen()
static let softLogoutScreen = SoftLogoutScreen()
static let startChatScreen = StartChatScreen()
@ -73,7 +74,6 @@ struct A11yIdentifiers {
let emailUsername = "login-email_username"
let password = "login-password"
let `continue` = "login-continue"
let changeServer = "login-change_server"
let oidc = "login-oidc"
let unsupportedServer = "login-unsupported_server"
}
@ -106,6 +106,11 @@ struct A11yIdentifiers {
let unignore = "room_member_details-unignore"
}
struct ServerConfirmationScreen {
let `continue` = "server_confirmation-continue"
let changeServer = "server_confirmation-change_server"
}
struct SessionVerificationScreen {
let requestVerification = "session_verification-request_verification"
let startSasVerification = "session_verification-start_sas_verification"
@ -115,7 +120,7 @@ struct A11yIdentifiers {
let verificationComplete = "session_verification-verification_complete"
let close = "session_verification-close"
}
struct SoftLogoutScreen {
let title = "soft_logout-title"
let message = "soft_logout-message"

View File

@ -14,6 +14,7 @@
// limitations under the License.
//
import Combine
import SwiftUI
@MainActor
@ -26,6 +27,8 @@ class AuthenticationCoordinator: CoordinatorProtocol {
private let authenticationService: AuthenticationServiceProxyProtocol
private let navigationStackCoordinator: NavigationStackCoordinator
private var cancellables: Set<AnyCancellable> = []
weak var delegate: AuthenticationCoordinatorDelegate?
init(authenticationService: AuthenticationServiceProxyProtocol,
@ -64,17 +67,20 @@ class AuthenticationCoordinator: CoordinatorProtocol {
switch await authenticationService.configure(for: ServiceLocator.shared.settings.defaultHomeserverAddress) {
case .success:
stopLoading()
showLoginScreen()
showServerConfirmationScreen()
case .failure:
stopLoading()
showServerSelectionScreen()
showServerSelectionScreen(isModallyPresented: false)
}
}
private func showServerSelectionScreen() {
private func showServerSelectionScreen(isModallyPresented: Bool) {
let navigationCoordinator = NavigationStackCoordinator()
let userIndicatorController: UserIndicatorControllerProtocol! = isModallyPresented ? UserIndicatorController(rootCoordinator: navigationCoordinator) : ServiceLocator.shared.userIndicatorController
let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
isModallyPresented: false)
userIndicatorController: userIndicatorController,
isModallyPresented: isModallyPresented)
let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)
coordinator.callback = { [weak self] action in
@ -82,18 +88,46 @@ class AuthenticationCoordinator: CoordinatorProtocol {
switch action {
case .updated:
self.showLoginScreen()
if isModallyPresented {
navigationStackCoordinator.setSheetCoordinator(nil)
} else {
showLoginScreen()
}
case .dismiss:
MXLog.failure("ServerSelectionScreen is requesting dismiss when part of a stack.")
navigationStackCoordinator.setSheetCoordinator(nil)
}
}
if isModallyPresented {
navigationCoordinator.setRootCoordinator(coordinator)
navigationStackCoordinator.setSheetCoordinator(userIndicatorController)
} else {
navigationStackCoordinator.push(coordinator)
}
}
private func showServerConfirmationScreen() {
let parameters = ServerConfirmationScreenCoordinatorParameters(authenticationService: authenticationService,
authenticationFlow: .login)
let coordinator = ServerConfirmationScreenCoordinator(parameters: parameters)
coordinator.actions.sink { [weak self] action in
guard let self else { return }
switch action {
case .confirm:
self.showLoginScreen()
case .changeServer:
self.showServerSelectionScreen(isModallyPresented: true)
}
}
.store(in: &cancellables)
navigationStackCoordinator.push(coordinator)
}
private func showLoginScreen() {
let parameters = LoginScreenCoordinatorParameters(authenticationService: authenticationService,
navigationStackCoordinator: navigationStackCoordinator)
let parameters = LoginScreenCoordinatorParameters(authenticationService: authenticationService)
let coordinator = LoginScreenCoordinator(parameters: parameters)
coordinator.callback = { [weak self] action in

View File

@ -20,8 +20,6 @@ import SwiftUI
struct LoginScreenCoordinatorParameters {
/// The service used to authenticate the user.
let authenticationService: AuthenticationServiceProxyProtocol
/// The navigation router used to present the server selection screen.
let navigationStackCoordinator: NavigationStackCoordinator
}
enum LoginScreenCoordinatorAction {
@ -37,7 +35,6 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
private let oidcAuthenticationPresenter: OIDCAuthenticationPresenter
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
private var navigationStackCoordinator: NavigationStackCoordinator { parameters.navigationStackCoordinator }
var callback: (@MainActor (LoginScreenCoordinatorAction) -> Void)?
@ -46,7 +43,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
init(parameters: LoginScreenCoordinatorParameters) {
self.parameters = parameters
viewModel = LoginScreenViewModel(homeserver: parameters.authenticationService.homeserver)
viewModel = LoginScreenViewModel(homeserver: parameters.authenticationService.homeserver.value)
oidcAuthenticationPresenter = OIDCAuthenticationPresenter(authenticationService: parameters.authenticationService)
}
@ -58,8 +55,6 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
guard let self else { return }
switch action {
case .selectServer:
self.presentServerSelectionScreen()
case .parseUsername(let username):
self.parseUsername(username)
case .forgotPassword:
@ -193,35 +188,10 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
/// Updates the view model with a different homeserver.
private func updateViewModel() {
viewModel.update(homeserver: authenticationService.homeserver)
viewModel.update(homeserver: authenticationService.homeserver.value)
indicateSuccess()
}
/// Presents the server selection screen as a modal.
private func presentServerSelectionScreen() {
let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
isModallyPresented: false)
let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)
coordinator.callback = { [weak self, weak coordinator] action in
guard let self, let coordinator else { return }
self.serverSelectionCoordinator(coordinator, didCompleteWith: action)
}
navigationStackCoordinator.push(coordinator)
}
/// Handles the result from the server selection modal, dismissing it after updating the view.
private func serverSelectionCoordinator(_ coordinator: ServerSelectionScreenCoordinator,
didCompleteWith action: ServerSelectionScreenCoordinatorAction) {
if action == .updated {
updateViewModel()
}
navigationStackCoordinator.pop()
}
/// Shows the forgot password screen.
private func showForgotPasswordScreen() {
viewModel.displayError(.alert("Not implemented."))

View File

@ -17,8 +17,6 @@
import Foundation
enum LoginScreenViewModelAction: CustomStringConvertible {
/// The user would like to select another server.
case selectServer
/// Parse the username and update the homeserver if included.
case parseUsername(String)
/// The user would like to reset their password.
@ -31,8 +29,6 @@ enum LoginScreenViewModelAction: CustomStringConvertible {
/// A string representation of the action, ignoring any associated values that could leak PII.
var description: String {
switch self {
case .selectServer:
return "selectServer"
case .parseUsername:
return "parseUsername"
case .forgotPassword:
@ -77,8 +73,6 @@ struct LoginScreenBindings {
}
enum LoginScreenViewAction {
/// The user would like to select another server.
case selectServer
/// Parse the username to detect if a homeserver is included.
case parseUsername
/// The user would like to reset their password.

View File

@ -30,8 +30,6 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc
override func process(viewAction: LoginScreenViewAction) {
switch viewAction {
case .selectServer:
callback?(.selectServer)
case .parseUsername:
callback?(.parseUsername(state.bindings.username))
case .forgotPassword:

View File

@ -31,9 +31,6 @@ struct LoginScreen: View {
.padding(.top, UIConstants.titleTopPaddingToNavigationBar)
.padding(.bottom, 32)
serverInfo
.padding(.bottom, 32)
switch context.viewState.loginMode {
case .password:
loginForm
@ -51,19 +48,18 @@ struct LoginScreen: View {
.alert(item: $context.alertInfo) { $0.alert }
}
/// The header containing a Welcome Back title.
/// The header containing the title and icon.
var header: some View {
Text(L10n.screenLoginTitle)
.font(.compound.headingLGBold)
.multilineTextAlignment(.center)
.foregroundColor(.element.primaryContent)
}
/// The sever information section that includes a button to select a different server.
var serverInfo: some View {
LoginServerInfoSection(address: context.viewState.homeserver.address) {
context.send(viewAction: .selectServer)
VStack(spacing: 8) {
AuthenticationIconImage(image: Image(systemName: "lock.fill"))
.padding(.bottom, 8)
Text(L10n.screenLoginTitleWithHomeserver(context.viewState.homeserver.address))
.font(.compound.headingMDBold)
.multilineTextAlignment(.center)
.foregroundColor(.element.primaryContent)
}
.padding(.horizontal, 16)
}
/// The form with text fields for username and password, along with a submit button.

View File

@ -1,65 +0,0 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
/// A view that shows information about the chosen homeserver,
/// along with an edit button to pick a different one.
struct LoginServerInfoSection: View {
// MARK: - Public
/// The address shown for the server.
let address: String
/// The action performed when tapping the edit button.
let editAction: () -> Void
// MARK: - Views
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(L10n.screenLoginServerHeader)
.font(.compound.bodySM)
.foregroundColor(.element.primaryContent)
.padding(.horizontal, 16)
Button(action: editAction) {
HStack {
Text(address)
.font(.compound.bodyMDSemibold)
.foregroundColor(.element.primaryContent)
.padding(.horizontal, 16)
.padding(.vertical)
Spacer()
Image(systemName: "chevron.right")
.font(.compound.bodyMD.bold())
.foregroundColor(.element.tertiaryContent)
.padding(.trailing, 16)
}
.background(RoundedRectangle(cornerRadius: 14).fill(Color.element.system))
}
.accessibilityIdentifier(A11yIdentifiers.loginScreen.changeServer)
}
}
}
struct LoginServerInfoSection_Previews: PreviewProvider {
static var previews: some View {
LoginServerInfoSection(address: "matrix.org", editAction: { })
.padding()
}
}

View File

@ -0,0 +1,64 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
import SwiftUI
struct ServerConfirmationScreenCoordinatorParameters {
let authenticationService: AuthenticationServiceProxyProtocol
let authenticationFlow: AuthenticationFlow
}
enum ServerConfirmationScreenCoordinatorAction {
case confirm
case changeServer
}
final class ServerConfirmationScreenCoordinator: CoordinatorProtocol {
private let parameters: ServerConfirmationScreenCoordinatorParameters
private var viewModel: ServerConfirmationScreenViewModelProtocol
private let actionsSubject: PassthroughSubject<ServerConfirmationScreenCoordinatorAction, Never> = .init()
private var cancellables: Set<AnyCancellable> = .init()
var actions: AnyPublisher<ServerConfirmationScreenCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(parameters: ServerConfirmationScreenCoordinatorParameters) {
self.parameters = parameters
viewModel = ServerConfirmationScreenViewModel(authenticationService: parameters.authenticationService,
authenticationFlow: parameters.authenticationFlow)
}
func start() {
viewModel.actions.sink { [weak self] action in
guard let self else { return }
switch action {
case .confirm:
self.actionsSubject.send(.confirm)
case .changeServer:
self.actionsSubject.send(.changeServer)
}
}
.store(in: &cancellables)
}
func toPresentable() -> AnyView {
AnyView(ServerConfirmationScreen(context: viewModel.context))
}
}

View File

@ -0,0 +1,64 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
enum ServerConfirmationScreenViewModelAction {
/// The user would like to continue with the current homeserver.
case confirm
/// The user would like to change to a different homeserver.
case changeServer
}
struct ServerConfirmationScreenViewState: BindableState {
/// The homeserver address input by the user.
var homeserverAddress: String
/// The flow being attempted on the selected homeserver.
let authenticationFlow: AuthenticationFlow
/// The screen's title.
var title: String {
switch authenticationFlow {
case .login:
return L10n.screenServerConfirmationTitleLogin(homeserverAddress)
case .register:
return L10n.screenServerConfirmationTitleRegister(homeserverAddress)
}
}
/// The message shown beneath the title.
var message: String {
switch authenticationFlow {
case .login:
if homeserverAddress == "matrix.org" {
return L10n.screenServerConfirmationMessageLoginMatrixDotOrg
} else if homeserverAddress == "element.io" {
return L10n.screenServerConfirmationMessageLoginElementDotIo
} else {
return ""
}
case .register:
return L10n.screenServerConfirmationMessageRegister
}
}
}
enum ServerConfirmationScreenViewAction {
/// The user would like to continue with the current homeserver.
case confirm
/// The user would like to change to a different homeserver.
case changeServer
}

View File

@ -0,0 +1,52 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
import SwiftUI
typealias ServerConfirmationScreenViewModelType = StateStoreViewModel<ServerConfirmationScreenViewState, ServerConfirmationScreenViewAction>
class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType, ServerConfirmationScreenViewModelProtocol {
private var actionsSubject: PassthroughSubject<ServerConfirmationScreenViewModelAction, Never> = .init()
var actions: AnyPublisher<ServerConfirmationScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(authenticationService: AuthenticationServiceProxyProtocol, authenticationFlow: AuthenticationFlow) {
super.init(initialViewState: ServerConfirmationScreenViewState(homeserverAddress: authenticationService.homeserver.value.address,
authenticationFlow: authenticationFlow))
authenticationService.homeserver
.receive(on: DispatchQueue.main)
.sink { [weak self] homeserver in
guard let self else { return }
state.homeserverAddress = homeserver.address
}
.store(in: &cancellables)
}
// MARK: - Public
override func process(viewAction: ServerConfirmationScreenViewAction) {
switch viewAction {
case .confirm:
actionsSubject.send(.confirm)
case .changeServer:
actionsSubject.send(.changeServer)
}
}
}

View File

@ -0,0 +1,23 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
@MainActor
protocol ServerConfirmationScreenViewModelProtocol {
var actions: AnyPublisher<ServerConfirmationScreenViewModelAction, Never> { get }
var context: ServerConfirmationScreenViewModelType.Context { get }
}

View File

@ -0,0 +1,99 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
struct ServerConfirmationScreen: View {
@ObservedObject var context: ServerConfirmationScreenViewModel.Context
var body: some View {
ScrollView {
header
.padding(.top, UIConstants.iconTopPaddingToNavigationBar)
.padding(.horizontal, 16)
.readableFrame()
}
.background(Color.element.background.ignoresSafeArea())
.safeAreaInset(edge: .bottom) {
buttons
.padding(.horizontal, 16)
.padding(.vertical, 16)
.readableFrame()
.background(Color.element.background.ignoresSafeArea())
}
}
/// The main content of the view to be shown in a scroll view.
var header: some View {
VStack(spacing: 8) {
AuthenticationIconImage(image: Image(systemName: "person.crop.circle.fill"))
.padding(.bottom, 8)
Text(context.viewState.title)
.font(.compound.headingMDBold)
.multilineTextAlignment(.center)
.foregroundColor(.element.primaryContent)
.fixedSize(horizontal: false, vertical: true)
Text(context.viewState.message)
.font(.compound.bodyMD)
.multilineTextAlignment(.center)
.foregroundColor(.element.tertiaryContent)
}
.padding(.horizontal, 16)
}
/// The action buttons shown at the bottom of the view.
var buttons: some View {
VStack(spacing: 24) {
Button { context.send(viewAction: .confirm) } label: {
Text(L10n.actionContinue)
}
.buttonStyle(.elementAction(.xLarge))
.accessibilityIdentifier(A11yIdentifiers.serverConfirmationScreen.continue)
Button { context.send(viewAction: .changeServer) } label: {
Text(L10n.screenServerConfirmationChangeServer)
.font(.compound.bodyLGSemibold)
.padding(.vertical, 14)
}
.accessibilityIdentifier(A11yIdentifiers.serverConfirmationScreen.changeServer)
}
}
}
// MARK: - Previews
struct ServerConfirmationScreen_Previews: PreviewProvider {
static let loginViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationServiceProxy(),
authenticationFlow: .login)
static let registerViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationServiceProxy(),
authenticationFlow: .register)
static var previews: some View {
NavigationStack {
ServerConfirmationScreen(context: loginViewModel.context)
.toolbar(.visible, for: .navigationBar)
}
.previewDisplayName("Login")
NavigationStack {
ServerConfirmationScreen(context: registerViewModel.context)
.toolbar(.visible, for: .navigationBar)
}
.previewDisplayName("Register")
}
}

View File

@ -39,7 +39,7 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
init(parameters: ServerSelectionScreenCoordinatorParameters) {
self.parameters = parameters
viewModel = ServerSelectionScreenViewModel(homeserverAddress: parameters.authenticationService.homeserver.address,
viewModel = ServerSelectionScreenViewModel(homeserverAddress: parameters.authenticationService.homeserver.value.address,
isModallyPresented: parameters.isModallyPresented)
userIndicatorController = parameters.userIndicatorController
}

View File

@ -53,7 +53,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
let homeserver = parameters.authenticationService.homeserver
viewModel = SoftLogoutScreenViewModel(credentials: parameters.credentials,
homeserver: homeserver,
homeserver: homeserver.value,
keyBackupNeeded: parameters.keyBackupNeeded)
oidcAuthenticationPresenter = OIDCAuthenticationPresenter(authenticationService: parameters.authenticationService)

View File

@ -87,7 +87,7 @@ struct SoftLogoutScreen: View {
.accessibilityIdentifier(A11yIdentifiers.softLogoutScreen.password)
Button { context.send(viewAction: .forgotPassword) } label: {
Text(UntranslatedL10n.softLogoutForgotPassword)
Text(L10n.actionForgotPassword)
.font(.compound.bodyLG)
}
.frame(maxWidth: .infinity, alignment: .trailing)

View File

@ -14,6 +14,7 @@
// limitations under the License.
//
import Combine
import Foundation
import MatrixRustSDK
@ -21,7 +22,9 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
private let authenticationService: AuthenticationService
private let userSessionStore: UserSessionStoreProtocol
private(set) var homeserver = LoginHomeserver(address: ServiceLocator.shared.settings.defaultHomeserverAddress, loginMode: .unknown)
private let homeserverSubject: CurrentValueSubject<LoginHomeserver, Never> = .init(LoginHomeserver(address: ServiceLocator.shared.settings.defaultHomeserverAddress,
loginMode: .unknown))
var homeserver: CurrentValuePublisher<LoginHomeserver, Never> { homeserverSubject.asCurrentValuePublisher() }
init(userSessionStore: UserSessionStoreProtocol) {
self.userSessionStore = userSessionStore
@ -60,7 +63,7 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
}
}
self.homeserver = homeserver
homeserverSubject.send(homeserver)
return .success(())
} catch AuthenticationError.SlidingSyncNotAvailable {
MXLog.info("User entered a homeserver that isn't configured for sliding sync.")

View File

@ -17,6 +17,14 @@
import Foundation
import MatrixRustSDK
/// Represents a particular authentication flow.
enum AuthenticationFlow {
/// The flow for signing in to an existing account.
case login
/// The flow for creating a new account.
case register
}
enum AuthenticationServiceError: Error {
/// An error occurred during OIDC authentication.
case oidcError(OIDCError)
@ -29,7 +37,8 @@ enum AuthenticationServiceError: Error {
}
protocol AuthenticationServiceProxyProtocol {
var homeserver: LoginHomeserver { get }
/// The currently configured homeserver.
var homeserver: CurrentValuePublisher<LoginHomeserver, Never> { get }
/// Sets up the service for login on the specified homeserver address.
func configure(for homeserverAddress: String) async -> Result<Void, AuthenticationServiceError>

View File

@ -14,27 +14,33 @@
// limitations under the License.
//
import Combine
import Foundation
import MatrixRustSDK
class MockAuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
let validCredentials = (username: "alice", password: "12345678")
private(set) var homeserver: LoginHomeserver = .mockMatrixDotOrg
private let homeserverSubject: CurrentValueSubject<LoginHomeserver, Never>
var homeserver: CurrentValuePublisher<LoginHomeserver, Never> { homeserverSubject.asCurrentValuePublisher() }
init(homeserver: LoginHomeserver = .mockMatrixDotOrg) {
homeserverSubject = .init(homeserver)
}
func configure(for homeserverAddress: String) async -> Result<Void, AuthenticationServiceError> {
// Map the address to the mock homeservers
if LoginHomeserver.mockMatrixDotOrg.address.contains(homeserverAddress) {
homeserver = .mockMatrixDotOrg
homeserverSubject.send(.mockMatrixDotOrg)
return .success(())
} else if LoginHomeserver.mockOIDC.address.contains(homeserverAddress) {
homeserver = .mockOIDC
homeserverSubject.send(.mockOIDC)
return .success(())
} else if LoginHomeserver.mockBasicServer.address.contains(homeserverAddress) {
homeserver = .mockBasicServer
homeserverSubject.send(.mockBasicServer)
return .success(())
} else if LoginHomeserver.mockUnsupported.address.contains(homeserverAddress) {
homeserver = .mockUnsupported
homeserverSubject.send(.mockUnsupported)
return .success(())
} else {
// Otherwise fail with an invalid server.

View File

@ -75,8 +75,19 @@ class MockScreen: Identifiable {
switch id {
case .login:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = LoginScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
navigationStackCoordinator: navigationStackCoordinator))
let coordinator = LoginScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy()))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .serverConfirmationLogin:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = ServerConfirmationScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(homeserver: .mockMatrixDotOrg),
authenticationFlow: .login))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .serverConfirmationRegister:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = ServerConfirmationScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(homeserver: .mockOIDC),
authenticationFlow: .register))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .serverSelection:

View File

@ -18,6 +18,8 @@ import Foundation
enum UITestsScreenIdentifier: String {
case login
case serverConfirmationLogin
case serverConfirmationRegister
case serverSelection
case serverSelectionNonModal
case authenticationFlow

View File

@ -42,10 +42,10 @@ class LoginTests: XCTestCase {
XCTAssertTrue(getStartedButton.waitForExistence(timeout: 10.0))
getStartedButton.tap()
let editHomeserverButton = app.buttons[A11yIdentifiers.loginScreen.changeServer]
XCTAssertTrue(editHomeserverButton.waitForExistence(timeout: 10.0))
editHomeserverButton.tap()
let changeHomeserverButton = app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer]
XCTAssertTrue(changeHomeserverButton.waitForExistence(timeout: 10.0))
changeHomeserverButton.tap()
let homeserverTextField = app.textFields[A11yIdentifiers.changeServerScreen.server]
XCTAssertTrue(homeserverTextField.waitForExistence(timeout: 10.0))
@ -56,6 +56,10 @@ class LoginTests: XCTestCase {
XCTAssertTrue(confirmButton.waitForExistence(timeout: 10.0))
confirmButton.tap()
let continueButton = app.buttons[A11yIdentifiers.serverConfirmationScreen.continue]
XCTAssertTrue(continueButton.waitForExistence(timeout: 10.0))
continueButton.tap()
let usernameTextField = app.textFields[A11yIdentifiers.loginScreen.emailUsername]
XCTAssertTrue(usernameTextField.waitForExistence(timeout: 10.0))

View File

@ -27,8 +27,13 @@ class AuthenticationCoordinatorUITests: XCTestCase {
// Splash Screen: Tap get started button
app.buttons[A11yIdentifiers.onboardingScreen.signIn].tap()
// Login Screen: Enter valid credentials
// Server Confirmation: Tap continue button
app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap()
// Login Screen: Confirm password login is available and not OIDC.
XCTAssertFalse(app.buttons[A11yIdentifiers.loginScreen.oidc].exists, "The OIDC button shouldn't be shown before entering a supported homeserver.")
// Login Screen: Enter valid credentials
app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice\n")
app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("12345678")
@ -47,6 +52,9 @@ class AuthenticationCoordinatorUITests: XCTestCase {
// Splash Screen: Tap get started button
app.buttons[A11yIdentifiers.onboardingScreen.signIn].tap()
// Server Confirmation: Tap continue button
app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap()
// Login Screen: Enter invalid credentials
app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice")
app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("87654321")
@ -68,16 +76,16 @@ class AuthenticationCoordinatorUITests: XCTestCase {
// Splash Screen: Tap get started button
app.buttons[A11yIdentifiers.onboardingScreen.signIn].tap()
// Login Screen: Tap edit server button.
XCTAssertFalse(app.buttons[A11yIdentifiers.loginScreen.oidc].exists, "The OIDC button shouldn't be shown before entering a supported homeserver.")
app.buttons[A11yIdentifiers.loginScreen.changeServer].tap()
// Server Confirmation: Tap change server button
app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap()
// Server Selection: Clear the default and enter OIDC server.
// Server Selection: Clear the default, enter OIDC server and continue.
app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("company.com")
// Dismiss server screen.
app.buttons[A11yIdentifiers.changeServerScreen.continue].tap()
// Server Confirmation: Tap continue button
app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap()
// Then the login form should be updated for OIDC.
XCTAssertTrue(app.buttons[A11yIdentifiers.loginScreen.oidc].waitForExistence(timeout: 1), "The OIDC button should be shown after selecting a homeserver with OIDC.")
}

View File

@ -0,0 +1,31 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import ElementX
import XCTest
@MainActor
class ServerConfirmationScreenUITests: XCTestCase {
func testLoginScreen() async throws {
let app = Application.launch(.serverConfirmationLogin)
try await app.assertScreenshot(.serverConfirmationLogin)
}
func testRegisterScreen() async throws {
let app = Application.launch(.serverConfirmationRegister)
try await app.assertScreenshot(.serverConfirmationRegister)
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,45 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import XCTest
@testable import ElementX
class ServerConfirmationScreenViewStateTests: XCTestCase {
func testLoginMessageString() {
let matrixDotOrgLogin = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockMatrixDotOrg.address,
authenticationFlow: .login)
XCTAssertEqual(matrixDotOrgLogin.message, L10n.screenServerConfirmationMessageLoginMatrixDotOrg, "matrix.org should have a custom message.")
let elementDotIoLogin = ServerConfirmationScreenViewState(homeserverAddress: "element.io",
authenticationFlow: .login)
XCTAssertEqual(elementDotIoLogin.message, L10n.screenServerConfirmationMessageLoginElementDotIo, "element.io should have a custom message.")
let otherLogin = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockOIDC.address,
authenticationFlow: .login)
XCTAssertTrue(otherLogin.message.isEmpty, "Other servers should not show a message.")
}
func testRegisterMessageString() {
let matrixDotOrgLogin = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockMatrixDotOrg.address,
authenticationFlow: .register)
XCTAssertEqual(matrixDotOrgLogin.message, L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.")
let otherLogin = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockOIDC.address,
authenticationFlow: .register)
XCTAssertEqual(otherLogin.message, L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.")
}
}

View File

@ -0,0 +1,24 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import XCTest
@testable import ElementX
@MainActor
class ServerConfirmationScreenViewModelTests: XCTestCase {
// Nothing to test, the view model has no mutable state.
}