diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 2ad354504..8617b32bc 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -574,6 +574,9 @@ B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; }; B885D5F4233491AB00EE0D8E /* DeviceLinkingModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */; }; B885D5F62334A32100EE0D8E /* UIView+Constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */; }; + B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A62398B23E00211ABE /* QRCodeVC.swift */; }; + B886B4A92398BA1500211ABE /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A82398BA1500211ABE /* QRCode.swift */; }; + B886B4AB2398BE6800211ABE /* SpaceMono-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B886B4AA2398BE6800211ABE /* SpaceMono-Bold.ttf */; }; B891105C2320872800F15FCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B891105B2320872800F15FCC /* GoogleService-Info.plist */; }; B891105E2320872800F15FCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B891105B2320872800F15FCC /* GoogleService-Info.plist */; }; B891105F2320872800F15FCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B891105B2320872800F15FCC /* GoogleService-Info.plist */; }; @@ -1402,6 +1405,9 @@ B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = ""; }; B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModal.swift; sourceTree = ""; }; B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraints.swift"; sourceTree = ""; }; + B886B4A62398B23E00211ABE /* QRCodeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeVC.swift; sourceTree = ""; }; + B886B4A82398BA1500211ABE /* QRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCode.swift; sourceTree = ""; }; + B886B4AA2398BE6800211ABE /* SpaceMono-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SpaceMono-Bold.ttf"; path = "../../../../../Downloads/space-mono/SpaceMono-Bold.ttf"; sourceTree = ""; }; B891105B2320872800F15FCC /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQRCodeWrapperVC.swift; sourceTree = ""; }; B894D0702339D6F300B4D94D /* DeviceLinkingModalDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModalDelegate.swift; sourceTree = ""; }; @@ -2740,6 +2746,7 @@ B8BB82BA2394D47000BA5194 /* Loki */ = { isa = PBXGroup; children = ( + B886B4AA2398BE6800211ABE /* SpaceMono-Bold.ttf */, B8CCF6342396005F0091D419 /* SpaceMono-Regular.ttf */, ); path = Loki; @@ -2787,6 +2794,7 @@ isa = PBXGroup; children = ( B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */, + B886B4A82398BA1500211ABE /* QRCode.swift */, ); path = Utilities; sourceTree = ""; @@ -2804,6 +2812,7 @@ B8CCF63623961D6D0091D419 /* NewPrivateChatVC.swift */, B894D0742339EDCF00B4D94D /* NukeDataModal.swift */, B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */, + B886B4A62398B23E00211ABE /* QRCodeVC.swift */, B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */, B86BD08523399CEF000F5AE3 /* SeedModal.swift */, B8CCF6422397711F0091D419 /* SettingsVC.swift */, @@ -3294,6 +3303,7 @@ 34C3C78F2040A4F70000134C /* sonarping.mp3 in Resources */, AD83FF431A73426500B5C81A /* audio_play_button@2x.png in Resources */, 34661FB820C1C0D60056EDD6 /* message_sent.aiff in Resources */, + B886B4AB2398BE6800211ABE /* SpaceMono-Bold.ttf in Resources */, 45CB2FA81CB7146C00E1B343 /* Launch Screen.storyboard in Resources */, B633C5C31A1D190B0059AC12 /* mute_off@2x.png in Resources */, AD83FF411A73426500B5C81A /* audio_play_button_blue@2x.png in Resources */, @@ -3880,6 +3890,7 @@ 34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */, 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */, 45B27B862037FFB400A539DF /* DebugUIFileBrowser.swift in Sources */, + B886B4A92398BA1500211ABE /* QRCode.swift in Sources */, 3496955D219B605E00DCFE74 /* PhotoCollectionPickerController.swift in Sources */, 34CE88E71F2FB9A10098030F /* ProfileViewController.m in Sources */, 3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */, @@ -3951,6 +3962,7 @@ B8162F0322891AD600D46544 /* FriendRequestView.swift in Sources */, 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */, 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, + B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */, 4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */, 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */, 340FC8AB204DAC8D007AEB0F /* DomainFrontingCountryViewController.m in Sources */, diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 5177b29ba..3b4e1293c 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -139,6 +139,7 @@ dripicons-v2.ttf ElegantIcons.ttf fontawesome-webfont.ttf + SpaceMono-Bold.ttf SpaceMono-Regular.ttf UIApplicationShortcutItems diff --git a/Signal/src/Loki/Redesign/Style Guide/Fonts.swift b/Signal/src/Loki/Redesign/Style Guide/Fonts.swift index acd362d2e..7611aa3f6 100644 --- a/Signal/src/Loki/Redesign/Style Guide/Fonts.swift +++ b/Signal/src/Loki/Redesign/Style Guide/Fonts.swift @@ -5,4 +5,8 @@ final class Fonts : NSObject { @objc static func spaceMono(ofSize size: CGFloat) -> UIFont { return UIFont(name: "SpaceMono-Regular", size: size)! } + + @objc static func boldSpaceMono(ofSize size: CGFloat) -> UIFont { + return UIFont(name: "SpaceMono-Bold", size: size)! + } } diff --git a/Signal/src/Loki/Redesign/Utilities/QRCode.swift b/Signal/src/Loki/Redesign/Utilities/QRCode.swift new file mode 100644 index 000000000..fe7d04cb2 --- /dev/null +++ b/Signal/src/Loki/Redesign/Utilities/QRCode.swift @@ -0,0 +1,21 @@ + +enum QRCode { + + static func generate(for string: String, isInverted: Bool = true) -> UIImage { + let data = string.data(using: .utf8) + var qrCodeAsCIImage: CIImage + let filter1 = CIFilter(name: "CIQRCodeGenerator")! + filter1.setValue(data, forKey: "inputMessage") + qrCodeAsCIImage = filter1.outputImage! + if isInverted { + let filter2 = CIFilter(name: "CIColorInvert")! + filter2.setValue(qrCodeAsCIImage, forKey: "inputImage") + qrCodeAsCIImage = filter2.outputImage! + let filter3 = CIFilter(name: "CIMaskToAlpha")! + filter3.setValue(qrCodeAsCIImage, forKey: "inputImage") + qrCodeAsCIImage = filter3.outputImage! + } + let scaledQRCodeAsCIImage = qrCodeAsCIImage.transformed(by: CGAffineTransform(scaleX: 6.4, y: 6.4)) + return UIImage(ciImage: scaledQRCodeAsCIImage) + } +} diff --git a/Signal/src/Loki/Redesign/View Controllers/JoinPublicChatVC.swift b/Signal/src/Loki/Redesign/View Controllers/JoinPublicChatVC.swift index d5d4db7be..6a5d99c86 100644 --- a/Signal/src/Loki/Redesign/View Controllers/JoinPublicChatVC.swift +++ b/Signal/src/Loki/Redesign/View Controllers/JoinPublicChatVC.swift @@ -191,7 +191,7 @@ private final class EnterChatURLVC : UIViewController { nextButton.pin(.top, to: .top, of: nextButtonContainer) nextButtonContainer.pin(.trailing, to: .trailing, of: nextButton, withInset: 80) nextButtonContainer.pin(.bottom, to: .bottom, of: nextButton) - // Stack view + // Set up stack view let stackView = UIStackView(arrangedSubviews: [ chatURLTextField, UIView.spacer(withHeight: Values.smallSpacing), explanationLabel, UIView.vStretchingSpacer(), nextButtonContainer ]) stackView.axis = .vertical stackView.alignment = .fill diff --git a/Signal/src/Loki/Redesign/View Controllers/NewPrivateChatVC.swift b/Signal/src/Loki/Redesign/View Controllers/NewPrivateChatVC.swift index 4bac74001..5d5c45816 100644 --- a/Signal/src/Loki/Redesign/View Controllers/NewPrivateChatVC.swift +++ b/Signal/src/Loki/Redesign/View Controllers/NewPrivateChatVC.swift @@ -143,8 +143,7 @@ private final class EnterPublicKeyVC : UIViewController { weak var newPrivateChatVC: NewPrivateChatVC! private lazy var userHexEncodedPublicKey: String = { - let userDefaults = UserDefaults.standard - if let masterHexEncodedPublicKey = userDefaults.string(forKey: "masterDeviceHexEncodedPublicKey") { + if let masterHexEncodedPublicKey = UserDefaults.standard.string(forKey: "masterDeviceHexEncodedPublicKey") { return masterHexEncodedPublicKey } else { return OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey @@ -202,7 +201,7 @@ private final class EnterPublicKeyVC : UIViewController { nextButton.pin(.top, to: .top, of: nextButtonContainer) nextButtonContainer.pin(.trailing, to: .trailing, of: nextButton, withInset: 80) nextButtonContainer.pin(.bottom, to: .bottom, of: nextButton) - // Stack view + // Set up stack view let stackView = UIStackView(arrangedSubviews: [ publicKeyTextField, UIView.spacer(withHeight: Values.smallSpacing), explanationLabel, UIView.spacer(withHeight: Values.largeSpacing), separator, UIView.spacer(withHeight: Values.veryLargeSpacing), userPublicKeyLabel, UIView.spacer(withHeight: Values.veryLargeSpacing), buttonContainer, UIView.vStretchingSpacer(), nextButtonContainer ]) stackView.axis = .vertical stackView.alignment = .fill diff --git a/Signal/src/Loki/Redesign/View Controllers/QRCodeVC.swift b/Signal/src/Loki/Redesign/View Controllers/QRCodeVC.swift new file mode 100644 index 000000000..d3cacd9d8 --- /dev/null +++ b/Signal/src/Loki/Redesign/View Controllers/QRCodeVC.swift @@ -0,0 +1,271 @@ + +final class QRCodeVC : UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate { + private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) + private var pages: [UIViewController] = [] + private var targetVCIndex: Int? + private var tabBarTopConstraint: NSLayoutConstraint! + + // MARK: Settings + override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent } + + // MARK: Components + private lazy var tabBar: TabBar = { + let tabs = [ + TabBar.Tab(title: NSLocalizedString("View My QR Code", comment: "")) { [weak self] in + guard let self = self else { return } + self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil) + }, + TabBar.Tab(title: NSLocalizedString("Scan QR Code", comment: "")) { [weak self] in + guard let self = self else { return } + self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil) + } + ] + return TabBar(tabs: tabs) + }() + + private lazy var viewMyQRCodeVC: ViewMyQRCodeVC = { + let result = ViewMyQRCodeVC() + result.qrCodeVC = self + return result + }() + + private lazy var scanQRCodePlaceholderVC: ScanQRCodePlaceholderVC = { + let result = ScanQRCodePlaceholderVC() + result.qrCodeVC = self + return result + }() + + private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = { + let message = NSLocalizedString("Scan someone's QR code to start a conversation with them", comment: "") + let result = ScanQRCodeWrapperVC(message: message) + result.delegate = self + return result + }() + + // MARK: Lifecycle + override func viewDidLoad() { + // Set gradient background + view.backgroundColor = .clear + let gradient = Gradients.defaultLokiBackground + view.setGradient(gradient) + // Set navigation bar background color + let navigationBar = navigationController!.navigationBar + navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default) + navigationBar.shadowImage = UIImage() + navigationBar.isTranslucent = false + navigationBar.barTintColor = Colors.navigationBarBackground + // Customize title + let titleLabel = UILabel() + titleLabel.text = NSLocalizedString("QR Code", comment: "") + titleLabel.textColor = Colors.text + titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize) + navigationItem.titleView = titleLabel + // Set up page VC + let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized) + pages = [ viewMyQRCodeVC, (hasCameraAccess ? scanQRCodeWrapperVC : scanQRCodePlaceholderVC) ] + pageVC.dataSource = self + pageVC.delegate = self + pageVC.setViewControllers([ viewMyQRCodeVC ], direction: .forward, animated: false, completion: nil) + // Set up tab bar + view.addSubview(tabBar) + tabBar.pin(.leading, to: .leading, of: view) + tabBarTopConstraint = tabBar.pin(.top, to: .top, of: view) + view.pin(.trailing, to: .trailing, of: tabBar) + // Set up page VC constraints + let pageVCView = pageVC.view! + view.addSubview(pageVCView) + pageVCView.pin(.leading, to: .leading, of: view) + pageVCView.pin(.top, to: .bottom, of: tabBar) + view.pin(.trailing, to: .trailing, of: pageVCView) + view.pin(.bottom, to: .bottom, of: pageVCView) + let screen = UIScreen.main.bounds + pageVCView.set(.width, to: screen.width) + let height = navigationController!.view.bounds.height - navigationBar.height() - Values.tabBarHeight + pageVCView.set(.height, to: height) + viewMyQRCodeVC.constrainHeight(to: height) + scanQRCodePlaceholderVC.constrainHeight(to: height) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + tabBarTopConstraint.constant = navigationController!.navigationBar.height() + } + + // MARK: General + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + guard let index = pages.firstIndex(of: viewController), index != 0 else { return nil } + return pages[index - 1] + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + guard let index = pages.firstIndex(of: viewController), index != (pages.count - 1) else { return nil } + return pages[index + 1] + } + + fileprivate func handleCameraAccessGranted() { + pages[1] = scanQRCodeWrapperVC + pageVC.setViewControllers([ scanQRCodeWrapperVC ], direction: .forward, animated: false, completion: nil) + } + + // MARK: Updating + func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) { + guard let targetVC = pendingViewControllers.first, let index = pages.firstIndex(of: targetVC) else { return } + targetVCIndex = index + } + + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating isFinished: Bool, previousViewControllers: [UIViewController], transitionCompleted isCompleted: Bool) { + guard isCompleted, let index = targetVCIndex else { return } + tabBar.selectTab(at: index) + } + + // MARK: Interaction + @objc private func close() { + dismiss(animated: true, completion: nil) + } + + func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) { + let hexEncodedPublicKey = string + startNewPrivateChatIfPossible(with: hexEncodedPublicKey) + } + + fileprivate func startNewPrivateChatIfPossible(with hexEncodedPublicKey: String) { + if !ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) { + let alert = UIAlertController(title: NSLocalizedString("Invalid Public Key", comment: ""), message: NSLocalizedString("Please check the public key you entered and try again.", comment: ""), preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) + presentAlert(alert) + } else { + let thread = TSContactThread.getOrCreateThread(contactId: hexEncodedPublicKey) + presentingViewController?.dismiss(animated: true, completion: nil) + SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false) + } + } +} + +private final class ViewMyQRCodeVC : UIViewController { + weak var qrCodeVC: QRCodeVC! + private var bottomConstraint: NSLayoutConstraint! + + private lazy var userHexEncodedPublicKey: String = { + if let masterHexEncodedPublicKey = UserDefaults.standard.string(forKey: "masterDeviceHexEncodedPublicKey") { + return masterHexEncodedPublicKey + } else { + return OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey + } + }() + + // MARK: Lifecycle + override func viewDidLoad() { + // Remove background color + view.backgroundColor = .clear + // Set up title label + let titleLabel = UILabel() + titleLabel.textColor = Colors.text + titleLabel.font = .boldSystemFont(ofSize: Values.massiveFontSize) + titleLabel.text = NSLocalizedString("Scan Me", comment: "") + titleLabel.numberOfLines = 0 + titleLabel.textAlignment = .center + titleLabel.lineBreakMode = .byWordWrapping + // Set up QR code image view + let qrCodeImageView = UIImageView() + let qrCode = QRCode.generate(for: userHexEncodedPublicKey) + qrCodeImageView.image = qrCode + qrCodeImageView.contentMode = .scaleAspectFit + // Set up explanation label + let explanationLabel = UILabel() + explanationLabel.textColor = Colors.text + explanationLabel.font = Fonts.spaceMono(ofSize: Values.mediumFontSize) + let text = NSLocalizedString("This is your unique public QR code. Other users may scan this in order to begin a conversation with you.", comment: "") + let attributedText = NSMutableAttributedString(string: text) + attributedText.addAttribute(.font, value: Fonts.boldSpaceMono(ofSize: Values.mediumFontSize), range: (text as NSString).range(of: "your unique public QR code")) + explanationLabel.attributedText = attributedText + explanationLabel.numberOfLines = 0 + explanationLabel.textAlignment = .center + explanationLabel.lineBreakMode = .byWordWrapping + // Set up share button + let shareButton = Button(style: .regular, size: .large) + shareButton.setTitle(NSLocalizedString("Share", comment: ""), for: UIControl.State.normal) + shareButton.addTarget(self, action: #selector(shareQRCode), for: UIControl.Event.touchUpInside) + // Set up share button container + let shareButtonContainer = UIView() + shareButtonContainer.addSubview(shareButton) + shareButton.pin(.leading, to: .leading, of: shareButtonContainer, withInset: 80) + shareButton.pin(.top, to: .top, of: shareButtonContainer) + shareButtonContainer.pin(.trailing, to: .trailing, of: shareButton, withInset: 80) + shareButtonContainer.pin(.bottom, to: .bottom, of: shareButton) + // Set up stack view + let stackView = UIStackView(arrangedSubviews: [ titleLabel, qrCodeImageView, explanationLabel, shareButtonContainer, UIView.vStretchingSpacer() ]) + stackView.axis = .vertical + stackView.spacing = Values.largeSpacing + stackView.alignment = .fill + stackView.layoutMargins = UIEdgeInsets(top: Values.mediumSpacing, left: Values.largeSpacing, bottom: Values.mediumSpacing, right: Values.largeSpacing) + stackView.isLayoutMarginsRelativeArrangement = true + view.addSubview(stackView) + stackView.pin(.leading, to: .leading, of: view) + stackView.pin(.top, to: .top, of: view) + view.pin(.trailing, to: .trailing, of: stackView) + bottomConstraint = view.pin(.bottom, to: .bottom, of: stackView) + // Set up width constraint + view.set(.width, to: UIScreen.main.bounds.width) + } + + // MARK: General + func constrainHeight(to height: CGFloat) { + view.set(.height, to: height) + } + + // MARK: Interaction + @objc private func shareQRCode() { + let qrCode = QRCode.generate(for: userHexEncodedPublicKey, isInverted: false) + let shareVC = UIActivityViewController(activityItems: [ qrCode ], applicationActivities: nil) + qrCodeVC.navigationController!.present(shareVC, animated: true, completion: nil) + } +} + +private final class ScanQRCodePlaceholderVC : UIViewController { + weak var qrCodeVC: QRCodeVC! + + override func viewDidLoad() { + // Remove background color + view.backgroundColor = .clear + // Set up explanation label + let explanationLabel = UILabel() + explanationLabel.textColor = Colors.text + explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) + explanationLabel.text = NSLocalizedString("Loki Messenger needs camera access to scan QR codes", comment: "") + explanationLabel.numberOfLines = 0 + explanationLabel.textAlignment = .center + explanationLabel.lineBreakMode = .byWordWrapping + // Set up call to action button + let callToActionButton = UIButton() + callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) + callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal) + callToActionButton.setTitle(NSLocalizedString("Enable Camera Access", comment: ""), for: UIControl.State.normal) + callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside) + // Set up stack view + let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ]) + stackView.axis = .vertical + stackView.spacing = Values.mediumSpacing + stackView.alignment = .center + // Set up constraints + view.set(.width, to: UIScreen.main.bounds.width) + view.addSubview(stackView) + stackView.pin(.leading, to: .leading, of: view, withInset: Values.massiveSpacing) + view.pin(.trailing, to: .trailing, of: stackView, withInset: Values.massiveSpacing) + let verticalCenteringConstraint = stackView.center(.vertical, in: view) + verticalCenteringConstraint.constant = -16 // Makes things appear centered visually + } + + func constrainHeight(to height: CGFloat) { + view.set(.height, to: height) + } + + @objc private func requestCameraAccess() { + ows_ask(forCameraPermissions: { [weak self] hasCameraAccess in + if hasCameraAccess { + self?.qrCodeVC.handleCameraAccessGranted() + } else { + // Do nothing + } + }) + } +} diff --git a/Signal/src/Loki/Redesign/View Controllers/SettingsVC.swift b/Signal/src/Loki/Redesign/View Controllers/SettingsVC.swift index 0c32a9347..88ad7dfcb 100644 --- a/Signal/src/Loki/Redesign/View Controllers/SettingsVC.swift +++ b/Signal/src/Loki/Redesign/View Controllers/SettingsVC.swift @@ -178,9 +178,8 @@ final class SettingsVC : UIViewController { } @objc private func showQRCode() { - let qrCodeModal = QRCodeModal() - qrCodeModal.modalPresentationStyle = .overFullScreen - present(qrCodeModal, animated: true, completion: nil) + let qrCodeVC = QRCodeVC() + navigationController!.pushViewController(qrCodeVC, animated: true) } @objc private func copyPublicKey() { diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index e08bbed1e..a1f2fb7c4 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2711,3 +2711,8 @@ "Notifications can appear while your phone is locked. You may wish to limit what is shown in these notifications." = "Notifications can appear while your phone is locked. You may wish to limit what is shown in these notifications."; "Notifications" = "Notifications"; "Back" = "Back"; +"View My QR Code" = "View My QR Code"; +"Scan someone's QR code to start a conversation with them" = "Scan someone's QR code to start a conversation with them"; +"QR Code" = "QR Code"; +"Scan Me" = "Scan Me"; +"This is your unique public QR code. Other users may scan this in order to begin a conversation with you." = "This is your unique public QR code. Other users may scan this in order to begin a conversation with you.";