[Quick Restore] Add new QR code screen for registration

This commit is contained in:
Marissa Le Coz 2024-12-09 14:29:49 -05:00 committed by GitHub
parent 6073e5e05f
commit c2d2629316
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 278 additions and 39 deletions

View File

@ -2405,7 +2405,7 @@
D93830742A703969006CDCDE /* LocalUsernameManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93830732A703968006CDCDE /* LocalUsernameManager.swift */; };
D938307A2A704123006CDCDE /* UsernameLinkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93830792A704123006CDCDE /* UsernameLinkManager.swift */; };
D938307C2A704338006CDCDE /* LocalUsernameManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93830752A70396E006CDCDE /* LocalUsernameManagerTests.swift */; };
D938307E2A70441D006CDCDE /* Usernames+QRCodeColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D938307D2A70441D006CDCDE /* Usernames+QRCodeColor.swift */; };
D938307E2A70441D006CDCDE /* SignalBrandedQRCodes+QRCodeColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D938307D2A70441D006CDCDE /* SignalBrandedQRCodes+QRCodeColor.swift */; };
D93830802A705D64006CDCDE /* UsernameLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = D938307F2A705D64006CDCDE /* UsernameLogger.swift */; };
D93830812A7065C7006CDCDE /* UsernameValidationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C182BEF529ACFCB200E8E1E2 /* UsernameValidationManagerTests.swift */; };
D93830832A708E95006CDCDE /* UsernameChangeDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93830822A708E95006CDCDE /* UsernameChangeDelegate.swift */; };
@ -2567,7 +2567,7 @@
D9DCFDA52A37D12100C73C0B /* UsernameLinkPresentQRCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9DCFDA42A37D12100C73C0B /* UsernameLinkPresentQRCodeViewController.swift */; };
D9DCFDAB2A39402F00C73C0B /* SettingsHeaderButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9DCFDAA2A39402F00C73C0B /* SettingsHeaderButton.swift */; };
D9DCFDAE2A3BB25200C73C0B /* QRCodeGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9DCFDAD2A3BB25200C73C0B /* QRCodeGenerator.swift */; };
D9DCFDB02A3BC4A400C73C0B /* UsernameLinkQRCodeGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9DCFDAF2A3BC4A400C73C0B /* UsernameLinkQRCodeGenerator.swift */; };
D9DCFDB02A3BC4A400C73C0B /* SignalBrandedQRCodeGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9DCFDAF2A3BC4A400C73C0B /* SignalBrandedQRCodeGenerator.swift */; };
D9DCFDB22A3BDAB000C73C0B /* ExportableQRCodeGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9DCFDB12A3BDAB000C73C0B /* ExportableQRCodeGenerator.swift */; };
D9DCFDB42A3BDC7400C73C0B /* BasicDisplayQRCodeGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9DCFDB32A3BDC7400C73C0B /* BasicDisplayQRCodeGenerator.swift */; };
D9E335A929933B1A00825677 /* Usernames+HashedUsername.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9E335A829933B1A00825677 /* Usernames+HashedUsername.swift */; };
@ -2688,6 +2688,7 @@
E1368CBE18A1C36B00109378 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9EB5ABC1884C002007CBB57 /* MessageUI.framework */; };
E1447D8F2CCACFFD004D8FA2 /* MessageBackupCallLinkRecipientArchiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1447D8E2CCACFFA004D8FA2 /* MessageBackupCallLinkRecipientArchiver.swift */; };
E14EDF6E2A71AFDF00F0FD7C /* RecipientContextMenuHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14EDF6D2A71AFDF00F0FD7C /* RecipientContextMenuHelper.swift */; };
E15066C32CED49C800F6F9AF /* RegistrationQuickRestoreQRCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15066C22CED498600F6F9AF /* RegistrationQuickRestoreQRCodeViewController.swift */; };
E16026042CE7D2490032EA77 /* chat_item_group_change_chat_update_13.binproto in Resources */ = {isa = PBXBuildFile; fileRef = E16025DA2CE7D2490032EA77 /* chat_item_group_change_chat_update_13.binproto */; };
E16026052CE7D2490032EA77 /* chat_item_group_change_chat_update_33.txtproto in Resources */ = {isa = PBXBuildFile; fileRef = E16026032CE7D2490032EA77 /* chat_item_group_change_chat_update_33.txtproto */; };
E16026062CE7D2490032EA77 /* chat_item_group_change_chat_update_15.binproto in Resources */ = {isa = PBXBuildFile; fileRef = E16025DE2CE7D2490032EA77 /* chat_item_group_change_chat_update_15.binproto */; };
@ -2814,6 +2815,7 @@
E19B35492CD2E8C40078A678 /* ad_hoc_call_02.binproto in Resources */ = {isa = PBXBuildFile; fileRef = E19B35442CD2E8C40078A678 /* ad_hoc_call_02.binproto */; };
E19B354A2CD2E8C40078A678 /* ad_hoc_call_00.binproto in Resources */ = {isa = PBXBuildFile; fileRef = E19B35402CD2E8C40078A678 /* ad_hoc_call_00.binproto */; };
E19B354B2CD2E8C40078A678 /* ad_hoc_call_01.binproto in Resources */ = {isa = PBXBuildFile; fileRef = E19B35422CD2E8C40078A678 /* ad_hoc_call_01.binproto */; };
E19C3D942CF6B36500F2C496 /* SignalBrandedQRCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19C3D932CF6B35A00F2C496 /* SignalBrandedQRCodes.swift */; };
E1A090382A4B909B00F2BE8B /* RecipientHidingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A090372A4B909B00F2BE8B /* RecipientHidingManager.swift */; };
E1B32F842CA6162A002141F4 /* LinkPreviewCallLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CFAAA22C9DD2B1003145C3 /* LinkPreviewCallLink.swift */; };
E1B628DA2CCC59E600C4DC7F /* chat_item_view_once_11.binproto in Resources */ = {isa = PBXBuildFile; fileRef = E1B628D22CCC59E600C4DC7F /* chat_item_view_once_11.binproto */; };
@ -6157,7 +6159,7 @@
D93830752A70396E006CDCDE /* LocalUsernameManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalUsernameManagerTests.swift; sourceTree = "<group>"; };
D93830772A70399C006CDCDE /* ConsumableMockPromise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsumableMockPromise.swift; sourceTree = "<group>"; };
D93830792A704123006CDCDE /* UsernameLinkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UsernameLinkManager.swift; sourceTree = "<group>"; };
D938307D2A70441D006CDCDE /* Usernames+QRCodeColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Usernames+QRCodeColor.swift"; sourceTree = "<group>"; };
D938307D2A70441D006CDCDE /* SignalBrandedQRCodes+QRCodeColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SignalBrandedQRCodes+QRCodeColor.swift"; sourceTree = "<group>"; };
D938307F2A705D64006CDCDE /* UsernameLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UsernameLogger.swift; sourceTree = "<group>"; };
D93830822A708E95006CDCDE /* UsernameChangeDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsernameChangeDelegate.swift; sourceTree = "<group>"; };
D93830842A784210006CDCDE /* UsernameApiClientImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsernameApiClientImpl.swift; sourceTree = "<group>"; };
@ -6338,7 +6340,7 @@
D9DCFDA42A37D12100C73C0B /* UsernameLinkPresentQRCodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsernameLinkPresentQRCodeViewController.swift; sourceTree = "<group>"; };
D9DCFDAA2A39402F00C73C0B /* SettingsHeaderButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHeaderButton.swift; sourceTree = "<group>"; };
D9DCFDAD2A3BB25200C73C0B /* QRCodeGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeGenerator.swift; sourceTree = "<group>"; };
D9DCFDAF2A3BC4A400C73C0B /* UsernameLinkQRCodeGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsernameLinkQRCodeGenerator.swift; sourceTree = "<group>"; };
D9DCFDAF2A3BC4A400C73C0B /* SignalBrandedQRCodeGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalBrandedQRCodeGenerator.swift; sourceTree = "<group>"; };
D9DCFDB12A3BDAB000C73C0B /* ExportableQRCodeGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportableQRCodeGenerator.swift; sourceTree = "<group>"; };
D9DCFDB32A3BDC7400C73C0B /* BasicDisplayQRCodeGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicDisplayQRCodeGenerator.swift; sourceTree = "<group>"; };
D9E335A829933B1A00825677 /* Usernames+HashedUsername.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Usernames+HashedUsername.swift"; sourceTree = "<group>"; };
@ -6458,6 +6460,7 @@
D9FD2DF42CD959B000099627 /* StorageServiceRecordIkmCapabilityStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageServiceRecordIkmCapabilityStore.swift; sourceTree = "<group>"; };
E1447D8E2CCACFFA004D8FA2 /* MessageBackupCallLinkRecipientArchiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBackupCallLinkRecipientArchiver.swift; sourceTree = "<group>"; };
E14EDF6D2A71AFDF00F0FD7C /* RecipientContextMenuHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientContextMenuHelper.swift; sourceTree = "<group>"; };
E15066C22CED498600F6F9AF /* RegistrationQuickRestoreQRCodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationQuickRestoreQRCodeViewController.swift; sourceTree = "<group>"; };
E16025C02CE7D2490032EA77 /* chat_item_group_change_chat_update_00.binproto */ = {isa = PBXFileReference; lastKnownFileType = file; path = chat_item_group_change_chat_update_00.binproto; sourceTree = "<group>"; };
E16025C12CE7D2490032EA77 /* chat_item_group_change_chat_update_00.txtproto */ = {isa = PBXFileReference; lastKnownFileType = text; path = chat_item_group_change_chat_update_00.txtproto; sourceTree = "<group>"; };
E16025C22CE7D2490032EA77 /* chat_item_group_change_chat_update_01.binproto */ = {isa = PBXFileReference; lastKnownFileType = file; path = chat_item_group_change_chat_update_01.binproto; sourceTree = "<group>"; };
@ -6584,6 +6587,7 @@
E19B35432CD2E8C40078A678 /* ad_hoc_call_01.txtproto */ = {isa = PBXFileReference; lastKnownFileType = text; path = ad_hoc_call_01.txtproto; sourceTree = "<group>"; };
E19B35442CD2E8C40078A678 /* ad_hoc_call_02.binproto */ = {isa = PBXFileReference; lastKnownFileType = file; path = ad_hoc_call_02.binproto; sourceTree = "<group>"; };
E19B35452CD2E8C40078A678 /* ad_hoc_call_02.txtproto */ = {isa = PBXFileReference; lastKnownFileType = text; path = ad_hoc_call_02.txtproto; sourceTree = "<group>"; };
E19C3D932CF6B35A00F2C496 /* SignalBrandedQRCodes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalBrandedQRCodes.swift; sourceTree = "<group>"; };
E1A090372A4B909B00F2BE8B /* RecipientHidingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientHidingManager.swift; sourceTree = "<group>"; };
E1A0AD8B16E13FDD0071E604 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
E1B628BC2CCC59E600C4DC7F /* chat_item_view_once_00.binproto */ = {isa = PBXFileReference; lastKnownFileType = file; path = chat_item_view_once_00.binproto; sourceTree = "<group>"; };
@ -9586,6 +9590,7 @@
F9D47A4729D1D5DB00E6E080 /* RegistrationPinAttemptsExhaustedAndMustCreateNewPinViewController.swift */,
F9E3006029A02D8800DCA219 /* RegistrationPinViewController.swift */,
F9EECB00299EC9D8005FDD52 /* RegistrationProfileViewController.swift */,
E15066C22CED498600F6F9AF /* RegistrationQuickRestoreQRCodeViewController.swift */,
F9440E7129C0FA490016FE95 /* RegistrationReglockTimeoutViewController.swift */,
C1F09BA02BB32E5900F9E7F5 /* RegistrationRestoreFromBackupViewController.swift */,
F933FC3E2992E77300D78DB0 /* RegistrationSplashViewController.swift */,
@ -11810,7 +11815,6 @@
D9C2D777299B07D300D79715 /* Usernames+BetterIdentifierChecker.swift */,
D9E335A829933B1A00825677 /* Usernames+HashedUsername.swift */,
D99840C3297A0ECE00F7ED6D /* Usernames+ParsedUsername.swift */,
D938307D2A70441D006CDCDE /* Usernames+QRCodeColor.swift */,
D925F552298349EF00158EE4 /* Usernames+UsernameLink.swift */,
D99840BE297A04EB00F7ED6D /* Usernames.swift */,
C15837EE29A592BA00176927 /* UsernameValidationManager.swift */,
@ -12214,7 +12218,7 @@
D9DCFDB12A3BDAB000C73C0B /* ExportableQRCodeGenerator.swift */,
D9DCFDAD2A3BB25200C73C0B /* QRCodeGenerator.swift */,
88A4CC11246CE2F50082211F /* QRCodeView.swift */,
D9DCFDAF2A3BC4A400C73C0B /* UsernameLinkQRCodeGenerator.swift */,
D9DCFDAF2A3BC4A400C73C0B /* SignalBrandedQRCodeGenerator.swift */,
);
path = QRCodes;
sourceTree = "<group>";
@ -12369,6 +12373,15 @@
path = SDSCodableModel;
sourceTree = "<group>";
};
E19C3D922CF6B34200F2C496 /* QRCodes */ = {
isa = PBXGroup;
children = (
D938307D2A70441D006CDCDE /* SignalBrandedQRCodes+QRCodeColor.swift */,
E19C3D932CF6B35A00F2C496 /* SignalBrandedQRCodes.swift */,
);
path = QRCodes;
sourceTree = "<group>";
};
E75DD3DC2810CD3500E32C36 /* subscriptions */ = {
isa = PBXGroup;
children = (
@ -12841,6 +12854,7 @@
668A01282C2B6088007B8808 /* PromiseKit */,
F9C5CA00289453B100548EEE /* Protocols */,
F9C5C9A1289453B100548EEE /* Protos */,
E19C3D922CF6B34200F2C496 /* QRCodes */,
668A00D32C2B5E4C007B8808 /* Randomness */,
6600F352298C8FBB00B1EDB7 /* Registration */,
F9C5C9C6289453B100548EEE /* RemoteAttestation */,
@ -16507,6 +16521,7 @@
F9D47A4829D1D5DB00E6E080 /* RegistrationPinAttemptsExhaustedAndMustCreateNewPinViewController.swift in Sources */,
F9E3006129A02D8800DCA219 /* RegistrationPinViewController.swift in Sources */,
F9EECB01299EC9D8005FDD52 /* RegistrationProfileViewController.swift in Sources */,
E15066C32CED49C800F6F9AF /* RegistrationQuickRestoreQRCodeViewController.swift in Sources */,
F9440E7229C0FA490016FE95 /* RegistrationReglockTimeoutViewController.swift in Sources */,
C1F09BA12BB32E5900F9E7F5 /* RegistrationRestoreFromBackupViewController.swift in Sources */,
F933FC3F2992E77300D78DB0 /* RegistrationSplashViewController.swift in Sources */,
@ -16543,6 +16558,7 @@
886BB3D325BA0CA400079781 /* SetWallpaperViewController.swift in Sources */,
F915A77029CB6D4C00EB6F68 /* ShareActivityUtil.swift in Sources */,
880D90302481E617003D2B14 /* SignalApp.swift in Sources */,
D9DCFDB02A3BC4A400C73C0B /* SignalBrandedQRCodeGenerator.swift in Sources */,
D9E43C2D2CC194140001536E /* SignalCall.swift in Sources */,
8822558D26B9D1D7001A33C4 /* SignalDotMePhoneNumberLink.swift in Sources */,
D9E43C0F2CC194140001536E /* SimulatorCallUIAdaptee.swift in Sources */,
@ -16611,7 +16627,6 @@
D9DCFDA52A37D12100C73C0B /* UsernameLinkPresentQRCodeViewController.swift in Sources */,
D9317FDC2A4CE48D00075A92 /* UsernameLinkQRCodeColorPickerViewController.swift in Sources */,
D9CAFAE62A538CA200B32BDE /* UsernameLinkQRCodeContentController.swift in Sources */,
D9DCFDB02A3BC4A400C73C0B /* UsernameLinkQRCodeGenerator.swift in Sources */,
B9FF37362B9286C6005ADDB8 /* UsernameLinkScanQRCodeSheet.swift in Sources */,
D9CAFAE42A538BDF00B32BDE /* UsernameLinkScanQRCodeViewController.swift in Sources */,
D99840CE2981A09900F7ED6D /* UsernameLinkShareSheetViewController.swift in Sources */,
@ -17601,6 +17616,8 @@
7255A4C62B98DEFB00E95368 /* SignalAttachment+VideoSegmenting.swift in Sources */,
7255A4C72B98DEFB00E95368 /* SignalAttachment.swift in Sources */,
664E8D882BD6D87700C4968A /* SignalAttachmentCloner.swift in Sources */,
D938307E2A70441D006CDCDE /* SignalBrandedQRCodes+QRCodeColor.swift in Sources */,
E19C3D942CF6B36500F2C496 /* SignalBrandedQRCodes.swift in Sources */,
F9C5CC8F289453B300548EEE /* SignalIOS.pb.swift in Sources */,
F9C5CC9E289453B300548EEE /* SignalIOSProto.swift in Sources */,
725465382BA01FAA00EABFD2 /* SignalMessagingJobQueues.swift in Sources */,
@ -17853,7 +17870,6 @@
7255A4C42B98D81000E95368 /* Usernames+BetterIdentifierChecker.swift in Sources */,
D9E335A929933B1A00825677 /* Usernames+HashedUsername.swift in Sources */,
D925F561298D8F9400158EE4 /* Usernames+ParsedUsername.swift in Sources */,
D938307E2A70441D006CDCDE /* Usernames+QRCodeColor.swift in Sources */,
D925F560298D8F9400158EE4 /* Usernames+UsernameLink.swift in Sources */,
D925F563298D8F9400158EE4 /* Usernames.swift in Sources */,
C15837EF29A592BA00176927 /* UsernameValidationManager.swift in Sources */,

View File

@ -11,6 +11,7 @@ public enum RegistrationStep: Equatable {
case registrationSplash
case changeNumberSplash
case permissions
case scanQuickRegistrationQrCode(RegistrationQuickRestoreQRCodeState)
// MARK: - Actually registering
@ -117,6 +118,7 @@ public enum RegistrationStep: Equatable {
case .registrationSplash: return "registrationSplash"
case .changeNumberSplash: return "changeNumberSplash"
case .permissions: return "permissions"
case .scanQuickRegistrationQrCode: return "scanQuickRegistrationQrCode"
case .phoneNumberEntry: return "phoneNumberEntry"
case .verificationCodeEntry: return "verificationCodeEntry"
case .transferSelection: return "transferSelection"

View File

@ -160,6 +160,18 @@ public class RegistrationNavigationController: OWSNavigationController {
// but its overkill so we have not.
update: nil
)
case .scanQuickRegistrationQrCode(let state):
return Controller(
type: RegistrationQuickRestoreQRCodeViewController.self,
make: { presenter in
return RegistrationQuickRestoreQRCodeViewController(
state: state,
presenter: presenter
)
},
// State never changes.
update: nil
)
case .phoneNumberEntry(let state):
switch state {
case .registration(let registrationMode):
@ -404,7 +416,7 @@ extension RegistrationNavigationController: RegistrationSplashPresenter {
}
public func restoreOrTransfer() {
// TODO: Enter "Restore or Transfer" flow.
// TODO [Quick Restore]: Enter "Restore or Transfer" flow.
}
public func switchToDeviceLinkingMode() {
@ -591,6 +603,12 @@ extension RegistrationNavigationController: RegistrationRestoreFromBackupPresent
}
}
extension RegistrationNavigationController: RegistrationQuickRestoreQRCodePresenter {
func cancel() {
// TODO [Quick Restore]: Pop back to the very first screen in the flow (splash).
}
}
private protocol AnyController {
var viewType: UIViewController.Type { get }

View File

@ -0,0 +1,163 @@
//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalServiceKit
import SignalUI
import SwiftUI
protocol RegistrationQuickRestoreQRCodePresenter: AnyObject {
func cancel()
}
public struct RegistrationQuickRestoreQRCodeState: Equatable {
let url: URL
}
class RegistrationQuickRestoreQRCodeViewController: OWSViewController, OWSNavigationChildController {
private let state: RegistrationQuickRestoreQRCodeState
private weak var presenter: RegistrationQuickRestoreQRCodePresenter?
init(
state: RegistrationQuickRestoreQRCodeState,
presenter: RegistrationQuickRestoreQRCodePresenter
) {
self.state = state
self.presenter = presenter
super.init()
self.addChild(hostingController)
self.view.addSubview(hostingController.view)
hostingController.view.autoPinEdgesToSuperviewEdges()
hostingController.didMove(toParent: self)
}
private lazy var hostingController = UIHostingController(rootView: ContentStack(
url: self.state.url,
cancelAction: { [weak self] in
self?.presenter?.cancel()
}
))
// MARK: OWSNavigationChildController
public var preferredNavigationBarStyle: OWSNavigationBarStyle { .solid }
public var navbarBackgroundColorOverride: UIColor? { .clear }
public var prefersNavigationBarHidden: Bool { true }
}
// MARK: - SwiftUI
private struct ContentStack: View {
let url: URL
let cancelAction: () -> Void
var body: some View {
VStack {
Spacer()
Text(OWSLocalizedString(
"REGISTRATION_SCAN_QR_CODE_TITLE",
comment: "Title for screen containing QR code that users scan with their old phone when they want to transfer/restore their message history to a new device."
))
.font(.title)
.fontWeight(.semibold)
.padding(.horizontal, 8)
.multilineTextAlignment(.center)
Spacer()
QRCode(url: url)
Spacer()
TutorialStack()
Spacer()
Spacer()
Button(CommonStrings.cancelButton, action: self.cancelAction)
.font(.body.weight(.bold))
.tint(Color.Signal.ultramarine)
.padding(.vertical, 14)
Spacer()
}
}
}
private struct TutorialStack: View {
var body: some View {
VStack(alignment: .leading, spacing: 24) {
Label(
OWSLocalizedString(
"REGISTRATION_SCAN_QR_CODE_TUTORIAL_OPEN_SIGNAL",
comment: "Tutorial text describing the first step to scanning the restore/transfer QR code with your old phone: opening Signal"
),
image: "device-phone"
)
Label(
OWSLocalizedString(
"REGISTRATION_SCAN_QR_CODE_TUTORIAL_TAP_CAMERA",
comment: "Tutorial text describing the second step to scanning the restore/transfer QR code with your old phone: tap the camera icon"
),
image: "camera"
)
Label(
OWSLocalizedString(
"REGISTRATION_SCAN_QR_CODE_TUTORIAL_SCAN",
comment: "Tutorial text describing the third step to scanning the restore/transfer QR code with your old phone: scan the code"
),
image: "qr_code"
)
}
.foregroundStyle(.secondary)
.padding(.horizontal, 8)
}
}
private struct QRCode: View {
let url: URL
var body: some View {
ZStack {
Color(.ows_gray02)
.frame(width: 296, height: 296)
.cornerRadius(24)
Color(.ows_white)
.frame(width: 216, height: 216)
.cornerRadius(12)
QRCodeViewRepresentable(url: url)
.frame(width: 212, height: 212)
}
}
}
private struct QRCodeViewRepresentable: UIViewRepresentable {
var url: URL
private let qrCodeColor = SignalBrandedQRCodes.QRCodeColor.blue.foreground
func makeUIView(context: Context) -> QRCodeView {
let view = QRCodeView(useCircularWrapper: false)
view.setContentHuggingPriority(.required, for: .vertical)
if
let qrCodeImage = SignalBrandedQRCodeGenerator(
foregroundColor: qrCodeColor,
backgroundColor: .clear
).generateQRCode(url: url)
{
let templateImage = qrCodeImage.withRenderingMode(.alwaysTemplate)
view.setQR(templateImage: templateImage, tintColor: qrCodeColor)
}
return view
}
func updateUIView(_ qrCodeView: QRCodeView, context: Context) {
// The url will never change, so there's no need to implement this.
}
}
#if DEBUG
#Preview() {
ContentStack(url: URL(string: "www.signal.org")!, cancelAction: {})
}
#endif

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "device-phone.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -28,7 +28,7 @@ class UsernameLinkPresentQRCodeViewController: OWSTableViewController2 {
usernameLink: Usernames.UsernameLink
) -> UsernameLinkState {
if
let qrCodeImage = UsernameLinkQRCodeGenerator(
let qrCodeImage = SignalBrandedQRCodeGenerator(
foregroundColor: .ows_black,
backgroundColor: .clear
).generateQRCode(url: usernameLink.url)
@ -55,7 +55,7 @@ class UsernameLinkPresentQRCodeViewController: OWSTableViewController2 {
private weak var usernameChangeDelegate: UsernameChangeDelegate?
private var qrCodeColor: Usernames.QRCodeColor!
private var qrCodeColor: SignalBrandedQRCodes.QRCodeColor!
private var _usernameLinkState: UsernameLinkState!
/// A layer of indirection to avoid needing to handle `nil` in switches,
@ -312,7 +312,7 @@ class UsernameLinkPresentQRCodeViewController: OWSTableViewController2 {
guard
let (usernameLink, _) = self.usernameLinkState.linkParams,
let qrCode = UsernameLinkQRCodeGenerator(
let qrCode = SignalBrandedQRCodeGenerator(
foregroundColor: self.qrCodeColor.foreground,
backgroundColor: qrCodeBackgroundColor
).generateQRCode(url: usernameLink.url)
@ -698,7 +698,7 @@ extension UsernameLinkPresentQRCodeViewController: SheetDismissalDelegate {
}
extension UsernameLinkPresentQRCodeViewController: UsernameLinkQRCodeColorPickerDelegate {
func didFinalizeSelectedColor(color: Usernames.QRCodeColor) {
func didFinalizeSelectedColor(color: SignalBrandedQRCodes.QRCodeColor) {
db.write { tx in
localUsernameManager.setUsernameLinkQRCodeColor(
color: color,

View File

@ -7,12 +7,12 @@ import SignalServiceKit
import SignalUI
protocol UsernameLinkQRCodeColorPickerDelegate: SheetDismissalDelegate {
func didFinalizeSelectedColor(color: Usernames.QRCodeColor)
func didFinalizeSelectedColor(color: SignalBrandedQRCodes.QRCodeColor)
}
class UsernameLinkQRCodeColorPickerViewController: OWSTableViewController2 {
private let startingColor: Usernames.QRCodeColor
private var currentColor: Usernames.QRCodeColor
private let startingColor: SignalBrandedQRCodes.QRCodeColor
private var currentColor: SignalBrandedQRCodes.QRCodeColor
private let username: String
private let qrCodeTemplateImage: UIImage
@ -20,7 +20,7 @@ class UsernameLinkQRCodeColorPickerViewController: OWSTableViewController2 {
private weak var colorPickerDelegate: UsernameLinkQRCodeColorPickerDelegate?
init(
currentColor: Usernames.QRCodeColor,
currentColor: SignalBrandedQRCodes.QRCodeColor,
username: String,
qrCodeTemplateImage: UIImage,
delegate: UsernameLinkQRCodeColorPickerDelegate
@ -99,8 +99,8 @@ class UsernameLinkQRCodeColorPickerViewController: OWSTableViewController2 {
}
private func buildColorOptionsView() -> UIView {
let colorOptionButtons: [Usernames.QRCodeColor: ColorOptionButton] = {
return Usernames.QRCodeColor.allCases.reduce(into: [:]) { partial, color in
let colorOptionButtons: [SignalBrandedQRCodes.QRCodeColor: ColorOptionButton] = {
return SignalBrandedQRCodes.QRCodeColor.allCases.reduce(into: [:]) { partial, color in
let button = ColorOptionButton(
size: 56,
color: color.background,
@ -113,7 +113,7 @@ class UsernameLinkQRCodeColorPickerViewController: OWSTableViewController2 {
}
}()
func stack(colors: [Usernames.QRCodeColor]) -> UIStackView {
func stack(colors: [SignalBrandedQRCodes.QRCodeColor]) -> UIStackView {
let stack = UIStackView(arrangedSubviews: colors.map { color in
return colorOptionButtons[color]!
})
@ -211,7 +211,7 @@ class UsernameLinkQRCodeColorPickerViewController: OWSTableViewController2 {
dismiss(animated: true)
}
private func didSelectColor(color selectedColor: Usernames.QRCodeColor) {
private func didSelectColor(color selectedColor: SignalBrandedQRCodes.QRCodeColor) {
currentColor = selectedColor
reloadTableContents()
}

View File

@ -6,12 +6,12 @@
import SignalServiceKit
import SignalUI
/// A generator producing styled QR codes for username links.
/// A generator producing styled QR codes.
///
/// The QR codes have a configurable foreground and background, and contain
/// aesthetic features such as an overlaid Signal logo and rounded "pixels".
/// They are scaled up so as to appropriately render the rounded shapes.
class UsernameLinkQRCodeGenerator: QRCodeGenerator {
class SignalBrandedQRCodeGenerator: QRCodeGenerator {
/// For the following constants:
/// - "Point" refers to a coordinate ("pixel") in the QR code.
/// - "Pixel" refers to a pixel in the image returned by the generator.

View File

@ -6133,6 +6133,18 @@
/* No comment provided by engineer. */
"REGISTRATION_RESTRICTED_MESSAGE" = "You need to register before you can send a message.";
/* Title for screen containing QR code that users scan with their old phone when they want to transfer/restore their message history to a new device. */
"REGISTRATION_SCAN_QR_CODE_TITLE" = "Scan this code with your old phone";
/* Tutorial text describing the first step to scanning the restore/transfer QR code with your old phone: opening Signal */
"REGISTRATION_SCAN_QR_CODE_TUTORIAL_OPEN_SIGNAL" = "Open Signal on your old device";
/* Tutorial text describing the third step to scanning the restore/transfer QR code with your old phone: scan the code */
"REGISTRATION_SCAN_QR_CODE_TUTORIAL_SCAN" = "Scan this code with the camera";
/* Tutorial text describing the second step to scanning the restore/transfer QR code with your old phone: tap the camera icon */
"REGISTRATION_SCAN_QR_CODE_TUTORIAL_TAP_CAMERA" = "Tap the camera icon";
/* Button when sending a verification code via sms failed, but resending via voice call might succeed. */
"REGISTRATION_SMS_CODE_FAILED_TRY_VOICE_BUTTON" = "Voice Call";

View File

@ -426,7 +426,7 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
}
}
private extension Usernames.QRCodeColor {
private extension SignalBrandedQRCodes.QRCodeColor {
var backupProtoColor: BackupProto_AccountData.UsernameLink.Color {
switch self {
case .blue: return .blue
@ -442,7 +442,7 @@ private extension Usernames.QRCodeColor {
}
private extension BackupProto_AccountData.UsernameLink.Color {
var qrCodeColor: Usernames.QRCodeColor {
var qrCodeColor: SignalBrandedQRCodes.QRCodeColor {
switch self {
case .blue: return .blue
case .white: return .white

View File

@ -25,8 +25,8 @@ public class MockLocalUsernameManager: LocalUsernameManager {
public func setLocalUsername(username: String, usernameLink: Usernames.UsernameLink, tx: DBWriteTransaction) { owsFail("Not implemented!") }
public func clearLocalUsername(tx: DBWriteTransaction) { owsFail("Not implemented!") }
public func usernameLinkQRCodeColor(tx: DBReadTransaction) -> Usernames.QRCodeColor { owsFail("Not implemented!") }
public func setUsernameLinkQRCodeColor(color: Usernames.QRCodeColor, tx: DBWriteTransaction) { owsFail("Not implemented!") }
public func usernameLinkQRCodeColor(tx: DBReadTransaction) -> SignalBrandedQRCodes.QRCodeColor { owsFail("Not implemented!") }
public func setUsernameLinkQRCodeColor(color: SignalBrandedQRCodes.QRCodeColor, tx: DBWriteTransaction) { owsFail("Not implemented!") }
public func reserveUsername(usernameCandidates: Usernames.HashedUsername.GeneratedCandidates) -> Guarantee<Usernames.RemoteMutationResult<Usernames.ReservationResult>> { owsFail("Not implemented!") }
public func confirmUsername(reservedUsername: Usernames.HashedUsername, tx: DBWriteTransaction) -> Guarantee<Usernames.RemoteMutationResult<Usernames.ConfirmationResult>> { owsFail("Not implemented!") }
public func deleteUsername(tx: DBWriteTransaction) -> Guarantee<Usernames.RemoteMutationResult<Void>> { owsFail("Not implemented!") }

View File

@ -3,10 +3,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
//
/// Preset color options for the username link QR code.
/// Preset color options for the Signal-branded QR code.
///
/// Exposes a set of colors to use for various parts of the QR code rendering.
public extension Usernames {
public extension SignalBrandedQRCodes {
enum QRCodeColor: String, UnknownEnumCodable, CaseIterable {
case blue
case white
@ -17,7 +17,7 @@ public extension Usernames {
case pink
case purple
public static var unknown: Usernames.QRCodeColor { .blue }
public static var unknown: SignalBrandedQRCodes.QRCodeColor { .blue }
/// Background color for the QR code.
public var background: UIColor {

View File

@ -0,0 +1,7 @@
//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
/// A namespace for Signal-branded QR code related models.
public enum SignalBrandedQRCodes {}

View File

@ -1411,7 +1411,7 @@ class StorageServiceAccountRecordUpdater: StorageServiceRecordUpdater {
)
localUsernameManager.setUsernameLinkQRCodeColor(
color: Usernames.QRCodeColor(proto: remoteUsernameLinkProto.color),
color: SignalBrandedQRCodes.QRCodeColor(proto: remoteUsernameLinkProto.color),
tx: transaction.asV2Write
)
} else {
@ -1990,7 +1990,7 @@ extension StorageServiceProtoOptionalBool {
}
}
private extension Usernames.QRCodeColor {
private extension SignalBrandedQRCodes.QRCodeColor {
var asProto: StorageServiceProtoAccountRecordUsernameLinkColor {
switch self {
case .blue: return .blue

View File

@ -45,11 +45,11 @@ public protocol LocalUsernameManager {
func clearLocalUsername(tx: DBWriteTransaction)
/// Returns the color to be used for the local user's username link QR code.
func usernameLinkQRCodeColor(tx: DBReadTransaction) -> Usernames.QRCodeColor
func usernameLinkQRCodeColor(tx: DBReadTransaction) -> SignalBrandedQRCodes.QRCodeColor
/// Sets the color to be used for the local user's username link QR code.
func setUsernameLinkQRCodeColor(
color: Usernames.QRCodeColor,
color: SignalBrandedQRCodes.QRCodeColor,
tx: DBWriteTransaction
)
@ -236,7 +236,7 @@ class LocalUsernameManagerImpl: LocalUsernameManager {
return nil
}
func usernameLinkColor(tx: DBReadTransaction) -> Usernames.QRCodeColor {
func usernameLinkColor(tx: DBReadTransaction) -> SignalBrandedQRCodes.QRCodeColor {
return (try? kvStore.getCodableValue(
forKey: Constants.usernameLinkQRCodeColorKey,
transaction: tx
@ -252,7 +252,7 @@ class LocalUsernameManagerImpl: LocalUsernameManager {
kvStore.setData(usernameLink?.entropy, key: Constants.usernameLinkEntropyKey, transaction: tx)
}
func setUsernameLinkColor(color: Usernames.QRCodeColor, tx: DBWriteTransaction) {
func setUsernameLinkColor(color: SignalBrandedQRCodes.QRCodeColor, tx: DBWriteTransaction) {
try? kvStore.setCodable(color, key: Constants.usernameLinkQRCodeColorKey, transaction: tx)
}
}
@ -366,12 +366,12 @@ class LocalUsernameManagerImpl: LocalUsernameManager {
func usernameLinkQRCodeColor(
tx: DBReadTransaction
) -> Usernames.QRCodeColor {
) -> SignalBrandedQRCodes.QRCodeColor {
return usernameStore.usernameLinkColor(tx: tx)
}
func setUsernameLinkQRCodeColor(
color: Usernames.QRCodeColor,
color: SignalBrandedQRCodes.QRCodeColor,
tx: DBWriteTransaction
) {
usernameStore.setUsernameLinkColor(color: color, tx: tx)

View File

@ -92,7 +92,7 @@ class LocalUsernameManagerTests: XCTestCase {
}
func testUsernameQRCodeColorChanges() {
func color() -> Usernames.QRCodeColor {
func color() -> SignalBrandedQRCodes.QRCodeColor {
return mockDB.read { tx in
return localUsernameManager.usernameLinkQRCodeColor(tx: tx)
}