274 lines
9.1 KiB
Swift
274 lines
9.1 KiB
Swift
//
|
|
// Copyright 2020 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import MultipeerConnectivity
|
|
import SignalServiceKit
|
|
import SignalUI
|
|
import SwiftUI
|
|
|
|
class ProvisioningTransferQRCodeViewController: ProvisioningBaseViewController {
|
|
private let provisioningTransferQRCodeViewModel: ProvisioningTransferQRCodeView.Model
|
|
|
|
override init(provisioningController: ProvisioningController) {
|
|
provisioningTransferQRCodeViewModel = ProvisioningTransferQRCodeView.Model(url: nil)
|
|
|
|
super.init(provisioningController: provisioningController)
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
view.backgroundColor = .Signal.background
|
|
|
|
view.addSubview(primaryView)
|
|
primaryView.autoPinEdgesToSuperviewEdges()
|
|
|
|
let qrCodeHostingViewContainer = HostingContainer(wrappedView: ProvisioningTransferQRCodeView(
|
|
model: provisioningTransferQRCodeViewModel,
|
|
onGetHelpTapped: { [weak self] in self?.didTapHelp() },
|
|
onCancelTapped: { [weak self] in self?.didTapCancel() }
|
|
))
|
|
|
|
addChild(qrCodeHostingViewContainer)
|
|
primaryView.addSubview(qrCodeHostingViewContainer.view)
|
|
qrCodeHostingViewContainer.view.autoPinEdgesToSuperviewMargins()
|
|
qrCodeHostingViewContainer.didMove(toParent: self)
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
|
|
AppEnvironment.shared.deviceTransferServiceRef.addObserver(self)
|
|
|
|
Task { @MainActor in
|
|
do {
|
|
let url = try AppEnvironment.shared.deviceTransferServiceRef.startAcceptingTransfersFromOldDevices(
|
|
mode: .linked // TODO: .primary
|
|
)
|
|
|
|
provisioningTransferQRCodeViewModel.qrCodeViewModel.qrCodeURL = url
|
|
} catch {
|
|
owsFailDebug("error \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
|
|
AppEnvironment.shared.deviceTransferServiceRef.removeObserver(self)
|
|
AppEnvironment.shared.deviceTransferServiceRef.stopAcceptingTransfersFromOldDevices()
|
|
}
|
|
|
|
// MARK: - Events
|
|
|
|
weak var permissionActionSheetController: ActionSheetController?
|
|
|
|
private func didTapHelp() {
|
|
let turnOnView = TurnOnPermissionView(
|
|
title: OWSLocalizedString(
|
|
"LOCAL_NETWORK_PERMISSION_ACTION_SHEET_TITLE",
|
|
comment: "Title for local network permission action sheet"
|
|
),
|
|
message: OWSLocalizedString(
|
|
"LOCAL_NETWORK_PERMISSION_ACTION_SHEET_BODY",
|
|
comment: "Body for local network permission action sheet"
|
|
),
|
|
steps: [
|
|
.init(
|
|
icon: #imageLiteral(resourceName: "settings-app-icon-32"),
|
|
text: OWSLocalizedString(
|
|
"LOCAL_NETWORK_PERMISSION_ACTION_SHEET_STEP_ONE",
|
|
comment: "First step for local network permission action sheet"
|
|
)
|
|
),
|
|
.init(
|
|
icon: UIImage(resource: UIApplication.shared.currentAppIcon.previewImageResource),
|
|
text: OWSLocalizedString(
|
|
"LOCAL_NETWORK_PERMISSION_ACTION_SHEET_STEP_TWO",
|
|
comment: "Second step for local network permission action sheet"
|
|
)
|
|
),
|
|
.init(
|
|
icon: UIImage(imageLiteralResourceName: "toggle-32"),
|
|
text: OWSLocalizedString(
|
|
"LOCAL_NETWORK_PERMISSION_ACTION_SHEET_STEP_THREE",
|
|
comment: "Third step for local network permission action sheet"
|
|
)
|
|
)
|
|
],
|
|
button: primaryButton(
|
|
title: OWSLocalizedString(
|
|
"LOCAL_NETWORK_PERMISSION_ACTION_SHEET_NEED_HELP",
|
|
comment: "A button asking the user if they need further help getting their transfer working."
|
|
),
|
|
selector: #selector(didTapContactSupport)
|
|
)
|
|
)
|
|
|
|
let actionSheetController = ActionSheetController()
|
|
permissionActionSheetController = actionSheetController
|
|
actionSheetController.customHeader = turnOnView
|
|
actionSheetController.isCancelable = true
|
|
presentActionSheet(actionSheetController)
|
|
}
|
|
|
|
private func didTapCancel() {
|
|
guard let navigationController = navigationController else {
|
|
owsFailDebug("Unexpectedly missing nav controller!")
|
|
return
|
|
}
|
|
|
|
provisioningController.pushTransferChoiceView(onto: navigationController)
|
|
}
|
|
|
|
@objc
|
|
private func didTapContactSupport() {
|
|
Logger.info("")
|
|
|
|
permissionActionSheetController?.dismiss(animated: true)
|
|
permissionActionSheetController = nil
|
|
|
|
ContactSupportActionSheet.present(
|
|
emailFilter: .deviceTransfer,
|
|
logDumper: .fromGlobals(),
|
|
fromViewController: self
|
|
)
|
|
}
|
|
|
|
override func shouldShowBackButton() -> Bool {
|
|
// Never show the back button here
|
|
return false
|
|
}
|
|
}
|
|
|
|
extension ProvisioningTransferQRCodeViewController: DeviceTransferServiceObserver {
|
|
func deviceTransferServiceDiscoveredNewDevice(peerId: MCPeerID, discoveryInfo: [String: String]?) {}
|
|
|
|
func deviceTransferServiceDidStartTransfer(progress: Progress) {
|
|
provisioningController.accountTransferInProgress(fromViewController: self, progress: progress)
|
|
}
|
|
|
|
func deviceTransferServiceDidEndTransfer(error: DeviceTransferService.Error?) {
|
|
if let error = error {
|
|
owsFailDebug("unexpected error while rendering QR code \(error)")
|
|
}
|
|
}
|
|
|
|
func deviceTransferServiceDidRequestAppRelaunch() {
|
|
owsFail("Relaunch not supported for provisioning; only on the receiving device during transfer")
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
private struct ProvisioningTransferQRCodeView: View {
|
|
class Model: ObservableObject {
|
|
let qrCodeViewModel: QRCodeViewRepresentable.Model
|
|
|
|
init(url: URL?) {
|
|
qrCodeViewModel = QRCodeViewRepresentable.Model(qrCodeURL: url)
|
|
}
|
|
}
|
|
|
|
@ObservedObject
|
|
private var model: Model
|
|
|
|
private let onGetHelpTapped: () -> Void
|
|
private let onCancelTapped: () -> Void
|
|
|
|
init(
|
|
model: Model,
|
|
onGetHelpTapped: @escaping () -> Void,
|
|
onCancelTapped: @escaping () -> Void
|
|
) {
|
|
self.model = model
|
|
self.onGetHelpTapped = onGetHelpTapped
|
|
self.onCancelTapped = onCancelTapped
|
|
}
|
|
|
|
var body: some View {
|
|
GeometryReader { overallGeometry in
|
|
VStack(spacing: 12) {
|
|
Text(OWSLocalizedString(
|
|
"DEVICE_TRANSFER_QRCODE_TITLE",
|
|
comment: "The title for the device transfer qr code view"
|
|
))
|
|
.font(.title)
|
|
.fontWeight(.semibold)
|
|
.foregroundStyle(Color.Signal.label)
|
|
|
|
Text(OWSLocalizedString(
|
|
"DEVICE_TRANSFER_QRCODE_EXPLANATION",
|
|
comment: "The explanation for the device transfer qr code view"
|
|
))
|
|
.font(.body)
|
|
.foregroundStyle(Color.Signal.secondaryLabel)
|
|
|
|
Spacer()
|
|
.frame(height: overallGeometry.size.height * 0.05)
|
|
|
|
GeometryReader { qrCodeGeometry in
|
|
ZStack {
|
|
Color(UIColor.ows_gray02)
|
|
.cornerRadius(24)
|
|
|
|
QRCodeViewRepresentable(
|
|
model: model.qrCodeViewModel,
|
|
qrCodeStylingMode: .brandedWithoutLogo
|
|
)
|
|
.padding(qrCodeGeometry.size.height * 0.1)
|
|
}
|
|
}
|
|
.aspectRatio(1, contentMode: .fit)
|
|
|
|
Spacer()
|
|
.frame(height: overallGeometry.size.height * (overallGeometry.size.isLandscape ? 0.05 : 0.1))
|
|
|
|
Button(OWSLocalizedString(
|
|
"DEVICE_TRANSFER_QRCODE_NOT_SEEING",
|
|
comment: "A prompt to provide further explanation if the user is not seeing the transfer on both devices."
|
|
)) {
|
|
onGetHelpTapped()
|
|
}
|
|
.font(.subheadline)
|
|
.foregroundStyle(Color.Signal.accent)
|
|
|
|
Button(CommonStrings.cancelButton) {
|
|
onCancelTapped()
|
|
}
|
|
.font(.subheadline)
|
|
.foregroundStyle(Color.Signal.accent)
|
|
}
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
private struct PreviewView: View {
|
|
let url: URL?
|
|
|
|
var body: some View {
|
|
ProvisioningTransferQRCodeView(
|
|
model: ProvisioningTransferQRCodeView.Model(url: url),
|
|
onGetHelpTapped: {},
|
|
onCancelTapped: {}
|
|
)
|
|
.padding(112)
|
|
}
|
|
}
|
|
|
|
#Preview("Loaded") {
|
|
PreviewView(url: URL(string: "https://signal.org")!)
|
|
}
|
|
|
|
#Preview("Loading") {
|
|
PreviewView(url: nil)
|
|
}
|