mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Configure the AuthenticationService later now that we have 2 flows on the start screen. (#3316)
* Don't query the homeserver until confirming it (or selecting a different one). * Setup the infrastructure to test AuthenticationService. Implement basic tests for configuration & password login. * Use the real AuthenticationService with a mock Client in all of the tests. * Add tests for the ServerConfirmationScreenViewModel. * Remove redundant view state and test for it.
This commit is contained in:
parent
af8c16150b
commit
a8dbda90d9
@ -83,6 +83,7 @@
|
|||||||
0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 187853A7E643995EE49FAD43 /* Localizable.stringsdict */; };
|
0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 187853A7E643995EE49FAD43 /* Localizable.stringsdict */; };
|
||||||
0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */; };
|
0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */; };
|
||||||
0EEC614342F823E5BF966C2C /* AppLockTimerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */; };
|
0EEC614342F823E5BF966C2C /* AppLockTimerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */; };
|
||||||
|
0F4709282FCCFBEFED427B8A /* AuthenticationClientBuilderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4760CE2128FBC217304272AB /* AuthenticationClientBuilderMock.swift */; };
|
||||||
0F6C8033FA60CFD36F7CA205 /* AppLockSetupPINScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */; };
|
0F6C8033FA60CFD36F7CA205 /* AppLockSetupPINScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */; };
|
||||||
108D3C0707A90B0F848CDBB9 /* ResolveVerifiedUserSendFailureScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60011EF0086E49DBD78E16E5 /* ResolveVerifiedUserSendFailureScreenModels.swift */; };
|
108D3C0707A90B0F848CDBB9 /* ResolveVerifiedUserSendFailureScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60011EF0086E49DBD78E16E5 /* ResolveVerifiedUserSendFailureScreenModels.swift */; };
|
||||||
109AEB7D33C4497727AFB87F /* TimelineInteractionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BA894BC09972DC45E497D37 /* TimelineInteractionHandler.swift */; };
|
109AEB7D33C4497727AFB87F /* TimelineInteractionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BA894BC09972DC45E497D37 /* TimelineInteractionHandler.swift */; };
|
||||||
@ -150,6 +151,7 @@
|
|||||||
206F0DBAB6AF042CA1FF2C0D /* SettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */; };
|
206F0DBAB6AF042CA1FF2C0D /* SettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */; };
|
||||||
208C19811613F9A10F8A7B75 /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */; };
|
208C19811613F9A10F8A7B75 /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */; };
|
||||||
20C16A3F718802B0E4A19C83 /* URLComponentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76310030C831D4610A705603 /* URLComponentsTests.swift */; };
|
20C16A3F718802B0E4A19C83 /* URLComponentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76310030C831D4610A705603 /* URLComponentsTests.swift */; };
|
||||||
|
210DB40676DF2A23E69C2D06 /* AuthenticationClientBuilderFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */; };
|
||||||
2118E35D312951B241067BD5 /* MessageComposerTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345172AD4377E83A44BD864F /* MessageComposerTextField.swift */; };
|
2118E35D312951B241067BD5 /* MessageComposerTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345172AD4377E83A44BD864F /* MessageComposerTextField.swift */; };
|
||||||
211B5F524E851178EE549417 /* CurrentValuePublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */; };
|
211B5F524E851178EE549417 /* CurrentValuePublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */; };
|
||||||
21813AF91CFC6F3E3896DB53 /* AppLockSetupBiometricsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10F130DF775CE6BC51A4E392 /* AppLockSetupBiometricsScreenModels.swift */; };
|
21813AF91CFC6F3E3896DB53 /* AppLockSetupBiometricsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10F130DF775CE6BC51A4E392 /* AppLockSetupBiometricsScreenModels.swift */; };
|
||||||
@ -225,7 +227,6 @@
|
|||||||
3116693C5EB476E028990416 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74611A4182DCF5F4D42696EC /* XCTestCase.swift */; };
|
3116693C5EB476E028990416 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74611A4182DCF5F4D42696EC /* XCTestCase.swift */; };
|
||||||
3118D9ABFD4BE5A3492FF88A /* ElementCallConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC437C491EA6996513B1CEAB /* ElementCallConfiguration.swift */; };
|
3118D9ABFD4BE5A3492FF88A /* ElementCallConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC437C491EA6996513B1CEAB /* ElementCallConfiguration.swift */; };
|
||||||
32B7891D937377A59606EDFC /* UserFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8599815136EFF5B73F38 /* UserFlowTests.swift */; };
|
32B7891D937377A59606EDFC /* UserFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8599815136EFF5B73F38 /* UserFlowTests.swift */; };
|
||||||
32FC143630CE22A9E403370B /* MockAuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA38899517F08FE2AF34EB45 /* MockAuthenticationService.swift */; };
|
|
||||||
339BC18777912E1989F2F17D /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584A61D9C459FAFEF038A7C0 /* Section.swift */; };
|
339BC18777912E1989F2F17D /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584A61D9C459FAFEF038A7C0 /* Section.swift */; };
|
||||||
33CAC1226DFB8B5D8447D286 /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; };
|
33CAC1226DFB8B5D8447D286 /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; };
|
||||||
33F1FB19F222BA9930AB1A00 /* RoomListFiltersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6372DD10DED30E7AD7BCE21 /* RoomListFiltersView.swift */; };
|
33F1FB19F222BA9930AB1A00 /* RoomListFiltersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6372DD10DED30E7AD7BCE21 /* RoomListFiltersView.swift */; };
|
||||||
@ -290,6 +291,7 @@
|
|||||||
407DCE030E0F9B7C9861D38A /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = 1081D3630AAD3ACEDDEC3A98 /* LRUCache */; };
|
407DCE030E0F9B7C9861D38A /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = 1081D3630AAD3ACEDDEC3A98 /* LRUCache */; };
|
||||||
40B79D20A873620F7F128A2C /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FA991289149D31F4286747 /* UserPreference.swift */; };
|
40B79D20A873620F7F128A2C /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FA991289149D31F4286747 /* UserPreference.swift */; };
|
||||||
414F50CFCFEEE2611127DCFB /* RestorationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3558A15CFB934F9229301527 /* RestorationToken.swift */; };
|
414F50CFCFEEE2611127DCFB /* RestorationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3558A15CFB934F9229301527 /* RestorationToken.swift */; };
|
||||||
|
41C5DA0C06F30311A221E85B /* ClientSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */; };
|
||||||
41CE5E1289C8768FC5B6490C /* RoomTimelineItemViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2D52E36AD614B3C003EF6 /* RoomTimelineItemViewState.swift */; };
|
41CE5E1289C8768FC5B6490C /* RoomTimelineItemViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2D52E36AD614B3C003EF6 /* RoomTimelineItemViewState.swift */; };
|
||||||
41DFDD212D1BE57CA50D783B /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = 81DB3AB6CE996AB3954F4F03 /* KZFileWatchers */; };
|
41DFDD212D1BE57CA50D783B /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = 81DB3AB6CE996AB3954F4F03 /* KZFileWatchers */; };
|
||||||
41F553349AF44567184822D8 /* APNSPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D670124FC3E84F23A62CCF /* APNSPayload.swift */; };
|
41F553349AF44567184822D8 /* APNSPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D670124FC3E84F23A62CCF /* APNSPayload.swift */; };
|
||||||
@ -405,10 +407,10 @@
|
|||||||
5AE6404C4FD4848ACCFF9EDC /* SecureBackupLogoutConfirmationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */; };
|
5AE6404C4FD4848ACCFF9EDC /* SecureBackupLogoutConfirmationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */; };
|
||||||
5B6E5AD224509E6C0B520D6E /* RoomMemberDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DDF49CEBC0DFC59C308335F /* RoomMemberDetailsScreenViewModelProtocol.swift */; };
|
5B6E5AD224509E6C0B520D6E /* RoomMemberDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DDF49CEBC0DFC59C308335F /* RoomMemberDetailsScreenViewModelProtocol.swift */; };
|
||||||
5B7D24A318AFF75AD611A026 /* RoomDirectorySearchScreenScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6BFF453838CF6C3982C5A3 /* RoomDirectorySearchScreenScreenViewModelTests.swift */; };
|
5B7D24A318AFF75AD611A026 /* RoomDirectorySearchScreenScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6BFF453838CF6C3982C5A3 /* RoomDirectorySearchScreenScreenViewModelTests.swift */; };
|
||||||
|
5BBDF9926CB645DE2F7BC258 /* EventTimelineItemSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86D447D771CEF6194348F5F /* EventTimelineItemSDKMock.swift */; };
|
||||||
5BC6C4ADFE7F2A795ECDE130 /* SecureBackupKeyBackupScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D4EEBE8C098BBADD10939 /* SecureBackupKeyBackupScreenCoordinator.swift */; };
|
5BC6C4ADFE7F2A795ECDE130 /* SecureBackupKeyBackupScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D4EEBE8C098BBADD10939 /* SecureBackupKeyBackupScreenCoordinator.swift */; };
|
||||||
5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; };
|
5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; };
|
||||||
5C164551F7D26E24F09083D3 /* StaticLocationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C616D90B1E2F033CAA325439 /* StaticLocationScreenViewModelProtocol.swift */; };
|
5C164551F7D26E24F09083D3 /* StaticLocationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C616D90B1E2F033CAA325439 /* StaticLocationScreenViewModelProtocol.swift */; };
|
||||||
5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */; };
|
|
||||||
5D27B6537591471A42C89027 /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E04B2A976CC4C8CC1807C /* EmoteRoomTimelineItem.swift */; };
|
5D27B6537591471A42C89027 /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E04B2A976CC4C8CC1807C /* EmoteRoomTimelineItem.swift */; };
|
||||||
5D52925FEB1B780C65B0529F /* PinnedEventsTimelineScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA4F6D7000EDCD187E0989E7 /* PinnedEventsTimelineScreen.swift */; };
|
5D52925FEB1B780C65B0529F /* PinnedEventsTimelineScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA4F6D7000EDCD187E0989E7 /* PinnedEventsTimelineScreen.swift */; };
|
||||||
5D53AE9342A4C06B704247ED /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; };
|
5D53AE9342A4C06B704247ED /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; };
|
||||||
@ -541,6 +543,7 @@
|
|||||||
79741C1953269FF1A211D246 /* RoomPollsHistoryScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E14FF533D25A0692F7CEB0 /* RoomPollsHistoryScreenViewModel.swift */; };
|
79741C1953269FF1A211D246 /* RoomPollsHistoryScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E14FF533D25A0692F7CEB0 /* RoomPollsHistoryScreenViewModel.swift */; };
|
||||||
798BF3072137833FBD3F4C96 /* TimelineDeliveryStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F91544AC136BF6477BDAB8 /* TimelineDeliveryStatusView.swift */; };
|
798BF3072137833FBD3F4C96 /* TimelineDeliveryStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F91544AC136BF6477BDAB8 /* TimelineDeliveryStatusView.swift */; };
|
||||||
79959F8E45C3749997482A7F /* TimelineItemBubbledStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A459AE4B6566B2FA99E86B2 /* TimelineItemBubbledStylerView.swift */; };
|
79959F8E45C3749997482A7F /* TimelineItemBubbledStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A459AE4B6566B2FA99E86B2 /* TimelineItemBubbledStylerView.swift */; };
|
||||||
|
79D57E9AE03A2DC689D14EA2 /* UserSessionStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */; };
|
||||||
7A02EB29F3B993AB20E0A198 /* RoomPollsHistoryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C8C368A611B9CB79C7F5FA /* RoomPollsHistoryScreen.swift */; };
|
7A02EB29F3B993AB20E0A198 /* RoomPollsHistoryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C8C368A611B9CB79C7F5FA /* RoomPollsHistoryScreen.swift */; };
|
||||||
7A0D335D38ECA095A575B4F7 /* TimelineStyler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB0E533508094156D8024C3 /* TimelineStyler.swift */; };
|
7A0D335D38ECA095A575B4F7 /* TimelineStyler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB0E533508094156D8024C3 /* TimelineStyler.swift */; };
|
||||||
7A170A5A4A352954BB2A1B96 /* AuthenticationStartScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E8C8817F59BEC7E358EB78 /* AuthenticationStartScreen.swift */; };
|
7A170A5A4A352954BB2A1B96 /* AuthenticationStartScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E8C8817F59BEC7E358EB78 /* AuthenticationStartScreen.swift */; };
|
||||||
@ -655,6 +658,7 @@
|
|||||||
915B4CDAF220D9AEB4047D45 /* PollInteractionHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 259E5B05BDE6E20C26CF11B4 /* PollInteractionHandlerProtocol.swift */; };
|
915B4CDAF220D9AEB4047D45 /* PollInteractionHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 259E5B05BDE6E20C26CF11B4 /* PollInteractionHandlerProtocol.swift */; };
|
||||||
91ABC91758A6E4A5FAA2E9C4 /* ReadReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */; };
|
91ABC91758A6E4A5FAA2E9C4 /* ReadReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */; };
|
||||||
91C6AC0E9D2B9C0C76CC6AD4 /* RoomDirectorySearchScreenScreenModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3984C93B8E9B10C92DADF9EE /* RoomDirectorySearchScreenScreenModelProtocol.swift */; };
|
91C6AC0E9D2B9C0C76CC6AD4 /* RoomDirectorySearchScreenScreenModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3984C93B8E9B10C92DADF9EE /* RoomDirectorySearchScreenScreenModelProtocol.swift */; };
|
||||||
|
92012C96039BC8C2CAEBA9E2 /* AuthenticationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671C338B7259DC5774816885 /* AuthenticationServiceTests.swift */; };
|
||||||
9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; };
|
9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; };
|
||||||
92720AB0DA9AB5EEF1DAF56B /* SecureBackupLogoutConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC017C3CB6B0F7C63F460F2 /* SecureBackupLogoutConfirmationScreenViewModel.swift */; };
|
92720AB0DA9AB5EEF1DAF56B /* SecureBackupLogoutConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC017C3CB6B0F7C63F460F2 /* SecureBackupLogoutConfirmationScreenViewModel.swift */; };
|
||||||
9278EC51D24E57445B290521 /* AudioSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB284643AF7AB131E307DCE0 /* AudioSessionProtocol.swift */; };
|
9278EC51D24E57445B290521 /* AudioSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB284643AF7AB131E307DCE0 /* AudioSessionProtocol.swift */; };
|
||||||
@ -749,6 +753,7 @@
|
|||||||
A4B123C635F70DDD4BC2FAC9 /* BlockedUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76A706B3EEA32B882DA5E2D /* BlockedUsersScreenViewModelProtocol.swift */; };
|
A4B123C635F70DDD4BC2FAC9 /* BlockedUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76A706B3EEA32B882DA5E2D /* BlockedUsersScreenViewModelProtocol.swift */; };
|
||||||
A4C29D373986AFE4559696D5 /* SecureBackupKeyBackupScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4525E8C0FBDD27D1ACE90952 /* SecureBackupKeyBackupScreenViewModelProtocol.swift */; };
|
A4C29D373986AFE4559696D5 /* SecureBackupKeyBackupScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4525E8C0FBDD27D1ACE90952 /* SecureBackupKeyBackupScreenViewModelProtocol.swift */; };
|
||||||
A4E885358D7DD5A072A06824 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = CCE5BF78B125320CBF3BB834 /* PostHog */; };
|
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 */; };
|
A52090A4FE0DB826578DFC03 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0724EBDFE8BB4C9E5547C57D /* Client.swift */; };
|
||||||
A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9780389F8A53E4D26E23DD03 /* LoginScreenViewModelProtocol.swift */; };
|
A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9780389F8A53E4D26E23DD03 /* LoginScreenViewModelProtocol.swift */; };
|
||||||
A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */; };
|
A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */; };
|
||||||
@ -1054,7 +1059,6 @@
|
|||||||
EDF8919F15DE0FF00EF99E70 /* DocumentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F5567A7EF6F2AB9473236F6 /* DocumentPicker.swift */; };
|
EDF8919F15DE0FF00EF99E70 /* DocumentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F5567A7EF6F2AB9473236F6 /* DocumentPicker.swift */; };
|
||||||
EE4E2C1922BBF5169E213555 /* PillAttachmentViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B53D6C5C0D14B04D3AB3F6E /* PillAttachmentViewProvider.swift */; };
|
EE4E2C1922BBF5169E213555 /* PillAttachmentViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B53D6C5C0D14B04D3AB3F6E /* PillAttachmentViewProvider.swift */; };
|
||||||
EE56238683BC3ECA9BA00684 /* GlobalSearchScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4D639E27D5882A6A71AECF /* GlobalSearchScreenViewModelTests.swift */; };
|
EE56238683BC3ECA9BA00684 /* GlobalSearchScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4D639E27D5882A6A71AECF /* GlobalSearchScreenViewModelTests.swift */; };
|
||||||
EE57A96130DD8DB053790AE2 /* EventTimelineItemSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7A6BBC686B1F840FA807FB /* EventTimelineItemSDKMock.swift */; };
|
|
||||||
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; };
|
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; };
|
||||||
EEAE954289DE813A61656AE0 /* LayoutDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */; };
|
EEAE954289DE813A61656AE0 /* LayoutDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */; };
|
||||||
EEB9C1555C63B93CA9C372C2 /* EmojiPickerScreenHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5E29E9A22F45534FBD5B58 /* EmojiPickerScreenHeaderView.swift */; };
|
EEB9C1555C63B93CA9C372C2 /* EmojiPickerScreenHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5E29E9A22F45534FBD5B58 /* EmojiPickerScreenHeaderView.swift */; };
|
||||||
@ -1219,6 +1223,7 @@
|
|||||||
052B2F924572AFD70B5F500E /* StartChatScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenViewModel.swift; sourceTree = "<group>"; };
|
052B2F924572AFD70B5F500E /* StartChatScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionUITests.swift; sourceTree = "<group>"; };
|
054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionUITests.swift; sourceTree = "<group>"; };
|
||||||
05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRecoveryKeyConfirmationBanner.swift; sourceTree = "<group>"; };
|
05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRecoveryKeyConfirmationBanner.swift; sourceTree = "<group>"; };
|
||||||
|
0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderFactoryMock.swift; sourceTree = "<group>"; };
|
||||||
05596E4A11A8C9346E9E54AE /* SoftLogoutScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenCoordinator.swift; sourceTree = "<group>"; };
|
05596E4A11A8C9346E9E54AE /* SoftLogoutScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
05A3E8741D199CD1A37F4CBF /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
|
05A3E8741D199CD1A37F4CBF /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
|
||||||
05AF58372CA884A789EB9C5A /* AppMediatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorProtocol.swift; sourceTree = "<group>"; };
|
05AF58372CA884A789EB9C5A /* AppMediatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorProtocol.swift; sourceTree = "<group>"; };
|
||||||
@ -1311,7 +1316,6 @@
|
|||||||
1BA5A62DA4B543827FF82354 /* LAContextMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LAContextMock.swift; sourceTree = "<group>"; };
|
1BA5A62DA4B543827FF82354 /* LAContextMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LAContextMock.swift; sourceTree = "<group>"; };
|
||||||
1C21A715237F2B6D6E80998C /* SecureBackupControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerProtocol.swift; sourceTree = "<group>"; };
|
1C21A715237F2B6D6E80998C /* SecureBackupControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerProtocol.swift; sourceTree = "<group>"; };
|
||||||
1C25B6EBEB414431187D73B7 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
|
1C25B6EBEB414431187D73B7 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
|
||||||
1C7A6BBC686B1F840FA807FB /* EventTimelineItemSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTimelineItemSDKMock.swift; sourceTree = "<group>"; };
|
|
||||||
1C7F63EB1525E697CAEB002B /* BlankFormCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlankFormCoordinator.swift; sourceTree = "<group>"; };
|
1C7F63EB1525E697CAEB002B /* BlankFormCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlankFormCoordinator.swift; sourceTree = "<group>"; };
|
||||||
1CC575D1895FA62591451A93 /* RoomMemberDetailsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreen.swift; sourceTree = "<group>"; };
|
1CC575D1895FA62591451A93 /* RoomMemberDetailsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreen.swift; sourceTree = "<group>"; };
|
||||||
1CD7C0A2750998C2D77AD00F /* JoinRoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModel.swift; sourceTree = "<group>"; };
|
1CD7C0A2750998C2D77AD00F /* JoinRoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
@ -1323,7 +1327,6 @@
|
|||||||
1D9F148717D74F73BE724434 /* LongPressWithFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongPressWithFeedback.swift; sourceTree = "<group>"; };
|
1D9F148717D74F73BE724434 /* LongPressWithFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongPressWithFeedback.swift; sourceTree = "<group>"; };
|
||||||
1DA7E93C2E148B96EF6A8500 /* TimelineItemAccessibilityModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemAccessibilityModifier.swift; sourceTree = "<group>"; };
|
1DA7E93C2E148B96EF6A8500 /* TimelineItemAccessibilityModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemAccessibilityModifier.swift; sourceTree = "<group>"; };
|
||||||
1DB2FC2AA9A07EE792DF65CF /* NotificationPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenModels.swift; sourceTree = "<group>"; };
|
1DB2FC2AA9A07EE792DF65CF /* NotificationPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenModels.swift; sourceTree = "<group>"; };
|
||||||
1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenUITests.swift; sourceTree = "<group>"; };
|
|
||||||
1DE7969EBCAF078813E18EA1 /* RoomRolesAndPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsScreenModels.swift; sourceTree = "<group>"; };
|
1DE7969EBCAF078813E18EA1 /* RoomRolesAndPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsScreenModels.swift; sourceTree = "<group>"; };
|
||||||
1DF8F7A3AD83D04C08D75E01 /* RoomDetailsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
1DF8F7A3AD83D04C08D75E01 /* RoomDetailsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
1DFE0E493FB55E5A62E7852A /* ProposedViewSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProposedViewSize.swift; sourceTree = "<group>"; };
|
1DFE0E493FB55E5A62E7852A /* ProposedViewSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProposedViewSize.swift; sourceTree = "<group>"; };
|
||||||
@ -1519,6 +1522,7 @@
|
|||||||
471BB7276C97AF60B3A5463B /* RoomDirectorySearchProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxy.swift; sourceTree = "<group>"; };
|
471BB7276C97AF60B3A5463B /* RoomDirectorySearchProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxy.swift; sourceTree = "<group>"; };
|
||||||
475D47D0BFE961B02BAC5D49 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
475D47D0BFE961B02BAC5D49 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
475EB595D7527E9A8A14043E /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/Localizable.strings; sourceTree = "<group>"; };
|
475EB595D7527E9A8A14043E /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
4760CE2128FBC217304272AB /* AuthenticationClientBuilderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderMock.swift; sourceTree = "<group>"; };
|
||||||
47873756E45B46683D97DC32 /* LegalInformationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenModels.swift; sourceTree = "<group>"; };
|
47873756E45B46683D97DC32 /* LegalInformationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenModels.swift; sourceTree = "<group>"; };
|
||||||
47EBB5D698CE9A25BB553A2D /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
47EBB5D698CE9A25BB553A2D /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
||||||
47F29139BC2A804CE5E0757E /* MediaUploadPreviewScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModel.swift; sourceTree = "<group>"; };
|
47F29139BC2A804CE5E0757E /* MediaUploadPreviewScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
@ -1646,6 +1650,7 @@
|
|||||||
66AFD800AF033D8B0D11191A /* UserPropertiesExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPropertiesExt.swift; sourceTree = "<group>"; };
|
66AFD800AF033D8B0D11191A /* UserPropertiesExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPropertiesExt.swift; sourceTree = "<group>"; };
|
||||||
66F2402D738694F98729A441 /* RoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProvider.swift; sourceTree = "<group>"; };
|
66F2402D738694F98729A441 /* RoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProvider.swift; sourceTree = "<group>"; };
|
||||||
66F91544AC136BF6477BDAB8 /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = "<group>"; };
|
66F91544AC136BF6477BDAB8 /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = "<group>"; };
|
||||||
|
671C338B7259DC5774816885 /* AuthenticationServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceTests.swift; sourceTree = "<group>"; };
|
||||||
6722709BD6178E10B70C9641 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SAS.strings; sourceTree = "<group>"; };
|
6722709BD6178E10B70C9641 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||||
68010886142843705E342645 /* ProgressMaskModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressMaskModifier.swift; sourceTree = "<group>"; };
|
68010886142843705E342645 /* ProgressMaskModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressMaskModifier.swift; sourceTree = "<group>"; };
|
||||||
6861FE915C7B5466E6962BBA /* StartChatScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreen.swift; sourceTree = "<group>"; };
|
6861FE915C7B5466E6962BBA /* StartChatScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreen.swift; sourceTree = "<group>"; };
|
||||||
@ -1805,6 +1810,7 @@
|
|||||||
8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorMock.swift; sourceTree = "<group>"; };
|
8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorMock.swift; sourceTree = "<group>"; };
|
||||||
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = "<group>"; };
|
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = "<group>"; };
|
||||||
8E1584F8BCF407BB94F48F04 /* EncryptionResetPasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreen.swift; sourceTree = "<group>"; };
|
8E1584F8BCF407BB94F48F04 /* EncryptionResetPasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreen.swift; sourceTree = "<group>"; };
|
||||||
|
8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientSDKMock.swift; sourceTree = "<group>"; };
|
||||||
8F21ED7205048668BEB44A38 /* AppActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppActivityView.swift; sourceTree = "<group>"; };
|
8F21ED7205048668BEB44A38 /* AppActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppActivityView.swift; sourceTree = "<group>"; };
|
||||||
8F6210134203BE1F2DD5C679 /* RoomDirectoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectoryCell.swift; sourceTree = "<group>"; };
|
8F6210134203BE1F2DD5C679 /* RoomDirectoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectoryCell.swift; sourceTree = "<group>"; };
|
||||||
8F841F219ACDFC1D3F42FEFB /* RoomChangeRolesScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenViewModelTests.swift; sourceTree = "<group>"; };
|
8F841F219ACDFC1D3F42FEFB /* RoomChangeRolesScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
@ -1871,6 +1877,7 @@
|
|||||||
9CE3C90E487B255B735D73C8 /* RoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModel.swift; sourceTree = "<group>"; };
|
9CE3C90E487B255B735D73C8 /* RoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
9CF1EE0AA78470C674554262 /* PillTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachment.swift; sourceTree = "<group>"; };
|
9CF1EE0AA78470C674554262 /* PillTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachment.swift; sourceTree = "<group>"; };
|
||||||
9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConstants.swift; sourceTree = "<group>"; };
|
9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConstants.swift; sourceTree = "<group>"; };
|
||||||
|
9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStoreMock.swift; sourceTree = "<group>"; };
|
||||||
9ECF11669EF253E98AA2977A /* CompletionSuggestionServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionSuggestionServiceProtocol.swift; sourceTree = "<group>"; };
|
9ECF11669EF253E98AA2977A /* CompletionSuggestionServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionSuggestionServiceProtocol.swift; sourceTree = "<group>"; };
|
||||||
9F1DF3FFFE5ED2B8133F43A7 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = "<group>"; };
|
9F1DF3FFFE5ED2B8133F43A7 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = "<group>"; };
|
||||||
9F40FB0A43DAECEC27C73722 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/SAS.strings; sourceTree = "<group>"; };
|
9F40FB0A43DAECEC27C73722 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||||
@ -1981,6 +1988,7 @@
|
|||||||
B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = "<group>"; };
|
B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = "<group>"; };
|
||||||
B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModel.swift; sourceTree = "<group>"; };
|
B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = "<group>"; };
|
B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = "<group>"; };
|
||||||
|
B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderFactory.swift; sourceTree = "<group>"; };
|
||||||
B6E4AB573FAEBB7B853DD04C /* AppHooks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppHooks.swift; sourceTree = "<group>"; };
|
B6E4AB573FAEBB7B853DD04C /* AppHooks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppHooks.swift; sourceTree = "<group>"; };
|
||||||
B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = "<group>"; };
|
B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = "<group>"; };
|
||||||
B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineView.swift; sourceTree = "<group>"; };
|
B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
@ -1994,6 +2002,7 @@
|
|||||||
B81B6170DB690013CEB646F4 /* MapLibreModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLibreModels.swift; sourceTree = "<group>"; };
|
B81B6170DB690013CEB646F4 /* MapLibreModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLibreModels.swift; sourceTree = "<group>"; };
|
||||||
B8516302ACCA94A0E680AB3B /* VoiceMessageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageButton.swift; sourceTree = "<group>"; };
|
B8516302ACCA94A0E680AB3B /* VoiceMessageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageButton.swift; sourceTree = "<group>"; };
|
||||||
B858A61F2A570DFB8DE570A7 /* AggregratedReaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AggregratedReaction.swift; sourceTree = "<group>"; };
|
B858A61F2A570DFB8DE570A7 /* AggregratedReaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AggregratedReaction.swift; sourceTree = "<group>"; };
|
||||||
|
B86D447D771CEF6194348F5F /* EventTimelineItemSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTimelineItemSDKMock.swift; sourceTree = "<group>"; };
|
||||||
B8A3B7637DDBD6AA97AC2545 /* CameraPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPicker.swift; sourceTree = "<group>"; };
|
B8A3B7637DDBD6AA97AC2545 /* CameraPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPicker.swift; sourceTree = "<group>"; };
|
||||||
B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCoordinators.swift; sourceTree = "<group>"; };
|
B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCoordinators.swift; sourceTree = "<group>"; };
|
||||||
B902EA6CD3296B0E10EE432B /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = "<group>"; };
|
B902EA6CD3296B0E10EE432B /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = "<group>"; };
|
||||||
@ -2138,7 +2147,6 @@
|
|||||||
D95E8C0EFEC0C6F96EDAA71A /* PreviewTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = PreviewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
D95E8C0EFEC0C6F96EDAA71A /* PreviewTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = PreviewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
DA14564EE143F73F7E4D1F79 /* RoomNotificationSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenModels.swift; sourceTree = "<group>"; };
|
DA14564EE143F73F7E4D1F79 /* RoomNotificationSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenModels.swift; sourceTree = "<group>"; };
|
||||||
DA2AEC1AB349A341FE13DEC1 /* StartChatScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenUITests.swift; sourceTree = "<group>"; };
|
DA2AEC1AB349A341FE13DEC1 /* StartChatScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenUITests.swift; sourceTree = "<group>"; };
|
||||||
DA38899517F08FE2AF34EB45 /* MockAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAuthenticationService.swift; sourceTree = "<group>"; };
|
|
||||||
DA3D82522494E78746B2214E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/SAS.strings; sourceTree = "<group>"; };
|
DA3D82522494E78746B2214E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||||
DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageCache.swift; sourceTree = "<group>"; };
|
DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageCache.swift; sourceTree = "<group>"; };
|
||||||
DB06F22CFA34885B40976061 /* RoomDetailsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreen.swift; sourceTree = "<group>"; };
|
DB06F22CFA34885B40976061 /* RoomDetailsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreen.swift; sourceTree = "<group>"; };
|
||||||
@ -2887,10 +2895,11 @@
|
|||||||
children = (
|
children = (
|
||||||
69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */,
|
69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */,
|
||||||
3BAC027034248429A438886B /* AppMediatorMock.swift */,
|
3BAC027034248429A438886B /* AppMediatorMock.swift */,
|
||||||
|
0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */,
|
||||||
|
4760CE2128FBC217304272AB /* AuthenticationClientBuilderMock.swift */,
|
||||||
E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */,
|
E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */,
|
||||||
4E600B315B920B9687F8EE1B /* ComposerDraftServiceMock.swift */,
|
4E600B315B920B9687F8EE1B /* ComposerDraftServiceMock.swift */,
|
||||||
E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */,
|
E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */,
|
||||||
1C7A6BBC686B1F840FA807FB /* EventTimelineItemSDKMock.swift */,
|
|
||||||
867DC9530C42F7B5176BE465 /* JoinedRoomProxyMock.swift */,
|
867DC9530C42F7B5176BE465 /* JoinedRoomProxyMock.swift */,
|
||||||
8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */,
|
8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */,
|
||||||
382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */,
|
382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */,
|
||||||
@ -2907,7 +2916,9 @@
|
|||||||
7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */,
|
7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */,
|
||||||
AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */,
|
AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */,
|
||||||
F4469F6AE311BDC439B3A5EC /* UserSessionMock.swift */,
|
F4469F6AE311BDC439B3A5EC /* UserSessionMock.swift */,
|
||||||
|
9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */,
|
||||||
B23135B06B044CB811139D2F /* Generated */,
|
B23135B06B044CB811139D2F /* Generated */,
|
||||||
|
E5E545F92D01588360A9BAC5 /* SDK */,
|
||||||
);
|
);
|
||||||
path = Mocks;
|
path = Mocks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -3790,6 +3801,7 @@
|
|||||||
89233612A8632AD7E2803620 /* AudioPlayerStateTests.swift */,
|
89233612A8632AD7E2803620 /* AudioPlayerStateTests.swift */,
|
||||||
C55CC239AE12339C565F6C9A /* AudioRecorderStateTests.swift */,
|
C55CC239AE12339C565F6C9A /* AudioRecorderStateTests.swift */,
|
||||||
2441E2424E78A40FC95DBA76 /* AudioRecorderTests.swift */,
|
2441E2424E78A40FC95DBA76 /* AudioRecorderTests.swift */,
|
||||||
|
671C338B7259DC5774816885 /* AuthenticationServiceTests.swift */,
|
||||||
8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */,
|
8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */,
|
||||||
93E1FF0DFBB3768F79FDBF6D /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift */,
|
93E1FF0DFBB3768F79FDBF6D /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift */,
|
||||||
240610DF32F3213BEC5611D7 /* BlockedUsersScreenViewModelTests.swift */,
|
240610DF32F3213BEC5611D7 /* BlockedUsersScreenViewModelTests.swift */,
|
||||||
@ -4405,7 +4417,6 @@
|
|||||||
295E28C3B9EAADF519BF2F44 /* AuthenticationFlowCoordinatorUITests.swift */,
|
295E28C3B9EAADF519BF2F44 /* AuthenticationFlowCoordinatorUITests.swift */,
|
||||||
C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */,
|
C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */,
|
||||||
F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */,
|
F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */,
|
||||||
1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */,
|
|
||||||
3368395F06AA180138E185B6 /* PollFormScreenUITests.swift */,
|
3368395F06AA180138E185B6 /* PollFormScreenUITests.swift */,
|
||||||
C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */,
|
C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */,
|
||||||
45571C2EBD98ED7E0CEA7AF7 /* RoomRolesAndPermissionsUITests.swift */,
|
45571C2EBD98ED7E0CEA7AF7 /* RoomRolesAndPermissionsUITests.swift */,
|
||||||
@ -4651,9 +4662,9 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0F569CFB77E0D40BD82203D9 /* AuthenticationClientBuilder.swift */,
|
0F569CFB77E0D40BD82203D9 /* AuthenticationClientBuilder.swift */,
|
||||||
|
B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */,
|
||||||
F3A1AB5A84D843B6AC8D5F1E /* AuthenticationService.swift */,
|
F3A1AB5A84D843B6AC8D5F1E /* AuthenticationService.swift */,
|
||||||
5E75948AA1FE1D1A7809931F /* AuthenticationServiceProtocol.swift */,
|
5E75948AA1FE1D1A7809931F /* AuthenticationServiceProtocol.swift */,
|
||||||
DA38899517F08FE2AF34EB45 /* MockAuthenticationService.swift */,
|
|
||||||
A69869844D2B6F5BD9AABF85 /* OIDCConfigurationProxy.swift */,
|
A69869844D2B6F5BD9AABF85 /* OIDCConfigurationProxy.swift */,
|
||||||
);
|
);
|
||||||
path = Authentication;
|
path = Authentication;
|
||||||
@ -5243,6 +5254,15 @@
|
|||||||
path = Screens;
|
path = Screens;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
E5E545F92D01588360A9BAC5 /* SDK */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */,
|
||||||
|
B86D447D771CEF6194348F5F /* EventTimelineItemSDKMock.swift */,
|
||||||
|
);
|
||||||
|
path = SDK;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
E600AACDF87CDBCE32683236 /* Resources */ = {
|
E600AACDF87CDBCE32683236 /* Resources */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -6072,6 +6092,7 @@
|
|||||||
C1429699A6A5BB09A25775C1 /* AudioPlayerStateTests.swift in Sources */,
|
C1429699A6A5BB09A25775C1 /* AudioPlayerStateTests.swift in Sources */,
|
||||||
3042527CB344A9EF1157FC26 /* AudioRecorderStateTests.swift in Sources */,
|
3042527CB344A9EF1157FC26 /* AudioRecorderStateTests.swift in Sources */,
|
||||||
192A3CDCD0174AD1E4A128E4 /* AudioRecorderTests.swift in Sources */,
|
192A3CDCD0174AD1E4A128E4 /* AudioRecorderTests.swift in Sources */,
|
||||||
|
92012C96039BC8C2CAEBA9E2 /* AuthenticationServiceTests.swift in Sources */,
|
||||||
8ED8AF57A06F5EE9978ED23F /* AuthenticationStartScreenViewModelTests.swift in Sources */,
|
8ED8AF57A06F5EE9978ED23F /* AuthenticationStartScreenViewModelTests.swift in Sources */,
|
||||||
CEAEA57B7665C8E790599A78 /* BlockedUsersScreenViewModelTests.swift in Sources */,
|
CEAEA57B7665C8E790599A78 /* BlockedUsersScreenViewModelTests.swift in Sources */,
|
||||||
1B2F9F368619FFF8C63C87CC /* BugReportScreenViewModelTests.swift in Sources */,
|
1B2F9F368619FFF8C63C87CC /* BugReportScreenViewModelTests.swift in Sources */,
|
||||||
@ -6278,6 +6299,9 @@
|
|||||||
7BD2123144A32F082CECC108 /* AudioRoomTimelineView.swift in Sources */,
|
7BD2123144A32F082CECC108 /* AudioRoomTimelineView.swift in Sources */,
|
||||||
9278EC51D24E57445B290521 /* AudioSessionProtocol.swift in Sources */,
|
9278EC51D24E57445B290521 /* AudioSessionProtocol.swift in Sources */,
|
||||||
8A6CB15C8FC68F557750BF54 /* AuthenticationClientBuilder.swift in Sources */,
|
8A6CB15C8FC68F557750BF54 /* AuthenticationClientBuilder.swift in Sources */,
|
||||||
|
210DB40676DF2A23E69C2D06 /* AuthenticationClientBuilderFactory.swift in Sources */,
|
||||||
|
A51C65E5A3C9F2464A91A380 /* AuthenticationClientBuilderFactoryMock.swift in Sources */,
|
||||||
|
0F4709282FCCFBEFED427B8A /* AuthenticationClientBuilderMock.swift in Sources */,
|
||||||
67E9926C4572C54F59FCA91A /* AuthenticationFlowCoordinator.swift in Sources */,
|
67E9926C4572C54F59FCA91A /* AuthenticationFlowCoordinator.swift in Sources */,
|
||||||
9847B056C1A216C314D21E68 /* AuthenticationService.swift in Sources */,
|
9847B056C1A216C314D21E68 /* AuthenticationService.swift in Sources */,
|
||||||
56DACDD379A86A1F5DEFE7BE /* AuthenticationServiceProtocol.swift in Sources */,
|
56DACDD379A86A1F5DEFE7BE /* AuthenticationServiceProtocol.swift in Sources */,
|
||||||
@ -6333,6 +6357,7 @@
|
|||||||
1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */,
|
1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */,
|
||||||
DDFBDEE1DC32BDD5488F898C /* ClientProxyMock.swift in Sources */,
|
DDFBDEE1DC32BDD5488F898C /* ClientProxyMock.swift in Sources */,
|
||||||
24BDDD09A90B8BFE3793F3AA /* ClientProxyProtocol.swift in Sources */,
|
24BDDD09A90B8BFE3793F3AA /* ClientProxyProtocol.swift in Sources */,
|
||||||
|
41C5DA0C06F30311A221E85B /* ClientSDKMock.swift in Sources */,
|
||||||
0C797CD650DFD2876BEC5173 /* CollapsibleReactionLayout.swift in Sources */,
|
0C797CD650DFD2876BEC5173 /* CollapsibleReactionLayout.swift in Sources */,
|
||||||
78A3D84BA47DAC69B4D0A34C /* CollapsibleRoomTimelineView.swift in Sources */,
|
78A3D84BA47DAC69B4D0A34C /* CollapsibleRoomTimelineView.swift in Sources */,
|
||||||
0DC815CA24E1BD7F408F37D3 /* CollapsibleTimelineItem.swift in Sources */,
|
0DC815CA24E1BD7F408F37D3 /* CollapsibleTimelineItem.swift in Sources */,
|
||||||
@ -6423,7 +6448,7 @@
|
|||||||
50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */,
|
50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */,
|
||||||
F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */,
|
F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */,
|
||||||
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */,
|
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */,
|
||||||
EE57A96130DD8DB053790AE2 /* EventTimelineItemSDKMock.swift in Sources */,
|
5BBDF9926CB645DE2F7BC258 /* EventTimelineItemSDKMock.swift in Sources */,
|
||||||
63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */,
|
63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */,
|
||||||
5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */,
|
5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */,
|
||||||
D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */,
|
D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */,
|
||||||
@ -6562,7 +6587,6 @@
|
|||||||
F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */,
|
F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */,
|
||||||
C13128AAA787A4C2CBE4EE82 /* MessageForwardingScreenViewModelProtocol.swift in Sources */,
|
C13128AAA787A4C2CBE4EE82 /* MessageForwardingScreenViewModelProtocol.swift in Sources */,
|
||||||
C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */,
|
C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */,
|
||||||
32FC143630CE22A9E403370B /* MockAuthenticationService.swift in Sources */,
|
|
||||||
B659E3A49889E749E3239EA7 /* MockMediaProvider.swift in Sources */,
|
B659E3A49889E749E3239EA7 /* MockMediaProvider.swift in Sources */,
|
||||||
09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */,
|
09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */,
|
||||||
B721125D17A0BA86794F29FB /* MockServerSelectionScreenState.swift in Sources */,
|
B721125D17A0BA86794F29FB /* MockServerSelectionScreenState.swift in Sources */,
|
||||||
@ -6982,6 +7006,7 @@
|
|||||||
6586E1F1D5F0651D0638FFAF /* UserSessionMock.swift in Sources */,
|
6586E1F1D5F0651D0638FFAF /* UserSessionMock.swift in Sources */,
|
||||||
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */,
|
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */,
|
||||||
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */,
|
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */,
|
||||||
|
79D57E9AE03A2DC689D14EA2 /* UserSessionStoreMock.swift in Sources */,
|
||||||
AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */,
|
AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */,
|
||||||
F07D88421A9BC4D03D4A5055 /* VideoRoomTimelineItem.swift in Sources */,
|
F07D88421A9BC4D03D4A5055 /* VideoRoomTimelineItem.swift in Sources */,
|
||||||
1A83DD22F3E6F76B13B6E2F9 /* VideoRoomTimelineItemContent.swift in Sources */,
|
1A83DD22F3E6F76B13B6E2F9 /* VideoRoomTimelineItemContent.swift in Sources */,
|
||||||
@ -7026,7 +7051,6 @@
|
|||||||
7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */,
|
7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */,
|
||||||
94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */,
|
94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */,
|
||||||
9F19096BFA629C0AC282B1E4 /* CreateRoomScreenUITests.swift in Sources */,
|
9F19096BFA629C0AC282B1E4 /* CreateRoomScreenUITests.swift in Sources */,
|
||||||
5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */,
|
|
||||||
0CF81807BE5FBFC9E2BBCECF /* PollFormScreenUITests.swift in Sources */,
|
0CF81807BE5FBFC9E2BBCECF /* PollFormScreenUITests.swift in Sources */,
|
||||||
44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */,
|
44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */,
|
||||||
D29E046C1E3045E0346C479D /* RoomRolesAndPermissionsUITests.swift in Sources */,
|
D29E046C1E3045E0346C479D /* RoomRolesAndPermissionsUITests.swift in Sources */,
|
||||||
|
@ -485,7 +485,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
encryptionKeyProvider: EncryptionKeyProvider(),
|
encryptionKeyProvider: EncryptionKeyProvider(),
|
||||||
appSettings: appSettings,
|
appSettings: appSettings,
|
||||||
appHooks: appHooks)
|
appHooks: appHooks)
|
||||||
_ = await authenticationService.configure(for: userSession.clientProxy.homeserver)
|
_ = await authenticationService.configure(for: userSession.clientProxy.homeserver, flow: .login)
|
||||||
|
|
||||||
let parameters = SoftLogoutScreenCoordinatorParameters(authenticationService: authenticationService,
|
let parameters = SoftLogoutScreenCoordinatorParameters(authenticationService: authenticationService,
|
||||||
credentials: credentials,
|
credentials: credentials,
|
||||||
|
@ -86,11 +86,11 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .loginManually:
|
case .loginManually:
|
||||||
Task { await self.startAuthentication(flow: .login) }
|
showServerConfirmationScreen(authenticationFlow: .login)
|
||||||
case .loginWithQR:
|
case .loginWithQR:
|
||||||
startQRCodeLogin()
|
startQRCodeLogin()
|
||||||
case .register:
|
case .register:
|
||||||
Task { await self.startAuthentication(flow: .register) }
|
showServerConfirmationScreen(authenticationFlow: .register)
|
||||||
case .reportProblem:
|
case .reportProblem:
|
||||||
showReportProblemScreen()
|
showReportProblemScreen()
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
switch action {
|
switch action {
|
||||||
case .signInManually:
|
case .signInManually:
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
Task { await self.startAuthentication(flow: .login) }
|
showServerConfirmationScreen(authenticationFlow: .login)
|
||||||
case .cancel:
|
case .cancel:
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
case .done(let userSession):
|
case .done(let userSession):
|
||||||
@ -137,25 +137,14 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
bugReportFlowCoordinator?.start()
|
bugReportFlowCoordinator?.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startAuthentication(flow: AuthenticationFlow) async {
|
// TODO: Move this method after showServerConfirmationScreen
|
||||||
startLoading()
|
private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow) {
|
||||||
|
|
||||||
switch await authenticationService.configure(for: appSettings.defaultHomeserverAddress) {
|
|
||||||
case .success:
|
|
||||||
stopLoading()
|
|
||||||
showServerConfirmationScreen(authenticationFlow: flow)
|
|
||||||
case .failure:
|
|
||||||
stopLoading()
|
|
||||||
showServerSelectionScreen(authenticationFlow: flow, isModallyPresented: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow, isModallyPresented: Bool) {
|
|
||||||
let navigationCoordinator = NavigationStackCoordinator()
|
let navigationCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService,
|
let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService,
|
||||||
userIndicatorController: userIndicatorController,
|
authenticationFlow: authenticationFlow,
|
||||||
isModallyPresented: isModallyPresented)
|
slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL,
|
||||||
|
userIndicatorController: userIndicatorController)
|
||||||
let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)
|
let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
coordinator.actions
|
coordinator.actions
|
||||||
@ -164,42 +153,26 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .updated:
|
case .updated:
|
||||||
if isModallyPresented {
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
|
||||||
} else {
|
|
||||||
// We are here because the default server failed to respond.
|
|
||||||
if authenticationService.homeserver.value.loginMode == .password {
|
|
||||||
if authenticationFlow == .login {
|
|
||||||
// Add the password login screen directly to the flow, its fine.
|
|
||||||
showLoginScreen()
|
|
||||||
} else {
|
|
||||||
// Add the web registration screen directly to the flow, its fine.
|
|
||||||
showWebRegistration()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// OIDC is presented from the confirmation screen so replace the
|
|
||||||
// server selection screen which was inserted to handle the failure.
|
|
||||||
navigationStackCoordinator.pop()
|
|
||||||
showServerConfirmationScreen(authenticationFlow: authenticationFlow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
if isModallyPresented {
|
navigationCoordinator.setRootCoordinator(coordinator)
|
||||||
navigationCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setSheetCoordinator(navigationCoordinator)
|
||||||
navigationStackCoordinator.setSheetCoordinator(navigationCoordinator)
|
|
||||||
} else {
|
|
||||||
navigationStackCoordinator.push(coordinator)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showServerConfirmationScreen(authenticationFlow: AuthenticationFlow) {
|
private func showServerConfirmationScreen(authenticationFlow: AuthenticationFlow) {
|
||||||
|
// Reset the service back to the default homeserver before continuing. This ensures
|
||||||
|
// we check that registration is supported if it was previously configured for login.
|
||||||
|
authenticationService.reset()
|
||||||
|
|
||||||
let parameters = ServerConfirmationScreenCoordinatorParameters(authenticationService: authenticationService,
|
let parameters = ServerConfirmationScreenCoordinatorParameters(authenticationService: authenticationService,
|
||||||
authenticationFlow: authenticationFlow)
|
authenticationFlow: authenticationFlow,
|
||||||
|
slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL,
|
||||||
|
userIndicatorController: userIndicatorController)
|
||||||
let coordinator = ServerConfirmationScreenCoordinator(parameters: parameters)
|
let coordinator = ServerConfirmationScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
coordinator.actions.sink { [weak self] action in
|
coordinator.actions.sink { [weak self] action in
|
||||||
@ -215,7 +188,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
showLoginScreen()
|
showLoginScreen()
|
||||||
}
|
}
|
||||||
case .changeServer:
|
case .changeServer:
|
||||||
showServerSelectionScreen(authenticationFlow: authenticationFlow, isModallyPresented: true)
|
showServerSelectionScreen(authenticationFlow: authenticationFlow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
// Please see LICENSE in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
extension AuthenticationClientBuilderFactoryMock {
|
||||||
|
struct Configuration {
|
||||||
|
var builderConfiguration: AuthenticationClientBuilderMock.Configuration = .init()
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init(configuration: Configuration) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
let clientBuilder = AuthenticationClientBuilderMock(configuration: configuration.builderConfiguration)
|
||||||
|
makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReturnValue = clientBuilder
|
||||||
|
}
|
||||||
|
}
|
47
ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift
Normal file
47
ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
// Please see LICENSE in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
extension AuthenticationClientBuilderMock {
|
||||||
|
struct Configuration {
|
||||||
|
var homeserverClients = [
|
||||||
|
"matrix.org": ClientSDKMock(configuration: .init()),
|
||||||
|
"example.com": ClientSDKMock(configuration: .init(serverAddress: "example.com",
|
||||||
|
homeserverURL: "https://matrix.example.com",
|
||||||
|
slidingSyncVersion: .native,
|
||||||
|
supportsPasswordLogin: true,
|
||||||
|
elementWellKnown: "")),
|
||||||
|
"company.com": ClientSDKMock(configuration: .init(serverAddress: "company.com",
|
||||||
|
homeserverURL: "https://matrix.company.com",
|
||||||
|
slidingSyncVersion: .native,
|
||||||
|
oidcLoginURL: "https://auth.company.com/oidc",
|
||||||
|
supportsPasswordLogin: false,
|
||||||
|
elementWellKnown: "")),
|
||||||
|
"server.net": ClientSDKMock(configuration: .init(serverAddress: "server.net",
|
||||||
|
homeserverURL: "https://matrix.example.com",
|
||||||
|
slidingSyncVersion: .native,
|
||||||
|
supportsPasswordLogin: false,
|
||||||
|
elementWellKnown: ""))
|
||||||
|
]
|
||||||
|
var qrCodeClient = ClientSDKMock(configuration: .init())
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init(configuration: Configuration) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
buildHomeserverAddressClosure = { address in
|
||||||
|
guard let client = configuration.homeserverClients[address] else {
|
||||||
|
throw ClientBuildError.ServerUnreachable(message: "Not a known homeserver.")
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReturnValue = configuration.qrCodeClient
|
||||||
|
}
|
||||||
|
}
|
@ -1819,6 +1819,230 @@ class AudioSessionMock: AudioSessionProtocol {
|
|||||||
try setActiveOptionsClosure?(active, options)
|
try setActiveOptionsClosure?(active, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class AuthenticationClientBuilderFactoryMock: AuthenticationClientBuilderFactoryProtocol {
|
||||||
|
|
||||||
|
//MARK: - makeBuilder
|
||||||
|
|
||||||
|
var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount = 0
|
||||||
|
var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCalled: Bool {
|
||||||
|
return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount > 0
|
||||||
|
}
|
||||||
|
var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedArguments: (sessionDirectories: SessionDirectories, passphrase: String, clientSessionDelegate: ClientSessionDelegate, appSettings: AppSettings, appHooks: AppHooks)?
|
||||||
|
var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedInvocations: [(sessionDirectories: SessionDirectories, passphrase: String, clientSessionDelegate: ClientSessionDelegate, appSettings: AppSettings, appHooks: AppHooks)] = []
|
||||||
|
|
||||||
|
var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue: AuthenticationClientBuilderProtocol!
|
||||||
|
var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReturnValue: AuthenticationClientBuilderProtocol! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: AuthenticationClientBuilderProtocol? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksClosure: ((SessionDirectories, String, ClientSessionDelegate, AppSettings, AppHooks) -> AuthenticationClientBuilderProtocol)?
|
||||||
|
|
||||||
|
func makeBuilder(sessionDirectories: SessionDirectories, passphrase: String, clientSessionDelegate: ClientSessionDelegate, appSettings: AppSettings, appHooks: AppHooks) -> AuthenticationClientBuilderProtocol {
|
||||||
|
makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount += 1
|
||||||
|
makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedArguments = (sessionDirectories: sessionDirectories, passphrase: passphrase, clientSessionDelegate: clientSessionDelegate, appSettings: appSettings, appHooks: appHooks)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedInvocations.append((sessionDirectories: sessionDirectories, passphrase: passphrase, clientSessionDelegate: clientSessionDelegate, appSettings: appSettings, appHooks: appHooks))
|
||||||
|
}
|
||||||
|
if let makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksClosure = makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksClosure {
|
||||||
|
return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksClosure(sessionDirectories, passphrase, clientSessionDelegate, appSettings, appHooks)
|
||||||
|
} else {
|
||||||
|
return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class AuthenticationClientBuilderMock: AuthenticationClientBuilderProtocol {
|
||||||
|
|
||||||
|
//MARK: - build
|
||||||
|
|
||||||
|
var buildHomeserverAddressThrowableError: Error?
|
||||||
|
var buildHomeserverAddressUnderlyingCallsCount = 0
|
||||||
|
var buildHomeserverAddressCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return buildHomeserverAddressUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = buildHomeserverAddressUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
buildHomeserverAddressUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
buildHomeserverAddressUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var buildHomeserverAddressCalled: Bool {
|
||||||
|
return buildHomeserverAddressCallsCount > 0
|
||||||
|
}
|
||||||
|
var buildHomeserverAddressReceivedHomeserverAddress: String?
|
||||||
|
var buildHomeserverAddressReceivedInvocations: [String] = []
|
||||||
|
|
||||||
|
var buildHomeserverAddressUnderlyingReturnValue: ClientProtocol!
|
||||||
|
var buildHomeserverAddressReturnValue: ClientProtocol! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return buildHomeserverAddressUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: ClientProtocol? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = buildHomeserverAddressUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
buildHomeserverAddressUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
buildHomeserverAddressUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var buildHomeserverAddressClosure: ((String) async throws -> ClientProtocol)?
|
||||||
|
|
||||||
|
func build(homeserverAddress: String) async throws -> ClientProtocol {
|
||||||
|
if let error = buildHomeserverAddressThrowableError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
buildHomeserverAddressCallsCount += 1
|
||||||
|
buildHomeserverAddressReceivedHomeserverAddress = homeserverAddress
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.buildHomeserverAddressReceivedInvocations.append(homeserverAddress)
|
||||||
|
}
|
||||||
|
if let buildHomeserverAddressClosure = buildHomeserverAddressClosure {
|
||||||
|
return try await buildHomeserverAddressClosure(homeserverAddress)
|
||||||
|
} else {
|
||||||
|
return buildHomeserverAddressReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - buildWithQRCode
|
||||||
|
|
||||||
|
var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerThrowableError: Error?
|
||||||
|
var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount = 0
|
||||||
|
var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerCalled: Bool {
|
||||||
|
return buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerCallsCount > 0
|
||||||
|
}
|
||||||
|
var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReceivedArguments: (qrCodeData: QrCodeData, oidcConfiguration: OIDCConfigurationProxy, progressListener: QrLoginProgressListenerProxy)?
|
||||||
|
var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReceivedInvocations: [(qrCodeData: QrCodeData, oidcConfiguration: OIDCConfigurationProxy, progressListener: QrLoginProgressListenerProxy)] = []
|
||||||
|
|
||||||
|
var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue: ClientProtocol!
|
||||||
|
var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReturnValue: ClientProtocol! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: ClientProtocol? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerClosure: ((QrCodeData, OIDCConfigurationProxy, QrLoginProgressListenerProxy) async throws -> ClientProtocol)?
|
||||||
|
|
||||||
|
func buildWithQRCode(qrCodeData: QrCodeData, oidcConfiguration: OIDCConfigurationProxy, progressListener: QrLoginProgressListenerProxy) async throws -> ClientProtocol {
|
||||||
|
if let error = buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerThrowableError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerCallsCount += 1
|
||||||
|
buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReceivedArguments = (qrCodeData: qrCodeData, oidcConfiguration: oidcConfiguration, progressListener: progressListener)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReceivedInvocations.append((qrCodeData: qrCodeData, oidcConfiguration: oidcConfiguration, progressListener: progressListener))
|
||||||
|
}
|
||||||
|
if let buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerClosure = buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerClosure {
|
||||||
|
return try await buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerClosure(qrCodeData, oidcConfiguration, progressListener)
|
||||||
|
} else {
|
||||||
|
return buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
class BugReportServiceMock: BugReportServiceProtocol {
|
class BugReportServiceMock: BugReportServiceProtocol {
|
||||||
var crashedLastRun: Bool {
|
var crashedLastRun: Bool {
|
||||||
get { return underlyingCrashedLastRun }
|
get { return underlyingCrashedLastRun }
|
||||||
@ -15296,6 +15520,271 @@ class UserSessionMock: UserSessionProtocol {
|
|||||||
var underlyingCallbacks: PassthroughSubject<UserSessionCallback, Never>!
|
var underlyingCallbacks: PassthroughSubject<UserSessionCallback, Never>!
|
||||||
|
|
||||||
}
|
}
|
||||||
|
class UserSessionStoreMock: UserSessionStoreProtocol {
|
||||||
|
var hasSessions: Bool {
|
||||||
|
get { return underlyingHasSessions }
|
||||||
|
set(value) { underlyingHasSessions = value }
|
||||||
|
}
|
||||||
|
var underlyingHasSessions: Bool!
|
||||||
|
var userIDs: [String] = []
|
||||||
|
var clientSessionDelegate: ClientSessionDelegate {
|
||||||
|
get { return underlyingClientSessionDelegate }
|
||||||
|
set(value) { underlyingClientSessionDelegate = value }
|
||||||
|
}
|
||||||
|
var underlyingClientSessionDelegate: ClientSessionDelegate!
|
||||||
|
|
||||||
|
//MARK: - reset
|
||||||
|
|
||||||
|
var resetUnderlyingCallsCount = 0
|
||||||
|
var resetCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return resetUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = resetUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
resetUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
resetUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var resetCalled: Bool {
|
||||||
|
return resetCallsCount > 0
|
||||||
|
}
|
||||||
|
var resetClosure: (() -> Void)?
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
resetCallsCount += 1
|
||||||
|
resetClosure?()
|
||||||
|
}
|
||||||
|
//MARK: - restoreUserSession
|
||||||
|
|
||||||
|
var restoreUserSessionUnderlyingCallsCount = 0
|
||||||
|
var restoreUserSessionCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return restoreUserSessionUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = restoreUserSessionUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
restoreUserSessionUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
restoreUserSessionUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var restoreUserSessionCalled: Bool {
|
||||||
|
return restoreUserSessionCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var restoreUserSessionUnderlyingReturnValue: Result<UserSessionProtocol, UserSessionStoreError>!
|
||||||
|
var restoreUserSessionReturnValue: Result<UserSessionProtocol, UserSessionStoreError>! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return restoreUserSessionUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Result<UserSessionProtocol, UserSessionStoreError>? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = restoreUserSessionUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
restoreUserSessionUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
restoreUserSessionUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var restoreUserSessionClosure: (() async -> Result<UserSessionProtocol, UserSessionStoreError>)?
|
||||||
|
|
||||||
|
func restoreUserSession() async -> Result<UserSessionProtocol, UserSessionStoreError> {
|
||||||
|
restoreUserSessionCallsCount += 1
|
||||||
|
if let restoreUserSessionClosure = restoreUserSessionClosure {
|
||||||
|
return await restoreUserSessionClosure()
|
||||||
|
} else {
|
||||||
|
return restoreUserSessionReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - userSession
|
||||||
|
|
||||||
|
var userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount = 0
|
||||||
|
var userSessionForSessionDirectoriesPassphraseCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var userSessionForSessionDirectoriesPassphraseCalled: Bool {
|
||||||
|
return userSessionForSessionDirectoriesPassphraseCallsCount > 0
|
||||||
|
}
|
||||||
|
var userSessionForSessionDirectoriesPassphraseReceivedArguments: (client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?)?
|
||||||
|
var userSessionForSessionDirectoriesPassphraseReceivedInvocations: [(client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?)] = []
|
||||||
|
|
||||||
|
var userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue: Result<UserSessionProtocol, UserSessionStoreError>!
|
||||||
|
var userSessionForSessionDirectoriesPassphraseReturnValue: Result<UserSessionProtocol, UserSessionStoreError>! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Result<UserSessionProtocol, UserSessionStoreError>? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var userSessionForSessionDirectoriesPassphraseClosure: ((ClientProtocol, SessionDirectories, String?) async -> Result<UserSessionProtocol, UserSessionStoreError>)?
|
||||||
|
|
||||||
|
func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result<UserSessionProtocol, UserSessionStoreError> {
|
||||||
|
userSessionForSessionDirectoriesPassphraseCallsCount += 1
|
||||||
|
userSessionForSessionDirectoriesPassphraseReceivedArguments = (client: client, sessionDirectories: sessionDirectories, passphrase: passphrase)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.userSessionForSessionDirectoriesPassphraseReceivedInvocations.append((client: client, sessionDirectories: sessionDirectories, passphrase: passphrase))
|
||||||
|
}
|
||||||
|
if let userSessionForSessionDirectoriesPassphraseClosure = userSessionForSessionDirectoriesPassphraseClosure {
|
||||||
|
return await userSessionForSessionDirectoriesPassphraseClosure(client, sessionDirectories, passphrase)
|
||||||
|
} else {
|
||||||
|
return userSessionForSessionDirectoriesPassphraseReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//MARK: - logout
|
||||||
|
|
||||||
|
var logoutUserSessionUnderlyingCallsCount = 0
|
||||||
|
var logoutUserSessionCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return logoutUserSessionUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = logoutUserSessionUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
logoutUserSessionUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
logoutUserSessionUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var logoutUserSessionCalled: Bool {
|
||||||
|
return logoutUserSessionCallsCount > 0
|
||||||
|
}
|
||||||
|
var logoutUserSessionReceivedUserSession: UserSessionProtocol?
|
||||||
|
var logoutUserSessionReceivedInvocations: [UserSessionProtocol] = []
|
||||||
|
var logoutUserSessionClosure: ((UserSessionProtocol) -> Void)?
|
||||||
|
|
||||||
|
func logout(userSession: UserSessionProtocol) {
|
||||||
|
logoutUserSessionCallsCount += 1
|
||||||
|
logoutUserSessionReceivedUserSession = userSession
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.logoutUserSessionReceivedInvocations.append(userSession)
|
||||||
|
}
|
||||||
|
logoutUserSessionClosure?(userSession)
|
||||||
|
}
|
||||||
|
//MARK: - clearCache
|
||||||
|
|
||||||
|
var clearCacheForUnderlyingCallsCount = 0
|
||||||
|
var clearCacheForCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return clearCacheForUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = clearCacheForUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
clearCacheForUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
clearCacheForUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var clearCacheForCalled: Bool {
|
||||||
|
return clearCacheForCallsCount > 0
|
||||||
|
}
|
||||||
|
var clearCacheForReceivedUserID: String?
|
||||||
|
var clearCacheForReceivedInvocations: [String] = []
|
||||||
|
var clearCacheForClosure: ((String) -> Void)?
|
||||||
|
|
||||||
|
func clearCache(for userID: String) {
|
||||||
|
clearCacheForCallsCount += 1
|
||||||
|
clearCacheForReceivedUserID = userID
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.clearCacheForReceivedInvocations.append(userID)
|
||||||
|
}
|
||||||
|
clearCacheForClosure?(userID)
|
||||||
|
}
|
||||||
|
}
|
||||||
class VoiceMessageCacheMock: VoiceMessageCacheProtocol {
|
class VoiceMessageCacheMock: VoiceMessageCacheProtocol {
|
||||||
var urlForRecording: URL {
|
var urlForRecording: URL {
|
||||||
get { return underlyingUrlForRecording }
|
get { return underlyingUrlForRecording }
|
||||||
|
75
ElementX/Sources/Mocks/SDK/ClientSDKMock.swift
Normal file
75
ElementX/Sources/Mocks/SDK/ClientSDKMock.swift
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
// Please see LICENSE in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
extension ClientSDKMock {
|
||||||
|
struct Configuration {
|
||||||
|
// MARK: Authentication
|
||||||
|
|
||||||
|
var serverAddress = "matrix.org"
|
||||||
|
var homeserverURL = "https://matrix-client.matrix.org"
|
||||||
|
var slidingSyncVersion = SlidingSyncVersion.native
|
||||||
|
var oidcLoginURL: String?
|
||||||
|
var supportsPasswordLogin = true
|
||||||
|
var elementWellKnown = "{\"registration_helper_url\":\"https://develop.element.io/#/mobile_register\"}"
|
||||||
|
var validCredentials = (username: "alice", password: "12345678")
|
||||||
|
|
||||||
|
// MARK: Session
|
||||||
|
|
||||||
|
var userID: String?
|
||||||
|
var session = Session(accessToken: UUID().uuidString,
|
||||||
|
refreshToken: nil,
|
||||||
|
userId: "@alice:matrix.org",
|
||||||
|
deviceId: UUID().uuidString,
|
||||||
|
homeserverUrl: "https://matrix-client.matrix.org",
|
||||||
|
oidcData: nil,
|
||||||
|
slidingSyncVersion: .native)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MockError: Error { case generic }
|
||||||
|
|
||||||
|
convenience init(configuration: Configuration) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
homeserverLoginDetailsReturnValue = HomeserverLoginDetailsSDKMock(configuration: configuration)
|
||||||
|
slidingSyncVersionReturnValue = configuration.slidingSyncVersion
|
||||||
|
userIdServerNameThrowableError = MockError.generic
|
||||||
|
serverReturnValue = "https://\(configuration.serverAddress)"
|
||||||
|
getUrlUrlReturnValue = configuration.elementWellKnown
|
||||||
|
urlForOidcLoginOidcConfigurationReturnValue = OidcAuthorizationDataSDKMock(configuration: configuration)
|
||||||
|
loginUsernamePasswordInitialDeviceNameDeviceIdClosure = { username, password, _, _ in
|
||||||
|
guard username == configuration.validCredentials.username,
|
||||||
|
password == configuration.validCredentials.password else {
|
||||||
|
throw MockError.generic // use the matrix error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userIdReturnValue = configuration.userID
|
||||||
|
sessionReturnValue = configuration.session
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension HomeserverLoginDetailsSDKMock {
|
||||||
|
convenience init(configuration: ClientSDKMock.Configuration) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
slidingSyncVersionReturnValue = configuration.slidingSyncVersion
|
||||||
|
supportsPasswordLoginReturnValue = configuration.supportsPasswordLogin
|
||||||
|
supportsOidcLoginReturnValue = configuration.oidcLoginURL != nil
|
||||||
|
urlReturnValue = configuration.homeserverURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OidcAuthorizationDataSDKMock {
|
||||||
|
convenience init(configuration: ClientSDKMock.Configuration) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
loginUrlReturnValue = configuration.oidcLoginURL
|
||||||
|
}
|
||||||
|
}
|
17
ElementX/Sources/Mocks/UserSessionStoreMock.swift
Normal file
17
ElementX/Sources/Mocks/UserSessionStoreMock.swift
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
// Please see LICENSE in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
extension UserSessionStoreMock {
|
||||||
|
struct Configuration { }
|
||||||
|
|
||||||
|
convenience init(configuration: Configuration) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
userSessionForSessionDirectoriesPassphraseReturnValue = .success(UserSessionMock(.init(clientProxy: ClientProxyMock(.init()))))
|
||||||
|
clientSessionDelegate = KeychainControllerMock()
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,11 @@ struct LoginHomeserver: Equatable {
|
|||||||
self.registrationHelperURL = registrationHelperURL
|
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
|
/// Sanitizes a user entered homeserver address with the following rules
|
||||||
/// - Trim any whitespace.
|
/// - Trim any whitespace.
|
||||||
/// - Lowercase the address.
|
/// - Lowercase the address.
|
||||||
|
@ -145,7 +145,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
|||||||
startLoading(isInteractionBlocking: false)
|
startLoading(isInteractionBlocking: false)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
switch await authenticationService.configure(for: homeserverDomain) {
|
switch await authenticationService.configure(for: homeserverDomain, flow: .login) {
|
||||||
case .success:
|
case .success:
|
||||||
stopLoading()
|
stopLoading()
|
||||||
if authenticationService.homeserver.value.loginMode == .oidc {
|
if authenticationService.homeserver.value.loginMode == .oidc {
|
||||||
|
@ -11,6 +11,8 @@ import SwiftUI
|
|||||||
struct ServerConfirmationScreenCoordinatorParameters {
|
struct ServerConfirmationScreenCoordinatorParameters {
|
||||||
let authenticationService: AuthenticationServiceProtocol
|
let authenticationService: AuthenticationServiceProtocol
|
||||||
let authenticationFlow: AuthenticationFlow
|
let authenticationFlow: AuthenticationFlow
|
||||||
|
let slidingSyncLearnMoreURL: URL
|
||||||
|
let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ServerConfirmationScreenCoordinatorAction {
|
enum ServerConfirmationScreenCoordinatorAction {
|
||||||
@ -29,7 +31,9 @@ final class ServerConfirmationScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
init(parameters: ServerConfirmationScreenCoordinatorParameters) {
|
init(parameters: ServerConfirmationScreenCoordinatorParameters) {
|
||||||
viewModel = ServerConfirmationScreenViewModel(authenticationService: parameters.authenticationService,
|
viewModel = ServerConfirmationScreenViewModel(authenticationService: parameters.authenticationService,
|
||||||
authenticationFlow: parameters.authenticationFlow)
|
authenticationFlow: parameters.authenticationFlow,
|
||||||
|
slidingSyncLearnMoreURL: parameters.slidingSyncLearnMoreURL,
|
||||||
|
userIndicatorController: parameters.userIndicatorController)
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
@ -19,11 +19,11 @@ struct ServerConfirmationScreenViewState: BindableState {
|
|||||||
var homeserverAddress: String
|
var homeserverAddress: String
|
||||||
/// The flow being attempted on the selected homeserver.
|
/// The flow being attempted on the selected homeserver.
|
||||||
let authenticationFlow: AuthenticationFlow
|
let authenticationFlow: AuthenticationFlow
|
||||||
/// Whether or not the homeserver supports registration.
|
|
||||||
var homeserverSupportsRegistration = false
|
|
||||||
/// The presentation anchor used for OIDC authentication.
|
/// The presentation anchor used for OIDC authentication.
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
|
var bindings = ServerConfirmationScreenBindings()
|
||||||
|
|
||||||
/// The screen's title.
|
/// The screen's title.
|
||||||
var title: String {
|
var title: String {
|
||||||
switch authenticationFlow {
|
switch authenticationFlow {
|
||||||
@ -46,23 +46,16 @@ struct ServerConfirmationScreenViewState: BindableState {
|
|||||||
""
|
""
|
||||||
}
|
}
|
||||||
case .register:
|
case .register:
|
||||||
if canContinue {
|
L10n.screenServerConfirmationMessageRegister
|
||||||
L10n.screenServerConfirmationMessageRegister
|
|
||||||
} else {
|
|
||||||
L10n.errorAccountCreationNotPossible
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether or not it is valid to continue the flow.
|
|
||||||
var canContinue: Bool {
|
|
||||||
switch authenticationFlow {
|
|
||||||
case .login: true
|
|
||||||
case .register: homeserverSupportsRegistration
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ServerConfirmationScreenBindings {
|
||||||
|
/// Information describing the currently displayed alert.
|
||||||
|
var alertInfo: AlertInfo<ServerConfirmationScreenAlert>?
|
||||||
|
}
|
||||||
|
|
||||||
enum ServerConfirmationScreenViewAction {
|
enum ServerConfirmationScreenViewAction {
|
||||||
/// Updates the window used as the OIDC presentation anchor.
|
/// Updates the window used as the OIDC presentation anchor.
|
||||||
case updateWindow(UIWindow)
|
case updateWindow(UIWindow)
|
||||||
@ -71,3 +64,16 @@ enum ServerConfirmationScreenViewAction {
|
|||||||
/// The user would like to change to a different homeserver.
|
/// The user would like to change to a different homeserver.
|
||||||
case changeServer
|
case changeServer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ServerConfirmationScreenAlert: Hashable {
|
||||||
|
/// An alert that informs the user that a server could not be found.
|
||||||
|
case homeserverNotFound
|
||||||
|
/// An alert that informs the user about a bad well-known file.
|
||||||
|
case invalidWellKnown(String)
|
||||||
|
/// An alert that allows the user to learn about sliding sync.
|
||||||
|
case slidingSync
|
||||||
|
/// An alert that informs the user that registration isn't supported.
|
||||||
|
case registration
|
||||||
|
/// An unknown error has occurred.
|
||||||
|
case unknownError
|
||||||
|
}
|
||||||
|
@ -11,46 +11,118 @@ import SwiftUI
|
|||||||
typealias ServerConfirmationScreenViewModelType = StateStoreViewModel<ServerConfirmationScreenViewState, ServerConfirmationScreenViewAction>
|
typealias ServerConfirmationScreenViewModelType = StateStoreViewModel<ServerConfirmationScreenViewState, ServerConfirmationScreenViewAction>
|
||||||
|
|
||||||
class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType, ServerConfirmationScreenViewModelProtocol {
|
class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType, ServerConfirmationScreenViewModelProtocol {
|
||||||
|
let authenticationService: AuthenticationServiceProtocol
|
||||||
|
let authenticationFlow: AuthenticationFlow
|
||||||
|
let slidingSyncLearnMoreURL: URL
|
||||||
|
let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
|
|
||||||
private var actionsSubject: PassthroughSubject<ServerConfirmationScreenViewModelAction, Never> = .init()
|
private var actionsSubject: PassthroughSubject<ServerConfirmationScreenViewModelAction, Never> = .init()
|
||||||
|
|
||||||
var actions: AnyPublisher<ServerConfirmationScreenViewModelAction, Never> {
|
var actions: AnyPublisher<ServerConfirmationScreenViewModelAction, Never> {
|
||||||
actionsSubject.eraseToAnyPublisher()
|
actionsSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(authenticationService: AuthenticationServiceProtocol, authenticationFlow: AuthenticationFlow) {
|
init(authenticationService: AuthenticationServiceProtocol,
|
||||||
let homeserver = authenticationService.homeserver.value
|
authenticationFlow: AuthenticationFlow,
|
||||||
|
slidingSyncLearnMoreURL: URL,
|
||||||
|
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||||
|
self.authenticationService = authenticationService
|
||||||
|
self.authenticationFlow = authenticationFlow
|
||||||
|
self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL
|
||||||
|
self.userIndicatorController = userIndicatorController
|
||||||
|
|
||||||
|
let homeserver = authenticationService.homeserver.value
|
||||||
super.init(initialViewState: ServerConfirmationScreenViewState(homeserverAddress: homeserver.address,
|
super.init(initialViewState: ServerConfirmationScreenViewState(homeserverAddress: homeserver.address,
|
||||||
authenticationFlow: authenticationFlow,
|
authenticationFlow: authenticationFlow))
|
||||||
homeserverSupportsRegistration: homeserver.supportsRegistration))
|
|
||||||
|
|
||||||
authenticationService.homeserver
|
authenticationService.homeserver
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] homeserver in
|
.sink { [weak self] homeserver in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
state.homeserverAddress = homeserver.address
|
state.homeserverAddress = homeserver.address
|
||||||
state.homeserverSupportsRegistration = homeserver.supportsRegistration
|
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
|
||||||
|
|
||||||
override func process(viewAction: ServerConfirmationScreenViewAction) {
|
override func process(viewAction: ServerConfirmationScreenViewAction) {
|
||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .updateWindow(let window):
|
case .updateWindow(let window):
|
||||||
guard state.window != window else { return }
|
guard state.window != window else { return }
|
||||||
Task { state.window = window }
|
Task { state.window = window }
|
||||||
case .confirm:
|
case .confirm:
|
||||||
actionsSubject.send(.confirm)
|
Task { await configureAndContinue() }
|
||||||
case .changeServer:
|
case .changeServer:
|
||||||
actionsSubject.send(.changeServer)
|
actionsSubject.send(.changeServer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// MARK: - Private
|
||||||
extension LoginHomeserver {
|
|
||||||
var supportsRegistration: Bool {
|
private func configureAndContinue() async {
|
||||||
loginMode == .oidc || (address == "matrix.org" && registrationHelperURL != nil)
|
let homeserver = authenticationService.homeserver.value
|
||||||
|
|
||||||
|
// If the login mode is unknown, the service hasn't be configured and we need to do it now.
|
||||||
|
// Otherwise we can continue the flow as server selection has been performed and succeeded.
|
||||||
|
guard homeserver.loginMode == .unknown || authenticationService.flow != authenticationFlow else {
|
||||||
|
actionsSubject.send(.confirm)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
startLoading()
|
||||||
|
defer { stopLoading() }
|
||||||
|
|
||||||
|
switch await authenticationService.configure(for: homeserver.address, flow: authenticationFlow) {
|
||||||
|
case .success:
|
||||||
|
actionsSubject.send(.confirm)
|
||||||
|
case .failure(let error):
|
||||||
|
switch error {
|
||||||
|
case .invalidServer, .invalidHomeserverAddress:
|
||||||
|
displayError(.homeserverNotFound)
|
||||||
|
case .invalidWellKnown(let error):
|
||||||
|
displayError(.invalidWellKnown(error))
|
||||||
|
case .slidingSyncNotAvailable:
|
||||||
|
displayError(.slidingSync)
|
||||||
|
case .registrationNotSupported:
|
||||||
|
displayError(.registration)
|
||||||
|
default:
|
||||||
|
displayError(.unknownError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func startLoading(label: String = L10n.commonLoading) {
|
||||||
|
userIndicatorController.submitIndicator(UserIndicator(type: .modal,
|
||||||
|
title: label,
|
||||||
|
persistent: true))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopLoading() {
|
||||||
|
userIndicatorController.retractAllIndicators()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func displayError(_ type: ServerConfirmationScreenAlert) {
|
||||||
|
switch type {
|
||||||
|
case .homeserverNotFound:
|
||||||
|
state.bindings.alertInfo = AlertInfo(id: .homeserverNotFound,
|
||||||
|
title: L10n.errorUnknown,
|
||||||
|
message: L10n.screenChangeServerErrorInvalidHomeserver)
|
||||||
|
case .invalidWellKnown(let error):
|
||||||
|
state.bindings.alertInfo = AlertInfo(id: .invalidWellKnown(error),
|
||||||
|
title: L10n.commonServerNotSupported,
|
||||||
|
message: L10n.screenChangeServerErrorInvalidWellKnown(error))
|
||||||
|
case .slidingSync:
|
||||||
|
let openURL = { UIApplication.shared.open(self.slidingSyncLearnMoreURL) }
|
||||||
|
state.bindings.alertInfo = AlertInfo(id: .slidingSync,
|
||||||
|
title: L10n.commonServerNotSupported,
|
||||||
|
message: L10n.screenChangeServerErrorNoSlidingSyncMessage,
|
||||||
|
primaryButton: .init(title: L10n.actionLearnMore, role: .cancel, action: openURL),
|
||||||
|
secondaryButton: .init(title: L10n.actionCancel, action: nil))
|
||||||
|
case .registration:
|
||||||
|
state.bindings.alertInfo = AlertInfo(id: .registration,
|
||||||
|
title: L10n.errorUnknown,
|
||||||
|
message: L10n.errorAccountCreationNotPossible)
|
||||||
|
case .unknownError:
|
||||||
|
state.bindings.alertInfo = AlertInfo(id: .unknownError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ struct ServerConfirmationScreen: View {
|
|||||||
}
|
}
|
||||||
.background()
|
.background()
|
||||||
.backgroundStyle(.compound.bgCanvasDefault)
|
.backgroundStyle(.compound.bgCanvasDefault)
|
||||||
|
.alert(item: $context.alertInfo)
|
||||||
.introspect(.window, on: .supportedVersions) { window in
|
.introspect(.window, on: .supportedVersions) { window in
|
||||||
context.send(viewAction: .updateWindow(window))
|
context.send(viewAction: .updateWindow(window))
|
||||||
}
|
}
|
||||||
@ -53,7 +54,6 @@ struct ServerConfirmationScreen: View {
|
|||||||
}
|
}
|
||||||
.buttonStyle(.compound(.primary))
|
.buttonStyle(.compound(.primary))
|
||||||
.accessibilityIdentifier(A11yIdentifiers.serverConfirmationScreen.continue)
|
.accessibilityIdentifier(A11yIdentifiers.serverConfirmationScreen.continue)
|
||||||
.disabled(!context.viewState.canContinue)
|
|
||||||
|
|
||||||
Button { context.send(viewAction: .changeServer) } label: {
|
Button { context.send(viewAction: .changeServer) } label: {
|
||||||
Text(L10n.screenServerConfirmationChangeServer)
|
Text(L10n.screenServerConfirmationChangeServer)
|
||||||
@ -68,10 +68,8 @@ struct ServerConfirmationScreen: View {
|
|||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
|
||||||
struct ServerConfirmationScreen_Previews: PreviewProvider, TestablePreview {
|
struct ServerConfirmationScreen_Previews: PreviewProvider, TestablePreview {
|
||||||
static let loginViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationService(),
|
static let loginViewModel = makeViewModel(flow: .login)
|
||||||
authenticationFlow: .login)
|
static let registerViewModel = makeViewModel(flow: .register)
|
||||||
static let registerViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationService(),
|
|
||||||
authenticationFlow: .register)
|
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
@ -86,4 +84,11 @@ struct ServerConfirmationScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
}
|
}
|
||||||
.previewDisplayName("Register")
|
.previewDisplayName("Register")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func makeViewModel(flow: AuthenticationFlow) -> ServerConfirmationScreenViewModel {
|
||||||
|
ServerConfirmationScreenViewModel(authenticationService: AuthenticationService.mock,
|
||||||
|
authenticationFlow: flow,
|
||||||
|
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
||||||
|
userIndicatorController: UserIndicatorControllerMock())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,30 +11,22 @@ enum MockServerSelectionScreenState: CaseIterable {
|
|||||||
case matrix
|
case matrix
|
||||||
case emptyAddress
|
case emptyAddress
|
||||||
case invalidAddress
|
case invalidAddress
|
||||||
case nonModal
|
|
||||||
|
|
||||||
/// Generate the view struct for the screen state.
|
/// Generate the view struct for the screen state.
|
||||||
@MainActor var viewModel: ServerSelectionScreenViewModel {
|
@MainActor var viewModel: ServerSelectionScreenViewModel {
|
||||||
switch self {
|
switch self {
|
||||||
case .matrix:
|
case .matrix:
|
||||||
return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org",
|
return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org",
|
||||||
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
|
||||||
isModallyPresented: true)
|
|
||||||
|
|
||||||
case .emptyAddress:
|
case .emptyAddress:
|
||||||
return ServerSelectionScreenViewModel(homeserverAddress: "",
|
return ServerSelectionScreenViewModel(homeserverAddress: "",
|
||||||
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
|
||||||
isModallyPresented: true)
|
|
||||||
case .invalidAddress:
|
case .invalidAddress:
|
||||||
let viewModel = ServerSelectionScreenViewModel(homeserverAddress: "thisisbad",
|
let viewModel = ServerSelectionScreenViewModel(homeserverAddress: "thisisbad",
|
||||||
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
|
||||||
isModallyPresented: true)
|
|
||||||
viewModel.displayError(.footerMessage(L10n.errorUnknown))
|
viewModel.displayError(.footerMessage(L10n.errorUnknown))
|
||||||
return viewModel
|
return viewModel
|
||||||
case .nonModal:
|
|
||||||
return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org",
|
|
||||||
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
|
||||||
isModallyPresented: false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ import SwiftUI
|
|||||||
struct ServerSelectionScreenCoordinatorParameters {
|
struct ServerSelectionScreenCoordinatorParameters {
|
||||||
/// The service used to authenticate the user.
|
/// The service used to authenticate the user.
|
||||||
let authenticationService: AuthenticationServiceProtocol
|
let authenticationService: AuthenticationServiceProtocol
|
||||||
|
let authenticationFlow: AuthenticationFlow
|
||||||
|
let slidingSyncLearnMoreURL: URL
|
||||||
let userIndicatorController: UserIndicatorControllerProtocol
|
let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
/// Whether the screen is presented modally or within a navigation stack.
|
|
||||||
let isModallyPresented: Bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ServerSelectionScreenCoordinatorAction {
|
enum ServerSelectionScreenCoordinatorAction {
|
||||||
@ -38,8 +38,7 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
|
|||||||
init(parameters: ServerSelectionScreenCoordinatorParameters) {
|
init(parameters: ServerSelectionScreenCoordinatorParameters) {
|
||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
viewModel = ServerSelectionScreenViewModel(homeserverAddress: parameters.authenticationService.homeserver.value.address,
|
viewModel = ServerSelectionScreenViewModel(homeserverAddress: parameters.authenticationService.homeserver.value.address,
|
||||||
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
slidingSyncLearnMoreURL: parameters.slidingSyncLearnMoreURL)
|
||||||
isModallyPresented: parameters.isModallyPresented)
|
|
||||||
userIndicatorController = parameters.userIndicatorController
|
userIndicatorController = parameters.userIndicatorController
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +84,7 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
|
|||||||
startLoading()
|
startLoading()
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
switch await authenticationService.configure(for: homeserverAddress) {
|
switch await authenticationService.configure(for: homeserverAddress, flow: parameters.authenticationFlow) {
|
||||||
case .success:
|
case .success:
|
||||||
MXLog.info("Selected homeserver: \(homeserverAddress)")
|
MXLog.info("Selected homeserver: \(homeserverAddress)")
|
||||||
actionsSubject.send(.updated)
|
actionsSubject.send(.updated)
|
||||||
@ -107,6 +106,8 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
|
|||||||
viewModel.displayError(.invalidWellKnownAlert(error))
|
viewModel.displayError(.invalidWellKnownAlert(error))
|
||||||
case .slidingSyncNotAvailable:
|
case .slidingSyncNotAvailable:
|
||||||
viewModel.displayError(.slidingSyncAlert)
|
viewModel.displayError(.slidingSyncAlert)
|
||||||
|
case .registrationNotSupported:
|
||||||
|
viewModel.displayError(.registrationAlert) // TODO: [DOUG] Test me!
|
||||||
default:
|
default:
|
||||||
viewModel.displayError(.footerMessage(L10n.errorUnknown))
|
viewModel.displayError(.footerMessage(L10n.errorUnknown))
|
||||||
}
|
}
|
||||||
|
@ -22,19 +22,12 @@ struct ServerSelectionScreenViewState: BindableState {
|
|||||||
var bindings: ServerSelectionScreenBindings
|
var bindings: ServerSelectionScreenBindings
|
||||||
/// An error message to be shown in the text field footer.
|
/// An error message to be shown in the text field footer.
|
||||||
var footerErrorMessage: String?
|
var footerErrorMessage: String?
|
||||||
/// Whether the screen is presented modally or within a navigation stack.
|
|
||||||
var isModallyPresented: Bool
|
|
||||||
|
|
||||||
/// The message to show in the text field footer.
|
/// The message to show in the text field footer.
|
||||||
var footerMessage: AttributedString {
|
var footerMessage: AttributedString {
|
||||||
footerErrorMessage.map(AttributedString.init) ?? regularFooterMessage
|
footerErrorMessage.map(AttributedString.init) ?? regularFooterMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The title shown on the confirm button.
|
|
||||||
var buttonTitle: String {
|
|
||||||
isModallyPresented ? L10n.actionContinue : L10n.actionNext
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The text field is showing an error.
|
/// The text field is showing an error.
|
||||||
var isShowingFooterError: Bool {
|
var isShowingFooterError: Bool {
|
||||||
footerErrorMessage != nil
|
footerErrorMessage != nil
|
||||||
@ -45,10 +38,9 @@ struct ServerSelectionScreenViewState: BindableState {
|
|||||||
bindings.homeserverAddress.isEmpty || isShowingFooterError
|
bindings.homeserverAddress.isEmpty || isShowingFooterError
|
||||||
}
|
}
|
||||||
|
|
||||||
init(slidingSyncLearnMoreURL: URL, bindings: ServerSelectionScreenBindings, footerErrorMessage: String? = nil, isModallyPresented: Bool) {
|
init(slidingSyncLearnMoreURL: URL, bindings: ServerSelectionScreenBindings, footerErrorMessage: String? = nil) {
|
||||||
self.bindings = bindings
|
self.bindings = bindings
|
||||||
self.footerErrorMessage = footerErrorMessage
|
self.footerErrorMessage = footerErrorMessage
|
||||||
self.isModallyPresented = isModallyPresented
|
|
||||||
|
|
||||||
let linkPlaceholder = "{link}"
|
let linkPlaceholder = "{link}"
|
||||||
var message = AttributedString(L10n.screenChangeServerFormNotice(linkPlaceholder))
|
var message = AttributedString(L10n.screenChangeServerFormNotice(linkPlaceholder))
|
||||||
@ -82,4 +74,6 @@ enum ServerSelectionScreenErrorType: Hashable {
|
|||||||
case invalidWellKnownAlert(String)
|
case invalidWellKnownAlert(String)
|
||||||
/// An alert that allows the user to learn about sliding sync.
|
/// An alert that allows the user to learn about sliding sync.
|
||||||
case slidingSyncAlert
|
case slidingSyncAlert
|
||||||
|
/// An alert that informs the user that registration isn't supported.
|
||||||
|
case registrationAlert
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,12 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server
|
|||||||
actionsSubject.eraseToAnyPublisher()
|
actionsSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(homeserverAddress: String, slidingSyncLearnMoreURL: URL, isModallyPresented: Bool) {
|
init(homeserverAddress: String, slidingSyncLearnMoreURL: URL) {
|
||||||
self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL
|
self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL
|
||||||
let bindings = ServerSelectionScreenBindings(homeserverAddress: homeserverAddress)
|
let bindings = ServerSelectionScreenBindings(homeserverAddress: homeserverAddress)
|
||||||
|
|
||||||
super.init(initialViewState: ServerSelectionScreenViewState(slidingSyncLearnMoreURL: slidingSyncLearnMoreURL,
|
super.init(initialViewState: ServerSelectionScreenViewState(slidingSyncLearnMoreURL: slidingSyncLearnMoreURL,
|
||||||
bindings: bindings,
|
bindings: bindings))
|
||||||
isModallyPresented: isModallyPresented))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func process(viewAction: ServerSelectionScreenViewAction) {
|
override func process(viewAction: ServerSelectionScreenViewAction) {
|
||||||
@ -46,7 +45,7 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server
|
|||||||
state.footerErrorMessage = message
|
state.footerErrorMessage = message
|
||||||
}
|
}
|
||||||
case .invalidWellKnownAlert(let error):
|
case .invalidWellKnownAlert(let error):
|
||||||
state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert,
|
state.bindings.alertInfo = AlertInfo(id: .invalidWellKnownAlert(error),
|
||||||
title: L10n.commonServerNotSupported,
|
title: L10n.commonServerNotSupported,
|
||||||
message: L10n.screenChangeServerErrorInvalidWellKnown(error))
|
message: L10n.screenChangeServerErrorInvalidWellKnown(error))
|
||||||
case .slidingSyncAlert:
|
case .slidingSyncAlert:
|
||||||
@ -56,6 +55,10 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server
|
|||||||
message: L10n.screenChangeServerErrorNoSlidingSyncMessage,
|
message: L10n.screenChangeServerErrorNoSlidingSyncMessage,
|
||||||
primaryButton: .init(title: L10n.actionLearnMore, role: .cancel, action: openURL),
|
primaryButton: .init(title: L10n.actionLearnMore, role: .cancel, action: openURL),
|
||||||
secondaryButton: .init(title: L10n.actionCancel, action: nil))
|
secondaryButton: .init(title: L10n.actionCancel, action: nil))
|
||||||
|
case .registrationAlert:
|
||||||
|
state.bindings.alertInfo = AlertInfo(id: .registrationAlert,
|
||||||
|
title: L10n.errorUnknown,
|
||||||
|
message: L10n.errorAccountCreationNotPossible)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ struct ServerSelectionScreen: View {
|
|||||||
.onSubmit(submit)
|
.onSubmit(submit)
|
||||||
|
|
||||||
Button(action: submit) {
|
Button(action: submit) {
|
||||||
Text(context.viewState.buttonTitle)
|
Text(L10n.actionContinue)
|
||||||
}
|
}
|
||||||
.buttonStyle(.compound(.primary))
|
.buttonStyle(.compound(.primary))
|
||||||
.disabled(context.viewState.hasValidationError)
|
.disabled(context.viewState.hasValidationError)
|
||||||
@ -72,15 +72,12 @@ struct ServerSelectionScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ToolbarContentBuilder
|
|
||||||
var toolbar: some ToolbarContent {
|
var toolbar: some ToolbarContent {
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
if context.viewState.isModallyPresented {
|
Button { context.send(viewAction: .dismiss) } label: {
|
||||||
Button { context.send(viewAction: .dismiss) } label: {
|
Text(L10n.actionCancel)
|
||||||
Text(L10n.actionCancel)
|
|
||||||
}
|
|
||||||
.accessibilityIdentifier(A11yIdentifiers.changeServerScreen.dismiss)
|
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(A11yIdentifiers.changeServerScreen.dismiss)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,8 +8,16 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import MatrixRustSDK
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
// sourcery: AutoMockable
|
||||||
|
protocol AuthenticationClientBuilderProtocol {
|
||||||
|
func build(homeserverAddress: String) async throws -> ClientProtocol
|
||||||
|
func buildWithQRCode(qrCodeData: QrCodeData,
|
||||||
|
oidcConfiguration: OIDCConfigurationProxy,
|
||||||
|
progressListener: QrLoginProgressListenerProxy) async throws -> ClientProtocol
|
||||||
|
}
|
||||||
|
|
||||||
/// A wrapper around `ClientBuilder` to share reusable code between Normal and QR logins.
|
/// A wrapper around `ClientBuilder` to share reusable code between Normal and QR logins.
|
||||||
struct AuthenticationClientBuilder {
|
struct AuthenticationClientBuilder: AuthenticationClientBuilderProtocol {
|
||||||
let sessionDirectories: SessionDirectories
|
let sessionDirectories: SessionDirectories
|
||||||
let passphrase: String
|
let passphrase: String
|
||||||
let clientSessionDelegate: ClientSessionDelegate
|
let clientSessionDelegate: ClientSessionDelegate
|
||||||
@ -18,7 +26,7 @@ struct AuthenticationClientBuilder {
|
|||||||
let appHooks: AppHooks
|
let appHooks: AppHooks
|
||||||
|
|
||||||
/// Builds a Client for login using OIDC or password authentication.
|
/// Builds a Client for login using OIDC or password authentication.
|
||||||
func build(homeserverAddress: String) async throws -> Client {
|
func build(homeserverAddress: String) async throws -> ClientProtocol {
|
||||||
if appSettings.slidingSyncDiscovery == .forceNative {
|
if appSettings.slidingSyncDiscovery == .forceNative {
|
||||||
return try await makeClientBuilder(slidingSync: .forceNative).serverNameOrHomeserverUrl(serverNameOrUrl: homeserverAddress).build()
|
return try await makeClientBuilder(slidingSync: .forceNative).serverNameOrHomeserverUrl(serverNameOrUrl: homeserverAddress).build()
|
||||||
}
|
}
|
||||||
@ -38,7 +46,7 @@ struct AuthenticationClientBuilder {
|
|||||||
/// Builds a Client, authenticating with the given QR code data.
|
/// Builds a Client, authenticating with the given QR code data.
|
||||||
func buildWithQRCode(qrCodeData: QrCodeData,
|
func buildWithQRCode(qrCodeData: QrCodeData,
|
||||||
oidcConfiguration: OIDCConfigurationProxy,
|
oidcConfiguration: OIDCConfigurationProxy,
|
||||||
progressListener: QrLoginProgressListenerProxy) async throws -> Client {
|
progressListener: QrLoginProgressListenerProxy) async throws -> ClientProtocol {
|
||||||
if appSettings.slidingSyncDiscovery == .forceNative {
|
if appSettings.slidingSyncDiscovery == .forceNative {
|
||||||
return try await makeClientBuilder(slidingSync: .forceNative).buildWithQrCode(qrCodeData: qrCodeData,
|
return try await makeClientBuilder(slidingSync: .forceNative).buildWithQrCode(qrCodeData: qrCodeData,
|
||||||
oidcConfiguration: oidcConfiguration.rustValue,
|
oidcConfiguration: oidcConfiguration.rustValue,
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
// Please see LICENSE in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
// sourcery: AutoMockable
|
||||||
|
protocol AuthenticationClientBuilderFactoryProtocol {
|
||||||
|
func makeBuilder(sessionDirectories: SessionDirectories,
|
||||||
|
passphrase: String,
|
||||||
|
clientSessionDelegate: ClientSessionDelegate,
|
||||||
|
appSettings: AppSettings,
|
||||||
|
appHooks: AppHooks) -> AuthenticationClientBuilderProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper around `ClientBuilder` to share reusable code between Normal and QR logins.
|
||||||
|
struct AuthenticationClientBuilderFactory: AuthenticationClientBuilderFactoryProtocol {
|
||||||
|
func makeBuilder(sessionDirectories: SessionDirectories,
|
||||||
|
passphrase: String,
|
||||||
|
clientSessionDelegate: ClientSessionDelegate,
|
||||||
|
appSettings: AppSettings,
|
||||||
|
appHooks: AppHooks) -> AuthenticationClientBuilderProtocol {
|
||||||
|
AuthenticationClientBuilder(sessionDirectories: sessionDirectories,
|
||||||
|
passphrase: passphrase,
|
||||||
|
clientSessionDelegate: clientSessionDelegate,
|
||||||
|
appSettings: appSettings,
|
||||||
|
appHooks: appHooks)
|
||||||
|
}
|
||||||
|
}
|
@ -10,31 +10,39 @@ import Foundation
|
|||||||
import MatrixRustSDK
|
import MatrixRustSDK
|
||||||
|
|
||||||
class AuthenticationService: AuthenticationServiceProtocol {
|
class AuthenticationService: AuthenticationServiceProtocol {
|
||||||
private var client: Client?
|
private var client: ClientProtocol?
|
||||||
private var sessionDirectories: SessionDirectories
|
private var sessionDirectories: SessionDirectories
|
||||||
private let passphrase: String
|
private let passphrase: String
|
||||||
|
|
||||||
|
private let clientBuilderFactory: AuthenticationClientBuilderFactoryProtocol
|
||||||
private let userSessionStore: UserSessionStoreProtocol
|
private let userSessionStore: UserSessionStoreProtocol
|
||||||
private let appSettings: AppSettings
|
private let appSettings: AppSettings
|
||||||
private let appHooks: AppHooks
|
private let appHooks: AppHooks
|
||||||
|
|
||||||
private let homeserverSubject: CurrentValueSubject<LoginHomeserver, Never>
|
private let homeserverSubject: CurrentValueSubject<LoginHomeserver, Never>
|
||||||
var homeserver: CurrentValuePublisher<LoginHomeserver, Never> { homeserverSubject.asCurrentValuePublisher() }
|
var homeserver: CurrentValuePublisher<LoginHomeserver, Never> { homeserverSubject.asCurrentValuePublisher() }
|
||||||
|
private(set) var flow: AuthenticationFlow
|
||||||
|
|
||||||
init(userSessionStore: UserSessionStoreProtocol, encryptionKeyProvider: EncryptionKeyProviderProtocol, appSettings: AppSettings, appHooks: AppHooks) {
|
init(userSessionStore: UserSessionStoreProtocol,
|
||||||
|
encryptionKeyProvider: EncryptionKeyProviderProtocol,
|
||||||
|
clientBuilderFactory: AuthenticationClientBuilderFactoryProtocol = AuthenticationClientBuilderFactory(),
|
||||||
|
appSettings: AppSettings,
|
||||||
|
appHooks: AppHooks) {
|
||||||
sessionDirectories = .init()
|
sessionDirectories = .init()
|
||||||
passphrase = encryptionKeyProvider.generateKey().base64EncodedString()
|
passphrase = encryptionKeyProvider.generateKey().base64EncodedString()
|
||||||
|
self.clientBuilderFactory = clientBuilderFactory
|
||||||
self.userSessionStore = userSessionStore
|
self.userSessionStore = userSessionStore
|
||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
self.appHooks = appHooks
|
self.appHooks = appHooks
|
||||||
|
|
||||||
homeserverSubject = .init(LoginHomeserver(address: appSettings.defaultHomeserverAddress,
|
// When updating these, don't forget to update the reset method too.
|
||||||
loginMode: .unknown))
|
homeserverSubject = .init(LoginHomeserver(address: appSettings.defaultHomeserverAddress, loginMode: .unknown))
|
||||||
|
flow = .login
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
|
|
||||||
func configure(for homeserverAddress: String) async -> Result<Void, AuthenticationServiceError> {
|
func configure(for homeserverAddress: String, flow: AuthenticationFlow) async -> Result<Void, AuthenticationServiceError> {
|
||||||
do {
|
do {
|
||||||
var homeserver = LoginHomeserver(address: homeserverAddress, loginMode: .unknown)
|
var homeserver = LoginHomeserver(address: homeserverAddress, loginMode: .unknown)
|
||||||
|
|
||||||
@ -57,7 +65,12 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
|||||||
case .failure: nil
|
case .failure: nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if flow == .register, !homeserver.supportsRegistration {
|
||||||
|
return .failure(.registrationNotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
self.client = client
|
self.client = client
|
||||||
|
self.flow = flow
|
||||||
homeserverSubject.send(homeserver)
|
homeserverSubject.send(homeserver)
|
||||||
return .success(())
|
return .success(())
|
||||||
} catch ClientBuildError.WellKnownDeserializationError(let error) {
|
} catch ClientBuildError.WellKnownDeserializationError(let error) {
|
||||||
@ -150,18 +163,24 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
homeserverSubject.send(LoginHomeserver(address: appSettings.defaultHomeserverAddress, loginMode: .unknown))
|
||||||
|
flow = .login
|
||||||
|
client = nil
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func makeClientBuilder() -> AuthenticationClientBuilder {
|
private func makeClientBuilder() -> AuthenticationClientBuilderProtocol {
|
||||||
// Use a fresh session directory each time the user enters a different server
|
// Use a fresh session directory each time the user enters a different server
|
||||||
// so that caches (e.g. server versions) are always fresh for the new server.
|
// so that caches (e.g. server versions) are always fresh for the new server.
|
||||||
rotateSessionDirectory()
|
rotateSessionDirectory()
|
||||||
|
|
||||||
return AuthenticationClientBuilder(sessionDirectories: sessionDirectories,
|
return clientBuilderFactory.makeBuilder(sessionDirectories: sessionDirectories,
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
clientSessionDelegate: userSessionStore.clientSessionDelegate,
|
clientSessionDelegate: userSessionStore.clientSessionDelegate,
|
||||||
appSettings: appSettings,
|
appSettings: appSettings,
|
||||||
appHooks: appHooks)
|
appHooks: appHooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func rotateSessionDirectory() {
|
private func rotateSessionDirectory() {
|
||||||
@ -169,7 +188,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
|||||||
sessionDirectories = .init()
|
sessionDirectories = .init()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func userSession(for client: Client) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
private func userSession(for client: ClientProtocol) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
||||||
switch await userSessionStore.userSession(for: client, sessionDirectories: sessionDirectories, passphrase: passphrase) {
|
switch await userSessionStore.userSession(for: client, sessionDirectories: sessionDirectories, passphrase: passphrase) {
|
||||||
case .success(let clientProxy):
|
case .success(let clientProxy):
|
||||||
return .success(clientProxy)
|
return .success(clientProxy)
|
||||||
@ -178,3 +197,13 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Mocks
|
||||||
|
|
||||||
|
extension AuthenticationService {
|
||||||
|
static var mock = AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()),
|
||||||
|
encryptionKeyProvider: EncryptionKeyProvider(),
|
||||||
|
clientBuilderFactory: AuthenticationClientBuilderFactoryMock(configuration: .init()),
|
||||||
|
appSettings: ServiceLocator.shared.settings,
|
||||||
|
appHooks: AppHooks())
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@ enum AuthenticationFlow {
|
|||||||
case register
|
case register
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AuthenticationServiceError: Error {
|
enum AuthenticationServiceError: Error, Equatable {
|
||||||
/// An error occurred during OIDC authentication.
|
/// An error occurred during OIDC authentication.
|
||||||
case oidcError(OIDCError)
|
case oidcError(OIDCError)
|
||||||
case invalidServer
|
case invalidServer
|
||||||
@ -24,6 +24,7 @@ enum AuthenticationServiceError: Error {
|
|||||||
case invalidHomeserverAddress
|
case invalidHomeserverAddress
|
||||||
case invalidWellKnown(String)
|
case invalidWellKnown(String)
|
||||||
case slidingSyncNotAvailable
|
case slidingSyncNotAvailable
|
||||||
|
case registrationNotSupported
|
||||||
case accountDeactivated
|
case accountDeactivated
|
||||||
case failedLoggingIn
|
case failedLoggingIn
|
||||||
case sessionTokenRefreshNotSupported
|
case sessionTokenRefreshNotSupported
|
||||||
@ -33,9 +34,11 @@ enum AuthenticationServiceError: Error {
|
|||||||
protocol AuthenticationServiceProtocol {
|
protocol AuthenticationServiceProtocol {
|
||||||
/// The currently configured homeserver.
|
/// The currently configured homeserver.
|
||||||
var homeserver: CurrentValuePublisher<LoginHomeserver, Never> { get }
|
var homeserver: CurrentValuePublisher<LoginHomeserver, Never> { get }
|
||||||
|
/// The type of flow the service is currently configured with.
|
||||||
|
var flow: AuthenticationFlow { get }
|
||||||
|
|
||||||
/// Sets up the service for login on the specified homeserver address.
|
/// Sets up the service for login on the specified homeserver address.
|
||||||
func configure(for homeserverAddress: String) async -> Result<Void, AuthenticationServiceError>
|
func configure(for homeserverAddress: String, flow: AuthenticationFlow) async -> Result<Void, AuthenticationServiceError>
|
||||||
/// Performs login using OIDC for the current homeserver.
|
/// Performs login using OIDC for the current homeserver.
|
||||||
func urlForOIDCLogin() async -> Result<OIDCAuthorizationDataProxy, AuthenticationServiceError>
|
func urlForOIDCLogin() async -> Result<OIDCAuthorizationDataProxy, AuthenticationServiceError>
|
||||||
/// Asks the SDK to abort an ongoing OIDC login if we didn't get a callback to complete the request with.
|
/// Asks the SDK to abort an ongoing OIDC login if we didn't get a callback to complete the request with.
|
||||||
@ -46,6 +49,9 @@ protocol AuthenticationServiceProtocol {
|
|||||||
func login(username: String, password: String, initialDeviceName: String?, deviceID: String?) async -> Result<UserSessionProtocol, AuthenticationServiceError>
|
func login(username: String, password: String, initialDeviceName: String?, deviceID: String?) async -> Result<UserSessionProtocol, AuthenticationServiceError>
|
||||||
/// Completes registration using the credentials obtained via the helper URL.
|
/// Completes registration using the credentials obtained via the helper URL.
|
||||||
func completeWebRegistration(using credentials: WebRegistrationCredentials) async -> Result<UserSessionProtocol, AuthenticationServiceError>
|
func completeWebRegistration(using credentials: WebRegistrationCredentials) async -> Result<UserSessionProtocol, AuthenticationServiceError>
|
||||||
|
|
||||||
|
/// Resets the current configuration requiring `configure(for:flow:)` to be called again.
|
||||||
|
func reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - OIDC
|
// MARK: - OIDC
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2022-2024 New Vector Ltd.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
// Please see LICENSE in the repository root for full details.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Combine
|
|
||||||
import Foundation
|
|
||||||
import MatrixRustSDK
|
|
||||||
|
|
||||||
class MockAuthenticationService: AuthenticationServiceProtocol {
|
|
||||||
let validCredentials = (username: "alice", password: "12345678")
|
|
||||||
|
|
||||||
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) {
|
|
||||||
homeserverSubject.send(.mockMatrixDotOrg)
|
|
||||||
return .success(())
|
|
||||||
} else if LoginHomeserver.mockOIDC.address.contains(homeserverAddress) {
|
|
||||||
homeserverSubject.send(.mockOIDC)
|
|
||||||
return .success(())
|
|
||||||
} else if LoginHomeserver.mockBasicServer.address.contains(homeserverAddress) {
|
|
||||||
homeserverSubject.send(.mockBasicServer)
|
|
||||||
return .success(())
|
|
||||||
} else if LoginHomeserver.mockUnsupported.address.contains(homeserverAddress) {
|
|
||||||
homeserverSubject.send(.mockUnsupported)
|
|
||||||
return .success(())
|
|
||||||
} else {
|
|
||||||
// Otherwise fail with an invalid server.
|
|
||||||
return .failure(.invalidServer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func urlForOIDCLogin() async -> Result<OIDCAuthorizationDataProxy, AuthenticationServiceError> {
|
|
||||||
.failure(.oidcError(.notSupported))
|
|
||||||
}
|
|
||||||
|
|
||||||
func abortOIDCLogin(data: OIDCAuthorizationDataProxy) async { }
|
|
||||||
|
|
||||||
func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthorizationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
|
||||||
.failure(.oidcError(.notSupported))
|
|
||||||
}
|
|
||||||
|
|
||||||
func login(username: String, password: String, initialDeviceName: String?, deviceID: String?) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
|
||||||
// Login only succeeds if the username and password match the valid credentials property
|
|
||||||
guard username == validCredentials.username, password == validCredentials.password else {
|
|
||||||
return .failure(.invalidCredentials)
|
|
||||||
}
|
|
||||||
|
|
||||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: username))))
|
|
||||||
return .success(userSession)
|
|
||||||
}
|
|
||||||
|
|
||||||
func completeWebRegistration(using credentials: WebRegistrationCredentials) async -> Result<any UserSessionProtocol, AuthenticationServiceError> {
|
|
||||||
.failure(.failedLoggingIn)
|
|
||||||
}
|
|
||||||
}
|
|
@ -81,7 +81,7 @@ final class QRCodeLoginService: QRCodeLoginServiceProtocol {
|
|||||||
sessionDirectories = .init()
|
sessionDirectories = .init()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func userSession(for client: Client) async -> Result<UserSessionProtocol, QRCodeLoginServiceError> {
|
private func userSession(for client: ClientProtocol) async -> Result<UserSessionProtocol, QRCodeLoginServiceError> {
|
||||||
switch await userSessionStore.userSession(for: client, sessionDirectories: sessionDirectories, passphrase: passphrase) {
|
switch await userSessionStore.userSession(for: client, sessionDirectories: sessionDirectories, passphrase: passphrase) {
|
||||||
case .success(let session):
|
case .success(let session):
|
||||||
return .success(session)
|
return .success(session)
|
||||||
|
@ -60,7 +60,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func userSession(for client: Client, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result<UserSessionProtocol, UserSessionStoreError> {
|
func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result<UserSessionProtocol, UserSessionStoreError> {
|
||||||
do {
|
do {
|
||||||
let session = try client.session()
|
let session = try client.session()
|
||||||
let userID = try client.userId()
|
let userID = try client.userId()
|
||||||
@ -146,7 +146,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupProxyForClient(_ client: Client) async -> ClientProxyProtocol {
|
private func setupProxyForClient(_ client: ClientProtocol) async -> ClientProxyProtocol {
|
||||||
await ClientProxy(client: client,
|
await ClientProxy(client: client,
|
||||||
networkMonitor: networkMonitor,
|
networkMonitor: networkMonitor,
|
||||||
appSettings: appSettings)
|
appSettings: appSettings)
|
||||||
|
@ -14,6 +14,7 @@ enum UserSessionStoreError: Error {
|
|||||||
case failedSettingUpSession
|
case failedSettingUpSession
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sourcery: AutoMockable
|
||||||
protocol UserSessionStoreProtocol {
|
protocol UserSessionStoreProtocol {
|
||||||
/// Deletes all data stored in the shared container and keychain
|
/// Deletes all data stored in the shared container and keychain
|
||||||
func reset()
|
func reset()
|
||||||
@ -31,7 +32,7 @@ protocol UserSessionStoreProtocol {
|
|||||||
func restoreUserSession() async -> Result<UserSessionProtocol, UserSessionStoreError>
|
func restoreUserSession() async -> Result<UserSessionProtocol, UserSessionStoreError>
|
||||||
|
|
||||||
/// Creates a user session for a new client from the SDK along with the passphrase used for the data stores.
|
/// Creates a user session for a new client from the SDK along with the passphrase used for the data stores.
|
||||||
func userSession(for client: Client, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result<UserSessionProtocol, UserSessionStoreError>
|
func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result<UserSessionProtocol, UserSessionStoreError>
|
||||||
|
|
||||||
/// Logs out of the specified session.
|
/// Logs out of the specified session.
|
||||||
func logout(userSession: UserSessionProtocol)
|
func logout(userSession: UserSessionProtocol)
|
||||||
|
@ -109,22 +109,16 @@ class MockScreen: Identifiable {
|
|||||||
|
|
||||||
lazy var coordinator: CoordinatorProtocol? = {
|
lazy var coordinator: CoordinatorProtocol? = {
|
||||||
switch id {
|
switch id {
|
||||||
case .login:
|
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
|
||||||
let coordinator = LoginScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationService(),
|
|
||||||
analytics: ServiceLocator.shared.analytics,
|
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController))
|
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
|
||||||
return navigationStackCoordinator
|
|
||||||
case .serverSelection:
|
case .serverSelection:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
let coordinator = ServerSelectionScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationService(),
|
let coordinator = ServerSelectionScreenCoordinator(parameters: .init(authenticationService: AuthenticationService.mock,
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
authenticationFlow: .login,
|
||||||
isModallyPresented: true))
|
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
||||||
|
userIndicatorController: ServiceLocator.shared.userIndicatorController))
|
||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
case .authenticationFlow:
|
case .authenticationFlow:
|
||||||
let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: MockAuthenticationService(),
|
let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: AuthenticationService.mock,
|
||||||
qrCodeLoginService: QRCodeLoginServiceMock(),
|
qrCodeLoginService: QRCodeLoginServiceMock(),
|
||||||
bugReportService: BugReportServiceMock(),
|
bugReportService: BugReportServiceMock(),
|
||||||
navigationRootCoordinator: navigationRootCoordinator,
|
navigationRootCoordinator: navigationRootCoordinator,
|
||||||
|
@ -20,7 +20,6 @@ enum UITestsScreenIdentifier: String {
|
|||||||
case createPoll
|
case createPoll
|
||||||
case createRoom
|
case createRoom
|
||||||
case createRoomNoUsers
|
case createRoomNoUsers
|
||||||
case login
|
|
||||||
case roomLayoutBottom
|
case roomLayoutBottom
|
||||||
case roomLayoutMiddle
|
case roomLayoutMiddle
|
||||||
case roomLayoutTop
|
case roomLayoutTop
|
||||||
|
@ -19,6 +19,10 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase {
|
|||||||
// Server Confirmation: Tap continue button
|
// Server Confirmation: Tap continue button
|
||||||
app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap()
|
app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap()
|
||||||
|
|
||||||
|
// Login Screen: Wait for continue button to appear
|
||||||
|
let continueButton = app.buttons[A11yIdentifiers.loginScreen.continue]
|
||||||
|
XCTAssertTrue(continueButton.waitForExistence(timeout: 2.0))
|
||||||
|
|
||||||
// Login Screen: Enter valid credentials
|
// Login Screen: Enter valid credentials
|
||||||
app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice\n")
|
app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice\n")
|
||||||
app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("12345678")
|
app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("12345678")
|
||||||
@ -39,20 +43,43 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase {
|
|||||||
// Server Confirmation: Tap continue button
|
// Server Confirmation: Tap continue button
|
||||||
app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap()
|
app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap()
|
||||||
|
|
||||||
|
// Login Screen: Wait for continue button to appear
|
||||||
|
let continueButton = app.buttons[A11yIdentifiers.loginScreen.continue]
|
||||||
|
XCTAssertTrue(continueButton.waitForExistence(timeout: 2.0))
|
||||||
|
|
||||||
// Login Screen: Enter invalid credentials
|
// Login Screen: Enter invalid credentials
|
||||||
app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice")
|
app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice")
|
||||||
app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("87654321")
|
app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("87654321")
|
||||||
|
|
||||||
// Login Screen: Tap next
|
// Login Screen: Tap continue
|
||||||
let nextButton = app.buttons[A11yIdentifiers.loginScreen.continue]
|
XCTAssertTrue(continueButton.isEnabled)
|
||||||
XCTAssertTrue(nextButton.waitForExistence(timeout: 2.0))
|
continueButton.tap()
|
||||||
XCTAssertTrue(nextButton.isEnabled)
|
|
||||||
nextButton.tap()
|
|
||||||
|
|
||||||
// Then login should fail.
|
// Then login should fail.
|
||||||
XCTAssertTrue(app.alerts.element.waitForExistence(timeout: 2.0), "An error alert should be shown when attempting login with invalid credentials.")
|
XCTAssertTrue(app.alerts.element.waitForExistence(timeout: 2.0), "An error alert should be shown when attempting login with invalid credentials.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testLoginWithUnsupportedUserID() async throws {
|
||||||
|
// Given the authentication flow.
|
||||||
|
let app = Application.launch(.authenticationFlow)
|
||||||
|
|
||||||
|
// Splash Screen: Tap get started button
|
||||||
|
app.buttons[A11yIdentifiers.authenticationStartScreen.signIn].tap()
|
||||||
|
|
||||||
|
// Server Confirmation: Tap continue button
|
||||||
|
app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap()
|
||||||
|
|
||||||
|
// Login Screen: Wait for continue button to appear
|
||||||
|
let continueButton = app.buttons[A11yIdentifiers.loginScreen.continue]
|
||||||
|
XCTAssertTrue(continueButton.waitForExistence(timeout: 2.0))
|
||||||
|
|
||||||
|
// When entering a username on a homeserver with an unsupported flow.
|
||||||
|
app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("@test:server.net\n")
|
||||||
|
|
||||||
|
// Then the screen should not allow login to continue.
|
||||||
|
try await app.assertScreenshot(.authenticationFlow, step: 1)
|
||||||
|
}
|
||||||
|
|
||||||
func testSelectingOIDCServer() {
|
func testSelectingOIDCServer() {
|
||||||
// Given the authentication flow.
|
// Given the authentication flow.
|
||||||
let app = Application.launch(.authenticationFlow)
|
let app = Application.launch(.authenticationFlow)
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2022-2024 New Vector Ltd.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
// Please see LICENSE in the repository root for full details.
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
class LoginScreenUITests: XCTestCase {
|
|
||||||
func testMatrixDotOrg() async throws {
|
|
||||||
// Given the initial login screen which defaults to matrix.org.
|
|
||||||
let app = Application.launch(.login)
|
|
||||||
try await app.assertScreenshot(.login)
|
|
||||||
|
|
||||||
// When typing in a username and password.
|
|
||||||
app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("@test:matrix.org")
|
|
||||||
app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("12345678")
|
|
||||||
|
|
||||||
// Then the form should be ready to submit.
|
|
||||||
try await app.assertScreenshot(.login, step: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUnsupported() async throws {
|
|
||||||
// Given the initial login screen.
|
|
||||||
let app = Application.launch(.login)
|
|
||||||
|
|
||||||
// When entering a username on a homeserver with an unsupported flow.
|
|
||||||
app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("@test:server.net\n")
|
|
||||||
|
|
||||||
// Then the screen should not allow login to continue.
|
|
||||||
try await app.assertScreenshot(.login, step: 1)
|
|
||||||
}
|
|
||||||
}
|
|
BIN
UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-15-en-GB.UI.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-15-en-GB.UI.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/login-0-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/login-0-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/login-0-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/login-0-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/login-1-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/login-1-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/login-1-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/login-1-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/login-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/login-iPad-10th-generation-en-GB.UI.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/login-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/login-iPhone-16-en-GB.UI.png
(Stored with Git LFS)
Binary file not shown.
96
UnitTests/Sources/AuthenticationServiceTests.swift
Normal file
96
UnitTests/Sources/AuthenticationServiceTests.swift
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
// Please see LICENSE in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ElementX
|
||||||
|
|
||||||
|
class AuthenticationServiceTests: XCTestCase {
|
||||||
|
var client: ClientSDKMock!
|
||||||
|
var userSessionStore: UserSessionStoreMock!
|
||||||
|
var encryptionKeyProvider: MockEncryptionKeyProvider!
|
||||||
|
|
||||||
|
var service: AuthenticationService!
|
||||||
|
|
||||||
|
func testLogin() 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)
|
||||||
|
|
||||||
|
switch await service.login(username: "alice", password: "12345678", initialDeviceName: nil, deviceID: nil) {
|
||||||
|
case .success:
|
||||||
|
XCTAssertEqual(client.loginUsernamePasswordInitialDeviceNameDeviceIdCallsCount, 1)
|
||||||
|
XCTAssertEqual(userSessionStore.userSessionForSessionDirectoriesPassphraseCallsCount, 1)
|
||||||
|
XCTAssertEqual(userSessionStore.userSessionForSessionDirectoriesPassphraseReceivedArguments?.passphrase,
|
||||||
|
encryptionKeyProvider.generateKey().base64EncodedString())
|
||||||
|
case .failure(let error):
|
||||||
|
XCTFail("Unexpected failure: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfigureRegister() async {
|
||||||
|
setupMocks()
|
||||||
|
|
||||||
|
switch await service.configure(for: "matrix.org", flow: .register) {
|
||||||
|
case .success:
|
||||||
|
break
|
||||||
|
case .failure(let error):
|
||||||
|
XCTFail("Unexpected failure: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(service.flow, .register)
|
||||||
|
XCTAssertEqual(service.homeserver.value, .mockMatrixDotOrg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfigureRegisterNoSupport() async {
|
||||||
|
let homeserverAddress = "example.com"
|
||||||
|
setupMocks(serverAddress: homeserverAddress)
|
||||||
|
|
||||||
|
switch await service.configure(for: homeserverAddress, flow: .register) {
|
||||||
|
case .success:
|
||||||
|
XCTFail("Configuration should have failed")
|
||||||
|
case .failure(let error):
|
||||||
|
XCTAssertEqual(error, .registrationNotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(service.flow, .login)
|
||||||
|
XCTAssertEqual(service.homeserver.value, .init(address: "matrix.org", loginMode: .unknown))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private func setupMocks(serverAddress: String = "matrix.org") {
|
||||||
|
let configuration: AuthenticationClientBuilderMock.Configuration = .init()
|
||||||
|
let clientBuilderFactory = AuthenticationClientBuilderFactoryMock(configuration: .init(builderConfiguration: configuration))
|
||||||
|
|
||||||
|
client = configuration.homeserverClients[serverAddress]
|
||||||
|
userSessionStore = UserSessionStoreMock(configuration: .init())
|
||||||
|
encryptionKeyProvider = MockEncryptionKeyProvider()
|
||||||
|
|
||||||
|
service = AuthenticationService(userSessionStore: userSessionStore,
|
||||||
|
encryptionKeyProvider: encryptionKeyProvider,
|
||||||
|
clientBuilderFactory: clientBuilderFactory,
|
||||||
|
appSettings: ServiceLocator.shared.settings,
|
||||||
|
appHooks: AppHooks())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MockEncryptionKeyProvider: EncryptionKeyProviderProtocol {
|
||||||
|
private let key = "12345678"
|
||||||
|
|
||||||
|
func generateKey() -> Data {
|
||||||
|
Data(key.utf8)
|
||||||
|
}
|
||||||
|
}
|
@ -26,18 +26,11 @@ class ServerConfirmationScreenViewStateTests: XCTestCase {
|
|||||||
|
|
||||||
func testRegisterMessageString() {
|
func testRegisterMessageString() {
|
||||||
let matrixDotOrgRegister = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockMatrixDotOrg.address,
|
let matrixDotOrgRegister = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockMatrixDotOrg.address,
|
||||||
authenticationFlow: .register,
|
authenticationFlow: .register)
|
||||||
homeserverSupportsRegistration: true)
|
|
||||||
XCTAssertEqual(matrixDotOrgRegister.message, L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.")
|
XCTAssertEqual(matrixDotOrgRegister.message, L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.")
|
||||||
|
|
||||||
let oidcRegister = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockOIDC.address,
|
let oidcRegister = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockOIDC.address,
|
||||||
authenticationFlow: .register,
|
authenticationFlow: .register)
|
||||||
homeserverSupportsRegistration: true)
|
|
||||||
XCTAssertEqual(oidcRegister.message, L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.")
|
XCTAssertEqual(oidcRegister.message, L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.")
|
||||||
|
|
||||||
let otherRegister = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockBasicServer.address,
|
|
||||||
authenticationFlow: .register,
|
|
||||||
homeserverSupportsRegistration: false)
|
|
||||||
XCTAssertEqual(otherRegister.message, L10n.errorAccountCreationNotPossible, "The registration message should always be the same.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,117 @@ import XCTest
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class ServerConfirmationScreenViewModelTests: XCTestCase {
|
class ServerConfirmationScreenViewModelTests: XCTestCase {
|
||||||
// Nothing to test, the view model has no mutable state.
|
var clientBuilderFactory: AuthenticationClientBuilderFactoryMock!
|
||||||
|
var service: AuthenticationServiceProtocol!
|
||||||
|
|
||||||
|
var viewModel: ServerConfirmationScreenViewModel!
|
||||||
|
var context: ServerConfirmationScreenViewModel.Context { viewModel.context }
|
||||||
|
|
||||||
|
func testConfirmLoginWithoutConfiguration() async throws {
|
||||||
|
// Given a view model for login using a service that hasn't been configured.
|
||||||
|
setupViewModel(authenticationFlow: .login)
|
||||||
|
XCTAssertEqual(service.homeserver.value.loginMode, .unknown)
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0)
|
||||||
|
|
||||||
|
// When continuing from the confirmation screen.
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { $0 == .confirm }
|
||||||
|
context.send(viewAction: .confirm)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
// Then a call to configure service should be made.
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1)
|
||||||
|
XCTAssertNotEqual(service.homeserver.value.loginMode, .unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfirmLoginAfterConfiguration() async throws {
|
||||||
|
// Given a view model for login using a service that has already been configured (via the server selection screen).
|
||||||
|
setupViewModel(authenticationFlow: .login)
|
||||||
|
guard case .success = await service.configure(for: viewModel.state.homeserverAddress, flow: .login) else {
|
||||||
|
XCTFail("The configuration should succeed.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
XCTAssertNotEqual(service.homeserver.value.loginMode, .unknown)
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1)
|
||||||
|
|
||||||
|
// When continuing from the confirmation screen.
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { $0 == .confirm }
|
||||||
|
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.
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfirmRegisterWithoutConfiguration() async throws {
|
||||||
|
// Given a view model for registration using a service that hasn't been configured.
|
||||||
|
setupViewModel(authenticationFlow: .register)
|
||||||
|
XCTAssertEqual(service.homeserver.value.loginMode, .unknown)
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0)
|
||||||
|
|
||||||
|
// When continuing from the confirmation screen.
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { $0 == .confirm }
|
||||||
|
context.send(viewAction: .confirm)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
// Then a call to configure service should be made.
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1)
|
||||||
|
XCTAssertNotEqual(service.homeserver.value.loginMode, .unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfirmRegisterAfterConfiguration() async throws {
|
||||||
|
// Given a view model for registration using a service that has already been configured (via the server selection screen).
|
||||||
|
setupViewModel(authenticationFlow: .register)
|
||||||
|
guard case .success = await service.configure(for: viewModel.state.homeserverAddress, flow: .register) else {
|
||||||
|
XCTFail("The configuration should succeed.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
XCTAssertNotEqual(service.homeserver.value.loginMode, .unknown)
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1)
|
||||||
|
|
||||||
|
// When continuing from the confirmation screen.
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { $0 == .confirm }
|
||||||
|
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.
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, elementWellKnown: false)
|
||||||
|
XCTAssertEqual(service.homeserver.value.loginMode, .unknown)
|
||||||
|
XCTAssertFalse(service.homeserver.value.supportsRegistration)
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0)
|
||||||
|
XCTAssertNil(context.alertInfo)
|
||||||
|
|
||||||
|
// When continuing from the confirmation screen.
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil }
|
||||||
|
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.
|
||||||
|
XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1)
|
||||||
|
XCTAssertEqual(context.alertInfo?.id, .registration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private func setupViewModel(authenticationFlow: AuthenticationFlow, elementWellKnown: Bool = true) {
|
||||||
|
let client = ClientSDKMock(configuration: elementWellKnown ? .init() : .init(elementWellKnown: ""))
|
||||||
|
let configuration = AuthenticationClientBuilderMock.Configuration(homeserverClients: ["matrix.org": client],
|
||||||
|
qrCodeClient: client)
|
||||||
|
|
||||||
|
clientBuilderFactory = AuthenticationClientBuilderFactoryMock(configuration: .init(builderConfiguration: configuration))
|
||||||
|
service = AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()),
|
||||||
|
encryptionKeyProvider: EncryptionKeyProvider(),
|
||||||
|
clientBuilderFactory: clientBuilderFactory,
|
||||||
|
appSettings: ServiceLocator.shared.settings,
|
||||||
|
appHooks: AppHooks())
|
||||||
|
|
||||||
|
viewModel = ServerConfirmationScreenViewModel(authenticationService: service,
|
||||||
|
authenticationFlow: authenticationFlow,
|
||||||
|
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
||||||
|
userIndicatorController: UserIndicatorControllerMock())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,7 @@ class ServerSelectionViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
@MainActor override func setUp() {
|
@MainActor override func setUp() {
|
||||||
viewModel = ServerSelectionScreenViewModel(homeserverAddress: "",
|
viewModel = ServerSelectionScreenViewModel(homeserverAddress: "",
|
||||||
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
|
||||||
isModallyPresented: true)
|
|
||||||
context = viewModel.context
|
context = viewModel.context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user