Signal-iOS/SignalUI/ViewControllers/SpamCaptchaViewController.swift
2026-05-07 11:07:28 -07:00

146 lines
4.8 KiB
Swift

//
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalServiceKit
import UIKit
public class SpamCaptchaViewController: UIViewController, CaptchaViewDelegate {
private lazy var captchaView: CaptchaView = {
let view = CaptchaView(context: .challenge)
view.delegate = self
return view
}()
var completionHandler: ((String?) -> Void)?
fileprivate init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .Signal.background
view.addSubview(captchaView)
captchaView.autoPinEdgesToSuperviewEdges()
captchaView.loadCaptcha()
isModalInPresentation = true
navigationItem.title = OWSLocalizedString("SPAM_CAPTCHA_VIEW_CONTROLLER", comment: "Title for the captcha view controller")
navigationItem.leftBarButtonItem = .systemItem(.stop) { [weak self] in
self?.completionHandler?(nil)
self?.completionHandler = nil
}
}
override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIDevice.current.isIPad ? .all : .portrait
}
// MARK: - CaptchaViewDelegate
public func captchaView(_: CaptchaView, didCompleteCaptchaWithToken token: String) {
completionHandler?(token)
completionHandler = nil
}
public func captchaViewDidFailToCompleteCaptcha(_: CaptchaView) {
captchaView.loadCaptcha()
}
// MARK: - Presentation
public static func presentActionSheet(from fromVC: UIViewController) {
let sheet = ActionSheetController(
title: OWSLocalizedString(
"SPAM_CAPTCHA_SHEET_TITLE",
comment: "Title for action sheet explaining captcha requirement.",
),
message: OWSLocalizedString(
"SPAM_CAPTCHA_SHEET_BODY",
comment: "Body for action sheet explaining captcha requirement.",
),
)
sheet.addAction(
ActionSheetAction(
title: CommonStrings.continueButton,
handler: { _ in
presentCaptchaVC(from: fromVC)
},
),
)
sheet.addAction(
ActionSheetAction(
title: CommonStrings.notNowButton,
style: .cancel,
handler: { _ in
let confirmationSheet = ActionSheetController(
title: OWSLocalizedString(
"SPAM_CAPTCHA_DISMISS_CONFIRMATION_TITLE",
comment: "Title for confirmation dialog confirming to ignore verification.",
),
message: OWSLocalizedString(
"SPAM_CAPTCHA_DISMISS_CONFIRMATION_MESSAGE",
comment: "Message for confirmation dialog confirming to ignore verification.",
),
)
confirmationSheet.addAction(
ActionSheetAction(
title: OWSLocalizedString("SPAM_CAPTCHA_SKIP_VERIFICATION_ACTION", comment: "Action to skip verification"),
style: .destructive,
),
)
confirmationSheet.addAction(
ActionSheetAction(
title: CommonStrings.cancelButton,
style: .cancel,
handler: { _ in
presentActionSheet(from: fromVC)
},
),
)
fromVC.present(confirmationSheet, animated: true, completion: nil)
},
),
)
fromVC.present(sheet, animated: true, completion: nil)
}
static func presentCaptchaVC(from fromVC: UIViewController) {
let vc = SpamCaptchaViewController()
vc.completionHandler = { token in
if let token {
fromVC.presentToast(
text: OWSLocalizedString(
"SPAM_CAPTCHA_COMPLETED_TOAST",
comment: "Text for toast presented after spam verification has been completed",
),
)
SSKEnvironment.shared.spamChallengeResolverRef.handleIncomingCaptchaChallengeToken(token)
}
vc.dismiss(animated: true)
}
let navVC = OWSNavigationController(rootViewController: vc)
fromVC.present(navVC, animated: true, completion: nil)
}
}
#if DEBUG
@available(iOS 17, *)
#Preview {
SpamCaptchaViewController()
}
#endif