Path: blob/main/Signal/src/ViewControllers/AppSettings/Profile/BadgeConfigurationViewController.swift
1 views
//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalServiceKit
import SignalUI
enum BadgeConfiguration {
case doNotDisplayPublicly
case display(featuredBadge: OWSUserProfileBadgeInfo)
}
protocol BadgeConfigurationDelegate: AnyObject {
func badgeConfiguration(_: BadgeConfigurationViewController, didCompleteWithBadgeSetting setting: BadgeConfiguration)
func badgeConfirmationDidCancel(_: BadgeConfigurationViewController)
}
final class BadgeConfigurationViewController: OWSTableViewController2, BadgeCollectionDataSource {
private weak var badgeConfigDelegate: BadgeConfigurationDelegate?
let availableBadges: [OWSUserProfileBadgeInfo]
private let initialDisplaySetting: Bool
private let avatarImage: UIImage?
private var displayBadgeOnProfile: Bool {
didSet {
updateNavigation()
updateTableContents()
}
}
var selectedBadgeIndex: Int = 0 {
didSet {
if !availableBadges.indices.contains(selectedBadgeIndex) {
owsFailDebug("Invalid badge index")
selectedBadgeIndex = oldValue
} else {
updateNavigation()
updateTableContents()
}
}
}
private var selectedPrimaryBadge: OWSUserProfileBadgeInfo? {
displayBadgeOnProfile ? availableBadges[safe: selectedBadgeIndex] : nil
}
private var hasUnsavedChanges: Bool {
if displayBadgeOnProfile, selectedBadgeIndex != 0 {
return true
} else {
return displayBadgeOnProfile != initialDisplaySetting
}
}
var showDismissalActivity = false {
didSet {
updateNavigation()
}
}
static func load(delegate: BadgeConfigurationDelegate, tx: DBReadTransaction) -> Self {
let badges = SSKEnvironment.shared.profileManagerRef.localUserProfile(tx: tx)?.badges ?? []
let shouldDisplayOnProfile = DonationSubscriptionManager.displayBadgesOnProfile(transaction: tx)
return Self(availableBadges: badges, shouldDisplayOnProfile: shouldDisplayOnProfile, delegate: delegate)
}
init(availableBadges: [OWSUserProfileBadgeInfo], shouldDisplayOnProfile: Bool, avatarImage: UIImage? = nil, delegate: BadgeConfigurationDelegate) {
self.availableBadges = availableBadges
self.initialDisplaySetting = shouldDisplayOnProfile
owsAssertDebug(availableBadges.indices.contains(selectedBadgeIndex))
self.displayBadgeOnProfile = self.initialDisplaySetting
self.badgeConfigDelegate = delegate
self.avatarImage = avatarImage
super.init()
}
override func viewDidLoad() {
super.viewDidLoad()
updateNavigation()
updateTableContents()
}
// MARK: - Navigation Bar
private func updateNavigation() {
if navigationController?.viewControllers.count == 1 {
navigationItem.leftBarButtonItem = .cancelButton { [weak self] in
self?.didTapCancel()
}
}
if hasUnsavedChanges, showDismissalActivity {
let indicatorStyle: UIActivityIndicatorView.Style
indicatorStyle = .medium
let spinner = UIActivityIndicatorView(style: indicatorStyle)
spinner.startAnimating()
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: spinner)
} else if hasUnsavedChanges, !showDismissalActivity {
navigationItem.rightBarButtonItem = .button(
title: CommonStrings.setButton,
style: .done,
action: { [weak self] in
self?.didTapDone()
},
)
} else {
navigationItem.rightBarButtonItem = nil
}
}
private func didTapCancel() {
let requestDismissal: () -> Void = { [weak self] in
guard let self else { return }
self.badgeConfigDelegate?.badgeConfirmationDidCancel(self)
}
if hasUnsavedChanges {
OWSActionSheets.showPendingChangesActionSheet(discardAction: requestDismissal)
} else {
requestDismissal()
}
}
private func didTapDone() {
if displayBadgeOnProfile, let selectedPrimaryBadge {
badgeConfigDelegate?.badgeConfiguration(self, didCompleteWithBadgeSetting: .display(featuredBadge: selectedPrimaryBadge))
} else {
badgeConfigDelegate?.badgeConfiguration(self, didCompleteWithBadgeSetting: .doNotDisplayPublicly)
}
}
override var isModalInPresentation: Bool {
get { hasUnsavedChanges }
set {}
}
// MARK: - TableContents
func updateTableContents() {
self.contents = OWSTableContents(
title: OWSLocalizedString("BADGE_CONFIGURATION_TITLE", comment: "The title for the badge configuration page"),
sections: [
OWSTableSection(
title: OWSLocalizedString(
"BADGE_CONFIGURATION_BADGE_SECTION_TITLE",
comment: "Section header for badge view section in the badge configuration page",
),
items: [
OWSTableItem(customCellBlock: { [weak self] in
let cell = OWSTableItem.newCell()
guard let self else { return cell }
let collectionView = BadgeCollectionView(dataSource: self)
if let localAddress = DependenciesBridge.shared.tsAccountManager.localIdentifiersWithMaybeSneakyTransaction?.aciAddress {
let localShortName = SSKEnvironment.shared.databaseStorageRef.read {
return SSKEnvironment.shared.contactManagerRef.displayName(for: localAddress, tx: $0).resolvedValue(useShortNameIfAvailable: true)
}
collectionView.badgeSelectionMode = .detailsSheet(owner: .local(shortName: localShortName))
} else {
owsFailDebug("Unexpectedly missing local address")
}
cell.contentView.addSubview(collectionView)
collectionView.autoPinEdgesToSuperviewMargins()
// Pre-layout the collection view so the UITableView caches the correct resolved
// autolayout height.
collectionView.layoutIfNeeded()
return cell
}, actionBlock: nil),
],
),
OWSTableSection(title: nil, items: [
.switch(
withText: OWSLocalizedString(
"DISPLAY_BADGES_ON_PROFILE_SETTING",
comment: "Title for switch to enable sharing of badges publicly",
),
isOn: { [weak self] in self?.displayBadgeOnProfile ?? false },
target: self,
selector: #selector(didTogglePublicDisplaySetting(_:)),
),
.item(
name: OWSLocalizedString(
"FEATURED_BADGE_SETTINGS_TITLE",
comment: "The title for the featured badge settings page",
),
textColor: displayBadgeOnProfile ? nil : .ows_gray45,
accessoryText: selectedPrimaryBadge?.badge?.localizedName,
accessoryType: .disclosureIndicator,
accessibilityIdentifier: "badge_configuration_row",
actionBlock: { [weak self] in
guard let self, let navController = self.navigationController else { return }
guard self.displayBadgeOnProfile else { return }
let featuredBadgeSettings = FeaturedBadgeViewController(avatarImage: self.avatarImage, badgeDataSource: self)
navController.pushViewController(featuredBadgeSettings, animated: true)
},
),
]),
],
)
}
@objc
private func didTogglePublicDisplaySetting(_ sender: UISwitch) {
displayBadgeOnProfile = sender.isOn
}
var shouldCancelNavigationBack: Bool {
if hasUnsavedChanges {
didTapCancel()
return true
} else {
return false
}
}
}