Don't show a megaphone for 1d after dismissing previous
This commit is contained in:
parent
0cc18a5285
commit
8b1379149c
@ -8,38 +8,51 @@ import SignalUI
|
||||
|
||||
class ExperienceUpgradeManager {
|
||||
|
||||
private enum StoreKeys {
|
||||
static let lastExperienceUpgradeDismissDate = "lastExperienceUpgradeDismissDate"
|
||||
}
|
||||
|
||||
private weak static var lastPresented: MegaphoneView?
|
||||
|
||||
private static let backupSettingsStore = BackupSettingsStore()
|
||||
private static var db: DB { DependenciesBridge.shared.db }
|
||||
private static var deviceStore: OWSDeviceStore { DependenciesBridge.shared.deviceStore }
|
||||
private static let experienceUpgradeStore = ExperienceUpgradeStore()
|
||||
private static let keyValueStore = NewKeyValueStore(collection: "ExperienceUpgradeManager")
|
||||
private static var remoteConfigManager: RemoteConfigManager { SSKEnvironment.shared.remoteConfigManagerRef }
|
||||
private static var tsAccountManager: TSAccountManager { DependenciesBridge.shared.tsAccountManager }
|
||||
|
||||
static func presentNext(fromViewController: UIViewController) -> Bool {
|
||||
|
||||
static func reconcilePresentedExperienceUpgrade(fromViewController: UIViewController) {
|
||||
let now = Date()
|
||||
var shouldClearNewDeviceNotification = false
|
||||
var shouldClearBackupsEnabledDetails = false
|
||||
|
||||
let nextExperienceUpgrade = db.read { tx -> ExperienceUpgrade? in
|
||||
let lastExperienceUpgradeDismissDate: Date
|
||||
let nextExperienceUpgrade: ExperienceUpgrade?
|
||||
(
|
||||
lastExperienceUpgradeDismissDate,
|
||||
nextExperienceUpgrade,
|
||||
) = db.read { tx in
|
||||
guard
|
||||
let registeredState = try? tsAccountManager.registeredState(tx: tx),
|
||||
let registrationDate = tsAccountManager.registrationDate(tx: tx)
|
||||
else {
|
||||
return nil
|
||||
return (.distantPast, nil)
|
||||
}
|
||||
|
||||
let now = Date()
|
||||
let timeIntervalSinceRegistration = now.timeIntervalSince(registrationDate)
|
||||
let lastExperienceUpgradeDismissDate = keyValueStore.fetchValue(
|
||||
Date.self,
|
||||
forKey: StoreKeys.lastExperienceUpgradeDismissDate,
|
||||
tx: tx,
|
||||
) ?? .distantPast
|
||||
|
||||
return allKnownExperienceUpgrades(transaction: tx)
|
||||
let nextExperienceUpgrade = allKnownExperienceUpgrades(transaction: tx)
|
||||
.first { upgrade in
|
||||
guard
|
||||
!upgrade.isComplete,
|
||||
!upgrade.isSnoozed(now: now),
|
||||
!upgrade.hasPassedNumberOfDaysToShow(now: now),
|
||||
timeIntervalSinceRegistration > upgrade.manifest.delayAfterRegistration,
|
||||
now.timeIntervalSince(registrationDate) > upgrade.manifest.delayAfterRegistration,
|
||||
now < upgrade.manifest.expirationDate,
|
||||
(registeredState.isPrimary || upgrade.manifest.showOnLinkedDevices)
|
||||
else {
|
||||
@ -114,6 +127,11 @@ class ExperienceUpgradeManager {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
lastExperienceUpgradeDismissDate,
|
||||
nextExperienceUpgrade,
|
||||
)
|
||||
}
|
||||
|
||||
if shouldClearNewDeviceNotification {
|
||||
@ -129,22 +147,24 @@ class ExperienceUpgradeManager {
|
||||
}
|
||||
|
||||
guard let nextExperienceUpgrade else {
|
||||
dismissLastPresented()
|
||||
return false
|
||||
_ = dismissLastPresented(now: now)
|
||||
return
|
||||
}
|
||||
|
||||
if
|
||||
let lastPresented,
|
||||
lastPresented.experienceUpgrade.manifest == nextExperienceUpgrade.manifest
|
||||
{
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, dismiss any currently present experience upgrade. It's
|
||||
// no longer next and may have been completed.
|
||||
dismissLastPresented()
|
||||
// If we're dismissing a megaphone, don't immediately present another.
|
||||
if dismissLastPresented(now: now) {
|
||||
return
|
||||
}
|
||||
|
||||
if
|
||||
now.timeIntervalSince(lastExperienceUpgradeDismissDate) > .day,
|
||||
let megaphone = self.megaphone(
|
||||
forExperienceUpgrade: nextExperienceUpgrade,
|
||||
fromViewController: fromViewController,
|
||||
@ -159,10 +179,6 @@ class ExperienceUpgradeManager {
|
||||
tx: tx,
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,22 +216,24 @@ class ExperienceUpgradeManager {
|
||||
return ExperienceUpgradeManifest.sortedByImportance(experienceUpgrades)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
static func dismissLastPresented(ifMatching manifest: ExperienceUpgradeManifest? = nil) {
|
||||
/// - Returns
|
||||
/// Whether or not we dismissed a megaphone.
|
||||
private static func dismissLastPresented(now: Date) -> Bool {
|
||||
guard let lastPresented else {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
if
|
||||
let manifest,
|
||||
lastPresented.experienceUpgrade.manifest != manifest
|
||||
{
|
||||
return
|
||||
db.write { tx in
|
||||
keyValueStore.writeValue(
|
||||
now,
|
||||
forKey: StoreKeys.lastExperienceUpgradeDismissDate,
|
||||
tx: tx,
|
||||
)
|
||||
}
|
||||
|
||||
lastPresented.dismiss(animated: false, completion: nil)
|
||||
self.lastPresented = nil
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@ -36,7 +36,6 @@ class BackupEnablementMegaphone: MegaphoneView {
|
||||
let primaryButton = MegaphoneView.Button(title: primaryButtonTitle) { [weak self] in
|
||||
SignalApp.shared.showAppSettings(mode: .backups())
|
||||
self?.markAsSnoozedWithSneakyTransaction()
|
||||
self?.dismiss(animated: true)
|
||||
}
|
||||
|
||||
let secondaryButton = snoozeButton(
|
||||
|
||||
@ -42,13 +42,11 @@ class BackupsEnabledNotificationMegaphone: MegaphoneView {
|
||||
)
|
||||
let primaryButton = MegaphoneView.Button(title: primaryButtonTitle) { [weak self] in
|
||||
SignalApp.shared.showAppSettings(mode: .backups())
|
||||
self?.markAsViewed()
|
||||
self?.dismiss(animated: true)
|
||||
self?.stopShowing()
|
||||
}
|
||||
|
||||
let secondaryButton = MegaphoneView.Button(title: CommonStrings.okButton) { [weak self] in
|
||||
self?.markAsViewed()
|
||||
self?.dismiss(animated: true)
|
||||
self?.stopShowing()
|
||||
}
|
||||
|
||||
buttons = [primaryButton, secondaryButton]
|
||||
@ -58,9 +56,11 @@ class BackupsEnabledNotificationMegaphone: MegaphoneView {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func markAsViewed() {
|
||||
private func stopShowing() {
|
||||
db.write { tx in
|
||||
backupSettingsStore.clearLastBackupEnabledDetails(tx: tx)
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,8 +7,6 @@ import SignalServiceKit
|
||||
import SignalUI
|
||||
|
||||
class ContactPermissionReminderMegaphone: MegaphoneView {
|
||||
weak var actionSheetController: ActionSheetController?
|
||||
|
||||
init(experienceUpgrade: ExperienceUpgrade, fromViewController: UIViewController) {
|
||||
super.init(experienceUpgrade: experienceUpgrade)
|
||||
|
||||
@ -27,8 +25,9 @@ class ContactPermissionReminderMegaphone: MegaphoneView {
|
||||
comment: "Action text for contact permission reminder megaphone",
|
||||
)
|
||||
|
||||
let primaryButton = MegaphoneView.Button(title: primaryButtonTitle) { [weak self] in
|
||||
guard let self else { return }
|
||||
let primaryButton = MegaphoneView.Button(title: primaryButtonTitle) {
|
||||
let actionSheetController = ActionSheetController()
|
||||
actionSheetController.isCancelable = true
|
||||
|
||||
let turnOnView: TurnOnPermissionView
|
||||
if #available(iOS 18, *) {
|
||||
@ -37,6 +36,7 @@ class ContactPermissionReminderMegaphone: MegaphoneView {
|
||||
.withRenderingMode(.alwaysTemplate)
|
||||
|
||||
turnOnView = TurnOnPermissionView(
|
||||
fromActionSheetController: actionSheetController,
|
||||
title: OWSLocalizedString(
|
||||
"CONTACT_PERMISSION_ACTION_SHEET_2_TITLE",
|
||||
comment: "Title for contact permission action sheet",
|
||||
@ -71,6 +71,7 @@ class ContactPermissionReminderMegaphone: MegaphoneView {
|
||||
)
|
||||
} else {
|
||||
turnOnView = TurnOnPermissionView(
|
||||
fromActionSheetController: actionSheetController,
|
||||
title: OWSLocalizedString(
|
||||
"CONTACT_PERMISSION_ACTION_SHEET_TITLE",
|
||||
comment: "Title for contact permission action sheet",
|
||||
@ -98,11 +99,8 @@ class ContactPermissionReminderMegaphone: MegaphoneView {
|
||||
)
|
||||
}
|
||||
|
||||
let actionSheetController = ActionSheetController()
|
||||
actionSheetController.customHeader = turnOnView
|
||||
actionSheetController.isCancelable = true
|
||||
fromViewController.presentActionSheet(actionSheetController)
|
||||
self.actionSheetController = actionSheetController
|
||||
}
|
||||
|
||||
let secondaryButton = snoozeButton(
|
||||
@ -119,9 +117,4 @@ class ContactPermissionReminderMegaphone: MegaphoneView {
|
||||
required init(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func dismiss(animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
super.dismiss(animated: animated, completion: completion)
|
||||
actionSheetController?.dismiss(animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,15 +56,10 @@ class CreateUsernameMegaphone: MegaphoneView {
|
||||
|
||||
private func onSetUpTapped(fromViewController: UIViewController) {
|
||||
markAsSnoozedWithSneakyTransaction()
|
||||
|
||||
dismiss(animated: true) {
|
||||
self.usernameSelectionCoordinator.present(fromViewController: fromViewController)
|
||||
}
|
||||
usernameSelectionCoordinator.present(fromViewController: fromViewController)
|
||||
}
|
||||
|
||||
private func onNotNowTapped() {
|
||||
markAsSnoozedWithSneakyTransaction()
|
||||
|
||||
dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,6 @@ import SignalServiceKit
|
||||
import UIKit
|
||||
|
||||
final class InactiveLinkedDeviceReminderMegaphone: MegaphoneView {
|
||||
private var inactiveLinkedDeviceFinder: InactiveLinkedDeviceFinder {
|
||||
DependenciesBridge.shared.inactiveLinkedDeviceFinder
|
||||
}
|
||||
|
||||
private let inactiveLinkedDevice: InactiveLinkedDevice
|
||||
|
||||
/// The number of days until the linked device represented by this megaphone
|
||||
@ -57,15 +53,14 @@ final class InactiveLinkedDeviceReminderMegaphone: MegaphoneView {
|
||||
"INACTIVE_LINKED_DEVICE_REMINDER_MEGAPHONE_DONT_REMIND_ME_BUTTON",
|
||||
comment: "Title for a button in an in-app megaphone about a user's inactive linked device, indicating the user doesn't want to be reminded.",
|
||||
)) {
|
||||
DependenciesBridge.shared.db.asyncWrite(
|
||||
block: { tx in
|
||||
self.inactiveLinkedDeviceFinder.permanentlyDisableFinders(tx: tx)
|
||||
},
|
||||
completionQueue: .main,
|
||||
completion: { [weak self] in
|
||||
self?.dismiss()
|
||||
},
|
||||
)
|
||||
let db = DependenciesBridge.shared.db
|
||||
let inactiveLinkedDeviceFinder = DependenciesBridge.shared.inactiveLinkedDeviceFinder
|
||||
|
||||
db.write { tx in
|
||||
inactiveLinkedDeviceFinder.permanentlyDisableFinders(tx: tx)
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
}
|
||||
let gotItButton = snoozeButton(
|
||||
fromViewController: fromViewController,
|
||||
|
||||
@ -60,12 +60,10 @@ class MegaphoneView: UIView {
|
||||
|
||||
guard !hasPresented else { return owsFailDebug("can only present once") }
|
||||
|
||||
guard titleText != nil, bodyText != nil else {
|
||||
return owsFailDebug("megaphone is not prepared for presentation")
|
||||
guard titleText != nil, bodyText != nil, !buttons.isEmpty else {
|
||||
owsFail("Megaphone missing required properties!")
|
||||
}
|
||||
|
||||
// Top section
|
||||
|
||||
let labelStack = createLabelStack()
|
||||
|
||||
let topStackSubviews: [UIView]
|
||||
@ -82,22 +80,7 @@ class MegaphoneView: UIView {
|
||||
topStackView.layoutMargins = UIEdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12)
|
||||
|
||||
stackView.addArrangedSubview(topStackView)
|
||||
|
||||
// Buttons
|
||||
|
||||
if buttons.count > 0 {
|
||||
stackView.addArrangedSubview(createButtonsStack())
|
||||
} else {
|
||||
let dismissButton = UIButton()
|
||||
dismissButton.setTemplateImage(Theme.iconImage(.buttonX), tintColor: Theme.darkThemePrimaryColor)
|
||||
dismissButton.addTarget(self, action: #selector(tappedDismiss), for: .touchUpInside)
|
||||
|
||||
addSubview(dismissButton)
|
||||
|
||||
dismissButton.autoSetDimensions(to: CGSize(square: 40))
|
||||
dismissButton.autoPinEdge(toSuperviewEdge: .trailing)
|
||||
dismissButton.autoPinEdge(toSuperviewEdge: .top)
|
||||
}
|
||||
stackView.addArrangedSubview(createButtonsStack())
|
||||
|
||||
fromViewController.view.addSubview(self)
|
||||
autoPinEdge(toSuperviewSafeArea: .leading, withInset: 8)
|
||||
@ -128,11 +111,6 @@ class MegaphoneView: UIView {
|
||||
darkThemeBackgroundOverlay.isHidden = !Theme.isDarkThemeEnabled
|
||||
}
|
||||
|
||||
@objc
|
||||
private func tappedDismiss() {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
private func createLabelStack() -> UIStackView {
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.numberOfLines = 0
|
||||
@ -216,18 +194,18 @@ class MegaphoneView: UIView {
|
||||
divider.autoPinWidthToSuperview()
|
||||
divider.autoPinHeightToSuperview(withMargin: 8)
|
||||
default:
|
||||
owsFailDebug("only supports 1 or 2 buttons")
|
||||
owsFail("Megaphones must have one or two buttons!")
|
||||
}
|
||||
|
||||
return buttonsStack
|
||||
}
|
||||
|
||||
func snoozeButton(fromViewController: UIViewController, snoozeTitle: String = MegaphoneStrings.remindMeLater) -> Button {
|
||||
return Button(title: snoozeTitle) { [weak self] in
|
||||
self?.markAsSnoozedWithSneakyTransaction()
|
||||
self?.dismiss {
|
||||
self?.presentToast(text: MegaphoneStrings.weWillRemindYouLater, fromViewController: fromViewController)
|
||||
}
|
||||
return Button(title: snoozeTitle) { [weak self, weak fromViewController] in
|
||||
guard let self, let fromViewController else { return }
|
||||
|
||||
markAsSnoozedWithSneakyTransaction()
|
||||
presentToast(text: MegaphoneStrings.weWillRemindYouLater, fromViewController: fromViewController)
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,6 +221,8 @@ class MegaphoneView: UIView {
|
||||
tx: tx,
|
||||
)
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
}
|
||||
|
||||
func markAsCompleteWithSneakyTransaction() {
|
||||
@ -255,5 +235,7 @@ class MegaphoneView: UIView {
|
||||
tx: tx,
|
||||
)
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,15 +45,13 @@ final class NewLinkedDeviceNotificationMegaphone: MegaphoneView {
|
||||
),
|
||||
) { [weak self] in
|
||||
SignalApp.shared.showAppSettings(mode: .linkedDevices)
|
||||
self?.markAsViewed()
|
||||
self?.dismiss()
|
||||
self?.stopShowing()
|
||||
}
|
||||
|
||||
let acknowledgeButton = Button(
|
||||
title: CommonStrings.acknowledgeButton,
|
||||
) { [weak self] in
|
||||
self?.markAsViewed()
|
||||
self?.dismiss()
|
||||
self?.stopShowing()
|
||||
}
|
||||
|
||||
buttons = [acknowledgeButton, viewDeviceButton]
|
||||
@ -64,9 +62,11 @@ final class NewLinkedDeviceNotificationMegaphone: MegaphoneView {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func markAsViewed() {
|
||||
private func stopShowing() {
|
||||
db.write { tx in
|
||||
deviceStore.clearMostRecentlyLinkedDeviceDetails(tx: tx)
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,8 +7,6 @@ import SignalServiceKit
|
||||
import SignalUI
|
||||
|
||||
class NotificationPermissionReminderMegaphone: MegaphoneView {
|
||||
weak var actionSheetController: ActionSheetController?
|
||||
|
||||
init(experienceUpgrade: ExperienceUpgrade, fromViewController: UIViewController) {
|
||||
super.init(experienceUpgrade: experienceUpgrade)
|
||||
|
||||
@ -27,10 +25,12 @@ class NotificationPermissionReminderMegaphone: MegaphoneView {
|
||||
comment: "Action text for notification permission reminder megaphone",
|
||||
)
|
||||
|
||||
let primaryButton = MegaphoneView.Button(title: primaryButtonTitle) { [weak self] in
|
||||
guard let self else { return }
|
||||
let primaryButton = MegaphoneView.Button(title: primaryButtonTitle) {
|
||||
let actionSheetController = ActionSheetController()
|
||||
actionSheetController.isCancelable = true
|
||||
|
||||
let turnOnView = TurnOnPermissionView(
|
||||
fromActionSheetController: actionSheetController,
|
||||
title: OWSLocalizedString(
|
||||
"NOTIFICATION_PERMISSION_ACTION_SHEET_TITLE",
|
||||
comment: "Title for notification permission action sheet",
|
||||
@ -64,11 +64,8 @@ class NotificationPermissionReminderMegaphone: MegaphoneView {
|
||||
],
|
||||
)
|
||||
|
||||
let actionSheetController = ActionSheetController()
|
||||
actionSheetController.customHeader = turnOnView
|
||||
actionSheetController.isCancelable = true
|
||||
fromViewController.presentActionSheet(actionSheetController)
|
||||
self.actionSheetController = actionSheetController
|
||||
}
|
||||
|
||||
let secondaryButton = snoozeButton(
|
||||
@ -85,20 +82,22 @@ class NotificationPermissionReminderMegaphone: MegaphoneView {
|
||||
required init(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func dismiss(animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
super.dismiss(animated: animated, completion: completion)
|
||||
actionSheetController?.dismiss(animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class TurnOnPermissionView: UIStackView {
|
||||
struct Step {
|
||||
let icon: UIImage?
|
||||
let text: String
|
||||
}
|
||||
|
||||
init(title: String, message: String, steps: [Step], button: UIButton? = nil) {
|
||||
init(
|
||||
fromActionSheetController: ActionSheetController,
|
||||
title: String,
|
||||
message: String,
|
||||
steps: [Step],
|
||||
) {
|
||||
super.init(frame: .zero)
|
||||
|
||||
axis = .vertical
|
||||
@ -122,10 +121,14 @@ class TurnOnPermissionView: UIStackView {
|
||||
}
|
||||
|
||||
// Button
|
||||
let primaryButton = button ?? UIButton(
|
||||
let primaryButton = UIButton(
|
||||
configuration: .largePrimary(title: CommonStrings.goToSettingsButton),
|
||||
primaryAction: UIAction { [weak self] _ in
|
||||
self?.goToSettings()
|
||||
primaryAction: UIAction { [weak self, weak fromActionSheetController] _ in
|
||||
guard let self, let fromActionSheetController else { return }
|
||||
|
||||
fromActionSheetController.dismiss(animated: true) {
|
||||
self.goToSettings()
|
||||
}
|
||||
},
|
||||
)
|
||||
let buttonContainer = UIView.container()
|
||||
|
||||
@ -17,30 +17,31 @@ class PinReminderMegaphone: MegaphoneView {
|
||||
|
||||
let primaryButtonTitle = OWSLocalizedString("PIN_REMINDER_MEGAPHONE_ACTION", comment: "Action text for PIN reminder megaphone")
|
||||
|
||||
let primaryButton = MegaphoneView.Button(title: primaryButtonTitle) { [weak self] in
|
||||
let vc = PinReminderViewController { result in
|
||||
let primaryButton = MegaphoneView.Button(title: primaryButtonTitle) { [weak fromViewController] in
|
||||
guard let fromViewController else { return }
|
||||
|
||||
let vc = PinReminderViewController { [weak self] pinReminderViewController, result in
|
||||
// Always dismiss the PIN reminder view (we dismiss the *megaphone* later).
|
||||
fromViewController.dismiss(animated: true)
|
||||
pinReminderViewController.dismiss(animated: true)
|
||||
|
||||
guard let self else { return }
|
||||
|
||||
switch result {
|
||||
case .succeeded:
|
||||
self.dismiss(animated: false)
|
||||
self.presentToastForNewRepetitionInterval(
|
||||
presentToastForNewRepetitionInterval(
|
||||
wasSuccessful: true,
|
||||
fromViewController: fromViewController,
|
||||
)
|
||||
case .canceled(didGuessWrong: true):
|
||||
self.dismiss(animated: false)
|
||||
self.presentToastForNewRepetitionInterval(
|
||||
presentToastForNewRepetitionInterval(
|
||||
wasSuccessful: false,
|
||||
fromViewController: fromViewController,
|
||||
)
|
||||
case .changedPin:
|
||||
self.dismiss(animated: false)
|
||||
case .canceled(didGuessWrong: false):
|
||||
case .changedPin, .canceled(didGuessWrong: false):
|
||||
break
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
}
|
||||
|
||||
fromViewController.present(vc, animated: true)
|
||||
|
||||
@ -45,12 +45,20 @@ class RecoveryKeyReminderMegaphone: MegaphoneView {
|
||||
BackupRecoveryKeyReminderCoordinator(
|
||||
aep: aep,
|
||||
fromViewController: fromViewController,
|
||||
onSuccess: {
|
||||
self.dismiss()
|
||||
self.presentToastForNewRepetitionInterval(fromViewController: fromViewController)
|
||||
onSuccess: { [weak self] in
|
||||
guard let self else { return }
|
||||
|
||||
db.write { tx in
|
||||
backupSettingsStore.setLastRecoveryKeyReminderDate(Date(), tx: tx)
|
||||
}
|
||||
|
||||
let toastText = OWSLocalizedString(
|
||||
"BACKUP_KEY_REMINDER_SUCCESSFUL_TOAST",
|
||||
comment: "Toast indicating that the Recovery Key was correct.",
|
||||
)
|
||||
presentToast(text: toastText, fromViewController: fromViewController)
|
||||
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
},
|
||||
).presentVerifyFlow()
|
||||
}
|
||||
@ -66,13 +74,4 @@ class RecoveryKeyReminderMegaphone: MegaphoneView {
|
||||
required init(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func presentToastForNewRepetitionInterval(fromViewController: UIViewController) {
|
||||
let toastText = OWSLocalizedString(
|
||||
"BACKUP_KEY_REMINDER_SUCCESSFUL_TOAST",
|
||||
comment: "Toast indicating that the Recovery Key was correct.",
|
||||
)
|
||||
|
||||
presentToast(text: toastText, fromViewController: fromViewController)
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,16 +80,13 @@ class RemoteMegaphone: MegaphoneView {
|
||||
switch action {
|
||||
case .snooze:
|
||||
markAsSnoozedWithSneakyTransaction()
|
||||
dismiss()
|
||||
case .finish:
|
||||
markAsCompleteWithSneakyTransaction()
|
||||
dismiss()
|
||||
case .donate:
|
||||
let done = { [weak self] in
|
||||
guard let self else { return }
|
||||
// Snooze regardless of outcome.
|
||||
self.markAsSnoozedWithSneakyTransaction()
|
||||
self.dismiss(animated: false)
|
||||
}
|
||||
|
||||
guard
|
||||
@ -134,7 +131,6 @@ class RemoteMegaphone: MegaphoneView {
|
||||
guard let self else { return }
|
||||
// Snooze regardless of outcome.
|
||||
self.markAsSnoozedWithSneakyTransaction()
|
||||
self.dismiss(animated: false)
|
||||
}
|
||||
|
||||
guard
|
||||
@ -153,7 +149,6 @@ class RemoteMegaphone: MegaphoneView {
|
||||
fromViewController.present(navController, animated: true, completion: done)
|
||||
case .unrecognized(let actionId):
|
||||
owsFailDebug("Unrecognized action with ID \(actionId) should never have made it into \(buttonDescriptor) button!")
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,7 +355,7 @@ class AccountSettingsViewController: OWSTableViewController2 {
|
||||
SSKEnvironment.shared.ows2FAManagerRef.setAreRemindersEnabled(false, transaction: transaction)
|
||||
}
|
||||
|
||||
ExperienceUpgradeManager.dismissLastPresented(ifMatching: .pinReminder)
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
} else {
|
||||
self.updateTableContents()
|
||||
}
|
||||
|
||||
@ -355,7 +355,7 @@ extension LinkedDevicesViewModel: LinkDeviceViewControllerDelegate {
|
||||
deviceStore.clearMostRecentlyLinkedDeviceDetails(tx: tx)
|
||||
}
|
||||
|
||||
ExperienceUpgradeManager.dismissLastPresented(ifMatching: .newLinkedDeviceNotification)
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.notificationPresenterRef.clearDeliveredNewLinkedDevicesNotifications()
|
||||
|
||||
@ -111,8 +111,8 @@ extension ChatListViewController {
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(reloadExperienceUpgrades),
|
||||
name: .inactivePrimaryDeviceChanged,
|
||||
selector: #selector(reconcileExperienceUpgrades),
|
||||
name: .megaphoneStateDidChange,
|
||||
object: nil,
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
@ -262,7 +262,7 @@ extension ChatListViewController {
|
||||
private func applicationDidBecomeActive(_ notification: NSNotification) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
showExperienceUpgradeIfNecessary()
|
||||
reconcileExperienceUpgrades()
|
||||
updateShouldBeUpdatingView()
|
||||
}
|
||||
|
||||
@ -341,13 +341,6 @@ extension ChatListViewController {
|
||||
updateUsernameReminderView()
|
||||
loadCoordinator.loadIfNecessary()
|
||||
}
|
||||
|
||||
@objc
|
||||
private func reloadExperienceUpgrades() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
showExperienceUpgradeIfNecessary()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Notifications
|
||||
|
||||
@ -239,7 +239,8 @@ public class ChatListViewController: OWSViewController, HomeTabViewController {
|
||||
|
||||
appReadiness.setUIIsReady()
|
||||
|
||||
showExperienceUpgradeIfNecessary()
|
||||
presentGetStartedBannerIfNecessary()
|
||||
reconcileExperienceUpgrades()
|
||||
requestReviewIfAppropriate()
|
||||
showFYISheetIfNecessary()
|
||||
|
||||
@ -360,12 +361,9 @@ public class ChatListViewController: OWSViewController, HomeTabViewController {
|
||||
|
||||
// MARK: - Experience Upgrades
|
||||
|
||||
/// Show an "experience ugprade" if necessary; this might be an `ExperienceUpgrade`
|
||||
/// proper, or other "onboarding" UX.
|
||||
func showExperienceUpgradeIfNecessary() {
|
||||
if !ExperienceUpgradeManager.presentNext(fromViewController: self) {
|
||||
presentGetStartedBannerIfNecessary()
|
||||
}
|
||||
@objc
|
||||
func reconcileExperienceUpgrades() {
|
||||
ExperienceUpgradeManager.reconcilePresentedExperienceUpgrade(fromViewController: self)
|
||||
}
|
||||
|
||||
// MARK: - FYI sheets
|
||||
|
||||
@ -14,7 +14,7 @@ public class PinReminderViewController: OWSViewController {
|
||||
case succeeded
|
||||
}
|
||||
|
||||
private let completionHandler: ((PinReminderResult) -> Void)?
|
||||
private let completionHandler: (PinReminderViewController, PinReminderResult) -> Void
|
||||
|
||||
private let contentView = UIView()
|
||||
private var contentViewBottomEdgeConstraint: NSLayoutConstraint?
|
||||
@ -94,7 +94,7 @@ public class PinReminderViewController: OWSViewController {
|
||||
|
||||
private let context: ViewControllerContext
|
||||
|
||||
init(completionHandler: ((PinReminderResult) -> Void)? = nil) {
|
||||
init(completionHandler: @escaping (PinReminderViewController, PinReminderResult) -> Void) {
|
||||
self.context = ViewControllerContext.shared
|
||||
self.completionHandler = completionHandler
|
||||
super.init()
|
||||
@ -341,7 +341,7 @@ public class PinReminderViewController: OWSViewController {
|
||||
showCancelButton: true,
|
||||
onSuccess: { [weak self] _ in
|
||||
guard let self else { return }
|
||||
completionHandler?(.changedPin)
|
||||
completionHandler(self, .changedPin)
|
||||
},
|
||||
)
|
||||
present(OWSNavigationController(rootViewController: viewController), animated: true)
|
||||
@ -355,7 +355,7 @@ public class PinReminderViewController: OWSViewController {
|
||||
// If they didn't try and enter a PIN, we do nothing and leave the megaphone.
|
||||
if hasGuessedWrong { SSKEnvironment.shared.ows2FAManagerRef.reminderCompleted(incorrectAttempts: true) }
|
||||
|
||||
self.completionHandler?(.canceled(didGuessWrong: hasGuessedWrong))
|
||||
completionHandler(self, .canceled(didGuessWrong: hasGuessedWrong))
|
||||
}
|
||||
|
||||
private func submitPressed() {
|
||||
@ -382,9 +382,9 @@ public class PinReminderViewController: OWSViewController {
|
||||
let success = db.read { tx in twoFactorManager.verifyPin(pin, tx: tx) }
|
||||
if success {
|
||||
twoFactorManager.reminderCompleted(incorrectAttempts: self.hasGuessedWrong)
|
||||
self.completionHandler?(.succeeded)
|
||||
completionHandler(self, .succeeded)
|
||||
} else if !silent {
|
||||
self.validationState = .mismatch
|
||||
validationState = .mismatch
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -526,7 +526,7 @@ public class PinSetupViewController: OWSViewController, OWSNavigationChildContro
|
||||
accountAttributesUpdater.scheduleAccountAttributesUpdate(authedAccount: .implicit(), tx: tx)
|
||||
}
|
||||
|
||||
ExperienceUpgradeManager.dismissLastPresented(ifMatching: .introducingPins)
|
||||
NotificationCenter.default.post(name: .megaphoneStateDidChange, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,10 +3,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
extension Notification.Name {
|
||||
public static let inactivePrimaryDeviceChanged = Notification.Name("inactivePrimaryDeviceChanged")
|
||||
}
|
||||
|
||||
public class InactivePrimaryDeviceStore: NSObject {
|
||||
private enum StoreKeys {
|
||||
static let hasInactivePrimaryDeviceAlert: String = "hasInactivePrimaryDevice"
|
||||
|
||||
@ -3,6 +3,12 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
extension Notification.Name {
|
||||
public static let megaphoneStateDidChange = Notification.Name("ExperienceUpgradeManager.MegaphoneStateDidChange")
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public struct ExperienceUpgradeStore {
|
||||
|
||||
public init() {}
|
||||
|
||||
@ -1141,7 +1141,7 @@ class OWSAuthConnectionUsingLibSignal: OWSChatConnectionUsingLibSignal<Authentic
|
||||
|
||||
// Megaphones might load before we setup the OWSChatConnection
|
||||
// so we should notify the UI that the value has changed.
|
||||
NotificationCenter.default.postOnMainThread(name: .inactivePrimaryDeviceChanged, object: nil)
|
||||
NotificationCenter.default.postOnMainThread(name: .megaphoneStateDidChange, object: nil)
|
||||
}
|
||||
|
||||
func chatConnection(_ chat: AuthenticatedChatConnection, didReceiveAlerts alerts: [String]) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user