mirror of https://github.com/oxen-io/session-ios
fix an issue that the screen stack is odd when sending message requests
parent
7310f49b59
commit
fea940c69d
@ -1,328 +0,0 @@
|
|||||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import AVFoundation
|
|
||||||
import SessionUIKit
|
|
||||||
import SessionMessagingKit
|
|
||||||
import SessionUtilitiesKit
|
|
||||||
|
|
||||||
final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, QRScannerDelegate {
|
|
||||||
private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
|
|
||||||
private var pages: [UIViewController] = []
|
|
||||||
private var targetVCIndex: Int?
|
|
||||||
|
|
||||||
// MARK: - Components
|
|
||||||
|
|
||||||
private lazy var tabBar: TabBar = {
|
|
||||||
let tabs = [
|
|
||||||
TabBar.Tab(title: "vc_qr_code_view_my_qr_code_tab_title".localized()) { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
|
|
||||||
},
|
|
||||||
TabBar.Tab(title: "vc_qr_code_view_scan_qr_code_tab_title".localized()) { [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 = "vc_qr_code_view_scan_qr_code_explanation".localized()
|
|
||||||
let result = ScanQRCodeWrapperVC(message: message)
|
|
||||||
result.delegate = self
|
|
||||||
|
|
||||||
return result
|
|
||||||
}()
|
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
setNavBarTitle("vc_qr_code_title".localized())
|
|
||||||
|
|
||||||
// Set up tab bar
|
|
||||||
view.addSubview(tabBar)
|
|
||||||
tabBar.pin(.top, to: .top, of: view.safeAreaLayoutGuide)
|
|
||||||
tabBar.pin(.leading, to: .leading, of: view)
|
|
||||||
tabBar.pin(.trailing, to: .trailing, of: view)
|
|
||||||
|
|
||||||
// Set up page VC
|
|
||||||
let containerView: UIView = UIView()
|
|
||||||
view.addSubview(containerView)
|
|
||||||
containerView.pin(.top, to: .bottom, of: tabBar)
|
|
||||||
containerView.pin(.leading, to: .leading, of: view)
|
|
||||||
containerView.pin(.trailing, to: .trailing, of: view)
|
|
||||||
containerView.pin(.bottom, to: .bottom, of: view)
|
|
||||||
|
|
||||||
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)
|
|
||||||
addChild(pageVC)
|
|
||||||
containerView.addSubview(pageVC.view)
|
|
||||||
|
|
||||||
pageVC.view.pin(to: containerView)
|
|
||||||
pageVC.didMove(toParent: self)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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() {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.pages[1] = self.scanQRCodeWrapperVC
|
|
||||||
self.pageVC.setViewControllers([ self.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: QRCodeScanningViewController, didDetectQRCodeWith string: String, onError: (() -> ())?) {
|
|
||||||
let hexEncodedPublicKey = string
|
|
||||||
startNewPrivateChatIfPossible(with: hexEncodedPublicKey, onError: onError)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func startNewPrivateChatIfPossible(with hexEncodedPublicKey: String, onError: (() -> ())?) {
|
|
||||||
if !KeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) {
|
|
||||||
let modal: ConfirmationModal = ConfirmationModal(
|
|
||||||
targetView: self.view,
|
|
||||||
info: ConfirmationModal.Info(
|
|
||||||
title: "invalid_session_id".localized(),
|
|
||||||
body: .text("INVALID_SESSION_ID_MESSAGE".localized()),
|
|
||||||
cancelTitle: "BUTTON_OK".localized(),
|
|
||||||
cancelStyle: .alert_text,
|
|
||||||
afterClosed: onError
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.present(modal, animated: true)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SessionApp.presentConversationCreatingIfNeeded(
|
|
||||||
for: hexEncodedPublicKey,
|
|
||||||
variant: .contact,
|
|
||||||
dismissing: presentingViewController,
|
|
||||||
animated: false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class ViewMyQRCodeVC : UIViewController {
|
|
||||||
weak var qrCodeVC: QRCodeVC!
|
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
// Remove background color
|
|
||||||
view.themeBackgroundColor = .clear
|
|
||||||
|
|
||||||
// Set up title label
|
|
||||||
let titleLabel = UILabel()
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? CGFloat(40) : Values.massiveFontSize)
|
|
||||||
titleLabel.text = "Scan Me"
|
|
||||||
titleLabel.themeTextColor = .textPrimary
|
|
||||||
titleLabel.textAlignment = .center
|
|
||||||
titleLabel.lineBreakMode = .byWordWrapping
|
|
||||||
titleLabel.numberOfLines = 1
|
|
||||||
titleLabel.set(.height, to: isIPhone5OrSmaller ? CGFloat(40) : Values.massiveFontSize)
|
|
||||||
|
|
||||||
// Set up QR code image view
|
|
||||||
let qrCodeImageView = UIImageView(
|
|
||||||
image: QRCode.generate(for: getUserHexEncodedPublicKey(), hasBackground: false)
|
|
||||||
.withRenderingMode(.alwaysTemplate)
|
|
||||||
)
|
|
||||||
qrCodeImageView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
|
||||||
qrCodeImageView.set(.width, to: .height, of: qrCodeImageView)
|
|
||||||
qrCodeImageView.heightAnchor
|
|
||||||
.constraint(lessThanOrEqualToConstant: (isIPhone5OrSmaller ? 180 : 240))
|
|
||||||
.isActive = true
|
|
||||||
|
|
||||||
#if targetEnvironment(simulator)
|
|
||||||
#else
|
|
||||||
// Note: For some reason setting this seems to stop the QRCode from rendering on the
|
|
||||||
// simulator so only doing it on device
|
|
||||||
qrCodeImageView.contentMode = .scaleAspectFit
|
|
||||||
#endif
|
|
||||||
|
|
||||||
let qrCodeImageViewBackgroundView = UIView()
|
|
||||||
qrCodeImageViewBackgroundView.layer.cornerRadius = 8
|
|
||||||
qrCodeImageViewBackgroundView.addSubview(qrCodeImageView)
|
|
||||||
qrCodeImageView.pin(
|
|
||||||
to: qrCodeImageViewBackgroundView,
|
|
||||||
withInset: 5 // The QRCode image has about 6pt of padding and we want 11 in total
|
|
||||||
)
|
|
||||||
|
|
||||||
ThemeManager.onThemeChange(observer: qrCodeImageView) { [weak qrCodeImageView, weak qrCodeImageViewBackgroundView] theme, _ in
|
|
||||||
switch theme.interfaceStyle {
|
|
||||||
case .light:
|
|
||||||
qrCodeImageView?.themeTintColorForced = .theme(theme, color: .textPrimary)
|
|
||||||
qrCodeImageViewBackgroundView?.themeBackgroundColorForced = nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
qrCodeImageView?.themeTintColorForced = .theme(theme, color: .backgroundPrimary)
|
|
||||||
qrCodeImageViewBackgroundView?.themeBackgroundColorForced = .color(.white)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up QR code image view container
|
|
||||||
let qrCodeImageViewContainer = UIView()
|
|
||||||
qrCodeImageViewContainer.accessibilityLabel = "Your QR code"
|
|
||||||
qrCodeImageViewContainer.isAccessibilityElement = true
|
|
||||||
qrCodeImageViewContainer.addSubview(qrCodeImageViewBackgroundView)
|
|
||||||
qrCodeImageViewBackgroundView.center(.horizontal, in: qrCodeImageViewContainer)
|
|
||||||
qrCodeImageViewBackgroundView.pin(.top, to: .top, of: qrCodeImageViewContainer)
|
|
||||||
qrCodeImageViewBackgroundView.pin(.bottom, to: .bottom, of: qrCodeImageViewContainer)
|
|
||||||
|
|
||||||
// Set up explanation label
|
|
||||||
let explanationLabel = UILabel()
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.mediumFontSize)
|
|
||||||
explanationLabel.text = "vc_view_my_qr_code_explanation".localized()
|
|
||||||
explanationLabel.themeTextColor = .textPrimary
|
|
||||||
explanationLabel.textAlignment = .center
|
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
|
||||||
explanationLabel.numberOfLines = 0
|
|
||||||
|
|
||||||
// Set up share button
|
|
||||||
let shareButton = SessionButton(style: .bordered, size: .large)
|
|
||||||
shareButton.setTitle("share".localized(), for: .normal)
|
|
||||||
shareButton.addTarget(self, action: #selector(shareQRCode), for: .touchUpInside)
|
|
||||||
|
|
||||||
// Set up share button container
|
|
||||||
let shareButtonContainer = UIView()
|
|
||||||
shareButtonContainer.addSubview(shareButton)
|
|
||||||
shareButton.pin(.top, to: .top, of: shareButtonContainer)
|
|
||||||
shareButton.pin(.bottom, to: .bottom, of: shareButtonContainer)
|
|
||||||
if UIDevice.current.isIPad {
|
|
||||||
shareButton.center(in: shareButtonContainer)
|
|
||||||
shareButton.set(.width, to: Values.iPadButtonWidth)
|
|
||||||
} else {
|
|
||||||
shareButton.pin(.leading, to: .leading, of: shareButtonContainer, withInset: 80)
|
|
||||||
shareButton.pin(.trailing, to: .trailing, of: shareButtonContainer, withInset: -80)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up stack view
|
|
||||||
let spacing = (isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing)
|
|
||||||
let stackView = UIStackView(
|
|
||||||
arrangedSubviews: [
|
|
||||||
titleLabel,
|
|
||||||
UIView.spacer(withHeight: spacing),
|
|
||||||
qrCodeImageViewContainer,
|
|
||||||
UIView.spacer(withHeight: spacing),
|
|
||||||
explanationLabel,
|
|
||||||
UIView.vStretchingSpacer(),
|
|
||||||
shareButtonContainer
|
|
||||||
]
|
|
||||||
)
|
|
||||||
stackView.axis = .vertical
|
|
||||||
stackView.alignment = .fill
|
|
||||||
stackView.layoutMargins = UIEdgeInsets(
|
|
||||||
top: Values.largeSpacing,
|
|
||||||
left: Values.largeSpacing,
|
|
||||||
bottom: Values.smallSpacing,
|
|
||||||
right: Values.largeSpacing
|
|
||||||
)
|
|
||||||
stackView.isLayoutMarginsRelativeArrangement = true
|
|
||||||
view.addSubview(stackView)
|
|
||||||
stackView.pin(to: view)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Interaction
|
|
||||||
|
|
||||||
@objc private func shareQRCode() {
|
|
||||||
let qrCode = QRCode.generate(for: getUserHexEncodedPublicKey(), hasBackground: true)
|
|
||||||
let shareVC = UIActivityViewController(activityItems: [ qrCode ], applicationActivities: nil)
|
|
||||||
if UIDevice.current.isIPad {
|
|
||||||
shareVC.excludedActivityTypes = []
|
|
||||||
shareVC.popoverPresentationController?.permittedArrowDirections = []
|
|
||||||
shareVC.popoverPresentationController?.sourceView = self.view
|
|
||||||
shareVC.popoverPresentationController?.sourceRect = self.view.bounds
|
|
||||||
}
|
|
||||||
qrCodeVC.navigationController!.present(shareVC, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class ScanQRCodePlaceholderVC : UIViewController {
|
|
||||||
weak var qrCodeVC: QRCodeVC!
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
// Remove background color
|
|
||||||
view.themeBackgroundColor = .clear
|
|
||||||
|
|
||||||
// Set up explanation label
|
|
||||||
let explanationLabel = UILabel()
|
|
||||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
|
||||||
explanationLabel.text = "vc_scan_qr_code_camera_access_explanation".localized()
|
|
||||||
explanationLabel.themeTextColor = .textPrimary
|
|
||||||
explanationLabel.textAlignment = .center
|
|
||||||
explanationLabel.lineBreakMode = .byWordWrapping
|
|
||||||
explanationLabel.numberOfLines = 0
|
|
||||||
|
|
||||||
// Set up call to action button
|
|
||||||
let callToActionButton = UIButton()
|
|
||||||
callToActionButton.titleLabel?.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
|
||||||
callToActionButton.setTitle("continue_2".localized(), for: .normal)
|
|
||||||
callToActionButton.setThemeTitleColor(.primary, for: .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.addSubview(stackView)
|
|
||||||
stackView.pin(.leading, to: .leading, of: view, withInset: Values.massiveSpacing)
|
|
||||||
stackView.pin(.trailing, to: .trailing, of: view, withInset: -Values.massiveSpacing)
|
|
||||||
stackView.center(.vertical, in: view, withInset: -16) // Makes things appear centered visually
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func requestCameraAccess() {
|
|
||||||
Permissions.requestCameraPermissionIfNeeded { [weak self] in
|
|
||||||
self?.qrCodeVC.handleCameraAccessGranted()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue