Path: blob/main/Signal/Provisioning/UserInterface/ProvisioningQRCodeViewController.swift
1 views
//
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalServiceKit
import SignalUI
import SwiftUI
class ProvisioningQRCodeViewController: ProvisioningBaseViewController, ProvisioningSocketManagerUIDelegate {
private let provisioningQRCodeViewModel: RotatingQRCodeView.Model
private let provisioningSocketManager: ProvisioningSocketManager
init(
provisioningController: ProvisioningController,
provisioningSocketManager: ProvisioningSocketManager,
) {
provisioningQRCodeViewModel = RotatingQRCodeView.Model(
urlDisplayMode: .loading,
onRefreshButtonPressed: { [weak provisioningSocketManager] in
provisioningSocketManager?.reset()
},
)
self.provisioningSocketManager = provisioningSocketManager
super.init(provisioningController: provisioningController)
provisioningSocketManager.delegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesBackButton = true
let qrCodeViewHostingContainer = HostingContainer(wrappedView: ProvisioningQRCodeView(
model: provisioningQRCodeViewModel,
))
addChild(qrCodeViewHostingContainer)
view.addSubview(qrCodeViewHostingContainer.view)
qrCodeViewHostingContainer.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
qrCodeViewHostingContainer.view.topAnchor.constraint(equalTo: contentLayoutGuide.topAnchor),
qrCodeViewHostingContainer.view.leadingAnchor.constraint(equalTo: contentLayoutGuide.leadingAnchor),
qrCodeViewHostingContainer.view.bottomAnchor.constraint(equalTo: contentLayoutGuide.bottomAnchor),
qrCodeViewHostingContainer.view.trailingAnchor.constraint(equalTo: contentLayoutGuide.trailingAnchor),
])
qrCodeViewHostingContainer.didMove(toParent: self)
provisioningQRCodeViewModel.updateURLDisplayMode(.loading)
provisioningSocketManager.start()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
provisioningSocketManager.stop()
}
// MARK: -
func reset() {
provisioningSocketManager.stop()
provisioningQRCodeViewModel.updateURLDisplayMode(.loading)
provisioningSocketManager.start()
}
func provisioningSocketManager(_ provisioningSocketManager: ProvisioningSocketManager, didUpdateProvisioningURL url: URL) {
provisioningQRCodeViewModel.updateURLDisplayMode(.loaded(url))
}
func provisioningSocketManagerDidPauseQRRotation(_ provisioningSocketManager: ProvisioningSocketManager) {
provisioningQRCodeViewModel.updateURLDisplayMode(.refreshButton)
}
}
// MARK: -
private struct ProvisioningQRCodeView: View {
@ObservedObject var model: RotatingQRCodeView.Model
var body: some View {
GeometryReader { overallGeometry in
VStack(spacing: 12) {
Text(OWSLocalizedString(
"SECONDARY_ONBOARDING_SCAN_CODE_TITLE",
comment: "header text while displaying a QR code which, when scanned, will link this device.",
))
.font(.title)
.fontWeight(.semibold)
.foregroundStyle(Color.Signal.label)
Text(OWSLocalizedString(
"SECONDARY_ONBOARDING_SCAN_CODE_BODY",
comment: "body text while displaying a QR code which, when scanned, will link this device.",
))
.font(.body)
.foregroundStyle(Color.Signal.secondaryLabel)
Spacer()
.frame(height: overallGeometry.size.height * 0.05)
RotatingQRCodeView(model: self.model)
Spacer()
.frame(height: overallGeometry.size.height * (overallGeometry.size.isLandscape ? 0.05 : 0.1))
#if TESTABLE_BUILD
if
#available(iOS 16.0, *),
let provisioningUrl = model.qrCodeViewModel.qrCodeURL
{
// If on a physical device, this postfixing with some text
// allows one to AirDrop the URL to macOS to be copied into
// a simulator, instead of having macOS automatically try
// and open the URL (which Signal Desktop will try, and
// fail, to handle).
ShareLink(item: "\(provisioningUrl) DELETETHIS") {
Text(LocalizationNotNeeded(
"Debug only: Share URL",
))
}
.simultaneousGesture(TapGesture().onEnded {
// When tapped, also copy to the clipboard for easy
// extraction from a simulator.
UIPasteboard.general.url = provisioningUrl
})
Button(LocalizationNotNeeded("Debug only: Copy URL")) {
UIPasteboard.general.url = provisioningUrl
}
}
#endif
Link(
OWSLocalizedString(
"SECONDARY_ONBOARDING_SCAN_CODE_HELP_TEXT",
comment: "Link text for page with troubleshooting info shown on the QR scanning screen",
),
destination: URL.Support.troubleshootingMultipleDevices,
)
.buttonStyle(Registration.UI.MediumSecondaryButtonStyle())
.padding(.bottom, NSDirectionalEdgeInsets.buttonContainerLayoutMargins.bottom)
}
.multilineTextAlignment(.center)
}
}
}
// MARK: -
private struct PreviewView: View {
let urlDisplayMode: RotatingQRCodeView.Model.URLDisplayMode
var body: some View {
ProvisioningQRCodeView(
model: RotatingQRCodeView.Model(
urlDisplayMode: urlDisplayMode,
onRefreshButtonPressed: {},
),
)
.padding(112)
}
}
#Preview("Loaded") {
PreviewView(urlDisplayMode: .loaded(URL(string: "https://signal.org")!))
}
#Preview("Loading") {
PreviewView(urlDisplayMode: .loading)
}
#Preview("Refresh Button") {
PreviewView(urlDisplayMode: .refreshButton)
}