Signal-iOS/Signal/src/ViewControllers/HomeView/HomeViewController.swift
Michelle Linington f723cedb89 Badges avatars with custom diameters
- We now lerp the badge sprite to the closest design spec available
- Also enables badges on internal builds
2021-11-11 20:18:30 -08:00

307 lines
12 KiB
Swift

//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc
public extension HomeViewController {
func reloadTableDataAndResetCellContentCache() {
AssertIsOnMainThread()
cellContentCache.clear()
conversationCellHeightCache = nil
reloadTableData()
}
func reloadTableData() {
AssertIsOnMainThread()
tableView.reloadData()
}
func updateCellVisibility() {
AssertIsOnMainThread()
for cell in tableView.visibleCells {
guard let cell = cell as? HomeViewCell else {
continue
}
updateCellVisibility(cell: cell, isCellVisible: true)
}
}
func updateCellVisibility(cell: HomeViewCell, isCellVisible: Bool) {
AssertIsOnMainThread()
cell.isCellVisible = self.isViewVisible && isCellVisible
}
func ensureCellAnimations() {
AssertIsOnMainThread()
for cell in tableView.visibleCells {
guard let cell = cell as? HomeViewCell else {
continue
}
cell.ensureCellAnimations()
}
}
// MARK: -
func configureUnreadPaymentsBannerSingle(_ paymentsReminderView: UIView,
paymentModel: TSPaymentModel,
transaction: SDSAnyReadTransaction) {
guard paymentModel.isIncoming,
!paymentModel.isUnidentified,
let address = paymentModel.address,
let paymentAmount = paymentModel.paymentAmount,
paymentAmount.isValid else {
configureUnreadPaymentsBannerMultiple(paymentsReminderView, unreadCount: 1)
return
}
guard nil != TSContactThread.getWithContactAddress(address, transaction: transaction) else {
configureUnreadPaymentsBannerMultiple(paymentsReminderView, unreadCount: 1)
return
}
let userName = contactsManager.shortDisplayName(for: address, transaction: transaction)
let formattedAmount = PaymentsFormat.format(paymentAmount: paymentAmount,
isShortForm: true,
withCurrencyCode: true,
withSpace: true)
let format = NSLocalizedString("PAYMENTS_NOTIFICATION_BANNER_1_WITH_DETAILS_FORMAT",
comment: "Format for the payments notification banner for a single payment notification with details. Embeds: {{ %1$@ the name of the user who sent you the payment, %2$@ the amount of the payment }}.")
let title = String(format: format, userName, formattedAmount)
let avatarView = ConversationAvatarView(sizeClass: .customDiameter(Self.paymentsBannerAvatarSize), localUserDisplayMode: .asUser)
avatarView.update(transaction) { config in
config.dataSource = .address(address)
}
let paymentsHistoryItem = PaymentsHistoryItem(paymentModel: paymentModel,
displayName: userName)
configureUnreadPaymentsBanner(paymentsReminderView,
title: title,
avatarView: avatarView) { [weak self] in
self?.showAppSettings(mode: .payment(paymentsHistoryItem: paymentsHistoryItem))
}
}
func configureUnreadPaymentsBannerMultiple(_ paymentsReminderView: UIView,
unreadCount: UInt) {
let title: String
if unreadCount == 1 {
title = NSLocalizedString("PAYMENTS_NOTIFICATION_BANNER_1",
comment: "Label for the payments notification banner for a single payment notification.")
} else {
let format = NSLocalizedString("PAYMENTS_NOTIFICATION_BANNER_N_FORMAT",
comment: "Format for the payments notification banner for multiple payment notifications. Embeds: {{ the number of unread payment notifications }}.")
title = String(format: format, OWSFormat.formatUInt(unreadCount))
}
let iconView = UIImageView.withTemplateImageName(Theme.iconName(.paymentNotification),
tintColor: (Theme.isDarkThemeEnabled
? .ows_gray15
: .ows_white))
iconView.autoSetDimensions(to: .square(24))
let iconCircleView = OWSLayerView.circleView(size: CGFloat(Self.paymentsBannerAvatarSize))
iconCircleView.backgroundColor = (Theme.isDarkThemeEnabled
? .ows_gray80
: .ows_gray95)
iconCircleView.addSubview(iconView)
iconView.autoCenterInSuperview()
configureUnreadPaymentsBanner(paymentsReminderView,
title: title,
avatarView: iconCircleView) { [weak self] in
self?.showAppSettings(mode: .payments)
}
}
private static let paymentsBannerAvatarSize: UInt = 40
private class PaymentsBannerView: UIView {
let block: () -> Void
required init(block: @escaping () -> Void) {
self.block = block
super.init(frame: .zero)
isUserInteractionEnabled = true
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTap)))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc
func didTap() {
block()
}
}
private func configureUnreadPaymentsBanner(_ paymentsReminderView: UIView,
title: String,
avatarView: UIView,
block: @escaping () -> Void) {
paymentsReminderView.removeAllSubviews()
let paymentsBannerView = PaymentsBannerView(block: block)
paymentsReminderView.addSubview(paymentsBannerView)
paymentsBannerView.autoPinEdgesToSuperviewEdges()
if UIDevice.current.isIPad {
paymentsReminderView.backgroundColor = (Theme.isDarkThemeEnabled
? .ows_gray75
: .ows_gray05)
} else {
paymentsReminderView.backgroundColor = (Theme.isDarkThemeEnabled
? .ows_gray90
: .ows_gray02)
}
avatarView.setCompressionResistanceHigh()
avatarView.setContentHuggingHigh()
let titleLabel = UILabel()
titleLabel.text = title
titleLabel.textColor = Theme.primaryTextColor
titleLabel.font = UIFont.ows_dynamicTypeSubheadlineClamped.ows_semibold
let viewLabel = UILabel()
viewLabel.text = CommonStrings.viewButton
viewLabel.textColor = Theme.accentBlueColor
viewLabel.font = UIFont.ows_dynamicTypeSubheadlineClamped
let textStack = UIStackView(arrangedSubviews: [ titleLabel, viewLabel ])
textStack.axis = .vertical
textStack.alignment = .leading
let dismissButton = OWSLayerView.circleView(size: 20)
dismissButton.backgroundColor = (Theme.isDarkThemeEnabled
? .ows_gray65
: .ows_gray05)
dismissButton.setCompressionResistanceHigh()
dismissButton.setContentHuggingHigh()
let dismissIcon = UIImageView.withTemplateImageName("x-16",
tintColor: (Theme.isDarkThemeEnabled
? .ows_white
: .ows_gray60))
dismissIcon.autoSetDimensions(to: .square(16))
dismissButton.addSubview(dismissIcon)
dismissIcon.autoCenterInSuperview()
let stack = UIStackView(arrangedSubviews: [ avatarView,
textStack,
dismissButton ])
stack.axis = .horizontal
stack.alignment = .center
stack.spacing = 10
stack.layoutMargins = UIEdgeInsets(
top: OWSTableViewController2.cellVInnerMargin,
left: OWSTableViewController2.cellHOuterLeftMargin(in: view),
bottom: OWSTableViewController2.cellVInnerMargin,
right: OWSTableViewController2.cellHOuterRightMargin(in: view)
)
stack.isLayoutMarginsRelativeArrangement = true
paymentsBannerView.addSubview(stack)
stack.autoPinEdgesToSuperviewEdges()
}
}
// MARK: -
public enum ShowAppSettingsMode {
case none
case payments
case payment(paymentsHistoryItem: PaymentsHistoryItem)
case paymentsTransferIn
case appearance
case avatarBuilder
}
// MARK: -
public extension HomeViewController {
@objc
func createAvatarBarButtonViewWithSneakyTransaction() -> UIView {
let avatarView = ConversationAvatarView(sizeClass: .twentyEight, localUserDisplayMode: .asUser)
databaseStorage.read { readTx in
avatarView.update(readTx) { config in
if let address = tsAccountManager.localAddress(with: readTx) {
config.dataSource = .address(address)
config.applyConfigurationSynchronously()
}
}
}
return avatarView
}
@objc
func showAppSettings() {
showAppSettings(mode: .none)
}
@objc
func showAppSettingsInAppearanceMode() {
showAppSettings(mode: .appearance)
}
@objc
func showAppSettingsInAvatarBuilderMode() {
showAppSettings(mode: .avatarBuilder)
}
func showAppSettings(mode: ShowAppSettingsMode) {
AssertIsOnMainThread()
Logger.info("")
// Dismiss any message actions if they're presented
if FeatureFlags.contextMenus {
conversationSplitViewController?.selectedConversationViewController?.dismissMessageContextMenu(animated: true)
} else {
conversationSplitViewController?.selectedConversationViewController?.dismissMessageActions(animated: true)
}
let navigationController = AppSettingsViewController.inModalNavigationController()
var completion: (() -> Void)?
var viewControllers = navigationController.viewControllers
switch mode {
case .none:
break
case .payments:
let paymentsSettings = PaymentsSettingsViewController(mode: .inAppSettings)
viewControllers += [ paymentsSettings ]
case .payment(let paymentsHistoryItem):
let paymentsSettings = PaymentsSettingsViewController(mode: .inAppSettings)
let paymentsDetail = PaymentsDetailViewController(paymentItem: paymentsHistoryItem)
viewControllers += [ paymentsSettings, paymentsDetail ]
case .paymentsTransferIn:
let paymentsSettings = PaymentsSettingsViewController(mode: .inAppSettings)
let paymentsTransferIn = PaymentsTransferInViewController()
viewControllers += [ paymentsSettings, paymentsTransferIn ]
case .appearance:
let appearance = AppearanceSettingsTableViewController()
viewControllers += [ appearance ]
case .avatarBuilder:
let profile = ProfileSettingsViewController()
viewControllers += [ profile ]
completion = { profile.presentAvatarSettingsView() }
}
navigationController.setViewControllers(viewControllers, animated: false)
presentFormSheet(navigationController, animated: true, completion: completion)
}
}