mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 20:09:04 +00:00
QR Code scan view (#2674)
This commit is contained in:
@@ -529,7 +529,6 @@
|
|||||||
7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */; };
|
7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */; };
|
||||||
7F7EA51A9A43125A8CB6AC90 /* NotificationSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D560DDA3B20C82766ACFAD /* NotificationSettingsScreenViewModel.swift */; };
|
7F7EA51A9A43125A8CB6AC90 /* NotificationSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D560DDA3B20C82766ACFAD /* NotificationSettingsScreenViewModel.swift */; };
|
||||||
7F941B063C94E1718DFC2CF3 /* RoomChangeRolesScreenRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E6EB7960BC9D0F7396B3BD /* RoomChangeRolesScreenRow.swift */; };
|
7F941B063C94E1718DFC2CF3 /* RoomChangeRolesScreenRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E6EB7960BC9D0F7396B3BD /* RoomChangeRolesScreenRow.swift */; };
|
||||||
7FED77802940EA7DF4D0D3A2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */; };
|
|
||||||
7FF6E1FBE6E9517FD29A1D8E /* RoomChangeRolesScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48A5C34C4E4268EF65D171EF /* RoomChangeRolesScreenModels.swift */; };
|
7FF6E1FBE6E9517FD29A1D8E /* RoomChangeRolesScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48A5C34C4E4268EF65D171EF /* RoomChangeRolesScreenModels.swift */; };
|
||||||
8015842CB4DE1BE414D2CDED /* AppLockSetupBiometricsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C62E07C1164F5120727A2A8 /* AppLockSetupBiometricsScreenCoordinator.swift */; };
|
8015842CB4DE1BE414D2CDED /* AppLockSetupBiometricsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C62E07C1164F5120727A2A8 /* AppLockSetupBiometricsScreenCoordinator.swift */; };
|
||||||
804C15D8ADE0EA7A5268F58A /* OverridableAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648DD1C10E4957CB791FE0B8 /* OverridableAvatarImage.swift */; };
|
804C15D8ADE0EA7A5268F58A /* OverridableAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648DD1C10E4957CB791FE0B8 /* OverridableAvatarImage.swift */; };
|
||||||
@@ -583,7 +582,6 @@
|
|||||||
8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E5E9C044BEB7C70B1378E91 /* UserSession.swift */; };
|
8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E5E9C044BEB7C70B1378E91 /* UserSession.swift */; };
|
||||||
8AC256AF0EC54658321C9241 /* LegalInformationScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E5725BC6C63604CB769145B /* LegalInformationScreenViewModelTests.swift */; };
|
8AC256AF0EC54658321C9241 /* LegalInformationScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E5725BC6C63604CB769145B /* LegalInformationScreenViewModelTests.swift */; };
|
||||||
8B1D5CE017EEC734CF5FE130 /* Encodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260004737C573A56FA01E86E /* Encodable.swift */; };
|
8B1D5CE017EEC734CF5FE130 /* Encodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260004737C573A56FA01E86E /* Encodable.swift */; };
|
||||||
8B408C574E35E1C9B43A50CE /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */; };
|
|
||||||
8B41D0357B91CD3B6F6A3BCA /* EmoteRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */; };
|
8B41D0357B91CD3B6F6A3BCA /* EmoteRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */; };
|
||||||
8B76191B9DDD1AC90A6E3A35 /* MediaFileHandleProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */; };
|
8B76191B9DDD1AC90A6E3A35 /* MediaFileHandleProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */; };
|
||||||
8BC8EF6705A78946C1F22891 /* SoftLogoutScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A7D4DDEEE5D2CA0C8D63CD /* SoftLogoutScreen.swift */; };
|
8BC8EF6705A78946C1F22891 /* SoftLogoutScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A7D4DDEEE5D2CA0C8D63CD /* SoftLogoutScreen.swift */; };
|
||||||
@@ -601,6 +599,7 @@
|
|||||||
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 */; };
|
||||||
@@ -1057,6 +1056,7 @@
|
|||||||
FD4C21F8DA1E273DE94FCD1A /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; };
|
FD4C21F8DA1E273DE94FCD1A /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; };
|
||||||
FD4DEC88210F35C35B2FB386 /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A1398EFF65090FDA1CB639 /* ProcessInfo.swift */; };
|
FD4DEC88210F35C35B2FB386 /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A1398EFF65090FDA1CB639 /* ProcessInfo.swift */; };
|
||||||
FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; };
|
FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; };
|
||||||
|
FDD5B4B616D9FF4DE3E9A418 /* QRCodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */; };
|
||||||
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; };
|
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; };
|
||||||
FEFD5290B31FCBA6999912C8 /* RoomChangePermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */; };
|
FEFD5290B31FCBA6999912C8 /* RoomChangePermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */; };
|
||||||
FF34BF2AF731340AF9414A18 /* SwipeRightAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4552D3466B1453F287223ADA /* SwipeRightAction.swift */; };
|
FF34BF2AF731340AF9414A18 /* SwipeRightAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4552D3466B1453F287223ADA /* SwipeRightAction.swift */; };
|
||||||
@@ -1678,12 +1678,14 @@
|
|||||||
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>"; };
|
||||||
91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenCoordinator.swift; sourceTree = "<group>"; };
|
91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
91CF6F7D08228D16BA69B63B /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
91CF6F7D08228D16BA69B63B /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCAuthenticationPresenter.swift; sourceTree = "<group>"; };
|
92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCAuthenticationPresenter.swift; sourceTree = "<group>"; };
|
||||||
|
92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScannerView.swift; sourceTree = "<group>"; };
|
||||||
92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTask.swift; sourceTree = "<group>"; };
|
92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTask.swift; sourceTree = "<group>"; };
|
||||||
9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = "<group>"; };
|
9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = "<group>"; };
|
||||||
933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaFrame.swift; sourceTree = "<group>"; };
|
933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaFrame.swift; sourceTree = "<group>"; };
|
||||||
@@ -2661,6 +2663,7 @@
|
|||||||
E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */,
|
E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */,
|
||||||
382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */,
|
382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.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 */,
|
||||||
@@ -4617,6 +4620,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BFA9EA59D5C0DA1BFC7B3621 /* QRCodeLoginScreen.swift */,
|
BFA9EA59D5C0DA1BFC7B3621 /* QRCodeLoginScreen.swift */,
|
||||||
|
92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */,
|
||||||
);
|
);
|
||||||
path = View;
|
path = View;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -5419,7 +5423,6 @@
|
|||||||
5F5488FBC9CFEB6F433D74A4 /* Localizable.strings in Resources */,
|
5F5488FBC9CFEB6F433D74A4 /* Localizable.strings in Resources */,
|
||||||
0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */,
|
0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */,
|
||||||
6860721DB3091BE08164C132 /* MapAssets.xcassets in Resources */,
|
6860721DB3091BE08164C132 /* MapAssets.xcassets in Resources */,
|
||||||
8B408C574E35E1C9B43A50CE /* PrivacyInfo.xcprivacy in Resources */,
|
|
||||||
C3317EF833AB4060988DF098 /* SAS.strings in Resources */,
|
C3317EF833AB4060988DF098 /* SAS.strings in Resources */,
|
||||||
CE1694C7BB93C3311524EF28 /* Untranslated.strings in Resources */,
|
CE1694C7BB93C3311524EF28 /* Untranslated.strings in Resources */,
|
||||||
2797C9D9BA642370F1C85D78 /* Untranslated.stringsdict in Resources */,
|
2797C9D9BA642370F1C85D78 /* Untranslated.stringsdict in Resources */,
|
||||||
@@ -5432,7 +5435,6 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
7FED77802940EA7DF4D0D3A2 /* PrivacyInfo.xcprivacy in Resources */,
|
|
||||||
D2D70B5DB1A5E4AF0CD88330 /* target.yml in Resources */,
|
D2D70B5DB1A5E4AF0CD88330 /* target.yml in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -6196,7 +6198,9 @@
|
|||||||
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 */,
|
||||||
BB04B1D8E7401C90506D401E /* QRCodeLoginServiceProtocol.swift in Sources */,
|
BB04B1D8E7401C90506D401E /* QRCodeLoginServiceProtocol.swift in Sources */,
|
||||||
|
FDD5B4B616D9FF4DE3E9A418 /* QRCodeScannerView.swift in Sources */,
|
||||||
9095B9E40DB5CF8BA26CE0D8 /* ReactionsSummaryView.swift in Sources */,
|
9095B9E40DB5CF8BA26CE0D8 /* ReactionsSummaryView.swift in Sources */,
|
||||||
743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */,
|
743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */,
|
||||||
8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */,
|
8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */,
|
||||||
|
|||||||
@@ -418,6 +418,12 @@
|
|||||||
"screen_invites_decline_direct_chat_title" = "Decline chat";
|
"screen_invites_decline_direct_chat_title" = "Decline chat";
|
||||||
"screen_invites_empty_list" = "No Invites";
|
"screen_invites_empty_list" = "No Invites";
|
||||||
"screen_invites_invited_you" = "%1$@ (%2$@) invited you";
|
"screen_invites_invited_you" = "%1$@ (%2$@) invited you";
|
||||||
|
"screen_join_room_join_action" = "Join room";
|
||||||
|
"screen_join_room_knock_action" = "Knock to join";
|
||||||
|
"screen_join_room_subtitle_knock" = "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved.";
|
||||||
|
"screen_join_room_subtitle_no_preview" = "You must be a member of this room to view the message history.";
|
||||||
|
"screen_join_room_title_knock" = "Want to join this room?";
|
||||||
|
"screen_join_room_title_no_preview" = "Preview is not available";
|
||||||
"screen_key_backup_disable_confirmation_action_turn_off" = "Turn off";
|
"screen_key_backup_disable_confirmation_action_turn_off" = "Turn off";
|
||||||
"screen_key_backup_disable_confirmation_description" = "You will lose your encrypted messages if you are signed out of all devices.";
|
"screen_key_backup_disable_confirmation_description" = "You will lose your encrypted messages if you are signed out of all devices.";
|
||||||
"screen_key_backup_disable_confirmation_title" = "Are you sure you want to turn off backup?";
|
"screen_key_backup_disable_confirmation_title" = "Are you sure you want to turn off backup?";
|
||||||
@@ -476,6 +482,7 @@
|
|||||||
"screen_polls_history_filter_ongoing" = "Ongoing";
|
"screen_polls_history_filter_ongoing" = "Ongoing";
|
||||||
"screen_polls_history_filter_past" = "Past";
|
"screen_polls_history_filter_past" = "Past";
|
||||||
"screen_polls_history_title" = "Polls";
|
"screen_polls_history_title" = "Polls";
|
||||||
|
"screen_qr_code_login_connecting_subtitle" = "Establishing connection";
|
||||||
"screen_qr_code_login_initial_state_item_1" = "Open Element on a desktop device";
|
"screen_qr_code_login_initial_state_item_1" = "Open Element 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";
|
||||||
"screen_qr_code_login_initial_state_item_3" = "Select %1$@";
|
"screen_qr_code_login_initial_state_item_3" = "Select %1$@";
|
||||||
@@ -483,6 +490,10 @@
|
|||||||
"screen_qr_code_login_initial_state_item_4" = "Select %1$@";
|
"screen_qr_code_login_initial_state_item_4" = "Select %1$@";
|
||||||
"screen_qr_code_login_initial_state_item_4_action" = "“Show QR code”";
|
"screen_qr_code_login_initial_state_item_4_action" = "“Show QR code”";
|
||||||
"screen_qr_code_login_initial_state_title" = "Open Element on another device to get the QR code";
|
"screen_qr_code_login_initial_state_title" = "Open Element on another device to get the QR code";
|
||||||
|
"screen_qr_code_login_invalid_scan_state_description" = "Use the QR code shown on the other device.";
|
||||||
|
"screen_qr_code_login_invalid_scan_state_retry_button" = "Try Again";
|
||||||
|
"screen_qr_code_login_invalid_scan_state_subtitle" = "Wrong QR code";
|
||||||
|
"screen_qr_code_login_scanning_state_title" = "Scan the QR code";
|
||||||
"screen_recovery_key_change_description" = "Get a new recovery key if you've lost your existing one. After changing your recovery key, your old one will no longer work.";
|
"screen_recovery_key_change_description" = "Get a new recovery key if you've lost your existing one. After changing your recovery key, your old one will no longer work.";
|
||||||
"screen_recovery_key_change_generate_key" = "Generate a new recovery key";
|
"screen_recovery_key_change_generate_key" = "Generate a new recovery key";
|
||||||
"screen_recovery_key_change_generate_key_description" = "Make sure you can store your recovery key somewhere safe";
|
"screen_recovery_key_change_generate_key_description" = "Make sure you can store your recovery key somewhere safe";
|
||||||
|
|||||||
@@ -406,7 +406,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
navigationRootCoordinator: navigationRootCoordinator,
|
navigationRootCoordinator: navigationRootCoordinator,
|
||||||
appSettings: appSettings,
|
appSettings: appSettings,
|
||||||
analytics: ServiceLocator.shared.analytics,
|
analytics: ServiceLocator.shared.analytics,
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
|
orientationManager: windowManager)
|
||||||
authenticationFlowCoordinator?.delegate = self
|
authenticationFlowCoordinator?.delegate = self
|
||||||
|
|
||||||
authenticationFlowCoordinator?.start()
|
authenticationFlowCoordinator?.start()
|
||||||
|
|||||||
@@ -30,6 +30,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 orientationManager: OrientationManagerProtocol
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
@@ -45,13 +46,15 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
navigationRootCoordinator: NavigationRootCoordinator,
|
navigationRootCoordinator: NavigationRootCoordinator,
|
||||||
appSettings: AppSettings,
|
appSettings: AppSettings,
|
||||||
analytics: AnalyticsService,
|
analytics: AnalyticsService,
|
||||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
userIndicatorController: UserIndicatorControllerProtocol,
|
||||||
|
orientationManager: WindowManagerProtocol) {
|
||||||
self.authenticationService = authenticationService
|
self.authenticationService = authenticationService
|
||||||
self.bugReportService = bugReportService
|
self.bugReportService = bugReportService
|
||||||
self.navigationRootCoordinator = navigationRootCoordinator
|
self.navigationRootCoordinator = navigationRootCoordinator
|
||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
self.analytics = analytics
|
self.analytics = analytics
|
||||||
self.userIndicatorController = userIndicatorController
|
self.userIndicatorController = userIndicatorController
|
||||||
|
self.orientationManager = orientationManager
|
||||||
|
|
||||||
navigationStackCoordinator = NavigationStackCoordinator()
|
navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
}
|
}
|
||||||
@@ -103,7 +106,8 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startQRCodeLogin() {
|
private func startQRCodeLogin() {
|
||||||
let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: QRCodeLoginService()))
|
let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: QRCodeLoginService(),
|
||||||
|
orientationManager: orientationManager))
|
||||||
coordinator.actionsPublisher.sink { [weak self] action in
|
coordinator.actionsPublisher.sink { [weak self] action in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1039,6 +1039,18 @@ internal enum L10n {
|
|||||||
internal static func screenInvitesInvitedYou(_ p1: Any, _ p2: Any) -> String {
|
internal static func screenInvitesInvitedYou(_ p1: Any, _ p2: Any) -> String {
|
||||||
return L10n.tr("Localizable", "screen_invites_invited_you", String(describing: p1), String(describing: p2))
|
return L10n.tr("Localizable", "screen_invites_invited_you", String(describing: p1), String(describing: p2))
|
||||||
}
|
}
|
||||||
|
/// Join room
|
||||||
|
internal static var screenJoinRoomJoinAction: String { return L10n.tr("Localizable", "screen_join_room_join_action") }
|
||||||
|
/// Knock to join
|
||||||
|
internal static var screenJoinRoomKnockAction: String { return L10n.tr("Localizable", "screen_join_room_knock_action") }
|
||||||
|
/// Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved.
|
||||||
|
internal static var screenJoinRoomSubtitleKnock: String { return L10n.tr("Localizable", "screen_join_room_subtitle_knock") }
|
||||||
|
/// You must be a member of this room to view the message history.
|
||||||
|
internal static var screenJoinRoomSubtitleNoPreview: String { return L10n.tr("Localizable", "screen_join_room_subtitle_no_preview") }
|
||||||
|
/// Want to join this room?
|
||||||
|
internal static var screenJoinRoomTitleKnock: String { return L10n.tr("Localizable", "screen_join_room_title_knock") }
|
||||||
|
/// Preview is not available
|
||||||
|
internal static var screenJoinRoomTitleNoPreview: String { return L10n.tr("Localizable", "screen_join_room_title_no_preview") }
|
||||||
/// Turn off
|
/// Turn off
|
||||||
internal static var screenKeyBackupDisableConfirmationActionTurnOff: String { return L10n.tr("Localizable", "screen_key_backup_disable_confirmation_action_turn_off") }
|
internal static var screenKeyBackupDisableConfirmationActionTurnOff: String { return L10n.tr("Localizable", "screen_key_backup_disable_confirmation_action_turn_off") }
|
||||||
/// You will lose your encrypted messages if you are signed out of all devices.
|
/// You will lose your encrypted messages if you are signed out of all devices.
|
||||||
@@ -1167,6 +1179,8 @@ internal enum L10n {
|
|||||||
internal static var screenPollsHistoryFilterPast: String { return L10n.tr("Localizable", "screen_polls_history_filter_past") }
|
internal static var screenPollsHistoryFilterPast: String { return L10n.tr("Localizable", "screen_polls_history_filter_past") }
|
||||||
/// Polls
|
/// Polls
|
||||||
internal static var screenPollsHistoryTitle: String { return L10n.tr("Localizable", "screen_polls_history_title") }
|
internal static var screenPollsHistoryTitle: String { return L10n.tr("Localizable", "screen_polls_history_title") }
|
||||||
|
/// Establishing connection
|
||||||
|
internal static var screenQrCodeLoginConnectingSubtitle: String { return L10n.tr("Localizable", "screen_qr_code_login_connecting_subtitle") }
|
||||||
/// Open Element on a desktop device
|
/// Open Element on a desktop device
|
||||||
internal static var screenQrCodeLoginInitialStateItem1: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_item_1") }
|
internal static var screenQrCodeLoginInitialStateItem1: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_item_1") }
|
||||||
/// Click on your avatar
|
/// Click on your avatar
|
||||||
@@ -1185,6 +1199,14 @@ internal enum L10n {
|
|||||||
internal static var screenQrCodeLoginInitialStateItem4Action: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_item_4_action") }
|
internal static var screenQrCodeLoginInitialStateItem4Action: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_item_4_action") }
|
||||||
/// Open Element on another device to get the QR code
|
/// Open Element on another device to get the QR code
|
||||||
internal static var screenQrCodeLoginInitialStateTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_title") }
|
internal static var screenQrCodeLoginInitialStateTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_title") }
|
||||||
|
/// Use the QR code shown on the other device.
|
||||||
|
internal static var screenQrCodeLoginInvalidScanStateDescription: String { return L10n.tr("Localizable", "screen_qr_code_login_invalid_scan_state_description") }
|
||||||
|
/// Try Again
|
||||||
|
internal static var screenQrCodeLoginInvalidScanStateRetryButton: String { return L10n.tr("Localizable", "screen_qr_code_login_invalid_scan_state_retry_button") }
|
||||||
|
/// Wrong QR code
|
||||||
|
internal static var screenQrCodeLoginInvalidScanStateSubtitle: String { return L10n.tr("Localizable", "screen_qr_code_login_invalid_scan_state_subtitle") }
|
||||||
|
/// Scan the QR code
|
||||||
|
internal static var screenQrCodeLoginScanningStateTitle: String { return L10n.tr("Localizable", "screen_qr_code_login_scanning_state_title") }
|
||||||
/// Get a new recovery key if you've lost your existing one. After changing your recovery key, your old one will no longer work.
|
/// Get a new recovery key if you've lost your existing one. After changing your recovery key, your old one will no longer work.
|
||||||
internal static var screenRecoveryKeyChangeDescription: String { return L10n.tr("Localizable", "screen_recovery_key_change_description") }
|
internal static var screenRecoveryKeyChangeDescription: String { return L10n.tr("Localizable", "screen_recovery_key_change_description") }
|
||||||
/// Generate a new recovery key
|
/// Generate a new recovery key
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-3
@@ -30,9 +30,11 @@ class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType
|
|||||||
init(appSettings: AppSettings) {
|
init(appSettings: AppSettings) {
|
||||||
self.appSettings = appSettings
|
self.appSettings = appSettings
|
||||||
super.init(initialViewState: AuthenticationStartScreenViewState())
|
super.init(initialViewState: AuthenticationStartScreenViewState())
|
||||||
appSettings.$qrCodeLoginEnabled
|
if !ProcessInfo.processInfo.isiOSAppOnMac {
|
||||||
.weakAssign(to: \.state.isQRCodeLoginEnabled, on: self)
|
appSettings.$qrCodeLoginEnabled
|
||||||
.store(in: &cancellables)
|
.weakAssign(to: \.state.isQRCodeLoginEnabled, on: self)
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func process(viewAction: AuthenticationStartScreenViewAction) {
|
override func process(viewAction: AuthenticationStartScreenViewAction) {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct QRCodeLoginScreenCoordinatorParameters {
|
struct QRCodeLoginScreenCoordinatorParameters {
|
||||||
let qrCodeLoginService: QRCodeLoginServiceProtocol
|
let qrCodeLoginService: QRCodeLoginServiceProtocol
|
||||||
|
let orientationManager: OrientationManagerProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
enum QRCodeLoginScreenCoordinatorAction {
|
enum QRCodeLoginScreenCoordinatorAction {
|
||||||
@@ -29,6 +30,7 @@ enum QRCodeLoginScreenCoordinatorAction {
|
|||||||
|
|
||||||
final class QRCodeLoginScreenCoordinator: CoordinatorProtocol {
|
final class QRCodeLoginScreenCoordinator: CoordinatorProtocol {
|
||||||
private let viewModel: QRCodeLoginScreenViewModelProtocol
|
private let viewModel: QRCodeLoginScreenViewModelProtocol
|
||||||
|
private let orientationManager: OrientationManagerProtocol
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
@@ -39,6 +41,7 @@ final class QRCodeLoginScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
init(parameters: QRCodeLoginScreenCoordinatorParameters) {
|
init(parameters: QRCodeLoginScreenCoordinatorParameters) {
|
||||||
viewModel = QRCodeLoginScreenViewModel(qrCodeLoginService: parameters.qrCodeLoginService)
|
viewModel = QRCodeLoginScreenViewModel(qrCodeLoginService: parameters.qrCodeLoginService)
|
||||||
|
orientationManager = parameters.orientationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
@@ -52,6 +55,13 @@ final class QRCodeLoginScreenCoordinator: CoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
orientationManager.setOrientation(.portrait)
|
||||||
|
orientationManager.lockOrientation(.portrait)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() {
|
||||||
|
orientationManager.lockOrientation(.all)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPresentable() -> AnyView {
|
func toPresentable() -> AnyView {
|
||||||
|
|||||||
@@ -56,12 +56,24 @@ enum QRCodeLoginScreenViewAction {
|
|||||||
case startScan
|
case startScan
|
||||||
}
|
}
|
||||||
|
|
||||||
enum QRCodeLoginState {
|
enum QRCodeLoginState: Equatable {
|
||||||
|
/// Initial state where the user is informed how to perform the scan
|
||||||
case initial
|
case initial
|
||||||
case scanning
|
/// The camera is scanning
|
||||||
|
case scan(QRCodeLoginScanningState)
|
||||||
|
/// Any full screen error state
|
||||||
case error(QRCodeLoginErrorState)
|
case error(QRCodeLoginErrorState)
|
||||||
|
|
||||||
enum QRCodeLoginErrorState {
|
enum QRCodeLoginErrorState: Equatable {
|
||||||
case noCameraPermission
|
case noCameraPermission
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum QRCodeLoginScanningState: Equatable {
|
||||||
|
/// the qr code is scanning
|
||||||
|
case scanning
|
||||||
|
/// the qr code has been detected and is being processed
|
||||||
|
case connecting
|
||||||
|
/// the qr code has been processed and is invalid
|
||||||
|
case invalid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,18 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startScanIfPossible() async {
|
private func startScanIfPossible() async {
|
||||||
state.state = await qrCodeLoginService.requestAuthorizationIfNeeded() ? .scanning : .error(.noCameraPermission)
|
state.state = await qrCodeLoginService.requestAuthorizationIfNeeded() ? .scan(.scanning) : .error(.noCameraPermission)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only for mocking initial states
|
||||||
|
fileprivate init(state: QRCodeLoginState) {
|
||||||
|
qrCodeLoginService = QRCodeLoginServiceMock(configuration: .init())
|
||||||
|
super.init(initialViewState: .init(state: state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension QRCodeLoginScreenViewModel {
|
||||||
|
static func mock(state: QRCodeLoginState) -> QRCodeLoginScreenViewModel {
|
||||||
|
QRCodeLoginScreenViewModel(state: state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct QRCodeLoginScreen: View {
|
struct QRCodeLoginScreen: View {
|
||||||
@ObservedObject var context: QRCodeLoginScreenViewModel.Context
|
@ObservedObject var context: QRCodeLoginScreenViewModel.Context
|
||||||
|
@State private var qrFrame = CGRect.zero
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
@@ -36,13 +37,15 @@ struct QRCodeLoginScreen: View {
|
|||||||
switch context.viewState.state {
|
switch context.viewState.state {
|
||||||
case .initial:
|
case .initial:
|
||||||
initialContent
|
initialContent
|
||||||
case .scanning, .error:
|
case .scan:
|
||||||
// TODO: Handle states
|
qrScanContent
|
||||||
|
case .error:
|
||||||
|
// TODO: Handle error states
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var initialContent: some View {
|
private var initialContent: some View {
|
||||||
FullscreenDialog {
|
FullscreenDialog {
|
||||||
VStack(alignment: .leading, spacing: 40) {
|
VStack(alignment: .leading, spacing: 40) {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
@@ -65,6 +68,75 @@ struct QRCodeLoginScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var qrScanContent: some View {
|
||||||
|
FullscreenDialog {
|
||||||
|
VStack(spacing: 40) {
|
||||||
|
VStack(spacing: 16) {
|
||||||
|
HeroImage(icon: \.takePhotoSolid, style: .subtle)
|
||||||
|
|
||||||
|
Text(L10n.screenQrCodeLoginScanningStateTitle)
|
||||||
|
.foregroundColor(.compound.textPrimary)
|
||||||
|
.font(.compound.headingMDBold)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
}
|
||||||
|
|
||||||
|
qrScanner
|
||||||
|
}
|
||||||
|
} bottomContent: {
|
||||||
|
qrScanFooter
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var qrScanFooter: some View {
|
||||||
|
if case let .scan(scanState) = context.viewState.state {
|
||||||
|
switch scanState {
|
||||||
|
case .connecting:
|
||||||
|
VStack(spacing: 4) {
|
||||||
|
ProgressView()
|
||||||
|
Text(L10n.screenQrCodeLoginConnectingSubtitle)
|
||||||
|
.foregroundColor(.compound.textSecondary)
|
||||||
|
.font(.compound.bodySM)
|
||||||
|
}
|
||||||
|
case .scanning:
|
||||||
|
// To keep the spacing consistent between states
|
||||||
|
Button("") { }
|
||||||
|
.buttonStyle(.compound(.primary))
|
||||||
|
.hidden()
|
||||||
|
case .invalid:
|
||||||
|
VStack(spacing: 16) {
|
||||||
|
Button(L10n.screenQrCodeLoginInvalidScanStateRetryButton) {
|
||||||
|
// TODO: Implement try again
|
||||||
|
}
|
||||||
|
.buttonStyle(.compound(.primary))
|
||||||
|
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
Label(L10n.screenQrCodeLoginInvalidScanStateSubtitle, icon: \.error, iconSize: .medium, relativeTo: .compound.bodyMDSemibold)
|
||||||
|
.labelStyle(.custom(spacing: 10))
|
||||||
|
.font(.compound.bodyMDSemibold)
|
||||||
|
.foregroundColor(.compound.textCriticalPrimary)
|
||||||
|
|
||||||
|
Text(L10n.screenQrCodeLoginInvalidScanStateDescription)
|
||||||
|
.foregroundColor(.compound.textSecondary)
|
||||||
|
.font(.compound.bodySM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var qrScanner: some View {
|
||||||
|
QRCodeScannerView()
|
||||||
|
.aspectRatio(1.0, contentMode: .fill)
|
||||||
|
.frame(maxWidth: 312)
|
||||||
|
.readFrame($qrFrame)
|
||||||
|
.background(.compound.bgCanvasDefault)
|
||||||
|
.overlay(
|
||||||
|
QRScannerViewOverlay(length: qrFrame.height)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@ToolbarContentBuilder
|
@ToolbarContentBuilder
|
||||||
private var toolbar: some ToolbarContent {
|
private var toolbar: some ToolbarContent {
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
@@ -75,12 +147,53 @@ struct QRCodeLoginScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct QRScannerViewOverlay: View {
|
||||||
|
let length: CGFloat
|
||||||
|
|
||||||
|
private let dashRatio: CGFloat = 80.0 / 312.0
|
||||||
|
private let emptyRatio: CGFloat = 232.0 / 312.0
|
||||||
|
private let dashPhaseRatio: CGFloat = 40.0 / 312.0
|
||||||
|
|
||||||
|
private var dashLength: CGFloat {
|
||||||
|
length * dashRatio
|
||||||
|
}
|
||||||
|
|
||||||
|
private var emptyLength: CGFloat {
|
||||||
|
length * emptyRatio
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dashPhase: CGFloat {
|
||||||
|
length * dashPhaseRatio
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Rectangle()
|
||||||
|
.stroke(.compound.textPrimary, style: StrokeStyle(lineWidth: 4.0, lineCap: .square, dash: [dashLength, emptyLength], dashPhase: dashPhase))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
|
||||||
struct QRCodeLoginScreen_Previews: PreviewProvider, TestablePreview {
|
struct QRCodeLoginScreen_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = QRCodeLoginScreenViewModel(qrCodeLoginService: QRCodeLoginServiceMock())
|
static let initialStateViewModel = QRCodeLoginScreenViewModel.mock(state: .initial)
|
||||||
|
|
||||||
|
static let scanningStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.scanning))
|
||||||
|
|
||||||
|
static let connectingStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.connecting))
|
||||||
|
|
||||||
|
static let invalidStateViewModel = QRCodeLoginScreenViewModel.mock(state: .scan(.invalid))
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
QRCodeLoginScreen(context: viewModel.context)
|
QRCodeLoginScreen(context: initialStateViewModel.context)
|
||||||
.previewDisplayName("Initial")
|
.previewDisplayName("Initial")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: scanningStateViewModel.context)
|
||||||
|
.previewDisplayName("Scanning")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: connectingStateViewModel.context)
|
||||||
|
.previewDisplayName("Connecting")
|
||||||
|
|
||||||
|
QRCodeLoginScreen(context: invalidStateViewModel.context)
|
||||||
|
.previewDisplayName("Invalid")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
import SwiftUI
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
struct QRCodeScannerView: UIViewControllerRepresentable {
|
||||||
|
func makeUIViewController(context: Context) -> QRScannerController {
|
||||||
|
let controller = QRScannerController()
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: QRScannerController, context: Context) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
final class QRScannerController: UIViewController {
|
||||||
|
private var captureSession = AVCaptureSession()
|
||||||
|
private var videoPreviewLayer: AVCaptureVideoPreviewLayer?
|
||||||
|
private var qrCodeFrameView: UIView?
|
||||||
|
|
||||||
|
var delegate: AVCaptureMetadataOutputObjectsDelegate?
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
// Get the back-facing camera for capturing videos
|
||||||
|
guard let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
|
||||||
|
MXLog.error("Failed to get the camera device")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let videoInput: AVCaptureDeviceInput
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
|
||||||
|
videoInput = try AVCaptureDeviceInput(device: captureDevice)
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
// If any error occurs, simply print it out and don't continue any more.
|
||||||
|
MXLog.error("ACaptureDeviceInput error: \(error)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the input device on the capture session.
|
||||||
|
captureSession.addInput(videoInput)
|
||||||
|
|
||||||
|
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
|
||||||
|
let captureMetadataOutput = AVCaptureMetadataOutput()
|
||||||
|
captureSession.addOutput(captureMetadataOutput)
|
||||||
|
|
||||||
|
// Set delegate and use the default dispatch queue to execute the call back
|
||||||
|
captureMetadataOutput.setMetadataObjectsDelegate(delegate, queue: DispatchQueue.main)
|
||||||
|
captureMetadataOutput.metadataObjectTypes = [.qr]
|
||||||
|
|
||||||
|
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
|
||||||
|
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
|
||||||
|
videoPreviewLayer = previewLayer
|
||||||
|
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
||||||
|
videoPreviewLayer?.frame = view.layer.bounds
|
||||||
|
view.layer.addSublayer(previewLayer)
|
||||||
|
|
||||||
|
// Start video capture.
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
self.captureSession.startRunning()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillLayoutSubviews() {
|
||||||
|
super.viewWillLayoutSubviews()
|
||||||
|
videoPreviewLayer?.frame = view.layer.bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -128,7 +128,8 @@ class MockScreen: Identifiable {
|
|||||||
navigationRootCoordinator: navigationRootCoordinator,
|
navigationRootCoordinator: navigationRootCoordinator,
|
||||||
appSettings: ServiceLocator.shared.settings,
|
appSettings: ServiceLocator.shared.settings,
|
||||||
analytics: ServiceLocator.shared.analytics,
|
analytics: ServiceLocator.shared.analytics,
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
|
orientationManager: windowManager)
|
||||||
flowCoordinator.start()
|
flowCoordinator.start()
|
||||||
retainedState.append(flowCoordinator)
|
retainedState.append(flowCoordinator)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:feb1e093f66598f864fc69cf1d27d573ebf560f45faae367a368d667af4c1fc9
|
||||||
|
size 119170
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:3dac2b751ad28ba1bff26ab347acaa203f1a9bc8111c946a93359821a078f135
|
||||||
|
size 139056
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:fab3b11a840a9c7e1d0913548c2b3a8b9e5bb7bbd1f8ad39f42ea8e1752f8ce7
|
||||||
|
size 108617
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:d1ab1222ff6bca79dd7daefcb772d46d96ed98fe3cdc992151bb2e1370052ffc
|
||||||
|
size 138588
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:c2d58a58ea521543158b54cf477e25b49e732afd32b06f7807dbb8995bc1f5f3
|
||||||
|
size 173491
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6c84232c26e308c664b7f43477462301a4639eabf871a320aac52feff9478647
|
||||||
|
size 122684
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:b993abba1f7f55d40e5ef7cba92bb5b92c32d8ec6e61752f922358ec0289ede8
|
||||||
|
size 61775
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:47b1d00666502211bb71b85a8cce5f6e94d14704fde0a725b134d8bc0d319365
|
||||||
|
size 79146
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:b41c9c5b2f2be75bab8f38f5af720c32d76c5700f75208a478ef9bf759a0960a
|
||||||
|
size 52335
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:b4f241b79fe1a5a1638d032380a8f014b393edcb4ecad306661e01d019690de7
|
||||||
|
size 82179
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:91b2615e13ad67a0546e89b08d0c801b6ba1c759fc4ac03c67d3737bba41a764
|
||||||
|
size 114400
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:4b16f40a5776c3edf0dd1848e95f07ae3911a551e13b1077a6e42043d9c2365c
|
||||||
|
size 66567
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
QR Code login screen displays now the camera view.
|
||||||
Reference in New Issue
Block a user