mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			551 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			551 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Swift
		
	
| // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | |
| 
 | |
| import UIKit
 | |
| import GRDB
 | |
| import SessionUIKit
 | |
| 
 | |
| class SettingsCell: UITableViewCell {
 | |
|     /// This value is here to allow the theming update callback to be released when preparing for reuse
 | |
|     private var instanceView: UIView = UIView()
 | |
|     private var subtitleExtraView: UIView?
 | |
|     private var onExtraAction: (() -> Void)?
 | |
|     
 | |
|     // MARK: - UI
 | |
|     
 | |
|     private lazy var stackViewImageHeightConstraint: NSLayoutConstraint = contentStackView.heightAnchor.constraint(equalTo: iconImageView.heightAnchor)
 | |
|     
 | |
|     private let topSeparator: UIView = {
 | |
|         let result: UIView = UIView.separator()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let contentStackView: UIStackView = {
 | |
|         let result: UIStackView = UIStackView()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.axis = .horizontal
 | |
|         result.distribution = .fill
 | |
|         result.alignment = .center
 | |
|         result.spacing = Values.mediumSpacing
 | |
|         result.isLayoutMarginsRelativeArrangement = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let iconImageView: UIImageView = {
 | |
|         let result: UIImageView = UIImageView()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.contentMode = .scaleAspectFit
 | |
|         result.themeTintColor = .textPrimary
 | |
|         result.layer.minificationFilter = .trilinear
 | |
|         result.layer.magnificationFilter = .trilinear
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let titleStackView: UIStackView = {
 | |
|         let result: UIStackView = UIStackView()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.axis = .vertical
 | |
|         result.distribution = .equalSpacing
 | |
|         result.alignment = .fill
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let titleLabel: UILabel = {
 | |
|         let result: UILabel = UILabel()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.font = .boldSystemFont(ofSize: Values.mediumFontSize)
 | |
|         result.themeTextColor = .textPrimary
 | |
|         result.numberOfLines = 0
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let subtitleLabel: UILabel = {
 | |
|         let result: UILabel = UILabel()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.font = .systemFont(ofSize: Values.smallFontSize)
 | |
|         result.themeTextColor = .textPrimary
 | |
|         result.numberOfLines = 0
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private lazy var extraActionButton: UIButton = {
 | |
|         let result: UIButton = UIButton()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.titleLabel?.font = .boldSystemFont(ofSize: Values.smallFontSize)
 | |
|         result.contentHorizontalAlignment = .left
 | |
|         result.contentEdgeInsets = UIEdgeInsets(
 | |
|             top: 8,
 | |
|             left: 0,
 | |
|             bottom: 0,
 | |
|             right: 0
 | |
|         )
 | |
|         result.addTarget(self, action: #selector(extraActionTapped), for: .touchUpInside)
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let actionContainerView: UIView = {
 | |
|         let result: UIView = UIView()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let pushChevronImageView: UIImageView = {
 | |
|         let result: UIImageView = UIImageView(image: UIImage(systemName: "chevron.right"))
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.themeTintColor = .textPrimary
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let toggleSwitch: UISwitch = {
 | |
|         let result: UISwitch = UISwitch()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.isUserInteractionEnabled = false // Triggered by didSelectCell instead
 | |
|         result.themeOnTintColor = .primary
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let dropDownImageView: UIImageView = {
 | |
|         let result: UIImageView = UIImageView(image: UIImage(systemName: "arrowtriangle.down.fill"))
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.themeTintColor = .textPrimary
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let dropDownLabel: UILabel = {
 | |
|         let result: UILabel = UILabel()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.font = .systemFont(ofSize: Values.smallFontSize, weight: .medium)
 | |
|         result.themeTextColor = .textPrimary
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let tickImageView: UIImageView = {
 | |
|         let result: UIImageView = UIImageView(image: UIImage(systemName: "checkmark"))
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.themeTintColor = .primary
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     public lazy var rightActionButtonContainerView: UIView = {
 | |
|         let result: UIView = UIView()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.themeBackgroundColor = .solidButton_background
 | |
|         result.layer.cornerRadius = 5
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private lazy var rightActionButtonLabel: UILabel = {
 | |
|         let result: UILabel = UILabel()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.font = .boldSystemFont(ofSize: Values.smallFontSize)
 | |
|         result.themeTextColor = .textPrimary
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private let botSeparator: UIView = {
 | |
|         let result: UIView = UIView.separator()
 | |
|         result.translatesAutoresizingMaskIntoConstraints = false
 | |
|         result.isHidden = true
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     // MARK: - Initialization
 | |
|     
 | |
|     override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
 | |
|         super.init(style: style, reuseIdentifier: reuseIdentifier)
 | |
|         
 | |
|         setupViewHierarchy()
 | |
|     }
 | |
| 
 | |
|     required init?(coder: NSCoder) {
 | |
|         super.init(coder: coder)
 | |
|         
 | |
|         setupViewHierarchy()
 | |
|     }
 | |
| 
 | |
|     private func setupViewHierarchy() {
 | |
|         contentView.addSubview(topSeparator)
 | |
|         contentView.addSubview(contentStackView)
 | |
|         contentView.addSubview(botSeparator)
 | |
|         
 | |
|         contentStackView.addArrangedSubview(iconImageView)
 | |
|         contentStackView.addArrangedSubview(titleStackView)
 | |
|         contentStackView.addArrangedSubview(actionContainerView)
 | |
|         contentStackView.addArrangedSubview(pushChevronImageView)
 | |
|         contentStackView.addArrangedSubview(toggleSwitch)
 | |
|         contentStackView.addArrangedSubview(tickImageView)
 | |
|         
 | |
|         titleStackView.addArrangedSubview(titleLabel)
 | |
|         titleStackView.addArrangedSubview(subtitleLabel)
 | |
|         titleStackView.addArrangedSubview(extraActionButton)
 | |
|         
 | |
|         actionContainerView.addSubview(dropDownImageView)
 | |
|         actionContainerView.addSubview(dropDownLabel)
 | |
|         actionContainerView.addSubview(rightActionButtonContainerView)
 | |
|         
 | |
|         rightActionButtonContainerView.addSubview(rightActionButtonLabel)
 | |
|         
 | |
|         setupLayout()
 | |
|     }
 | |
|     
 | |
|     private func setupLayout() {
 | |
|         topSeparator.pin(.top, to: .top, of: contentView)
 | |
|         topSeparator.pin(.left, to: .left, of: contentView)
 | |
|         topSeparator.pin(.right, to: .right, of: contentView)
 | |
|         
 | |
|         contentStackView.pin(to: contentView)
 | |
|         
 | |
|         titleLabel.setCompressionResistanceHorizontalLow()
 | |
|         subtitleLabel.setCompressionResistanceHorizontalLow()
 | |
|         
 | |
|         pushChevronImageView.setContentHuggingHigh()
 | |
|         pushChevronImageView.setCompressionResistanceHigh()
 | |
|         
 | |
|         toggleSwitch.setContentHuggingHigh()
 | |
|         toggleSwitch.setCompressionResistanceHigh()
 | |
|         
 | |
|         tickImageView.setContentHuggingHigh()
 | |
|         tickImageView.setCompressionResistanceHigh()
 | |
|         
 | |
|         actionContainerView.setContentHuggingHigh()
 | |
|         actionContainerView.setCompressionResistanceHigh()
 | |
|         actionContainerView.set(.height, to: .height, of: contentStackView)
 | |
|         
 | |
|         dropDownImageView.center(.vertical, in: actionContainerView)
 | |
|         dropDownImageView.pin(.left, to: .left, of: actionContainerView)
 | |
|         dropDownImageView.set(.width, to: 10)
 | |
|         dropDownImageView.set(.height, to: 10)
 | |
|         
 | |
|         dropDownLabel.setContentHuggingHigh()
 | |
|         dropDownLabel.setCompressionResistanceHigh()
 | |
|         dropDownLabel.center(.vertical, in: actionContainerView)
 | |
|         dropDownLabel.pin(.left, to: .right, of: dropDownImageView, withInset: Values.verySmallSpacing)
 | |
|         dropDownLabel.pin(.right, to: .right, of: actionContainerView)
 | |
|         
 | |
|         rightActionButtonContainerView.center(.vertical, in: actionContainerView)
 | |
|         rightActionButtonContainerView.pin(.left, to: .left, of: actionContainerView)
 | |
|         rightActionButtonContainerView.pin(.right, to: .right, of: actionContainerView)
 | |
|         
 | |
|         rightActionButtonLabel.setContentHuggingHigh()
 | |
|         rightActionButtonLabel.setCompressionResistanceHigh()
 | |
|         rightActionButtonLabel.pin(to: rightActionButtonContainerView, withInset: Values.smallSpacing)
 | |
|         
 | |
|         botSeparator.pin(.left, to: .left, of: contentView)
 | |
|         botSeparator.pin(.right, to: .right, of: contentView)
 | |
|         botSeparator.pin(.bottom, to: .bottom, of: contentView)
 | |
|     }
 | |
|     
 | |
|     override func layoutSubviews() {
 | |
|         super.layoutSubviews()
 | |
|         
 | |
|         // Need to force the contentStackView to layout if needed as it might not have updated it's
 | |
|         // sizing yet
 | |
|         self.contentStackView.layoutIfNeeded()
 | |
|         
 | |
|         // Position the 'subtitleExtraView' at the end of the last line of text
 | |
|         if
 | |
|             let subtitleExtraView: UIView = self.subtitleExtraView,
 | |
|             let subtitle: String = subtitleLabel.text,
 | |
|             let font: UIFont = subtitleLabel.font
 | |
|         {
 | |
|             let layoutManager: NSLayoutManager = NSLayoutManager()
 | |
|             let textStorage = NSTextStorage(
 | |
|                 attributedString: NSAttributedString(
 | |
|                     string: subtitle,
 | |
|                     attributes: [ .font: font ]
 | |
|                 )
 | |
|             )
 | |
|             textStorage.addLayoutManager(layoutManager)
 | |
|             
 | |
|             let textContainer: NSTextContainer = NSTextContainer(
 | |
|                 size: CGSize(
 | |
|                     width: subtitleLabel.bounds.size.width,
 | |
|                     height: 999
 | |
|                 )
 | |
|             )
 | |
|             textContainer.lineFragmentPadding = 0
 | |
|             layoutManager.addTextContainer(textContainer)
 | |
|             
 | |
|             var glyphRange: NSRange = NSRange()
 | |
|             layoutManager.characterRange(
 | |
|                 forGlyphRange: NSRange(location: subtitle.glyphCount - 1, length: 1),
 | |
|                 actualGlyphRange: &glyphRange
 | |
|             )
 | |
|             let lastGlyphRect: CGRect = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
 | |
|             
 | |
|             // Remove and re-add the 'subtitleExtraView' to clear any old constraints
 | |
|             subtitleExtraView.removeFromSuperview()
 | |
|             contentView.addSubview(subtitleExtraView)
 | |
|             
 | |
|             subtitleExtraView.pin(
 | |
|                 .top,
 | |
|                 to: .top,
 | |
|                 of: subtitleLabel,
 | |
|                 withInset: (lastGlyphRect.minY + ((lastGlyphRect.height / 2) - (subtitleExtraView.bounds.height / 2)))
 | |
|             )
 | |
|             subtitleExtraView.pin(
 | |
|                 .left,
 | |
|                 to: .left,
 | |
|                 of: subtitleLabel,
 | |
|                 withInset: lastGlyphRect.maxX
 | |
|             )
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     // MARK: - Content
 | |
|     
 | |
|     override func prepareForReuse() {
 | |
|         super.prepareForReuse()
 | |
|         
 | |
|         self.themeBackgroundColor = nil
 | |
|         self.selectedBackgroundView = nil
 | |
|         self.instanceView = UIView()
 | |
|         self.onExtraAction = nil
 | |
|         self.accessibilityIdentifier = nil
 | |
|         
 | |
|         stackViewImageHeightConstraint.isActive = false
 | |
|         iconImageView.removeConstraints(iconImageView.constraints)
 | |
|         iconImageView.image = nil
 | |
|         titleLabel.text = ""
 | |
|         titleLabel.themeTextColor = .textPrimary
 | |
|         subtitleLabel.text = ""
 | |
|         dropDownLabel.text = ""
 | |
|         
 | |
|         topSeparator.isHidden = true
 | |
|         iconImageView.isHidden = true
 | |
|         subtitleLabel.isHidden = true
 | |
|         extraActionButton.isHidden = true
 | |
|         actionContainerView.isHidden = true
 | |
|         pushChevronImageView.isHidden = true
 | |
|         toggleSwitch.isHidden = true
 | |
|         dropDownImageView.isHidden = true
 | |
|         dropDownLabel.isHidden = true
 | |
|         tickImageView.isHidden = true
 | |
|         tickImageView.alpha = 1
 | |
|         rightActionButtonContainerView.isHidden = true
 | |
|         botSeparator.isHidden = true
 | |
|         
 | |
|         subtitleExtraView?.removeFromSuperview()
 | |
|         subtitleExtraView = nil
 | |
|     }
 | |
|     
 | |
|     public func update(
 | |
|         icon: UIImage?,
 | |
|         iconSize: IconSize,
 | |
|         iconSetter: ((UIImageView) -> Void)?,
 | |
|         title: String,
 | |
|         subtitle: String?,
 | |
|         alignment: NSTextAlignment,
 | |
|         accessibilityIdentifier: String?,
 | |
|         subtitleExtraViewGenerator: (() -> UIView)?,
 | |
|         action: SettingsAction,
 | |
|         extraActionTitle: ((Theme, Theme.PrimaryColor) -> NSAttributedString)?,
 | |
|         onExtraAction: (() -> Void)?,
 | |
|         isFirstInSection: Bool,
 | |
|         isLastInSection: Bool
 | |
|     ) {
 | |
|         self.instanceView = UIView()
 | |
|         self.subtitleExtraView = subtitleExtraViewGenerator?()
 | |
|         self.onExtraAction = onExtraAction
 | |
|         self.accessibilityIdentifier = accessibilityIdentifier
 | |
|         
 | |
|         stackViewImageHeightConstraint.isActive = {
 | |
|             switch iconSize {
 | |
|                 case .small: return false
 | |
|                 case .large: return true   // Edge-to-edge in this case
 | |
|             }
 | |
|         }()
 | |
|         contentStackView.layoutMargins = UIEdgeInsets(
 | |
|             top: Values.mediumSpacing,
 | |
|             leading: {
 | |
|                 switch iconSize {
 | |
|                     case .small: return Values.largeSpacing
 | |
|                     case .large: return 0   // Edge-to-edge in this case
 | |
|                 }
 | |
|             }(),
 | |
|             bottom: Values.mediumSpacing,
 | |
|             trailing: Values.largeSpacing
 | |
|         )
 | |
|         
 | |
|         // Left content
 | |
|         iconImageView.set(.width, to: iconSize.size)
 | |
|         iconImageView.set(.height, to: iconSize.size)
 | |
|         iconImageView.image = icon
 | |
|         iconImageView.isHidden = (icon == nil && iconSetter == nil)
 | |
|         titleLabel.text = title
 | |
|         titleLabel.textAlignment = alignment
 | |
|         subtitleLabel.text = subtitle
 | |
|         subtitleLabel.isHidden = (subtitle == nil)
 | |
|         extraActionButton.isHidden = (extraActionTitle == nil)
 | |
|         
 | |
|         // Call the iconSetter closure if provided to set the icon
 | |
|         iconSetter?(iconImageView)
 | |
|         
 | |
|         // Separator/background Visibility
 | |
|         if action.shouldHaveBackground {
 | |
|             self.themeBackgroundColor = .settings_tabBackground
 | |
|             
 | |
|             topSeparator.isHidden = isFirstInSection
 | |
|             botSeparator.isHidden = !isLastInSection
 | |
|         }
 | |
|         else {
 | |
|             self.themeBackgroundColor = nil
 | |
|             
 | |
|             topSeparator.isHidden = true
 | |
|             botSeparator.isHidden = true
 | |
|         }
 | |
|         
 | |
|         // Highlight
 | |
|         let selectedBackgroundView = UIView()
 | |
|         selectedBackgroundView.themeBackgroundColor = .settings_tabHighlight
 | |
|         self.selectedBackgroundView = selectedBackgroundView
 | |
|         
 | |
|         // Action Behaviours
 | |
|         switch action {
 | |
|             case .threadInfo: break
 | |
|             
 | |
|             case .userDefaultsBool(let defaults, let key, let isEnabled, _):
 | |
|                 toggleSwitch.isHidden = false
 | |
|                 toggleSwitch.isEnabled = isEnabled
 | |
|                 
 | |
|                 // Remove the selection view if the setting is disabled
 | |
|                 if !isEnabled {
 | |
|                     self.selectedBackgroundView = UIView()
 | |
|                 }
 | |
|                 
 | |
|                 let newValue: Bool = defaults.bool(forKey: key)
 | |
|                 
 | |
|                 if newValue != toggleSwitch.isOn {
 | |
|                     toggleSwitch.setOn(newValue, animated: true)
 | |
|                 }
 | |
|             
 | |
|             case .settingBool(let key, _, let isEnabled):
 | |
|                 toggleSwitch.isHidden = false
 | |
|                 toggleSwitch.isEnabled = isEnabled
 | |
|                 
 | |
|                 // Remove the selection view if the setting is disabled
 | |
|                 if !isEnabled {
 | |
|                     self.selectedBackgroundView = UIView()
 | |
|                 }
 | |
|                 
 | |
|                 let newValue: Bool = Storage.shared[key]
 | |
|                 
 | |
|                 if newValue != toggleSwitch.isOn {
 | |
|                     toggleSwitch.setOn(newValue, animated: true)
 | |
|                 }
 | |
|                 
 | |
|             case .customToggle(let value, let isEnabled, _, _):
 | |
|                 toggleSwitch.isHidden = false
 | |
|                 toggleSwitch.isEnabled = isEnabled
 | |
|                 
 | |
|                 // Remove the selection view if the setting is disabled
 | |
|                 if !isEnabled {
 | |
|                     self.selectedBackgroundView = UIView()
 | |
|                 }
 | |
|                 
 | |
|                 if value != toggleSwitch.isOn {
 | |
|                     toggleSwitch.setOn(value, animated: true)
 | |
|                 }
 | |
| 
 | |
|             case .settingEnum(_, let value, _), .generalEnum(let value, _):
 | |
|                 actionContainerView.isHidden = false
 | |
|                 dropDownImageView.isHidden = false
 | |
|                 dropDownLabel.isHidden = false
 | |
|                 dropDownLabel.text = value
 | |
|                 
 | |
|             case .listSelection(let isSelected, let storedSelection, _, _):
 | |
|                 tickImageView.isHidden = (!isSelected() && !storedSelection)
 | |
|                 tickImageView.alpha = (!isSelected() && storedSelection ? 0.3 : 1)
 | |
|                 
 | |
|             case .trigger(let showChevron, _):
 | |
|                 pushChevronImageView.isHidden = !showChevron
 | |
|             
 | |
|             case .push(let showChevron, let textColor, _, _):
 | |
|                 titleLabel.themeTextColor = textColor
 | |
|                 pushChevronImageView.isHidden = !showChevron
 | |
|                 
 | |
|             case .present(_): break
 | |
|             
 | |
|             case .rightButtonAction(let title, _):
 | |
|                 actionContainerView.isHidden = false
 | |
|                 rightActionButtonContainerView.isHidden = false
 | |
|                 rightActionButtonLabel.text = title
 | |
|         }
 | |
|         
 | |
|         // Extra action
 | |
|         if let extraActionTitle: ((Theme, Theme.PrimaryColor) -> NSAttributedString) = extraActionTitle {
 | |
|             ThemeManager.onThemeChange(observer: instanceView) { [weak extraActionButton] theme, primaryColor in
 | |
|                 extraActionButton?.setAttributedTitle(
 | |
|                     extraActionTitle(theme, primaryColor),
 | |
|                     for: .normal
 | |
|                 )
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public func update(isEditing: Bool, animated: Bool) {}
 | |
|     
 | |
|     // MARK: - Interaction
 | |
|     
 | |
|     override func setHighlighted(_ highlighted: Bool, animated: Bool) {
 | |
|         super.setHighlighted(highlighted, animated: animated)
 | |
| 
 | |
|         rightActionButtonContainerView.themeBackgroundColor = (highlighted ?
 | |
|             .solidButton_highlight :
 | |
|             .solidButton_background
 | |
|         )
 | |
|     }
 | |
|     
 | |
|     override func setSelected(_ selected: Bool, animated: Bool) {
 | |
|         super.setSelected(selected, animated: animated)
 | |
|         
 | |
|         // Note: When initially triggering a selection we will be coming from the highlighted
 | |
|         // state but will have already set highlighted to false at this stage, as a result we
 | |
|         // need to swap back into the "highlighted" state so we can properly unhighlight within
 | |
|         // the "deselect" animation
 | |
|         guard !selected else {
 | |
|             rightActionButtonContainerView.themeBackgroundColor = .solidButton_highlight
 | |
|             return
 | |
|         }
 | |
|         guard animated else {
 | |
|             rightActionButtonContainerView.themeBackgroundColor = .solidButton_background
 | |
|             return
 | |
|         }
 | |
|         
 | |
|         UIView.animate(withDuration: 0.4) { [weak self] in
 | |
|             self?.rightActionButtonContainerView.themeBackgroundColor = .solidButton_background
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     @objc private func extraActionTapped() {
 | |
|         onExtraAction?()
 | |
|     }
 | |
| }
 |