diff --git a/Signal/AppLaunch/SignalApp.swift b/Signal/AppLaunch/SignalApp.swift index b1fcd28a24..600c1d66c8 100644 --- a/Signal/AppLaunch/SignalApp.swift +++ b/Signal/AppLaunch/SignalApp.swift @@ -105,6 +105,14 @@ extension SignalApp { conversationSplitViewController.showAppSettingsWithMode(mode, completion: completion) } + func showCameraCaptureView(completion: ((UINavigationController) -> Void)? = nil) { + guard let conversationSplitViewController else { + owsFailDebug("Missing conversationSplitViewController.") + return + } + conversationSplitViewController.showCameraView(completion: completion) + } + func showRegistration( loader: RegistrationCoordinatorLoader, desiredMode: RegistrationMode, diff --git a/Signal/DeviceTransfer/OutgoingDeviceRestorePresenter.swift b/Signal/DeviceTransfer/OutgoingDeviceRestorePresenter.swift index 4b9dd4d871..e8d0a7d4cd 100644 --- a/Signal/DeviceTransfer/OutgoingDeviceRestorePresenter.swift +++ b/Signal/DeviceTransfer/OutgoingDeviceRestorePresenter.swift @@ -43,8 +43,8 @@ class OutgoingDeviceRestorePresenter: OutgoingDeviceRestoreInitialPresenter { deviceProvisioningURL: provisioningURL ) - internalNavigationController.pushViewController( - OutgoingDeviceRestoreIntialViewController(presenter: self), + internalNavigationController.setViewControllers( + [OutgoingDeviceRestoreIntialViewController(presenter: self)], animated: false ) diff --git a/Signal/Images.xcassets/phone-qr.imageset/Contents.json b/Signal/Images.xcassets/phone-qr.imageset/Contents.json new file mode 100644 index 0000000000..2f9f66df61 --- /dev/null +++ b/Signal/Images.xcassets/phone-qr.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "phone_qr.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "phone_qr_dark 1.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/phone-qr.imageset/phone_qr.pdf b/Signal/Images.xcassets/phone-qr.imageset/phone_qr.pdf new file mode 100644 index 0000000000..47d029f527 Binary files /dev/null and b/Signal/Images.xcassets/phone-qr.imageset/phone_qr.pdf differ diff --git a/Signal/Images.xcassets/phone-qr.imageset/phone_qr_dark 1.pdf b/Signal/Images.xcassets/phone-qr.imageset/phone_qr_dark 1.pdf new file mode 100644 index 0000000000..e2b70ea1cd Binary files /dev/null and b/Signal/Images.xcassets/phone-qr.imageset/phone_qr_dark 1.pdf differ diff --git a/Signal/URLs/UrlOpener.swift b/Signal/URLs/UrlOpener.swift index 4e2e11409a..b86296e5ee 100644 --- a/Signal/URLs/UrlOpener.swift +++ b/Signal/URLs/UrlOpener.swift @@ -214,22 +214,44 @@ class UrlOpener { linkDeviceWarningActionSheet.addAction(.cancel) rootViewController.presentActionSheet(linkDeviceWarningActionSheet) - case .quickRestore(let url): + case .quickRestore: let registeredState = try tsAccountManager.registeredStateWithMaybeSneakyTransaction() guard registeredState.isPrimary else { Logger.warn("Ignoring URL; not primary device.") return } - let provisioningURL = DeviceProvisioningURL(urlString: url.absoluteString) - if let provisioningURL { - AppEnvironment.shared.outgoingDeviceRestorePresenter.present( - provisioningURL: provisioningURL, - presentingViewController: CurrentAppContext().frontmostViewController()!, - animated: true + let quickRestoreWarningActionSheet = ActionSheetController( + message: OWSLocalizedString( + "QUICK_RESTORE_URL_OPENED_ACTION_SHEET_EXTERNAL_URL_MESSAGE", + comment: "Message for an action sheet telling users how to use quick restore, when trying to open an external quick restore URL." ) + ) + + let showCameraViewAction = ActionSheetAction( + title: CommonStrings.continueButton + ) { _ in + SignalApp.shared.showCameraCaptureView { navController in + let sheet = HeroSheetViewController( + hero: .image(UIImage(named: "phone-qr")!), + title: OWSLocalizedString( + "QUICK_RESTORE_URL_OPENED_ACTION_SHEET_EXTERNAL_URL_ACTION_TITLE", + comment: "Title for sheet with info about scanning a Quick Restore QR code" + ), + body: OWSLocalizedString( + "QUICK_RESTORE_URL_OPENED_ACTION_SHEET_EXTERNAL_URL_ACTION_BODY", + comment: "Body for sheet with info about scanning a Quick Restore QR code" + ), + primaryButton: .dismissing(title: CommonStrings.okButton) + ) + navController.topViewController?.present(sheet, animated: true) + } } + quickRestoreWarningActionSheet.addAction(showCameraViewAction) + quickRestoreWarningActionSheet.addAction(.cancel) + rootViewController.presentActionSheet(quickRestoreWarningActionSheet) + case .completeIDEALDonation(let donationType): _ = try tsAccountManager.registeredStateWithMaybeSneakyTransaction() Task { [appReadiness, databaseStorage] in diff --git a/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+Camera.swift b/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+Camera.swift index bc8115b688..aa600d2a45 100644 --- a/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+Camera.swift +++ b/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+Camera.swift @@ -10,6 +10,10 @@ extension ChatListViewController: CameraFirstCaptureDelegate { @objc func showCameraView() { + presentCameraView() + } + + func presentCameraView(completion: ((UINavigationController) -> Void)? = nil) { // Dismiss any message actions if they're presented conversationSplitViewController?.selectedConversationViewController?.dismissMessageContextMenu(animated: true) @@ -39,6 +43,7 @@ extension ChatListViewController: CameraFirstCaptureDelegate { cameraModal.modalPresentationCapturesStatusBarAppearance = true cameraModal.setNeedsStatusBarAppearanceUpdate() } + completion?(cameraModal) }) } } diff --git a/Signal/src/ViewControllers/HomeView/ConversationSplitViewController.swift b/Signal/src/ViewControllers/HomeView/ConversationSplitViewController.swift index bd252c2ccf..75fbc18a49 100644 --- a/Signal/src/ViewControllers/HomeView/ConversationSplitViewController.swift +++ b/Signal/src/ViewControllers/HomeView/ConversationSplitViewController.swift @@ -562,6 +562,11 @@ class ConversationSplitViewController: UISplitViewController, ConversationSplit homeVC.chatListViewController.showAppSettings() } + @objc + func showCameraView(completion: ((UINavigationController) -> Void)? = nil) { + homeVC.chatListViewController.presentCameraView(completion: completion) + } + func showAppSettingsWithMode(_ mode: ChatListViewController.ShowAppSettingsMode, completion: (() -> Void)? = nil) { homeVC.chatListViewController.showAppSettings(mode: mode, completion: completion) } diff --git a/Signal/src/ViewControllers/Photos/PhotoCaptureViewController.swift b/Signal/src/ViewControllers/Photos/PhotoCaptureViewController.swift index 8860c5268e..f1010acc9b 100644 --- a/Signal/src/ViewControllers/Photos/PhotoCaptureViewController.swift +++ b/Signal/src/ViewControllers/Photos/PhotoCaptureViewController.swift @@ -1340,12 +1340,23 @@ extension PhotoCaptureViewController: QRCodeSampleBufferScannerDelegate { linkDeviceWarningActionSheet.addAction(cancelAction) presentActionSheet(linkDeviceWarningActionSheet) case .quickRestore: - self.dismiss(animated: true) { - AppEnvironment.shared.outgoingDeviceRestorePresenter.present( - provisioningURL: provisioningURL, - presentingViewController: CurrentAppContext().frontmostViewController()!, - animated: true - ) + let presentBlock = { + self.dismiss(animated: true) { + AppEnvironment.shared.outgoingDeviceRestorePresenter.present( + provisioningURL: provisioningURL, + presentingViewController: CurrentAppContext().frontmostViewController()!, + animated: true + ) + } + } + // If anything is presented over the phone capture view, dismiss it first - + // then dismiss the photo view and present the restore UI + if navigationController?.presentedViewController != nil { + self.navigationController?.presentedViewController?.dismiss(animated: true) { + presentBlock() + } + } else { + presentBlock() } } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index a4aba52b56..82aee53e21 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -6889,6 +6889,15 @@ /* Title of alert shown when push tokens sync job succeeds. */ "PUSH_REGISTER_SUCCESS" = "Successfully re-registered for push notifications."; +/* Body for sheet with info about scanning a Quick Restore QR code */ +"QUICK_RESTORE_URL_OPENED_ACTION_SHEET_EXTERNAL_URL_ACTION_BODY" = "Use this device to scan the QR code on the device you want to transfer to"; + +/* Title for sheet with info about scanning a Quick Restore QR code */ +"QUICK_RESTORE_URL_OPENED_ACTION_SHEET_EXTERNAL_URL_ACTION_TITLE" = "Scan QR Code"; + +/* Message for an action sheet telling users how to use quick restore, when trying to open an external quick restore URL. */ +"QUICK_RESTORE_URL_OPENED_ACTION_SHEET_EXTERNAL_URL_MESSAGE" = "To transfer this account to a new device, tap “Continue” and scan the QR code with the Signal camera. Make sure you only scan QR codes that come directly from Signal."; + /* Accessibility label stating the author of the message to which you are replying. Embeds: {{ the author of the message to which you are replying }}. */ "QUOTED_REPLY_ACCESSIBILITY_LABEL_FORMAT" = "Replying to %@.";