mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-06 04:18:31 +00:00
QR Code Login Flow (#2767)
Co-authored-by: Hugh Nimmo-Smith <hughns@users.noreply.github.com> Co-authored-by: Hugh Nimmo-Smith <hughns@element.io>
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 54;
|
objectVersion = 56;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXAggregateTarget section */
|
/* Begin PBXAggregateTarget section */
|
||||||
@@ -344,7 +344,6 @@
|
|||||||
523C6800ED85D5810CF18C19 /* OIDCAccountSettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D737F4672021D0A7D218CD /* OIDCAccountSettingsPresenter.swift */; };
|
523C6800ED85D5810CF18C19 /* OIDCAccountSettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D737F4672021D0A7D218CD /* OIDCAccountSettingsPresenter.swift */; };
|
||||||
52473A4D7B1FBD4CD1E770C8 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; };
|
52473A4D7B1FBD4CD1E770C8 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; };
|
||||||
5375902175B2FEA2949D7D74 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */; };
|
5375902175B2FEA2949D7D74 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */; };
|
||||||
538426B497672A097B212735 /* QRCodeLoginController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80A07343F18BB8EAC17B07B7 /* QRCodeLoginController.swift */; };
|
|
||||||
53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */; };
|
53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */; };
|
||||||
53A59720F4729D9BBFFB7CAB /* NotificationSettingsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD9CB3B9DFA353AB2B7CD9F8 /* NotificationSettingsEditScreenCoordinator.swift */; };
|
53A59720F4729D9BBFFB7CAB /* NotificationSettingsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD9CB3B9DFA353AB2B7CD9F8 /* NotificationSettingsEditScreenCoordinator.swift */; };
|
||||||
53C1E7F6A7D6409D89F36ED7 /* AggregatedReactionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */; };
|
53C1E7F6A7D6409D89F36ED7 /* AggregatedReactionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */; };
|
||||||
@@ -602,7 +601,6 @@
|
|||||||
8ED8AF57A06F5EE9978ED23F /* AuthenticationStartScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */; };
|
8ED8AF57A06F5EE9978ED23F /* AuthenticationStartScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */; };
|
||||||
8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */; };
|
8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */; };
|
||||||
8F2FAA98457750D9D664136F /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 4278261E147DB2DE5CFB7FC5 /* PostHog */; };
|
8F2FAA98457750D9D664136F /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 4278261E147DB2DE5CFB7FC5 /* PostHog */; };
|
||||||
90067BB37EBA60FA4AEF2DA3 /* QRCodeLoginServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B4ED923603F6110D4960C5 /* QRCodeLoginServiceMock.swift */; };
|
|
||||||
904F06C9C1AEF884C2077542 /* RoomDirectorySearchScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2E4EF80DFB8FE7C4469B15D /* RoomDirectorySearchScreen.swift */; };
|
904F06C9C1AEF884C2077542 /* RoomDirectorySearchScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2E4EF80DFB8FE7C4469B15D /* RoomDirectorySearchScreen.swift */; };
|
||||||
90733645AE76FB33DAD28C2B /* URLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE40D4A5DD857AC16EED945A /* URLSession.swift */; };
|
90733645AE76FB33DAD28C2B /* URLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE40D4A5DD857AC16EED945A /* URLSession.swift */; };
|
||||||
9095B9E40DB5CF8BA26CE0D8 /* ReactionsSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153726EDCE1ACBB3D466A916 /* ReactionsSummaryView.swift */; };
|
9095B9E40DB5CF8BA26CE0D8 /* ReactionsSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153726EDCE1ACBB3D466A916 /* ReactionsSummaryView.swift */; };
|
||||||
@@ -705,6 +703,8 @@
|
|||||||
A6F345328CCC5C9B0DAE2257 /* LogViewerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */; };
|
A6F345328CCC5C9B0DAE2257 /* LogViewerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */; };
|
||||||
A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; };
|
A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; };
|
||||||
A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; };
|
A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; };
|
||||||
|
A78DD8692C0766B1005A32A9 /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A78DD8682C0766B1005A32A9 /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift */; };
|
||||||
|
A78DD86B2C076B3F005A32A9 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = A78DD86A2C076B3F005A32A9 /* Data.swift */; };
|
||||||
A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
|
A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
|
||||||
A7FD7B992E6EE6E5A8429197 /* RoomSummaryDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142808B69851451AC32A2CEA /* RoomSummaryDetails.swift */; };
|
A7FD7B992E6EE6E5A8429197 /* RoomSummaryDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142808B69851451AC32A2CEA /* RoomSummaryDetails.swift */; };
|
||||||
A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; };
|
A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; };
|
||||||
@@ -837,6 +837,7 @@
|
|||||||
C85C7A201E4CFDA477ACEBEB /* AppLockSetupSettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8610C1D21565C950BCA6A454 /* AppLockSetupSettingsScreenViewModelProtocol.swift */; };
|
C85C7A201E4CFDA477ACEBEB /* AppLockSetupSettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8610C1D21565C950BCA6A454 /* AppLockSetupSettingsScreenViewModelProtocol.swift */; };
|
||||||
C8A9C595038AFA2D707AC8C1 /* NotificationPermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20E69F67D2A70ABD08CA6D54 /* NotificationPermissionsScreenViewModelProtocol.swift */; };
|
C8A9C595038AFA2D707AC8C1 /* NotificationPermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20E69F67D2A70ABD08CA6D54 /* NotificationPermissionsScreenViewModelProtocol.swift */; };
|
||||||
C8BD80891BAD688EF2C15CDB /* MediaUploadPreviewScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74DD0855F2F76D47E5555082 /* MediaUploadPreviewScreenCoordinator.swift */; };
|
C8BD80891BAD688EF2C15CDB /* MediaUploadPreviewScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74DD0855F2F76D47E5555082 /* MediaUploadPreviewScreenCoordinator.swift */; };
|
||||||
|
C8C7AF33AADF88B306CD2695 /* QRCodeLoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4427AF4B7FB7EF3E3D424C7 /* QRCodeLoginService.swift */; };
|
||||||
C8E0FA0FF2CD6613264FA6B9 /* MessageForwardingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEA446F8618DBA79A9239CC /* MessageForwardingScreen.swift */; };
|
C8E0FA0FF2CD6613264FA6B9 /* MessageForwardingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEA446F8618DBA79A9239CC /* MessageForwardingScreen.swift */; };
|
||||||
C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E0B4A34E69BD2132BEC521 /* MessageText.swift */; };
|
C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E0B4A34E69BD2132BEC521 /* MessageText.swift */; };
|
||||||
C9BE065FA7D4E77E4C61CB69 /* MapLibreModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81B6170DB690013CEB646F4 /* MapLibreModels.swift */; };
|
C9BE065FA7D4E77E4C61CB69 /* MapLibreModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81B6170DB690013CEB646F4 /* MapLibreModels.swift */; };
|
||||||
@@ -858,6 +859,7 @@
|
|||||||
CDAE3A37D4DF136F9D07DB61 /* RoomChangeRolesScreenSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */; };
|
CDAE3A37D4DF136F9D07DB61 /* RoomChangeRolesScreenSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */; };
|
||||||
CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */; };
|
CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */; };
|
||||||
CE1694C7BB93C3311524EF28 /* Untranslated.strings in Resources */ = {isa = PBXBuildFile; fileRef = D2F7194F440375338F8E2487 /* Untranslated.strings */; };
|
CE1694C7BB93C3311524EF28 /* Untranslated.strings in Resources */ = {isa = PBXBuildFile; fileRef = D2F7194F440375338F8E2487 /* Untranslated.strings */; };
|
||||||
|
CE3B7FC34FB2C279AAA5EA01 /* AVMetadataMachineReadableCodeObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3339B1DDB1341E833D2555BC /* AVMetadataMachineReadableCodeObject.swift */; };
|
||||||
CE6F237360875D3D573FD0B2 /* RoomNotificationSettingsProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD6B522BD637845AB9570B10 /* RoomNotificationSettingsProxy.swift */; };
|
CE6F237360875D3D573FD0B2 /* RoomNotificationSettingsProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD6B522BD637845AB9570B10 /* RoomNotificationSettingsProxy.swift */; };
|
||||||
CE9530A4CA661E090635C2F2 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; };
|
CE9530A4CA661E090635C2F2 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; };
|
||||||
CEAEA57B7665C8E790599A78 /* BlockedUsersScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240610DF32F3213BEC5611D7 /* BlockedUsersScreenViewModelTests.swift */; };
|
CEAEA57B7665C8E790599A78 /* BlockedUsersScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240610DF32F3213BEC5611D7 /* BlockedUsersScreenViewModelTests.swift */; };
|
||||||
@@ -941,6 +943,7 @@
|
|||||||
E313BDD2B8813144139B2E00 /* UserDiscoveryServiceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0287793F11C480E242B03DF5 /* UserDiscoveryServiceTest.swift */; };
|
E313BDD2B8813144139B2E00 /* UserDiscoveryServiceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0287793F11C480E242B03DF5 /* UserDiscoveryServiceTest.swift */; };
|
||||||
E3291AD16D7A5CB14781819C /* UserNotificationCenterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D8149FDDA0315CDC553B4B /* UserNotificationCenterProtocol.swift */; };
|
E3291AD16D7A5CB14781819C /* UserNotificationCenterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D8149FDDA0315CDC553B4B /* UserNotificationCenterProtocol.swift */; };
|
||||||
E32A18802EB37EEE3EF7B965 /* GlobalSearchScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B788615712FED326F73D3F83 /* GlobalSearchScreenViewModelProtocol.swift */; };
|
E32A18802EB37EEE3EF7B965 /* GlobalSearchScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B788615712FED326F73D3F83 /* GlobalSearchScreenViewModelProtocol.swift */; };
|
||||||
|
E362924A42934C9F0F97A956 /* OIDCConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69869844D2B6F5BD9AABF85 /* OIDCConfigurationProxy.swift */; };
|
||||||
E37044401D9951D6C02C0855 /* TracingConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED003DF1B7CF40E7073A2280 /* TracingConfiguration.swift */; };
|
E37044401D9951D6C02C0855 /* TracingConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED003DF1B7CF40E7073A2280 /* TracingConfiguration.swift */; };
|
||||||
E3AC72E3E58F364EF15C1CC7 /* NotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */; };
|
E3AC72E3E58F364EF15C1CC7 /* NotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */; };
|
||||||
E3CA565A4B9704F191B191F0 /* JoinedRoomSize+MemberCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */; };
|
E3CA565A4B9704F191B191F0 /* JoinedRoomSize+MemberCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */; };
|
||||||
@@ -1136,12 +1139,12 @@
|
|||||||
033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||||
035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = "<group>"; };
|
035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = "<group>"; };
|
||||||
0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = "<group>"; };
|
0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = "<group>"; };
|
||||||
0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; path = test_voice_message.m4a; sourceTree = "<group>"; };
|
0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_voice_message.m4a; sourceTree = "<group>"; };
|
||||||
03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = "<group>"; };
|
03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
044E501B8331B339874D1B96 /* CompoundIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompoundIcon.swift; sourceTree = "<group>"; };
|
044E501B8331B339874D1B96 /* CompoundIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompoundIcon.swift; sourceTree = "<group>"; };
|
||||||
045253F9967A535EE5B16691 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
|
045253F9967A535EE5B16691 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
|
||||||
048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||||
04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityIdentifiers.swift; sourceTree = "<group>"; };
|
04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityIdentifiers.swift; sourceTree = "<group>"; };
|
||||||
04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = "<group>"; };
|
04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = "<group>"; };
|
||||||
0516C69708D5CBDE1A8E77EC /* RoomDirectorySearchProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxyProtocol.swift; sourceTree = "<group>"; };
|
0516C69708D5CBDE1A8E77EC /* RoomDirectorySearchProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxyProtocol.swift; sourceTree = "<group>"; };
|
||||||
@@ -1200,7 +1203,7 @@
|
|||||||
127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValuePublisher.swift; sourceTree = "<group>"; };
|
127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValuePublisher.swift; sourceTree = "<group>"; };
|
||||||
128501375217576AF0FE3E92 /* RoomAttachmentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomAttachmentPicker.swift; sourceTree = "<group>"; };
|
128501375217576AF0FE3E92 /* RoomAttachmentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomAttachmentPicker.swift; sourceTree = "<group>"; };
|
||||||
12F1E7F9C2BE8BB751037826 /* WaitlistScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreenCoordinator.swift; sourceTree = "<group>"; };
|
12F1E7F9C2BE8BB751037826 /* WaitlistScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; path = IntegrationTests.xctestplan; sourceTree = "<group>"; };
|
1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = IntegrationTests.xctestplan; sourceTree = "<group>"; };
|
||||||
130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNTextInputNotificationResponse+Creator.swift"; sourceTree = "<group>"; };
|
130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNTextInputNotificationResponse+Creator.swift"; sourceTree = "<group>"; };
|
||||||
136F80A613B55BDD071DCEA5 /* JoinRoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenModels.swift; sourceTree = "<group>"; };
|
136F80A613B55BDD071DCEA5 /* JoinRoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenModels.swift; sourceTree = "<group>"; };
|
||||||
13802897C7AFA360EA74C0B0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
13802897C7AFA360EA74C0B0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
@@ -1288,7 +1291,7 @@
|
|||||||
25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxy.swift; sourceTree = "<group>"; };
|
25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxy.swift; sourceTree = "<group>"; };
|
||||||
25F8664F1FB95AF3C4202478 /* PollFormScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenCoordinator.swift; sourceTree = "<group>"; };
|
25F8664F1FB95AF3C4202478 /* PollFormScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
260004737C573A56FA01E86E /* Encodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encodable.swift; sourceTree = "<group>"; };
|
260004737C573A56FA01E86E /* Encodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encodable.swift; sourceTree = "<group>"; };
|
||||||
267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; path = PreviewTests.xctestplan; sourceTree = "<group>"; };
|
267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = PreviewTests.xctestplan; sourceTree = "<group>"; };
|
||||||
26B0A96B8FE4849227945067 /* VoiceMessageRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecorder.swift; sourceTree = "<group>"; };
|
26B0A96B8FE4849227945067 /* VoiceMessageRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecorder.swift; sourceTree = "<group>"; };
|
||||||
26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceProtocol.swift; sourceTree = "<group>"; };
|
26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceProtocol.swift; sourceTree = "<group>"; };
|
||||||
2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
@@ -1333,6 +1336,7 @@
|
|||||||
32B5E17028C02DFA7DDA3931 /* RoomMemberProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyProtocol.swift; sourceTree = "<group>"; };
|
32B5E17028C02DFA7DDA3931 /* RoomMemberProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyProtocol.swift; sourceTree = "<group>"; };
|
||||||
32C5DAA1773F57653BF1C4F9 /* SoftLogoutViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutViewModelTests.swift; sourceTree = "<group>"; };
|
32C5DAA1773F57653BF1C4F9 /* SoftLogoutViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
330AF4D121C3396F7A14B21D /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/SAS.strings; sourceTree = "<group>"; };
|
330AF4D121C3396F7A14B21D /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||||
|
3339B1DDB1341E833D2555BC /* AVMetadataMachineReadableCodeObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVMetadataMachineReadableCodeObject.swift; sourceTree = "<group>"; };
|
||||||
3368395F06AA180138E185B6 /* PollFormScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenUITests.swift; sourceTree = "<group>"; };
|
3368395F06AA180138E185B6 /* PollFormScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenUITests.swift; sourceTree = "<group>"; };
|
||||||
33AE897D86784CCA5E4E9227 /* ElementCallService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallService.swift; sourceTree = "<group>"; };
|
33AE897D86784CCA5E4E9227 /* ElementCallService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallService.swift; sourceTree = "<group>"; };
|
||||||
33E49C5C6F802B4D94CA78D1 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
|
33E49C5C6F802B4D94CA78D1 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
@@ -1346,7 +1350,7 @@
|
|||||||
3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = "<group>"; };
|
3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = "<group>"; };
|
||||||
35AFCF4C05DEED04E3DB1A16 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
35AFCF4C05DEED04E3DB1A16 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
35FA991289149D31F4286747 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = "<group>"; };
|
35FA991289149D31F4286747 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = "<group>"; };
|
||||||
36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||||
36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = "<group>"; };
|
36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = "<group>"; };
|
||||||
376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerURLBuildersTests.swift; sourceTree = "<group>"; };
|
376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerURLBuildersTests.swift; sourceTree = "<group>"; };
|
||||||
37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = "<group>"; };
|
37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = "<group>"; };
|
||||||
@@ -1625,7 +1629,6 @@
|
|||||||
7F615A00DB223FF3280204D2 /* UserDiscoveryServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoveryServiceProtocol.swift; sourceTree = "<group>"; };
|
7F615A00DB223FF3280204D2 /* UserDiscoveryServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoveryServiceProtocol.swift; sourceTree = "<group>"; };
|
||||||
7FB2253D36E81E045E1CB432 /* Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Duration.swift; sourceTree = "<group>"; };
|
7FB2253D36E81E045E1CB432 /* Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Duration.swift; sourceTree = "<group>"; };
|
||||||
7FDF541AE914059942B575B4 /* IdentityConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreenModels.swift; sourceTree = "<group>"; };
|
7FDF541AE914059942B575B4 /* IdentityConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreenModels.swift; sourceTree = "<group>"; };
|
||||||
80A07343F18BB8EAC17B07B7 /* QRCodeLoginController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginController.swift; sourceTree = "<group>"; };
|
|
||||||
80C4927D09099497233E9980 /* WaitlistScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreen.swift; sourceTree = "<group>"; };
|
80C4927D09099497233E9980 /* WaitlistScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreen.swift; sourceTree = "<group>"; };
|
||||||
80E815FF3CC5E5A355E3A25E /* RoomMessageEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageEventStringBuilder.swift; sourceTree = "<group>"; };
|
80E815FF3CC5E5A355E3A25E /* RoomMessageEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageEventStringBuilder.swift; sourceTree = "<group>"; };
|
||||||
8166F121C79C7B62BF01D508 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pt; path = pt.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
8166F121C79C7B62BF01D508 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pt; path = pt.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
@@ -1681,7 +1684,7 @@
|
|||||||
8D55702474F279D910D2D162 /* RoomStateEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilder.swift; sourceTree = "<group>"; };
|
8D55702474F279D910D2D162 /* RoomStateEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilder.swift; sourceTree = "<group>"; };
|
||||||
8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = "<group>"; };
|
8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = "<group>"; };
|
||||||
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = "<group>"; };
|
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UITests.xctestplan; 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>"; };
|
||||||
@@ -1690,7 +1693,6 @@
|
|||||||
90791B9C739C716A40E1B230 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
90791B9C739C716A40E1B230 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||||
907FA4DE17DEA1A3738EFB83 /* AudioRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecorder.swift; sourceTree = "<group>"; };
|
907FA4DE17DEA1A3738EFB83 /* AudioRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecorder.swift; sourceTree = "<group>"; };
|
||||||
90A55430639712CFACA34F43 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.swift; sourceTree = "<group>"; };
|
90A55430639712CFACA34F43 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
90B4ED923603F6110D4960C5 /* QRCodeLoginServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginServiceMock.swift; sourceTree = "<group>"; };
|
|
||||||
90DFF217B3D9D0941283278C /* RoomRolesAndPermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
90DFF217B3D9D0941283278C /* RoomRolesAndPermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
90F2F8998E5632668B0AD848 /* RoomTimelineItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemView.swift; sourceTree = "<group>"; };
|
90F2F8998E5632668B0AD848 /* RoomTimelineItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemView.swift; sourceTree = "<group>"; };
|
||||||
913C8E13B8B602C7B6C0C4AE /* PillTextAttachmentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachmentData.swift; sourceTree = "<group>"; };
|
913C8E13B8B602C7B6C0C4AE /* PillTextAttachmentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachmentData.swift; sourceTree = "<group>"; };
|
||||||
@@ -1767,9 +1769,12 @@
|
|||||||
A436057DBEA1A23CA8CB1FD7 /* UIFont+AttributedStringBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIFont+AttributedStringBuilder.h"; sourceTree = "<group>"; };
|
A436057DBEA1A23CA8CB1FD7 /* UIFont+AttributedStringBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIFont+AttributedStringBuilder.h"; sourceTree = "<group>"; };
|
||||||
A58E93D91DE3288010390DEE /* EmojiDetectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiDetectionTests.swift; sourceTree = "<group>"; };
|
A58E93D91DE3288010390DEE /* EmojiDetectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiDetectionTests.swift; sourceTree = "<group>"; };
|
||||||
A65F140F9FE5E8D4DAEFF354 /* RoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomProxy.swift; sourceTree = "<group>"; };
|
A65F140F9FE5E8D4DAEFF354 /* RoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomProxy.swift; sourceTree = "<group>"; };
|
||||||
|
A69869844D2B6F5BD9AABF85 /* OIDCConfigurationProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCConfigurationProxy.swift; sourceTree = "<group>"; };
|
||||||
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = "<group>"; };
|
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = "<group>"; };
|
||||||
A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallWidgetDriverProtocol.swift; sourceTree = "<group>"; };
|
A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallWidgetDriverProtocol.swift; sourceTree = "<group>"; };
|
||||||
A73A07BAEDD74C48795A996A /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = "<group>"; };
|
A73A07BAEDD74C48795A996A /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = "<group>"; };
|
||||||
|
A78DD8682C0766B1005A32A9 /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVMetadataMachineReadableCodeObjectExtensionsTest.swift; sourceTree = "<group>"; };
|
||||||
|
A78DD86A2C076B3F005A32A9 /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = "<group>"; };
|
||||||
A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = "<group>"; };
|
A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
A7D452AF7B5F7E3A0A7DB54C /* SessionVerificationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
A7D452AF7B5F7E3A0A7DB54C /* SessionVerificationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
A84D413BF49F0E980F010A6B /* LogViewerScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewerScreenCoordinator.swift; sourceTree = "<group>"; };
|
A84D413BF49F0E980F010A6B /* LogViewerScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewerScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
@@ -1830,13 +1835,14 @@
|
|||||||
B40233F2989AD49906BB310D /* RoomPollsHistoryScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModelTests.swift; sourceTree = "<group>"; };
|
B40233F2989AD49906BB310D /* RoomPollsHistoryScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenModels.swift; sourceTree = "<group>"; };
|
B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenModels.swift; sourceTree = "<group>"; };
|
||||||
B43456E73F8A2D52B69B9FB9 /* TemplateScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModel.swift; sourceTree = "<group>"; };
|
B43456E73F8A2D52B69B9FB9 /* TemplateScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
B4427AF4B7FB7EF3E3D424C7 /* QRCodeLoginService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginService.swift; sourceTree = "<group>"; };
|
||||||
B48B7AD4908C5C374517B892 /* MapAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = MapAssets.xcassets; sourceTree = "<group>"; };
|
B48B7AD4908C5C374517B892 /* MapAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = MapAssets.xcassets; sourceTree = "<group>"; };
|
||||||
B4CFE236419E830E8946639C /* Analytics+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Analytics+SwiftUI.swift"; sourceTree = "<group>"; };
|
B4CFE236419E830E8946639C /* Analytics+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Analytics+SwiftUI.swift"; sourceTree = "<group>"; };
|
||||||
B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProxyProtocol.swift; sourceTree = "<group>"; };
|
B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProxyProtocol.swift; sourceTree = "<group>"; };
|
||||||
B53AC78E49A297AC1D72A7CF /* AppMediator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediator.swift; sourceTree = "<group>"; };
|
B53AC78E49A297AC1D72A7CF /* AppMediator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediator.swift; sourceTree = "<group>"; };
|
||||||
B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = "<group>"; };
|
B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = "<group>"; };
|
||||||
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = "<group>"; };
|
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = "<group>"; };
|
B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = ConfettiScene.scn; sourceTree = "<group>"; };
|
||||||
B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineView.swift; sourceTree = "<group>"; };
|
B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineView.swift; 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>"; };
|
||||||
@@ -1947,7 +1953,7 @@
|
|||||||
CE47A97726F0675DEE387BF9 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = "<group>"; };
|
CE47A97726F0675DEE387BF9 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = "<group>"; };
|
||||||
CEE0E6043EFCF6FD2A341861 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
|
CEE0E6043EFCF6FD2A341861 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
|
||||||
CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = "<group>"; };
|
CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||||
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
||||||
CF48AF076424DBC1615C74AD /* AuthenticationServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceProxy.swift; sourceTree = "<group>"; };
|
CF48AF076424DBC1615C74AD /* AuthenticationServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceProxy.swift; sourceTree = "<group>"; };
|
||||||
D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = "<group>"; };
|
D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = "<group>"; };
|
||||||
D086854995173E897F993C26 /* AdvancedSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
D086854995173E897F993C26 /* AdvancedSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
@@ -2070,7 +2076,7 @@
|
|||||||
ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomCell.swift; sourceTree = "<group>"; };
|
ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomCell.swift; sourceTree = "<group>"; };
|
||||||
ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
||||||
ED33988DA4FD4FC666800106 /* SessionVerificationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModel.swift; sourceTree = "<group>"; };
|
ED33988DA4FD4FC666800106 /* SessionVerificationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = "<group>"; };
|
ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = "<group>"; };
|
||||||
ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreen.swift; sourceTree = "<group>"; };
|
ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreen.swift; sourceTree = "<group>"; };
|
||||||
ED983D4DCA5AFA6E1ED96099 /* StateRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRoomTimelineView.swift; sourceTree = "<group>"; };
|
ED983D4DCA5AFA6E1ED96099 /* StateRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = "<group>"; };
|
EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
@@ -2093,7 +2099,7 @@
|
|||||||
F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenModels.swift; sourceTree = "<group>"; };
|
F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenModels.swift; sourceTree = "<group>"; };
|
||||||
F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = "<group>"; };
|
F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = "<group>"; };
|
||||||
F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineItem.swift; sourceTree = "<group>"; };
|
F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; path = portrait_test_video.mp4; sourceTree = "<group>"; };
|
F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = portrait_test_video.mp4; sourceTree = "<group>"; };
|
||||||
F2E4EF80DFB8FE7C4469B15D /* RoomDirectorySearchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreen.swift; sourceTree = "<group>"; };
|
F2E4EF80DFB8FE7C4469B15D /* RoomDirectorySearchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreen.swift; sourceTree = "<group>"; };
|
||||||
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = "<group>"; };
|
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = "<group>"; };
|
||||||
F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = "<group>"; };
|
F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
@@ -2659,7 +2665,6 @@
|
|||||||
382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */,
|
382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */,
|
||||||
B2AD8A56CD37E23071A2F4BF /* PHGPostHogMock.swift */,
|
B2AD8A56CD37E23071A2F4BF /* PHGPostHogMock.swift */,
|
||||||
D38391154120264910D19528 /* PollMock.swift */,
|
D38391154120264910D19528 /* PollMock.swift */,
|
||||||
90B4ED923603F6110D4960C5 /* QRCodeLoginServiceMock.swift */,
|
|
||||||
894EE8F5B399A165BA2A6634 /* RoomDirectorySearchMock.swift */,
|
894EE8F5B399A165BA2A6634 /* RoomDirectorySearchMock.swift */,
|
||||||
36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */,
|
36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */,
|
||||||
F5D1BAA90F3A073D91B4F16B /* RoomNotificationSettingsProxyMock.swift */,
|
F5D1BAA90F3A073D91B4F16B /* RoomNotificationSettingsProxyMock.swift */,
|
||||||
@@ -2977,6 +2982,7 @@
|
|||||||
3E93A1BE7D8A2EBCAD51EEB4 /* Array.swift */,
|
3E93A1BE7D8A2EBCAD51EEB4 /* Array.swift */,
|
||||||
A73A07BAEDD74C48795A996A /* AsyncSequence.swift */,
|
A73A07BAEDD74C48795A996A /* AsyncSequence.swift */,
|
||||||
52BD6ED18E2EB61E28C340AD /* AttributedString.swift */,
|
52BD6ED18E2EB61E28C340AD /* AttributedString.swift */,
|
||||||
|
3339B1DDB1341E833D2555BC /* AVMetadataMachineReadableCodeObject.swift */,
|
||||||
B6E89E530A8E92EC44301CA1 /* Bundle.swift */,
|
B6E89E530A8E92EC44301CA1 /* Bundle.swift */,
|
||||||
A9FAFE1C2149E6AC8156ED2B /* Collection.swift */,
|
A9FAFE1C2149E6AC8156ED2B /* Collection.swift */,
|
||||||
E2B1CC9AA154F4D5435BF60A /* Comparable.swift */,
|
E2B1CC9AA154F4D5435BF60A /* Comparable.swift */,
|
||||||
@@ -3007,6 +3013,7 @@
|
|||||||
AE40D4A5DD857AC16EED945A /* URLSession.swift */,
|
AE40D4A5DD857AC16EED945A /* URLSession.swift */,
|
||||||
897DF5E9A70CE05A632FC8AF /* UTType.swift */,
|
897DF5E9A70CE05A632FC8AF /* UTType.swift */,
|
||||||
E992D7B8BE54B2AB454613AF /* XCUIElement.swift */,
|
E992D7B8BE54B2AB454613AF /* XCUIElement.swift */,
|
||||||
|
A78DD86A2C076B3F005A32A9 /* Data.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -3474,7 +3481,7 @@
|
|||||||
70CC0CDA4AFDF8299C56ADE7 /* QRCode */ = {
|
70CC0CDA4AFDF8299C56ADE7 /* QRCode */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
80A07343F18BB8EAC17B07B7 /* QRCodeLoginController.swift */,
|
B4427AF4B7FB7EF3E3D424C7 /* QRCodeLoginService.swift */,
|
||||||
536C0E2178949B290776EA4E /* QRCodeLoginServiceProtocol.swift */,
|
536C0E2178949B290776EA4E /* QRCodeLoginServiceProtocol.swift */,
|
||||||
);
|
);
|
||||||
path = QRCode;
|
path = QRCode;
|
||||||
@@ -3600,6 +3607,7 @@
|
|||||||
7583EAC171059A86B767209F /* MediaProvider */,
|
7583EAC171059A86B767209F /* MediaProvider */,
|
||||||
7DBC911559934065993A5FF4 /* NotificationManager */,
|
7DBC911559934065993A5FF4 /* NotificationManager */,
|
||||||
1C62F5382CC9D9F7DCEC344A /* UserDiscoveryService */,
|
1C62F5382CC9D9F7DCEC344A /* UserDiscoveryService */,
|
||||||
|
A78DD8682C0766B1005A32A9 /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift */,
|
||||||
);
|
);
|
||||||
path = Sources;
|
path = Sources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -4338,6 +4346,7 @@
|
|||||||
CF48AF076424DBC1615C74AD /* AuthenticationServiceProxy.swift */,
|
CF48AF076424DBC1615C74AD /* AuthenticationServiceProxy.swift */,
|
||||||
4F0CB536D1C3CC15AA740CC6 /* AuthenticationServiceProxyProtocol.swift */,
|
4F0CB536D1C3CC15AA740CC6 /* AuthenticationServiceProxyProtocol.swift */,
|
||||||
65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */,
|
65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */,
|
||||||
|
A69869844D2B6F5BD9AABF85 /* OIDCConfigurationProxy.swift */,
|
||||||
);
|
);
|
||||||
path = Authentication;
|
path = Authentication;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -5767,6 +5776,7 @@
|
|||||||
CC0D088F505F33A20DC5590F /* RoomStateEventStringBuilderTests.swift in Sources */,
|
CC0D088F505F33A20DC5590F /* RoomStateEventStringBuilderTests.swift in Sources */,
|
||||||
7691233E3572A9173FD96CB3 /* SecureBackupKeyBackupScreenViewModelTests.swift in Sources */,
|
7691233E3572A9173FD96CB3 /* SecureBackupKeyBackupScreenViewModelTests.swift in Sources */,
|
||||||
EB87DF90CF6F8D5D12404C6E /* SecureBackupLogoutConfirmationScreenViewModelTests.swift in Sources */,
|
EB87DF90CF6F8D5D12404C6E /* SecureBackupLogoutConfirmationScreenViewModelTests.swift in Sources */,
|
||||||
|
A78DD8692C0766B1005A32A9 /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift in Sources */,
|
||||||
06B31F84CE52A7A7C271267C /* SecureBackupRecoveryKeyScreenViewModelTests.swift in Sources */,
|
06B31F84CE52A7A7C271267C /* SecureBackupRecoveryKeyScreenViewModelTests.swift in Sources */,
|
||||||
1B8E30B35BF8F541C1318F19 /* SecureBackupScreenViewModelTests.swift in Sources */,
|
1B8E30B35BF8F541C1318F19 /* SecureBackupScreenViewModelTests.swift in Sources */,
|
||||||
53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */,
|
53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */,
|
||||||
@@ -5812,6 +5822,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
41F553349AF44567184822D8 /* APNSPayload.swift in Sources */,
|
41F553349AF44567184822D8 /* APNSPayload.swift in Sources */,
|
||||||
|
CE3B7FC34FB2C279AAA5EA01 /* AVMetadataMachineReadableCodeObject.swift in Sources */,
|
||||||
70394ECD2DCC70741538620D /* AccessibilityIdentifiers.swift in Sources */,
|
70394ECD2DCC70741538620D /* AccessibilityIdentifiers.swift in Sources */,
|
||||||
34433A509DFEC93579B3B35B /* AdvancedSettingsScreen.swift in Sources */,
|
34433A509DFEC93579B3B35B /* AdvancedSettingsScreen.swift in Sources */,
|
||||||
4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */,
|
4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */,
|
||||||
@@ -6058,6 +6069,7 @@
|
|||||||
BFEB24336DFD5F196E6F3456 /* IntentionalMentions.swift in Sources */,
|
BFEB24336DFD5F196E6F3456 /* IntentionalMentions.swift in Sources */,
|
||||||
2AAB2A77F1762A2648078A30 /* InteractiveQuickLook.swift in Sources */,
|
2AAB2A77F1762A2648078A30 /* InteractiveQuickLook.swift in Sources */,
|
||||||
C4D2BCAA54E2C62B94B24AF4 /* InviteUsersScreen.swift in Sources */,
|
C4D2BCAA54E2C62B94B24AF4 /* InviteUsersScreen.swift in Sources */,
|
||||||
|
A78DD86B2C076B3F005A32A9 /* Data.swift in Sources */,
|
||||||
E27C4D1A1F8BB77CA790B403 /* InviteUsersScreenCoordinator.swift in Sources */,
|
E27C4D1A1F8BB77CA790B403 /* InviteUsersScreenCoordinator.swift in Sources */,
|
||||||
C26DB49C06C00B5DF1A991A5 /* InviteUsersScreenModels.swift in Sources */,
|
C26DB49C06C00B5DF1A991A5 /* InviteUsersScreenModels.swift in Sources */,
|
||||||
61941DEE5F3834765770BE01 /* InviteUsersScreenSelectedItem.swift in Sources */,
|
61941DEE5F3834765770BE01 /* InviteUsersScreenSelectedItem.swift in Sources */,
|
||||||
@@ -6185,6 +6197,7 @@
|
|||||||
CBD2ABE4C1A47ECD99E1488E /* NotificationSettingsScreenViewModelProtocol.swift in Sources */,
|
CBD2ABE4C1A47ECD99E1488E /* NotificationSettingsScreenViewModelProtocol.swift in Sources */,
|
||||||
523C6800ED85D5810CF18C19 /* OIDCAccountSettingsPresenter.swift in Sources */,
|
523C6800ED85D5810CF18C19 /* OIDCAccountSettingsPresenter.swift in Sources */,
|
||||||
9A4E3D5AA44B041DAC3A0D81 /* OIDCAuthenticationPresenter.swift in Sources */,
|
9A4E3D5AA44B041DAC3A0D81 /* OIDCAuthenticationPresenter.swift in Sources */,
|
||||||
|
E362924A42934C9F0F97A956 /* OIDCConfigurationProxy.swift in Sources */,
|
||||||
11A6B8E3CBDBF0A4107FF4CE /* OnboardingFlowCoordinator.swift in Sources */,
|
11A6B8E3CBDBF0A4107FF4CE /* OnboardingFlowCoordinator.swift in Sources */,
|
||||||
3CE4C5071B6D2576E2473989 /* OrderedSet.swift in Sources */,
|
3CE4C5071B6D2576E2473989 /* OrderedSet.swift in Sources */,
|
||||||
AA5924D3B67F7ACD98BBEFDC /* OrientationManagerProtocol.swift in Sources */,
|
AA5924D3B67F7ACD98BBEFDC /* OrientationManagerProtocol.swift in Sources */,
|
||||||
@@ -6224,13 +6237,12 @@
|
|||||||
C7ABEBECDC513F7887DACF66 /* ProgressMaskModifier.swift in Sources */,
|
C7ABEBECDC513F7887DACF66 /* ProgressMaskModifier.swift in Sources */,
|
||||||
9B356742E035D90A8BB5CABE /* ProposedViewSize.swift in Sources */,
|
9B356742E035D90A8BB5CABE /* ProposedViewSize.swift in Sources */,
|
||||||
2835FD52F3F618D07F799B3D /* Publisher.swift in Sources */,
|
2835FD52F3F618D07F799B3D /* Publisher.swift in Sources */,
|
||||||
538426B497672A097B212735 /* QRCodeLoginController.swift in Sources */,
|
|
||||||
4949C8C12669D1B5E082366E /* QRCodeLoginScreen.swift in Sources */,
|
4949C8C12669D1B5E082366E /* QRCodeLoginScreen.swift in Sources */,
|
||||||
4D23D41B8109E010304050F8 /* QRCodeLoginScreenCoordinator.swift in Sources */,
|
4D23D41B8109E010304050F8 /* QRCodeLoginScreenCoordinator.swift in Sources */,
|
||||||
46FCD999E92D9717D24AAB94 /* QRCodeLoginScreenModels.swift in Sources */,
|
46FCD999E92D9717D24AAB94 /* QRCodeLoginScreenModels.swift in Sources */,
|
||||||
30E5628F74AD3C27A061BF25 /* QRCodeLoginScreenViewModel.swift in Sources */,
|
30E5628F74AD3C27A061BF25 /* QRCodeLoginScreenViewModel.swift in Sources */,
|
||||||
E9D2ED1C4186931E3D5FDA4E /* QRCodeLoginScreenViewModelProtocol.swift in Sources */,
|
E9D2ED1C4186931E3D5FDA4E /* QRCodeLoginScreenViewModelProtocol.swift in Sources */,
|
||||||
90067BB37EBA60FA4AEF2DA3 /* QRCodeLoginServiceMock.swift in Sources */,
|
C8C7AF33AADF88B306CD2695 /* QRCodeLoginService.swift in Sources */,
|
||||||
BB04B1D8E7401C90506D401E /* QRCodeLoginServiceProtocol.swift in Sources */,
|
BB04B1D8E7401C90506D401E /* QRCodeLoginServiceProtocol.swift in Sources */,
|
||||||
FDD5B4B616D9FF4DE3E9A418 /* QRCodeScannerView.swift in Sources */,
|
FDD5B4B616D9FF4DE3E9A418 /* QRCodeScannerView.swift in Sources */,
|
||||||
9095B9E40DB5CF8BA26CE0D8 /* ReactionsSummaryView.swift in Sources */,
|
9095B9E40DB5CF8BA26CE0D8 /* ReactionsSummaryView.swift in Sources */,
|
||||||
@@ -6786,9 +6798,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
||||||
OTHER_SWIFT_FLAGS = (
|
OTHER_SWIFT_FLAGS = "-DIS_NSE";
|
||||||
"-DIS_NSE",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse";
|
PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse";
|
||||||
PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)";
|
PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)";
|
||||||
PRODUCT_NAME = NSE;
|
PRODUCT_NAME = NSE;
|
||||||
@@ -6837,9 +6847,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
||||||
OTHER_SWIFT_FLAGS = (
|
OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP";
|
||||||
"-DIS_MAIN_APP",
|
|
||||||
);
|
|
||||||
PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills";
|
PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)";
|
||||||
PRODUCT_NAME = "$(APP_NAME)";
|
PRODUCT_NAME = "$(APP_NAME)";
|
||||||
@@ -6865,9 +6873,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
||||||
OTHER_SWIFT_FLAGS = (
|
OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP";
|
||||||
"-DIS_MAIN_APP",
|
|
||||||
);
|
|
||||||
PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills";
|
PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)";
|
||||||
PRODUCT_NAME = "$(APP_NAME)";
|
PRODUCT_NAME = "$(APP_NAME)";
|
||||||
@@ -7112,9 +7118,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
||||||
OTHER_SWIFT_FLAGS = (
|
OTHER_SWIFT_FLAGS = "-DIS_NSE";
|
||||||
"-DIS_NSE",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse";
|
PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse";
|
||||||
PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)";
|
PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)";
|
||||||
PRODUCT_NAME = NSE;
|
PRODUCT_NAME = NSE;
|
||||||
@@ -7323,7 +7327,7 @@
|
|||||||
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
|
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
|
||||||
requirement = {
|
requirement = {
|
||||||
kind = exactVersion;
|
kind = exactVersion;
|
||||||
version = 1.0.4;
|
version = 1.0.6;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
|
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
|
||||||
|
|||||||
@@ -139,8 +139,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
|
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "100598671d3e6186f77e2f48630e9cbcb63fd86b",
|
"revision" : "510689e69b36de3c6bb8313b92f2709e58d2e80f",
|
||||||
"version" : "1.0.4"
|
"version" : "1.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -500,6 +500,18 @@
|
|||||||
"screen_qr_code_login_connection_note_secure_state_title" = "Connection not secure";
|
"screen_qr_code_login_connection_note_secure_state_title" = "Connection not secure";
|
||||||
"screen_qr_code_login_device_code_subtitle" = "You’ll be asked to enter the two digits shown on this device.";
|
"screen_qr_code_login_device_code_subtitle" = "You’ll be asked to enter the two digits shown on this device.";
|
||||||
"screen_qr_code_login_device_code_title" = "Enter the number below on your other device";
|
"screen_qr_code_login_device_code_title" = "Enter the number below on your other device";
|
||||||
|
"screen_qr_code_login_device_not_signed_in_scan_state_description" = "Sign in to your other device and then try again, or use another device that’s already signed in.";
|
||||||
|
"screen_qr_code_login_device_not_signed_in_scan_state_subtitle" = "Other device not signed in";
|
||||||
|
"screen_qr_code_login_error_cancelled_subtitle" = "The sign in was cancelled on the other device.";
|
||||||
|
"screen_qr_code_login_error_cancelled_title" = "Sign in request cancelled";
|
||||||
|
"screen_qr_code_login_error_declined_subtitle" = "The request on your other device was not accepted.";
|
||||||
|
"screen_qr_code_login_error_declined_title" = "Sign in declined";
|
||||||
|
"screen_qr_code_login_error_expired_subtitle" = "Sign in expired. Please try again.";
|
||||||
|
"screen_qr_code_login_error_expired_title" = "The sign in was not completed in time";
|
||||||
|
"screen_qr_code_login_error_linking_not_suported_subtitle" = "Your other device does not support signing in to %@ with a QR code.\n\nTry signing in manually, or scan the QR code with another device.";
|
||||||
|
"screen_qr_code_login_error_linking_not_suported_title" = "QR code not supported";
|
||||||
|
"screen_qr_code_login_error_sliding_sync_not_supported_subtitle" = "Your account provider does not support %1$@.";
|
||||||
|
"screen_qr_code_login_error_sliding_sync_not_supported_title" = "%1$@ not supported";
|
||||||
"screen_qr_code_login_initial_state_button_title" = "Ready to scan";
|
"screen_qr_code_login_initial_state_button_title" = "Ready to scan";
|
||||||
"screen_qr_code_login_initial_state_item_1" = "Open %1$@ on a desktop device";
|
"screen_qr_code_login_initial_state_item_1" = "Open %1$@ on a desktop device";
|
||||||
"screen_qr_code_login_initial_state_item_2" = "Click on your avatar";
|
"screen_qr_code_login_initial_state_item_2" = "Click on your avatar";
|
||||||
|
|||||||
@@ -444,10 +444,16 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startAuthentication() {
|
private func startAuthentication() {
|
||||||
|
let encryptionKeyProvider = EncryptionKeyProvider()
|
||||||
let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore,
|
let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore,
|
||||||
encryptionKeyProvider: EncryptionKeyProvider(),
|
encryptionKeyProvider: encryptionKeyProvider,
|
||||||
appSettings: appSettings)
|
appSettings: appSettings)
|
||||||
|
let qrCodeLoginService = QRCodeLoginService(oidcConfiguration: appSettings.oidcConfiguration.rustValue,
|
||||||
|
encryptionKeyProvider: encryptionKeyProvider,
|
||||||
|
userSessionStore: userSessionStore)
|
||||||
|
|
||||||
authenticationFlowCoordinator = AuthenticationFlowCoordinator(authenticationService: authenticationService,
|
authenticationFlowCoordinator = AuthenticationFlowCoordinator(authenticationService: authenticationService,
|
||||||
|
qrCodeLoginService: qrCodeLoginService,
|
||||||
bugReportService: ServiceLocator.shared.bugReportService,
|
bugReportService: ServiceLocator.shared.bugReportService,
|
||||||
navigationRootCoordinator: navigationRootCoordinator,
|
navigationRootCoordinator: navigationRootCoordinator,
|
||||||
appMediator: appMediator,
|
appMediator: appMediator,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import AVFoundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class AppMediator: AppMediatorProtocol {
|
class AppMediator: AppMediatorProtocol {
|
||||||
@@ -64,4 +65,22 @@ class AppMediator: AppMediatorProtocol {
|
|||||||
func setIdleTimerDisabled(_ disabled: Bool) {
|
func setIdleTimerDisabled(_ disabled: Bool) {
|
||||||
application.isIdleTimerDisabled = disabled
|
application.isIdleTimerDisabled = disabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requestAuthorizationIfNeeded() async -> Bool {
|
||||||
|
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
||||||
|
|
||||||
|
// Determine if the user previously authorized camera access.
|
||||||
|
if status == .authorized {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var isAuthorized = false
|
||||||
|
// If the system hasn't determined the user's authorization status,
|
||||||
|
// explicitly prompt them for approval.
|
||||||
|
if status == .notDetermined {
|
||||||
|
isAuthorized = await AVCaptureDevice.requestAccess(for: .video)
|
||||||
|
}
|
||||||
|
|
||||||
|
return isAuthorized
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ protocol AppMediatorProtocol {
|
|||||||
func openAppSettings()
|
func openAppSettings()
|
||||||
|
|
||||||
func setIdleTimerDisabled(_ disabled: Bool)
|
func setIdleTimerDisabled(_ disabled: Bool)
|
||||||
|
|
||||||
|
func requestAuthorizationIfNeeded() async -> Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UIApplication.State: CustomStringConvertible {
|
extension UIApplication.State: CustomStringConvertible {
|
||||||
|
|||||||
@@ -154,6 +154,15 @@ final class AppSettings {
|
|||||||
return url
|
return url
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private(set) lazy var oidcConfiguration = OIDCConfigurationProxy(clientName: InfoPlistReader.main.bundleDisplayName,
|
||||||
|
redirectURI: oidcRedirectURL,
|
||||||
|
clientURI: websiteURL,
|
||||||
|
logoURI: logoURL,
|
||||||
|
tosURI: acceptableUseURL,
|
||||||
|
policyURI: privacyURL,
|
||||||
|
contacts: [supportEmailAddress],
|
||||||
|
staticRegistrations: oidcStaticRegistrations.mapKeys { $0.absoluteString })
|
||||||
|
|
||||||
/// A dictionary of accounts that have performed an initial sync through their proxy.
|
/// A dictionary of accounts that have performed an initial sync through their proxy.
|
||||||
///
|
///
|
||||||
/// This is a temporary workaround. In the future we should be able to receive a signal from the
|
/// This is a temporary workaround. In the future we should be able to receive a signal from the
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
private let appSettings: AppSettings
|
private let appSettings: AppSettings
|
||||||
private let analytics: AnalyticsService
|
private let analytics: AnalyticsService
|
||||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
|
private let qrCodeLoginService: QRCodeLoginServiceProtocol
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
weak var delegate: AuthenticationFlowCoordinatorDelegate?
|
weak var delegate: AuthenticationFlowCoordinatorDelegate?
|
||||||
|
|
||||||
init(authenticationService: AuthenticationServiceProxyProtocol,
|
init(authenticationService: AuthenticationServiceProxyProtocol,
|
||||||
|
qrCodeLoginService: QRCodeLoginServiceProtocol,
|
||||||
bugReportService: BugReportServiceProtocol,
|
bugReportService: BugReportServiceProtocol,
|
||||||
navigationRootCoordinator: NavigationRootCoordinator,
|
navigationRootCoordinator: NavigationRootCoordinator,
|
||||||
appMediator: AppMediatorProtocol,
|
appMediator: AppMediatorProtocol,
|
||||||
@@ -55,6 +57,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
self.analytics = analytics
|
self.analytics = analytics
|
||||||
self.userIndicatorController = userIndicatorController
|
self.userIndicatorController = userIndicatorController
|
||||||
|
self.qrCodeLoginService = qrCodeLoginService
|
||||||
|
|
||||||
navigationStackCoordinator = NavigationStackCoordinator()
|
navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
}
|
}
|
||||||
@@ -106,7 +109,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startQRCodeLogin() {
|
private func startQRCodeLogin() {
|
||||||
let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: QRCodeLoginService(),
|
let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: qrCodeLoginService,
|
||||||
orientationManager: appMediator.windowManager,
|
orientationManager: appMediator.windowManager,
|
||||||
appMediator: appMediator))
|
appMediator: appMediator))
|
||||||
coordinator.actionsPublisher.sink { [weak self] action in
|
coordinator.actionsPublisher.sink { [weak self] action in
|
||||||
@@ -114,8 +117,18 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch action {
|
switch action {
|
||||||
|
case .signInManually:
|
||||||
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
Task { await self.startAuthentication() }
|
||||||
case .cancel:
|
case .cancel:
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
case .done(let userSession):
|
||||||
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
// Since the qr code login flow includes verification
|
||||||
|
appSettings.hasRunIdentityConfirmationOnboarding = true
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.userHasSignedIn(userSession: userSession)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -87,9 +87,9 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
configureStateMachine()
|
configureStateMachine()
|
||||||
|
|
||||||
stateMachine.tryEvent(.next)
|
|
||||||
|
|
||||||
rootNavigationStackCoordinator.setFullScreenCoverCoordinator(navigationStackCoordinator, animated: !isNewLogin)
|
rootNavigationStackCoordinator.setFullScreenCoverCoordinator(navigationStackCoordinator, animated: !isNewLogin)
|
||||||
|
|
||||||
|
stateMachine.tryEvent(.next)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||||
@@ -134,6 +134,8 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
return .analyticsPrompt
|
return .analyticsPrompt
|
||||||
case (.initial, false, false, false, true):
|
case (.initial, false, false, false, true):
|
||||||
return .notificationPermissions
|
return .notificationPermissions
|
||||||
|
case (.initial, false, false, false, false):
|
||||||
|
return .finished
|
||||||
|
|
||||||
case (.identityConfirmation, _, _, _, _):
|
case (.identityConfirmation, _, _, _, _):
|
||||||
return .identityConfirmed
|
return .identityConfirmed
|
||||||
|
|||||||
@@ -1221,6 +1221,38 @@ internal enum L10n {
|
|||||||
internal static var screenQrCodeLoginDeviceCodeSubtitle: String { return L10n.tr("Localizable", "screen_qr_code_login_device_code_subtitle") }
|
internal static var screenQrCodeLoginDeviceCodeSubtitle: String { return L10n.tr("Localizable", "screen_qr_code_login_device_code_subtitle") }
|
||||||
/// Enter the number below on your other device
|
/// Enter the number below on your other device
|
||||||
internal static var screenQrCodeLoginDeviceCodeTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_device_code_title") }
|
internal static var screenQrCodeLoginDeviceCodeTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_device_code_title") }
|
||||||
|
/// Sign in to your other device and then try again, or use another device that’s already signed in.
|
||||||
|
internal static var screenQrCodeLoginDeviceNotSignedInScanStateDescription: String { return L10n.tr("Localizable", "screen_qr_code_login_device_not_signed_in_scan_state_description") }
|
||||||
|
/// Other device not signed in
|
||||||
|
internal static var screenQrCodeLoginDeviceNotSignedInScanStateSubtitle: String { return L10n.tr("Localizable", "screen_qr_code_login_device_not_signed_in_scan_state_subtitle") }
|
||||||
|
/// The sign in was cancelled on the other device.
|
||||||
|
internal static var screenQrCodeLoginErrorCancelledSubtitle: String { return L10n.tr("Localizable", "screen_qr_code_login_error_cancelled_subtitle") }
|
||||||
|
/// Sign in request cancelled
|
||||||
|
internal static var screenQrCodeLoginErrorCancelledTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_error_cancelled_title") }
|
||||||
|
/// The request on your other device was not accepted.
|
||||||
|
internal static var screenQrCodeLoginErrorDeclinedSubtitle: String { return L10n.tr("Localizable", "screen_qr_code_login_error_declined_subtitle") }
|
||||||
|
/// Sign in declined
|
||||||
|
internal static var screenQrCodeLoginErrorDeclinedTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_error_declined_title") }
|
||||||
|
/// Sign in expired. Please try again.
|
||||||
|
internal static var screenQrCodeLoginErrorExpiredSubtitle: String { return L10n.tr("Localizable", "screen_qr_code_login_error_expired_subtitle") }
|
||||||
|
/// The sign in was not completed in time
|
||||||
|
internal static var screenQrCodeLoginErrorExpiredTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_error_expired_title") }
|
||||||
|
/// Your other device does not support signing in to %@ with a QR code.
|
||||||
|
///
|
||||||
|
/// Try signing in manually, or scan the QR code with another device.
|
||||||
|
internal static func screenQrCodeLoginErrorLinkingNotSuportedSubtitle(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "screen_qr_code_login_error_linking_not_suported_subtitle", String(describing: p1))
|
||||||
|
}
|
||||||
|
/// QR code not supported
|
||||||
|
internal static var screenQrCodeLoginErrorLinkingNotSuportedTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_error_linking_not_suported_title") }
|
||||||
|
/// Your account provider does not support %1$@.
|
||||||
|
internal static func screenQrCodeLoginErrorSlidingSyncNotSupportedSubtitle(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "screen_qr_code_login_error_sliding_sync_not_supported_subtitle", String(describing: p1))
|
||||||
|
}
|
||||||
|
/// %1$@ not supported
|
||||||
|
internal static func screenQrCodeLoginErrorSlidingSyncNotSupportedTitle(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "screen_qr_code_login_error_sliding_sync_not_supported_title", String(describing: p1))
|
||||||
|
}
|
||||||
/// Ready to scan
|
/// Ready to scan
|
||||||
internal static var screenQrCodeLoginInitialStateButtonTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_button_title") }
|
internal static var screenQrCodeLoginInitialStateButtonTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_button_title") }
|
||||||
/// Open %1$@ on a desktop device
|
/// Open %1$@ on a desktop device
|
||||||
|
|||||||
@@ -17,10 +17,11 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
extension AppMediatorMock {
|
extension AppMediatorMock {
|
||||||
static var `default`: AppMediatorProtocol {
|
static var `default`: AppMediatorMock {
|
||||||
let mock = AppMediatorMock()
|
let mock = AppMediatorMock()
|
||||||
|
|
||||||
mock.underlyingAppState = .active
|
mock.underlyingAppState = .active
|
||||||
|
mock.requestAuthorizationIfNeededUnderlyingReturnValue = true
|
||||||
mock.underlyingWindowManager = WindowManagerMock()
|
mock.underlyingWindowManager = WindowManagerMock()
|
||||||
|
|
||||||
return mock
|
return mock
|
||||||
|
|||||||
@@ -1014,6 +1014,70 @@ class AppMediatorMock: AppMediatorProtocol {
|
|||||||
setIdleTimerDisabledReceivedInvocations.append(disabled)
|
setIdleTimerDisabledReceivedInvocations.append(disabled)
|
||||||
setIdleTimerDisabledClosure?(disabled)
|
setIdleTimerDisabledClosure?(disabled)
|
||||||
}
|
}
|
||||||
|
//MARK: - requestAuthorizationIfNeeded
|
||||||
|
|
||||||
|
var requestAuthorizationIfNeededUnderlyingCallsCount = 0
|
||||||
|
var requestAuthorizationIfNeededCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return requestAuthorizationIfNeededUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = requestAuthorizationIfNeededUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
requestAuthorizationIfNeededUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
requestAuthorizationIfNeededUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var requestAuthorizationIfNeededCalled: Bool {
|
||||||
|
return requestAuthorizationIfNeededCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestAuthorizationIfNeededUnderlyingReturnValue: Bool!
|
||||||
|
var requestAuthorizationIfNeededReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return requestAuthorizationIfNeededUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = requestAuthorizationIfNeededUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
requestAuthorizationIfNeededUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
requestAuthorizationIfNeededUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var requestAuthorizationIfNeededClosure: (() async -> Bool)?
|
||||||
|
|
||||||
|
func requestAuthorizationIfNeeded() async -> Bool {
|
||||||
|
requestAuthorizationIfNeededCallsCount += 1
|
||||||
|
if let requestAuthorizationIfNeededClosure = requestAuthorizationIfNeededClosure {
|
||||||
|
return await requestAuthorizationIfNeededClosure()
|
||||||
|
} else {
|
||||||
|
return requestAuthorizationIfNeededReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class AudioConverterMock: AudioConverterProtocol {
|
class AudioConverterMock: AudioConverterProtocol {
|
||||||
|
|
||||||
@@ -7203,18 +7267,23 @@ class PollInteractionHandlerMock: PollInteractionHandlerProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
class QRCodeLoginServiceMock: QRCodeLoginServiceProtocol {
|
class QRCodeLoginServiceMock: QRCodeLoginServiceProtocol {
|
||||||
|
var qrLoginProgressPublisher: AnyPublisher<QrLoginProgress, Never> {
|
||||||
|
get { return underlyingQrLoginProgressPublisher }
|
||||||
|
set(value) { underlyingQrLoginProgressPublisher = value }
|
||||||
|
}
|
||||||
|
var underlyingQrLoginProgressPublisher: AnyPublisher<QrLoginProgress, Never>!
|
||||||
|
|
||||||
//MARK: - requestAuthorizationIfNeeded
|
//MARK: - loginWithQRCode
|
||||||
|
|
||||||
var requestAuthorizationIfNeededUnderlyingCallsCount = 0
|
var loginWithQRCodeDataUnderlyingCallsCount = 0
|
||||||
var requestAuthorizationIfNeededCallsCount: Int {
|
var loginWithQRCodeDataCallsCount: Int {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return requestAuthorizationIfNeededUnderlyingCallsCount
|
return loginWithQRCodeDataUnderlyingCallsCount
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Int? = nil
|
var returnValue: Int? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = requestAuthorizationIfNeededUnderlyingCallsCount
|
returnValue = loginWithQRCodeDataUnderlyingCallsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@@ -7222,27 +7291,29 @@ class QRCodeLoginServiceMock: QRCodeLoginServiceProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
requestAuthorizationIfNeededUnderlyingCallsCount = newValue
|
loginWithQRCodeDataUnderlyingCallsCount = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
requestAuthorizationIfNeededUnderlyingCallsCount = newValue
|
loginWithQRCodeDataUnderlyingCallsCount = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var requestAuthorizationIfNeededCalled: Bool {
|
var loginWithQRCodeDataCalled: Bool {
|
||||||
return requestAuthorizationIfNeededCallsCount > 0
|
return loginWithQRCodeDataCallsCount > 0
|
||||||
}
|
}
|
||||||
|
var loginWithQRCodeDataReceivedData: Data?
|
||||||
|
var loginWithQRCodeDataReceivedInvocations: [Data] = []
|
||||||
|
|
||||||
var requestAuthorizationIfNeededUnderlyingReturnValue: Bool!
|
var loginWithQRCodeDataUnderlyingReturnValue: Result<UserSessionProtocol, QRCodeLoginServiceError>!
|
||||||
var requestAuthorizationIfNeededReturnValue: Bool! {
|
var loginWithQRCodeDataReturnValue: Result<UserSessionProtocol, QRCodeLoginServiceError>! {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return requestAuthorizationIfNeededUnderlyingReturnValue
|
return loginWithQRCodeDataUnderlyingReturnValue
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Bool? = nil
|
var returnValue: Result<UserSessionProtocol, QRCodeLoginServiceError>? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = requestAuthorizationIfNeededUnderlyingReturnValue
|
returnValue = loginWithQRCodeDataUnderlyingReturnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@@ -7250,22 +7321,24 @@ class QRCodeLoginServiceMock: QRCodeLoginServiceProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
requestAuthorizationIfNeededUnderlyingReturnValue = newValue
|
loginWithQRCodeDataUnderlyingReturnValue = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
requestAuthorizationIfNeededUnderlyingReturnValue = newValue
|
loginWithQRCodeDataUnderlyingReturnValue = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var requestAuthorizationIfNeededClosure: (() async -> Bool)?
|
var loginWithQRCodeDataClosure: ((Data) async -> Result<UserSessionProtocol, QRCodeLoginServiceError>)?
|
||||||
|
|
||||||
func requestAuthorizationIfNeeded() async -> Bool {
|
func loginWithQRCode(data: Data) async -> Result<UserSessionProtocol, QRCodeLoginServiceError> {
|
||||||
requestAuthorizationIfNeededCallsCount += 1
|
loginWithQRCodeDataCallsCount += 1
|
||||||
if let requestAuthorizationIfNeededClosure = requestAuthorizationIfNeededClosure {
|
loginWithQRCodeDataReceivedData = data
|
||||||
return await requestAuthorizationIfNeededClosure()
|
loginWithQRCodeDataReceivedInvocations.append(data)
|
||||||
|
if let loginWithQRCodeDataClosure = loginWithQRCodeDataClosure {
|
||||||
|
return await loginWithQRCodeDataClosure(data)
|
||||||
} else {
|
} else {
|
||||||
return requestAuthorizationIfNeededReturnValue
|
return loginWithQRCodeDataReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2024 New Vector Ltd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
struct QRCodeLoginServiceMockConfiguration {
|
|
||||||
var isAuthorized = true
|
|
||||||
}
|
|
||||||
|
|
||||||
extension QRCodeLoginServiceMock {
|
|
||||||
convenience init(configuration: QRCodeLoginServiceMockConfiguration) {
|
|
||||||
self.init()
|
|
||||||
requestAuthorizationIfNeededReturnValue = configuration.isAuthorized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Helpers to remove ECI headers from QR Code raw data
|
||||||
|
// https://gist.github.com/PetrusM/267e2ee8c1d8b5dca17eac085afa7d7c
|
||||||
|
import AVKit
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension AVMetadataMachineReadableCodeObject {
|
||||||
|
var binaryValue: Data? {
|
||||||
|
switch type {
|
||||||
|
case .qr:
|
||||||
|
guard let binaryValueWithProtocol,
|
||||||
|
let symbolVersion = (descriptor as? CIQRCodeDescriptor)?.symbolVersion else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
MXLog.info("QR code raw data: \(binaryValueWithProtocol.map { String(format: "%02x", $0) }.joined())")
|
||||||
|
MXLog.info("QR code symbol version: \(symbolVersion)")
|
||||||
|
return Self.removeQrProtocolData(binaryValueWithProtocol, symbolVersion: symbolVersion)
|
||||||
|
case .aztec:
|
||||||
|
guard let string = stringValue
|
||||||
|
else { return nil }
|
||||||
|
return string.data(using: String.Encoding.isoLatin1)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var binaryValueWithProtocol: Data? {
|
||||||
|
guard let descriptor else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch type {
|
||||||
|
case .qr:
|
||||||
|
return (descriptor as? CIQRCodeDescriptor)?.errorCorrectedPayload
|
||||||
|
case .aztec:
|
||||||
|
return (descriptor as? CIAztecCodeDescriptor)?.errorCorrectedPayload
|
||||||
|
case .pdf417:
|
||||||
|
return (descriptor as? CIPDF417CodeDescriptor)?.errorCorrectedPayload
|
||||||
|
case .dataMatrix:
|
||||||
|
return (descriptor as? CIDataMatrixCodeDescriptor)?.errorCorrectedPayload
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func removeQrProtocolData(_ input: Data, symbolVersion: Int) -> Data? {
|
||||||
|
var halves = input.halfBytes()
|
||||||
|
var batch = takeBatch(&halves, version: symbolVersion)
|
||||||
|
var output = batch
|
||||||
|
while !batch.isEmpty {
|
||||||
|
batch = takeBatch(&halves, version: symbolVersion)
|
||||||
|
output.append(contentsOf: batch)
|
||||||
|
}
|
||||||
|
let data = Data(output)
|
||||||
|
MXLog.info("QR code decoded binary data: \(data.map { String(format: "%02x", $0) }.joined())")
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func takeBatch(_ input: inout [HalfByte], version: Int) -> [UInt8] {
|
||||||
|
let characterCountLength = version > 9 ? 16 : 8
|
||||||
|
let mode = input.remove(at: 0)
|
||||||
|
var output = [UInt8]()
|
||||||
|
switch mode.value {
|
||||||
|
// If there is not only binary in the QRCode, then cases should be added here.
|
||||||
|
case 0x04: // Binary
|
||||||
|
let charactersCount: UInt16
|
||||||
|
if characterCountLength == 8 {
|
||||||
|
charactersCount = UInt16(input.takeUInt8())
|
||||||
|
} else {
|
||||||
|
charactersCount = UInt16(input.takeUInt16())
|
||||||
|
}
|
||||||
|
for _ in 0..<charactersCount {
|
||||||
|
output.append(input.takeUInt8())
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
case 0x00: // End of data
|
||||||
|
return []
|
||||||
|
default:
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct HalfByte {
|
||||||
|
let value: UInt8
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension [HalfByte] {
|
||||||
|
mutating func takeUInt8() -> UInt8 {
|
||||||
|
let left = remove(at: 0)
|
||||||
|
let right = remove(at: 0)
|
||||||
|
return UInt8(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func takeUInt16() -> UInt16 {
|
||||||
|
let first = remove(at: 0)
|
||||||
|
let second = remove(at: 0)
|
||||||
|
let third = remove(at: 0)
|
||||||
|
let fourth = remove(at: 0)
|
||||||
|
return UInt16(first, second, third, fourth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension Data {
|
||||||
|
func halfBytes() -> [HalfByte] {
|
||||||
|
var result = [HalfByte]()
|
||||||
|
forEach { (byte: UInt8) in
|
||||||
|
result.append(contentsOf: byte.halfBytes())
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_ halves: [HalfByte]) {
|
||||||
|
var halves = halves
|
||||||
|
var result = [UInt8]()
|
||||||
|
while halves.count > 1 {
|
||||||
|
result.append(halves.takeUInt8())
|
||||||
|
}
|
||||||
|
self.init(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension UInt8 {
|
||||||
|
func halfBytes() -> [HalfByte] {
|
||||||
|
[HalfByte(value: self >> 4), HalfByte(value: self & 0x0F)]
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_ left: HalfByte, _ right: HalfByte) {
|
||||||
|
self.init((left.value << 4) + (right.value & 0x0F))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension UInt16 {
|
||||||
|
init(_ first: HalfByte, _ second: HalfByte, _ third: HalfByte, _ fourth: HalfByte) {
|
||||||
|
let first = UInt16(first.value) << 12
|
||||||
|
let second = UInt16(second.value) << 8
|
||||||
|
let third = UInt16(third.value) << 4
|
||||||
|
let fourth = UInt16(fourth.value) & 0x0F
|
||||||
|
let result = first + second + third + fourth
|
||||||
|
self.init(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Data {
|
||||||
|
init?(hexString: String) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
var hex = hexString
|
||||||
|
|
||||||
|
// If the hex string has an odd length, prepend a zero
|
||||||
|
if hex.count % 2 != 0 {
|
||||||
|
hex = "0" + hex
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in stride(from: 0, to: hex.count, by: 2) {
|
||||||
|
let startIndex = hex.index(hex.startIndex, offsetBy: i)
|
||||||
|
let endIndex = hex.index(hex.startIndex, offsetBy: i + 2)
|
||||||
|
let byteString = String(hex[startIndex..<endIndex])
|
||||||
|
|
||||||
|
if let byte = UInt8(byteString, radix: 16) {
|
||||||
|
append(byte)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
@@ -31,11 +31,15 @@ class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType
|
|||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
super.init(initialViewState: AuthenticationStartScreenViewState())
|
super.init(initialViewState: AuthenticationStartScreenViewState())
|
||||||
if !ProcessInfo.processInfo.isiOSAppOnMac {
|
if !ProcessInfo.processInfo.isiOSAppOnMac {
|
||||||
|
if appSettings.isDevelopmentBuild {
|
||||||
|
state.isQRCodeLoginEnabled = true
|
||||||
|
} else {
|
||||||
appSettings.$qrCodeLoginEnabled
|
appSettings.$qrCodeLoginEnabled
|
||||||
.weakAssign(to: \.state.isQRCodeLoginEnabled, on: self)
|
.weakAssign(to: \.state.isQRCodeLoginEnabled, on: self)
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func process(viewAction: AuthenticationStartScreenViewAction) {
|
override func process(viewAction: AuthenticationStartScreenViewAction) {
|
||||||
switch viewAction {
|
switch viewAction {
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ struct QRCodeLoginScreenCoordinatorParameters {
|
|||||||
|
|
||||||
enum QRCodeLoginScreenCoordinatorAction {
|
enum QRCodeLoginScreenCoordinatorAction {
|
||||||
case cancel
|
case cancel
|
||||||
|
case signInManually
|
||||||
|
case done(userSession: UserSessionProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class QRCodeLoginScreenCoordinator: CoordinatorProtocol {
|
final class QRCodeLoginScreenCoordinator: CoordinatorProtocol {
|
||||||
@@ -52,8 +54,12 @@ final class QRCodeLoginScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
|
case .signInManually:
|
||||||
|
self.actionsSubject.send(.signInManually)
|
||||||
case .cancel:
|
case .cancel:
|
||||||
self.actionsSubject.send(.cancel)
|
self.actionsSubject.send(.cancel)
|
||||||
|
case .done(let userSession):
|
||||||
|
self.actionsSubject.send(.done(userSession: userSession))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import Foundation
|
|||||||
|
|
||||||
enum QRCodeLoginScreenViewModelAction {
|
enum QRCodeLoginScreenViewModelAction {
|
||||||
case cancel
|
case cancel
|
||||||
|
case signInManually
|
||||||
|
case done(userSession: UserSessionProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct QRCodeLoginScreenViewState: BindableState {
|
struct QRCodeLoginScreenViewState: BindableState {
|
||||||
@@ -44,11 +46,18 @@ struct QRCodeLoginScreenViewState: BindableState {
|
|||||||
AttributedString(L10n.screenQrCodeLoginConnectionNoteSecureStateListItem2),
|
AttributedString(L10n.screenQrCodeLoginConnectionNoteSecureStateListItem2),
|
||||||
AttributedString(L10n.screenQrCodeLoginConnectionNoteSecureStateListItem3)
|
AttributedString(L10n.screenQrCodeLoginConnectionNoteSecureStateListItem3)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
var bindings = QRCodeLoginScreenViewStateBindings()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct QRCodeLoginScreenViewStateBindings {
|
||||||
|
var qrResult: Data?
|
||||||
}
|
}
|
||||||
|
|
||||||
enum QRCodeLoginScreenViewAction {
|
enum QRCodeLoginScreenViewAction {
|
||||||
case cancel
|
case cancel
|
||||||
case startScan
|
case startScan
|
||||||
|
case signInManually
|
||||||
case openSettings
|
case openSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +74,11 @@ enum QRCodeLoginState: Equatable {
|
|||||||
enum QRCodeLoginErrorState: Equatable {
|
enum QRCodeLoginErrorState: Equatable {
|
||||||
case noCameraPermission
|
case noCameraPermission
|
||||||
case connectionNotSecure
|
case connectionNotSecure
|
||||||
|
case cancelled
|
||||||
|
case declined
|
||||||
|
case expired
|
||||||
|
case linkingNotSupported
|
||||||
|
case deviceNotSupported
|
||||||
case unknown
|
case unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +89,8 @@ enum QRCodeLoginState: Equatable {
|
|||||||
case connecting
|
case connecting
|
||||||
/// the qr code has been processed and is invalid
|
/// the qr code has been processed and is invalid
|
||||||
case invalid
|
case invalid
|
||||||
|
/// the qr code has been processed but it belongs to a device not signed in,
|
||||||
|
case deviceNotSignedIn
|
||||||
}
|
}
|
||||||
|
|
||||||
enum QRCodeLoginDisplayCodeState: Equatable {
|
enum QRCodeLoginDisplayCodeState: Equatable {
|
||||||
@@ -99,4 +115,42 @@ enum QRCodeLoginState: Equatable {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isScanning: Bool {
|
||||||
|
switch self {
|
||||||
|
case .scan(let state):
|
||||||
|
return state == .scanning
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isError: Bool {
|
||||||
|
switch self {
|
||||||
|
case .error:
|
||||||
|
return true
|
||||||
|
case let .scan(state):
|
||||||
|
switch state {
|
||||||
|
case .invalid, .deviceNotSignedIn:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var shouldDisplayCancelButton: Bool {
|
||||||
|
switch self {
|
||||||
|
case .initial:
|
||||||
|
return true
|
||||||
|
case .scan:
|
||||||
|
return true
|
||||||
|
case .error(let error):
|
||||||
|
return error == .noCameraPermission
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AVFoundation
|
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
import Foundation
|
||||||
|
|
||||||
typealias QRCodeLoginScreenViewModelType = StateStoreViewModel<QRCodeLoginScreenViewState, QRCodeLoginScreenViewAction>
|
typealias QRCodeLoginScreenViewModelType = StateStoreViewModel<QRCodeLoginScreenViewState, QRCodeLoginScreenViewAction>
|
||||||
|
|
||||||
@@ -29,11 +28,14 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
|
|||||||
actionsSubject.eraseToAnyPublisher()
|
actionsSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var scanTask: Task<Void, Never>?
|
||||||
|
|
||||||
init(qrCodeLoginService: QRCodeLoginServiceProtocol,
|
init(qrCodeLoginService: QRCodeLoginServiceProtocol,
|
||||||
appMediator: AppMediatorProtocol) {
|
appMediator: AppMediatorProtocol) {
|
||||||
self.qrCodeLoginService = qrCodeLoginService
|
self.qrCodeLoginService = qrCodeLoginService
|
||||||
self.appMediator = appMediator
|
self.appMediator = appMediator
|
||||||
super.init(initialViewState: QRCodeLoginScreenViewState())
|
super.init(initialViewState: QRCodeLoginScreenViewState())
|
||||||
|
setupSubscriptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
@@ -46,16 +48,109 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
|
|||||||
Task { await startScanIfPossible() }
|
Task { await startScanIfPossible() }
|
||||||
case .openSettings:
|
case .openSettings:
|
||||||
appMediator.openAppSettings()
|
appMediator.openAppSettings()
|
||||||
|
case .signInManually:
|
||||||
|
actionsSubject.send(.signInManually)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func setupSubscriptions() {
|
||||||
|
context.$viewState
|
||||||
|
// not using compactMap before remove duplicates because if there is an error, and the same code needs to be rescanned the transition to nil to clean the state would get ignored.
|
||||||
|
.map(\.bindings.qrResult)
|
||||||
|
.removeDuplicates()
|
||||||
|
.compactMap { $0 }
|
||||||
|
// this needs to be received on the main actor or the state change for connecting won't work properly
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] qrData in
|
||||||
|
self?.handleScan(qrData: qrData)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
qrCodeLoginService.qrLoginProgressPublisher
|
||||||
|
.removeDuplicates()
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] progress in
|
||||||
|
MXLog.info("QR Login Progress changed to: \(progress)")
|
||||||
|
|
||||||
|
guard let self,
|
||||||
|
// Let's not advance the state if the current state is already invalid
|
||||||
|
!state.state.isError else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch progress {
|
||||||
|
case .establishingSecureChannel(_, let stringCode):
|
||||||
|
state.state = .displayCode(.deviceCode(stringCode))
|
||||||
|
case .waitingForToken(let code):
|
||||||
|
state.state = .displayCode(.verificationCode(code))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
private func startScanIfPossible() async {
|
private func startScanIfPossible() async {
|
||||||
state.state = await qrCodeLoginService.requestAuthorizationIfNeeded() ? .scan(.scanning) : .error(.noCameraPermission)
|
state.bindings.qrResult = nil
|
||||||
|
state.state = await appMediator.requestAuthorizationIfNeeded() ? .scan(.scanning) : .error(.noCameraPermission)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleScan(qrData: Data) {
|
||||||
|
guard scanTask == nil else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.state = .scan(.connecting)
|
||||||
|
|
||||||
|
scanTask = Task { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer {
|
||||||
|
scanTask = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
MXLog.info("Scanning QR code: \(qrData)")
|
||||||
|
switch await qrCodeLoginService.loginWithQRCode(data: qrData) {
|
||||||
|
case let .success(session):
|
||||||
|
MXLog.info("QR Login completed")
|
||||||
|
actionsSubject.send(.done(userSession: session))
|
||||||
|
case .failure(let error):
|
||||||
|
handleError(error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleError(error: QRCodeLoginServiceError) {
|
||||||
|
MXLog.error("Failed to scan the QR code: \(error)")
|
||||||
|
switch error {
|
||||||
|
case .invalidQRCode:
|
||||||
|
state.state = .scan(.invalid)
|
||||||
|
case .deviceNotSignedIn:
|
||||||
|
state.state = .scan(.deviceNotSignedIn)
|
||||||
|
case .cancelled:
|
||||||
|
state.state = .error(.cancelled)
|
||||||
|
case .connectionInsecure:
|
||||||
|
state.state = .error(.connectionNotSecure)
|
||||||
|
case .declined:
|
||||||
|
state.state = .error(.declined)
|
||||||
|
case .linkingNotSupported:
|
||||||
|
state.state = .error(.linkingNotSupported)
|
||||||
|
case .expired:
|
||||||
|
state.state = .error(.expired)
|
||||||
|
case .deviceNotSupported:
|
||||||
|
state.state = .error(.deviceNotSupported)
|
||||||
|
case .failedLoggingIn, .unknown:
|
||||||
|
state.state = .error(.unknown)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only for mocking initial states
|
/// Only for mocking initial states
|
||||||
fileprivate init(state: QRCodeLoginState) {
|
fileprivate init(state: QRCodeLoginState) {
|
||||||
qrCodeLoginService = QRCodeLoginServiceMock(configuration: .init())
|
qrCodeLoginService = QRCodeLoginServiceMock()
|
||||||
appMediator = AppMediatorMock.default
|
appMediator = AppMediatorMock.default
|
||||||
super.init(initialViewState: .init(state: state))
|
super.init(initialViewState: .init(state: state))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ struct QRCodeLoginScreen: View {
|
|||||||
SFNumberedListView(items: context.viewState.initialStateListItems)
|
SFNumberedListView(items: context.viewState.initialStateListItems)
|
||||||
}
|
}
|
||||||
} bottomContent: {
|
} bottomContent: {
|
||||||
Button(L10n.actionContinue) {
|
Button(L10n.screenQrCodeLoginInitialStateButtonTitle) {
|
||||||
context.send(viewAction: .startScan)
|
context.send(viewAction: .startScan)
|
||||||
}
|
}
|
||||||
.buttonStyle(.compound(.primary))
|
.buttonStyle(.compound(.primary))
|
||||||
@@ -178,8 +178,11 @@ struct QRCodeLoginScreen: View {
|
|||||||
}
|
}
|
||||||
.buttonStyle(.compound(.primary))
|
.buttonStyle(.compound(.primary))
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 4) {
|
||||||
Label(L10n.screenQrCodeLoginInvalidScanStateSubtitle, icon: \.error, iconSize: .medium, relativeTo: .compound.bodyMDSemibold)
|
Label(L10n.screenQrCodeLoginInvalidScanStateSubtitle,
|
||||||
|
icon: \.error,
|
||||||
|
iconSize: .medium,
|
||||||
|
relativeTo: .compound.bodyMDSemibold)
|
||||||
.labelStyle(.custom(spacing: 10))
|
.labelStyle(.custom(spacing: 10))
|
||||||
.font(.compound.bodyMDSemibold)
|
.font(.compound.bodyMDSemibold)
|
||||||
.foregroundColor(.compound.textCriticalPrimary)
|
.foregroundColor(.compound.textCriticalPrimary)
|
||||||
@@ -187,6 +190,29 @@ struct QRCodeLoginScreen: View {
|
|||||||
Text(L10n.screenQrCodeLoginInvalidScanStateDescription)
|
Text(L10n.screenQrCodeLoginInvalidScanStateDescription)
|
||||||
.foregroundColor(.compound.textSecondary)
|
.foregroundColor(.compound.textSecondary)
|
||||||
.font(.compound.bodySM)
|
.font(.compound.bodySM)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .deviceNotSignedIn:
|
||||||
|
VStack(spacing: 16) {
|
||||||
|
Button(L10n.screenQrCodeLoginInvalidScanStateRetryButton) {
|
||||||
|
context.send(viewAction: .startScan)
|
||||||
|
}
|
||||||
|
.buttonStyle(.compound(.primary))
|
||||||
|
|
||||||
|
VStack(spacing: 4) {
|
||||||
|
Label(L10n.screenQrCodeLoginDeviceNotSignedInScanStateSubtitle,
|
||||||
|
icon: \.error,
|
||||||
|
iconSize: .medium,
|
||||||
|
relativeTo: .compound.bodyMDSemibold)
|
||||||
|
.labelStyle(.custom(spacing: 10))
|
||||||
|
.font(.compound.bodyMDSemibold)
|
||||||
|
.foregroundColor(.compound.textCriticalPrimary)
|
||||||
|
|
||||||
|
Text(L10n.screenQrCodeLoginDeviceNotSignedInScanStateDescription)
|
||||||
|
.foregroundColor(.compound.textSecondary)
|
||||||
|
.font(.compound.bodySM)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +220,7 @@ struct QRCodeLoginScreen: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var qrScanner: some View {
|
private var qrScanner: some View {
|
||||||
QRCodeScannerView()
|
QRCodeScannerView(result: $context.qrResult, isScanning: context.viewState.state.isScanning)
|
||||||
.aspectRatio(1.0, contentMode: .fill)
|
.aspectRatio(1.0, contentMode: .fill)
|
||||||
.frame(maxWidth: 312)
|
.frame(maxWidth: 312)
|
||||||
.readFrame($qrFrame)
|
.readFrame($qrFrame)
|
||||||
@@ -207,7 +233,7 @@ struct QRCodeLoginScreen: View {
|
|||||||
@ToolbarContentBuilder
|
@ToolbarContentBuilder
|
||||||
private var toolbar: some ToolbarContent {
|
private var toolbar: some ToolbarContent {
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
if !context.viewState.state.isDisplayingCode {
|
if context.viewState.state.shouldDisplayCancelButton {
|
||||||
Button(L10n.actionCancel) {
|
Button(L10n.actionCancel) {
|
||||||
context.send(viewAction: .cancel)
|
context.send(viewAction: .cancel)
|
||||||
}
|
}
|
||||||
@@ -273,25 +299,65 @@ struct QRCodeLoginScreen: View {
|
|||||||
SFNumberedListView(items: context.viewState.connectionNotSecureListItems)
|
SFNumberedListView(items: context.viewState.connectionNotSecureListItems)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
simpleErrorStack(errorState: errorState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private func simpleErrorStack(errorState: QRCodeLoginState.QRCodeLoginErrorState) -> some View {
|
||||||
|
let title = switch errorState {
|
||||||
|
case .cancelled:
|
||||||
|
L10n.screenQrCodeLoginErrorCancelledTitle
|
||||||
|
case .declined:
|
||||||
|
L10n.screenQrCodeLoginErrorDeclinedTitle
|
||||||
|
case .expired:
|
||||||
|
L10n.screenQrCodeLoginErrorExpiredTitle
|
||||||
|
case .linkingNotSupported:
|
||||||
|
L10n.screenQrCodeLoginErrorLinkingNotSuportedTitle
|
||||||
|
case .deviceNotSupported:
|
||||||
|
L10n.screenQrCodeLoginErrorSlidingSyncNotSupportedTitle(InfoPlistReader.main.bundleDisplayName)
|
||||||
case .unknown:
|
case .unknown:
|
||||||
|
L10n.commonSomethingWentWrong
|
||||||
|
default:
|
||||||
|
fatalError("This should not be displayed")
|
||||||
|
}
|
||||||
|
|
||||||
|
let subtitle: String = switch errorState {
|
||||||
|
case .cancelled:
|
||||||
|
L10n.screenQrCodeLoginErrorCancelledSubtitle
|
||||||
|
case .declined:
|
||||||
|
L10n.screenQrCodeLoginErrorDeclinedSubtitle
|
||||||
|
case .expired:
|
||||||
|
L10n.screenQrCodeLoginErrorExpiredSubtitle
|
||||||
|
case .linkingNotSupported:
|
||||||
|
L10n.screenQrCodeLoginErrorLinkingNotSuportedSubtitle(InfoPlistReader.main.bundleDisplayName)
|
||||||
|
case .deviceNotSupported:
|
||||||
|
L10n.screenQrCodeLoginErrorSlidingSyncNotSupportedSubtitle(InfoPlistReader.main.bundleDisplayName)
|
||||||
|
case .unknown:
|
||||||
|
L10n.screenQrCodeLoginUnknownErrorDescription
|
||||||
|
default:
|
||||||
|
fatalError("This should not be displayed")
|
||||||
|
}
|
||||||
|
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
HeroImage(icon: \.error, style: .critical)
|
HeroImage(icon: \.error, style: .critical)
|
||||||
|
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
Text(L10n.commonSomethingWentWrong)
|
Text(title)
|
||||||
.foregroundColor(.compound.textPrimary)
|
.foregroundColor(.compound.textPrimary)
|
||||||
.font(.compound.headingMDBold)
|
.font(.compound.headingMDBold)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|
||||||
Text(L10n.screenQrCodeLoginUnknownErrorDescription)
|
Text(subtitle)
|
||||||
.foregroundColor(.compound.textSecondary)
|
.foregroundColor(.compound.textSecondary)
|
||||||
.font(.compound.bodyMD)
|
.font(.compound.bodyMD)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
private func errorContentFooter(errorState: QRCodeLoginState.QRCodeLoginErrorState) -> some View {
|
private func errorContentFooter(errorState: QRCodeLoginState.QRCodeLoginErrorState) -> some View {
|
||||||
switch errorState {
|
switch errorState {
|
||||||
case .noCameraPermission:
|
case .noCameraPermission:
|
||||||
@@ -299,11 +365,30 @@ struct QRCodeLoginScreen: View {
|
|||||||
context.send(viewAction: .openSettings)
|
context.send(viewAction: .openSettings)
|
||||||
}
|
}
|
||||||
.buttonStyle(.compound(.primary))
|
.buttonStyle(.compound(.primary))
|
||||||
case .connectionNotSecure, .unknown:
|
case .connectionNotSecure, .unknown, .expired, .declined, .deviceNotSupported:
|
||||||
Button(L10n.screenQrCodeLoginStartOverButton) {
|
Button(L10n.screenQrCodeLoginStartOverButton) {
|
||||||
context.send(viewAction: .startScan)
|
context.send(viewAction: .startScan)
|
||||||
}
|
}
|
||||||
.buttonStyle(.compound(.primary))
|
.buttonStyle(.compound(.primary))
|
||||||
|
case .cancelled:
|
||||||
|
Button(L10n.actionTryAgain) {
|
||||||
|
context.send(viewAction: .startScan)
|
||||||
|
}
|
||||||
|
.buttonStyle(.compound(.primary))
|
||||||
|
case .linkingNotSupported:
|
||||||
|
VStack(spacing: 16) {
|
||||||
|
Button(L10n.screenOnboardingSignInManually) {
|
||||||
|
context.send(viewAction: .signInManually)
|
||||||
|
}
|
||||||
|
.buttonStyle(.compound(.primary))
|
||||||
|
|
||||||
|
Button(L10n.actionCancel) {
|
||||||
|
context.send(viewAction: .cancel)
|
||||||
|
}
|
||||||
|
.padding(.vertical, 13)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.buttonStyle(.compound(.plain))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,24 +421,40 @@ private struct QRScannerViewOverlay: View {
|
|||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
|
||||||
struct QRCodeLoginScreen_Previews: PreviewProvider, TestablePreview {
|
struct QRCodeLoginScreen_Previews: PreviewProvider, TestablePreview {
|
||||||
|
// Initial
|
||||||
static let initialStateViewModel = QRCodeLoginScreenViewModel.mock(state: .initial)
|
static let initialStateViewModel = QRCodeLoginScreenViewModel.mock(state: .initial)
|
||||||
|
|
||||||
|
// Scanning
|
||||||
static let scanningStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.scanning))
|
static let scanningStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.scanning))
|
||||||
|
|
||||||
static let connectingStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.connecting))
|
static let connectingStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.connecting))
|
||||||
|
|
||||||
static let invalidStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.invalid))
|
static let invalidStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.invalid))
|
||||||
|
|
||||||
|
static let deviceNotSignedInStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.deviceNotSignedIn))
|
||||||
|
|
||||||
|
// Display Code
|
||||||
|
static let deviceCodeStateViewModel = QRCodeLoginScreenViewModel.mock(state: .displayCode(.deviceCode("12")))
|
||||||
|
|
||||||
|
static let verificationCodeStateViewModel = QRCodeLoginScreenViewModel.mock(state: .displayCode(.verificationCode("123456")))
|
||||||
|
|
||||||
|
// Errors
|
||||||
static let noCameraPermissionStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.noCameraPermission))
|
static let noCameraPermissionStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.noCameraPermission))
|
||||||
|
|
||||||
static let connectionNotSecureStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.connectionNotSecure))
|
static let connectionNotSecureStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.connectionNotSecure))
|
||||||
|
|
||||||
|
static let linkingUnsupportedStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.linkingNotSupported))
|
||||||
|
|
||||||
|
static let cancelledStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.cancelled))
|
||||||
|
|
||||||
|
static let declinedStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.declined))
|
||||||
|
|
||||||
|
static let expiredStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.expired))
|
||||||
|
|
||||||
|
static let deviceNoSupportedViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.deviceNotSupported))
|
||||||
|
|
||||||
static let unknownErrorStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.unknown))
|
static let unknownErrorStateViewModel = QRCodeLoginScreenViewModel.mock(state: .error(.unknown))
|
||||||
|
|
||||||
static let deviceCodeStateViewModel = QRCodeLoginScreenViewModel.mock(state: .displayCode(.deviceCode("12")))
|
|
||||||
|
|
||||||
static let verificationCodeStateViewModel = QRCodeLoginScreenViewModel.mock(state: .displayCode(.verificationCode("123456")))
|
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
QRCodeLoginScreen(context: initialStateViewModel.context)
|
QRCodeLoginScreen(context: initialStateViewModel.context)
|
||||||
.previewDisplayName("Initial")
|
.previewDisplayName("Initial")
|
||||||
@@ -367,19 +468,37 @@ struct QRCodeLoginScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
QRCodeLoginScreen(context: invalidStateViewModel.context)
|
QRCodeLoginScreen(context: invalidStateViewModel.context)
|
||||||
.previewDisplayName("Invalid")
|
.previewDisplayName("Invalid")
|
||||||
|
|
||||||
QRCodeLoginScreen(context: noCameraPermissionStateViewModel.context)
|
QRCodeLoginScreen(context: deviceNotSignedInStateViewModel.context)
|
||||||
.previewDisplayName("No Camera Permission")
|
.previewDisplayName("Device not signed in")
|
||||||
|
|
||||||
QRCodeLoginScreen(context: connectionNotSecureStateViewModel.context)
|
|
||||||
.previewDisplayName("Connection not secure")
|
|
||||||
|
|
||||||
QRCodeLoginScreen(context: unknownErrorStateViewModel.context)
|
|
||||||
.previewDisplayName("Unknown error")
|
|
||||||
|
|
||||||
QRCodeLoginScreen(context: deviceCodeStateViewModel.context)
|
QRCodeLoginScreen(context: deviceCodeStateViewModel.context)
|
||||||
.previewDisplayName("Device code")
|
.previewDisplayName("Device code")
|
||||||
|
|
||||||
QRCodeLoginScreen(context: verificationCodeStateViewModel.context)
|
QRCodeLoginScreen(context: verificationCodeStateViewModel.context)
|
||||||
.previewDisplayName("Verification code")
|
.previewDisplayName("Verification code")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: noCameraPermissionStateViewModel.context)
|
||||||
|
.previewDisplayName("No Camera Permission")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: connectionNotSecureStateViewModel.context)
|
||||||
|
.previewDisplayName("Connection not secure")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: linkingUnsupportedStateViewModel.context)
|
||||||
|
.previewDisplayName("Linking unsupported")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: cancelledStateViewModel.context)
|
||||||
|
.previewDisplayName("Cancelled")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: declinedStateViewModel.context)
|
||||||
|
.previewDisplayName("Declined")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: expiredStateViewModel.context)
|
||||||
|
.previewDisplayName("Expired")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: deviceNoSupportedViewModel.context)
|
||||||
|
.previewDisplayName("Device not supported")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: unknownErrorStateViewModel.context)
|
||||||
|
.previewDisplayName("Unknown error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,48 @@ import SwiftUI
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
struct QRCodeScannerView: UIViewControllerRepresentable {
|
struct QRCodeScannerView: UIViewControllerRepresentable {
|
||||||
|
@Binding var result: Data?
|
||||||
|
var isScanning: Bool
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> QRScannerController {
|
func makeUIViewController(context: Context) -> QRScannerController {
|
||||||
let controller = QRScannerController()
|
let controller = QRScannerController()
|
||||||
|
controller.delegate = context.coordinator
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIViewController(_ uiViewController: QRScannerController, context: Context) { }
|
func updateUIViewController(_ uiViewController: QRScannerController, context: Context) {
|
||||||
|
if isScanning {
|
||||||
|
uiViewController.startScan()
|
||||||
|
} else {
|
||||||
|
uiViewController.stopScan()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator($result)
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate {
|
||||||
|
@Binding var scanResult: Data?
|
||||||
|
|
||||||
|
init(_ scanResult: Binding<Data?>) {
|
||||||
|
_scanResult = scanResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
|
||||||
|
// Check if the metadataObjects array is not nil and it contains at least one object.
|
||||||
|
guard metadataObjects.count > 0,
|
||||||
|
let metadataObj = metadataObjects[0] as? AVMetadataMachineReadableCodeObject,
|
||||||
|
metadataObj.type == AVMetadataObject.ObjectType.qr,
|
||||||
|
let data = metadataObj.binaryValue else {
|
||||||
|
MXLog.info("QRCodeScannerView: invalid qr scan")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
scanResult = data
|
||||||
|
MXLog.info("QRCodeScannerView: scanned data")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class QRScannerController: UIViewController {
|
final class QRScannerController: UIViewController {
|
||||||
@@ -33,7 +68,7 @@ final class QRScannerController: UIViewController {
|
|||||||
private var videoPreviewLayer: AVCaptureVideoPreviewLayer?
|
private var videoPreviewLayer: AVCaptureVideoPreviewLayer?
|
||||||
private var qrCodeFrameView: UIView?
|
private var qrCodeFrameView: UIView?
|
||||||
|
|
||||||
var delegate: AVCaptureMetadataOutputObjectsDelegate?
|
weak var delegate: AVCaptureMetadataOutputObjectsDelegate?
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
@@ -73,15 +108,26 @@ final class QRScannerController: UIViewController {
|
|||||||
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
||||||
videoPreviewLayer?.frame = view.layer.bounds
|
videoPreviewLayer?.frame = view.layer.bounds
|
||||||
view.layer.addSublayer(previewLayer)
|
view.layer.addSublayer(previewLayer)
|
||||||
|
|
||||||
// Start video capture.
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
|
||||||
self.captureSession.startRunning()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillLayoutSubviews() {
|
override func viewWillLayoutSubviews() {
|
||||||
super.viewWillLayoutSubviews()
|
super.viewWillLayoutSubviews()
|
||||||
videoPreviewLayer?.frame = view.layer.bounds
|
videoPreviewLayer?.frame = view.layer.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func startScan() {
|
||||||
|
// Start video capture.
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
self.captureSession.startRunning()
|
||||||
|
MXLog.info("QRCodeScannerView: capture session started")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopScan() {
|
||||||
|
// Stop video capture.
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
self.captureSession.stopRunning()
|
||||||
|
MXLog.info("QRCodeScannerView: capture session stopped")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-6
@@ -39,12 +39,6 @@ struct DeveloperOptionsScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section("QR Code") {
|
|
||||||
Toggle(isOn: $context.qrCodeLoginEnabled) {
|
|
||||||
Text("QR code login")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Section("Room") {
|
Section("Room") {
|
||||||
Toggle(isOn: $context.shouldCollapseRoomStateEvents) {
|
Toggle(isOn: $context.shouldCollapseRoomStateEvents) {
|
||||||
Text("Collapse room state events")
|
Text("Collapse room state events")
|
||||||
|
|||||||
@@ -33,21 +33,12 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
|||||||
homeserverSubject = .init(LoginHomeserver(address: appSettings.defaultHomeserverAddress,
|
homeserverSubject = .init(LoginHomeserver(address: appSettings.defaultHomeserverAddress,
|
||||||
loginMode: .unknown))
|
loginMode: .unknown))
|
||||||
|
|
||||||
let oidcConfiguration = OidcConfiguration(clientName: InfoPlistReader.main.bundleDisplayName,
|
|
||||||
redirectUri: appSettings.oidcRedirectURL.absoluteString,
|
|
||||||
clientUri: appSettings.websiteURL.absoluteString,
|
|
||||||
logoUri: appSettings.logoURL.absoluteString,
|
|
||||||
tosUri: appSettings.acceptableUseURL.absoluteString,
|
|
||||||
policyUri: appSettings.privacyURL.absoluteString,
|
|
||||||
contacts: [appSettings.supportEmailAddress],
|
|
||||||
staticRegistrations: appSettings.oidcStaticRegistrations.mapKeys { $0.absoluteString })
|
|
||||||
|
|
||||||
authenticationService = AuthenticationService(basePath: userSessionStore.baseDirectory.path,
|
authenticationService = AuthenticationService(basePath: userSessionStore.baseDirectory.path,
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
userAgent: UserAgentBuilder.makeASCIIUserAgent(),
|
userAgent: UserAgentBuilder.makeASCIIUserAgent(),
|
||||||
additionalRootCertificates: [],
|
additionalRootCertificates: [],
|
||||||
proxy: appSettings.websiteURL.globalProxy,
|
proxy: appSettings.websiteURL.globalProxy,
|
||||||
oidcConfiguration: oidcConfiguration,
|
oidcConfiguration: appSettings.oidcConfiguration.rustValue,
|
||||||
customSlidingSyncProxy: appSettings.slidingSyncProxyURL?.absoluteString,
|
customSlidingSyncProxy: appSettings.slidingSyncProxyURL?.absoluteString,
|
||||||
sessionDelegate: userSessionStore.clientSessionDelegate,
|
sessionDelegate: userSessionStore.clientSessionDelegate,
|
||||||
crossProcessRefreshLockId: InfoPlistReader.main.bundleIdentifier)
|
crossProcessRefreshLockId: InfoPlistReader.main.bundleIdentifier)
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct OIDCConfigurationProxy {
|
||||||
|
let clientName: String
|
||||||
|
let redirectURI: URL
|
||||||
|
let clientURI: URL
|
||||||
|
let logoURI: URL
|
||||||
|
let tosURI: URL
|
||||||
|
let policyURI: URL
|
||||||
|
let contacts: [String]
|
||||||
|
let staticRegistrations: [String: String]
|
||||||
|
}
|
||||||
|
|
||||||
|
#if canImport(MatrixRustSDK)
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
extension OIDCConfigurationProxy {
|
||||||
|
var rustValue: OidcConfiguration {
|
||||||
|
OidcConfiguration(clientName: clientName,
|
||||||
|
redirectUri: redirectURI.absoluteString,
|
||||||
|
clientUri: clientURI.absoluteString,
|
||||||
|
logoUri: logoURI.absoluteString,
|
||||||
|
tosUri: tosURI.absoluteString,
|
||||||
|
policyUri: policyURI.absoluteString,
|
||||||
|
contacts: contacts,
|
||||||
|
staticRegistrations: staticRegistrations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2024 New Vector Ltd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AVFoundation
|
|
||||||
|
|
||||||
final class QRCodeLoginService: QRCodeLoginServiceProtocol {
|
|
||||||
func requestAuthorizationIfNeeded() async -> Bool {
|
|
||||||
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
|
||||||
|
|
||||||
// Determine if the user previously authorized camera access.
|
|
||||||
if status == .authorized {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
var isAuthorized = false
|
|
||||||
// If the system hasn't determined the user's authorization status,
|
|
||||||
// explicitly prompt them for approval.
|
|
||||||
if status == .notDetermined {
|
|
||||||
isAuthorized = await AVCaptureDevice.requestAccess(for: .video)
|
|
||||||
}
|
|
||||||
|
|
||||||
return isAuthorized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
final class QRCodeLoginService: QRCodeLoginServiceProtocol {
|
||||||
|
private let oidcConfiguration: OidcConfiguration
|
||||||
|
private let passphrase: String
|
||||||
|
private let userSessionStore: UserSessionStoreProtocol
|
||||||
|
|
||||||
|
private let qrLoginProgressSubject = PassthroughSubject<QrLoginProgress, Never>()
|
||||||
|
var qrLoginProgressPublisher: AnyPublisher<QrLoginProgress, Never> {
|
||||||
|
qrLoginProgressSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
init(oidcConfiguration: OidcConfiguration,
|
||||||
|
encryptionKeyProvider: EncryptionKeyProviderProtocol,
|
||||||
|
userSessionStore: UserSessionStoreProtocol) {
|
||||||
|
self.oidcConfiguration = oidcConfiguration
|
||||||
|
self.userSessionStore = userSessionStore
|
||||||
|
passphrase = encryptionKeyProvider.generateKey().base64EncodedString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func loginWithQRCode(data: Data) async -> Result<UserSessionProtocol, QRCodeLoginServiceError> {
|
||||||
|
let qrData: QrCodeData
|
||||||
|
do {
|
||||||
|
qrData = try QrCodeData.fromBytes(bytes: data)
|
||||||
|
} catch {
|
||||||
|
MXLog.error("QRCode decode error: \(error)")
|
||||||
|
return .failure(.invalidQRCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
let listener = QrLoginProgressListenerProxy { [weak self] progress in
|
||||||
|
self?.qrLoginProgressSubject.send(progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let client = try await ClientBuilder()
|
||||||
|
.basePath(path: userSessionStore.baseDirectory.path(percentEncoded: false))
|
||||||
|
.passphrase(passphrase: passphrase)
|
||||||
|
.userAgent(userAgent: UserAgentBuilder.makeASCIIUserAgent())
|
||||||
|
.enableCrossProcessRefreshLock(processId: InfoPlistReader.main.bundleIdentifier,
|
||||||
|
sessionDelegate: userSessionStore.clientSessionDelegate)
|
||||||
|
.serverVersions(versions: ["v1.0", "v1.1", "v1.2", "v1.3", "v1.4", "v1.5"]) // FIXME: Quick and dirty fix for stopping version requests on startup https://github.com/matrix-org/matrix-rust-sdk/pull/1376
|
||||||
|
.buildWithQrCode(qrCodeData: qrData, oidcConfiguration: oidcConfiguration, progressListener: listener)
|
||||||
|
return await login(client: client)
|
||||||
|
} catch let error as HumanQrLoginError {
|
||||||
|
MXLog.error("QRCode login error: \(error)")
|
||||||
|
return .failure(error.serviceError)
|
||||||
|
} catch {
|
||||||
|
MXLog.error("QRCode login unknown error: \(error)")
|
||||||
|
return .failure(.unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func login(client: Client) async -> Result<UserSessionProtocol, QRCodeLoginServiceError> {
|
||||||
|
switch await userSessionStore.userSession(for: client, passphrase: passphrase) {
|
||||||
|
case .success(let session):
|
||||||
|
return .success(session)
|
||||||
|
case .failure(let error):
|
||||||
|
MXLog.error("QRCode login failed error: \(error)")
|
||||||
|
return .failure(.failedLoggingIn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class QrLoginProgressListenerProxy: QrLoginProgressListener {
|
||||||
|
private let onUpdateClosure: (QrLoginProgress) -> Void
|
||||||
|
|
||||||
|
init(onUpdateClosure: @escaping (QrLoginProgress) -> Void) {
|
||||||
|
self.onUpdateClosure = onUpdateClosure
|
||||||
|
}
|
||||||
|
|
||||||
|
func onUpdate(state: QrLoginProgress) {
|
||||||
|
onUpdateClosure(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension HumanQrLoginError {
|
||||||
|
var serviceError: QRCodeLoginServiceError {
|
||||||
|
switch self {
|
||||||
|
case .Cancelled:
|
||||||
|
return .cancelled
|
||||||
|
case .ConnectionInsecure:
|
||||||
|
return .connectionInsecure
|
||||||
|
case .Declined:
|
||||||
|
return .declined
|
||||||
|
case .LinkingNotSupported:
|
||||||
|
return .linkingNotSupported
|
||||||
|
case .Expired:
|
||||||
|
return .expired
|
||||||
|
case .InvalidQrCode:
|
||||||
|
return .invalidQRCode
|
||||||
|
case .SlidingSyncNotAvailable:
|
||||||
|
return .deviceNotSupported
|
||||||
|
case .OtherDeviceNotSignedIn:
|
||||||
|
return .deviceNotSignedIn
|
||||||
|
case .Unknown, .OidcMetadataInvalid:
|
||||||
|
return .unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,27 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
enum QRCodeLoginServiceError: Error {
|
||||||
|
case failedLoggingIn
|
||||||
|
case invalidQRCode
|
||||||
|
case cancelled
|
||||||
|
case connectionInsecure
|
||||||
|
case declined
|
||||||
|
case linkingNotSupported
|
||||||
|
case expired
|
||||||
|
case deviceNotSupported
|
||||||
|
case deviceNotSignedIn
|
||||||
|
case unknown
|
||||||
|
}
|
||||||
|
|
||||||
// sourcery: AutoMockable
|
// sourcery: AutoMockable
|
||||||
protocol QRCodeLoginServiceProtocol {
|
protocol QRCodeLoginServiceProtocol {
|
||||||
func requestAuthorizationIfNeeded() async -> Bool
|
var qrLoginProgressPublisher: AnyPublisher<QrLoginProgress, Never> { get }
|
||||||
|
|
||||||
|
func loginWithQRCode(data: Data) async -> Result<UserSessionProtocol, QRCodeLoginServiceError>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ class MockScreen: Identifiable {
|
|||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
case .authenticationFlow:
|
case .authenticationFlow:
|
||||||
let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: MockAuthenticationServiceProxy(),
|
let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: MockAuthenticationServiceProxy(),
|
||||||
|
qrCodeLoginService: QRCodeLoginServiceMock(),
|
||||||
bugReportService: BugReportServiceMock(),
|
bugReportService: BugReportServiceMock(),
|
||||||
navigationRootCoordinator: navigationRootCoordinator,
|
navigationRootCoordinator: navigationRootCoordinator,
|
||||||
appMediator: AppMediatorMock.default,
|
appMediator: AppMediatorMock.default,
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:06198ff9cd5220d7c1282079371728698a9b9ac6d8058a4e09e90c4284b15658
|
||||||
|
size 118241
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:81808a0d38699a96abea059176bf065956bbbe2f457cb58bd4f8ec008e043807
|
oid sha256:fb5249849da514c75ada263c8801339d7d23fe35d9e5e4096acb35a55b69b458
|
||||||
size 240316
|
size 233785
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:e8291c2203da7953c7b72e46b28ea7f3de99475fa5b89316cbdf1ddb14735f07
|
||||||
|
size 118456
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:32854697cdb9c9f4e84282193921cc0a2c959f34ab1bf6be2182ebaf06178935
|
||||||
|
size 156558
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:898bc820b2ddcf43903b67790fb4f33fd4cfe525360b3fd6daea043b0fa4bba3
|
||||||
|
size 121939
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:5aafdb89352ede1e946d9462cad3885be7bcd87038e7194420a31549488cd45a
|
||||||
|
size 122996
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:608c132245379fca44da426c23d05e4322d0b5205266c27e8c7f2d702c10c6ab
|
oid sha256:e762f2d946c682c59b7b6c1aaa56e5596bb64de67ec29eca6ef8b1ac92ad8d8c
|
||||||
size 175909
|
size 178544
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:137d7d05b66b23ddb027f2d44fa31f3dadd35d5e1668f570c648833ea016d485
|
oid sha256:64c7afdfb4698c972203b5987fcb9036b4aa75645969c22eefc54566a4e65747
|
||||||
size 139013
|
size 139192
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:16c2e970674c36f0e40b52d1005d3e45dbf5f29123cb866aea643d8d5ea8b976
|
||||||
|
size 153497
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:19143f0cb869a917d7bff9d6c510ad5c589dad58f2f6a1f96c43402c8cb7eaca
|
oid sha256:99707d136259b3fab5664ad24ca5923554f92d94c9e913be9d9b776e942b464d
|
||||||
size 130219
|
size 124228
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:c59ce9bc5b29e9b36d53f6dec406b4514f4d51cd65d34f544987078555936018
|
||||||
|
size 152784
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e532918bbc3dd6f19b1c6f595bf4e78328777f67ba168ca73820acf36c4b5fb3
|
oid sha256:9f881dfeb084cbd409e9fdf7913252c6d1b841e2f875a86916d7c69dde989970
|
||||||
size 364040
|
size 353783
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0cbe50417a3b6985b69a8465242d65af404dfccd919fc971aea864daaf4b6b47
|
||||||
|
size 150711
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:61c704f3d7b69ea192624ec6fb60fca7dfe6e089be632d4e22e72a3d1e13cdc6
|
||||||
|
size 208122
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:fb425e78ece3246a1b290c8b84d1caa04d4246c86bf20eaa0b74c774eb618327
|
||||||
|
size 151075
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:5af5a51a6ae879671bfdf417d9f9cc7715e20512d85379373f9e8104b4c2ee4c
|
||||||
|
size 163873
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:0b26435b6414ee0c2782fe084939075016fd26533fc9af16ed7645dd2f3a1347
|
oid sha256:c1cd2940d92227d7036640f6ea64eddd4f6245ab04662eee67b50e1162e09a9d
|
||||||
size 256639
|
size 262484
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:82fa86b47675cb62a236b024948037afca1cde901a22605791d2a91cd3c79b56
|
oid sha256:446735c3777cd614aa008beae3b0e3fc744e6c4cc44ac7f78dd082a9b02d7625
|
||||||
size 174326
|
size 174596
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:59fda8126fd1881e419aea528308604efadaefa1b18679e5f263ba3f84f14d2e
|
||||||
|
size 220251
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d2fc9f0ebae6cf4c1521ffbde06fbdad3bc15071db2a9461abf1b7d1415f113e
|
oid sha256:598203104fef16dfa93e6d77b1529892774c0be30f95344ff800b7c64698b6ac
|
||||||
size 164912
|
size 153117
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:1428741d616b4cdb28973473c2602392a9c2ca0ef414e90fa40d110a1ed29ef6
|
||||||
|
size 71029
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9d2b9794ad3b7d038113d0b87bd0f694194471adb29357e963f8143d955dc4ac
|
oid sha256:eca687f56b650ef5ac41219061a45dac5b56d9eaff54da40e869162ced207d08
|
||||||
size 164424
|
size 159528
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:c1ab0cb4abb4cad384c95d5b788434a8e6a3449fac4e6bd0d73a6cfc8b0bf55f
|
||||||
|
size 68137
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:4b42499314b0f777f32878783bb6ba28009da920c74a08b69e21f55d1677085c
|
||||||
|
size 94623
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:53f9ce852ff302cc2b9cc52d7d057becda1ae34eefc4cc8cfd98631e9d249c6a
|
||||||
|
size 72003
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:9d531f55191cb88f6678f6950a26d9f3eb382d95c41046c49d50a8c78724b9f1
|
||||||
|
size 72077
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:8e19a0e1e323537e6a70da304e0be9bbb11643371793514597e022e0301732ca
|
oid sha256:21d7f98e4f6c9b25aa5a812a5410b6fe2fb4f202b1e556514554ff6afe7e200a
|
||||||
size 124161
|
size 126332
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ed2670440f91d41f983081d877f7c084ca205724143f5b5d37dc6fdb52fc188c
|
oid sha256:8f4efd9d313010c3961beb4ad8f85c7179cfe17c0900bf6a3150ca1070670f0f
|
||||||
size 78633
|
size 78648
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:60d9429395be091a64a79de2bd8d03152777bbf9ac5dd353e1f0fe4a1406c04f
|
||||||
|
size 101877
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4a70dec75d62f4ebb3d00f613c3314c741bef3cc757d5c9b78358ca8b9535d52
|
oid sha256:6dabb5a3e2d2f084d783367321ae139da12a3d59cc2cd908a11fe5c27cc57e21
|
||||||
size 78473
|
size 73675
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:986eabb153ea07fd2caa7239fad835b13363674b67e3d40f743496eeea6c83ba
|
||||||
|
size 97338
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:54b0bc2bacf2504febf374970d5afdbf8f8f03b139005c5c81d04761faf66132
|
oid sha256:b02fa3348fb1bc56e1bd514322d157fae8247228239d20a369a178fe10f2eea8
|
||||||
size 268685
|
size 260577
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:73ffa2cb86e0228ea0788e53af38e3d3fc2aa1e14d5b3d1358dfc4bc3f5f474b
|
||||||
|
size 95518
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:be6ad96a182495c28b889d2c1a6d2456f1cf27b2f443cf0ee6a93d079bad80be
|
||||||
|
size 141938
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:a463d00012f4f3ea2bf35e87c6b35e425ba9865ae77b2852735b1f4c7c02627d
|
||||||
|
size 97564
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:db8e449dc3993188507e23f79de263ed15a9e75188d1e70ecc5370a444bbdebd
|
||||||
|
size 103468
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7c1f4e2f5924db1858c2417235b24e3333c3570622b02a85ec1e54e1bcf9504c
|
oid sha256:a412452ca11c6e7c53d7ad4e01faa8c4bc3979771912c2b96b13422cef30d9bd
|
||||||
size 194638
|
size 199595
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a1f5a01424b85977592a295500142b1ebf3c6d0720c45b03334f108f1bc03f1e
|
oid sha256:42f429a79217b6da6f567ad9e9c7cc365e65fe005ad4773bbf370998420f9e80
|
||||||
size 114199
|
size 114340
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6b479b466cc5b987468386a09286428f4105ade82f27296c194d4950bf7f28bf
|
||||||
|
size 157737
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5d7c1eb55621a0c337ecc6c7882fdd6c48d55ca6ba9bceaaa7e334dc123b0b1e
|
oid sha256:9743e035cb1d249ce7d4d9f7602d01d35d2887eb614a7cffb34d8abc357144ef
|
||||||
size 111028
|
size 102855
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AVKit
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ElementX
|
||||||
|
|
||||||
|
final class AVMetadataMachineReadableCodeObjectExtensionsTest: XCTestCase {
|
||||||
|
func testDecoQRCodeVersion8() {
|
||||||
|
// swiftlint:disable:next line_length
|
||||||
|
let rawDataHexString = "4a34d415452495802048bf94b094096e57d3ea43545604cf59b1704879d295cf7fdd99c62df7866da36005668747470733a2f2f73796e617073652d6f6964632e656c656d656e742e6465762f5f73796e617073652f636c69656e742f72656e64657a766f75732f3031485a32394d345936374a4e315658505759464e355a363638002168747470733a2f2f73796e617073652d6f6964632e656c656d656e742e6465762f0ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec"
|
||||||
|
// swiftlint:disable:next line_length
|
||||||
|
let expectedDecodedString = "4d415452495802048bf94b094096e57d3ea43545604cf59b1704879d295cf7fdd99c62df7866da36005668747470733a2f2f73796e617073652d6f6964632e656c656d656e742e6465762f5f73796e617073652f636c69656e742f72656e64657a766f75732f3031485a32394d345936374a4e315658505759464e355a363638002168747470733a2f2f73796e617073652d6f6964632e656c656d656e742e6465762f"
|
||||||
|
let symbolVersion = 8
|
||||||
|
|
||||||
|
guard let data = Data(hexString: rawDataHexString) else {
|
||||||
|
XCTFail("Could not initialise the raw data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let resultData = AVMetadataMachineReadableCodeObject.removeQrProtocolData(data, symbolVersion: symbolVersion) else {
|
||||||
|
XCTFail("Could not remove the protocol data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let resultString = resultData.map { String(format: "%02x", $0) }.joined()
|
||||||
|
XCTAssertEqual(expectedDecodedString, resultString)
|
||||||
|
|
||||||
|
guard let expectedResultData = Data(hexString: expectedDecodedString) else {
|
||||||
|
XCTFail("Could not initialise the decoded data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
XCTAssertEqual(expectedResultData, resultData)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,9 +14,104 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
@testable import ElementX
|
@testable import ElementX
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class QRCodeLoginScreenViewModelTests: XCTestCase { }
|
final class QRCodeLoginScreenViewModelTests: XCTestCase {
|
||||||
|
private var qrProgressSubject: PassthroughSubject<QrLoginProgress, Never>!
|
||||||
|
private var qrServiceMock: QRCodeLoginServiceMock!
|
||||||
|
private var appMediatorMock: AppMediatorMock!
|
||||||
|
private var viewModel: QRCodeLoginScreenViewModelProtocol!
|
||||||
|
|
||||||
|
private var context: QRCodeLoginScreenViewModelType.Context {
|
||||||
|
viewModel.context
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
qrProgressSubject = PassthroughSubject<QrLoginProgress, Never>()
|
||||||
|
qrServiceMock = QRCodeLoginServiceMock()
|
||||||
|
qrServiceMock.underlyingQrLoginProgressPublisher = qrProgressSubject.eraseToAnyPublisher()
|
||||||
|
appMediatorMock = AppMediatorMock.default
|
||||||
|
viewModel = QRCodeLoginScreenViewModel(qrCodeLoginService: qrServiceMock,
|
||||||
|
appMediator: appMediatorMock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInitialState() {
|
||||||
|
XCTAssertEqual(context.viewState.state, .initial)
|
||||||
|
XCTAssertNil(context.qrResult)
|
||||||
|
XCTAssertFalse(qrServiceMock.loginWithQRCodeDataCalled)
|
||||||
|
XCTAssertFalse(appMediatorMock.requestAuthorizationIfNeededCalled)
|
||||||
|
XCTAssertFalse(appMediatorMock.openAppSettingsCalled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRequestCameraPermission() async throws {
|
||||||
|
appMediatorMock.requestAuthorizationIfNeededReturnValue = false
|
||||||
|
XCTAssert(context.viewState.state == .initial)
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.state == .error(.noCameraPermission)
|
||||||
|
}
|
||||||
|
context.send(viewAction: .startScan)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
XCTAssertTrue(appMediatorMock.requestAuthorizationIfNeededCalled)
|
||||||
|
|
||||||
|
context.send(viewAction: .openSettings)
|
||||||
|
await Task.yield()
|
||||||
|
XCTAssertTrue(appMediatorMock.openAppSettingsCalled)
|
||||||
|
XCTAssertNil(context.qrResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLogin() async throws {
|
||||||
|
var isCompleted = false
|
||||||
|
qrServiceMock.loginWithQRCodeDataClosure = { _ in
|
||||||
|
while !isCompleted {
|
||||||
|
await Task.yield()
|
||||||
|
}
|
||||||
|
return .success(UserSessionMock(.init(clientProxy: ClientProxyMock())))
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssert(context.viewState.state == .initial)
|
||||||
|
|
||||||
|
var deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.state == .scan(.scanning)
|
||||||
|
}
|
||||||
|
context.send(viewAction: .startScan)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
XCTAssertTrue(appMediatorMock.requestAuthorizationIfNeededCalled)
|
||||||
|
|
||||||
|
deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.state == .scan(.connecting)
|
||||||
|
}
|
||||||
|
context.qrResult = .init()
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.state == .displayCode(.deviceCode("01"))
|
||||||
|
}
|
||||||
|
qrProgressSubject.send(.establishingSecureChannel(checkCode: 1, checkCodeString: "01"))
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.state == .displayCode(.verificationCode("ABCDEF"))
|
||||||
|
}
|
||||||
|
qrProgressSubject.send(.waitingForToken(userCode: "ABCDEF"))
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
let deferredAction = deferFulfillment(viewModel.actionsPublisher) { action in
|
||||||
|
switch action {
|
||||||
|
case .done:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qrProgressSubject.send(.done)
|
||||||
|
isCompleted = true
|
||||||
|
try await deferredAction.fulfill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ packages:
|
|||||||
# Element/Matrix dependencies
|
# Element/Matrix dependencies
|
||||||
MatrixRustSDK:
|
MatrixRustSDK:
|
||||||
url: https://github.com/element-hq/matrix-rust-components-swift
|
url: https://github.com/element-hq/matrix-rust-components-swift
|
||||||
exactVersion: 1.0.4
|
exactVersion: 1.0.6
|
||||||
# path: ../matrix-rust-sdk
|
# path: ../matrix-rust-sdk
|
||||||
Compound:
|
Compound:
|
||||||
url: https://github.com/element-hq/compound-ios
|
url: https://github.com/element-hq/compound-ios
|
||||||
|
|||||||
Reference in New Issue
Block a user