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.
session-ios/Session/Conversations/Views & Modals/InfoBanner.swift

244 lines
8.1 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import UIKit
import SessionUIKit
final class InfoBanner: UIView {
public enum Icon: Equatable, Hashable {
case none
case link
case close
var image: UIImage? {
switch self {
case .none: return nil
case .link: return UIImage(systemName: "arrow.up.right.square")?.withRenderingMode(.alwaysTemplate)
case .close:
return UIImage(
systemName: "xmark",
withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .bold)
)?.withRenderingMode(.alwaysTemplate)
}
}
}
public struct Info: Equatable, Hashable {
let font: UIFont
let message: String
let icon: Icon
let tintColor: ThemeValue
let backgroundColor: ThemeValue
let accessibility: Accessibility?
let labelAccessibility: Accessibility?
let height: CGFloat?
let onTap: (() -> Void)?
static var empty: Info = Info(font: .systemFont(ofSize: Values.smallFontSize), message: "")
public init(
font: UIFont,
message: String,
icon: Icon = .none,
tintColor: ThemeValue = .black,
backgroundColor: ThemeValue = .primary,
accessibility: Accessibility? = nil,
labelAccessibility: Accessibility? = nil,
height: CGFloat? = nil,
onTap: (() -> Void)? = nil
) {
self.font = font
self.message = message
self.icon = icon
self.tintColor = tintColor
self.backgroundColor = backgroundColor
self.accessibility = accessibility
self.labelAccessibility = labelAccessibility
self.height = height
self.onTap = onTap
}
func with(
font: UIFont? = nil,
message: String? = nil,
icon: Icon? = nil,
tintColor: ThemeValue? = nil,
backgroundColor: ThemeValue? = nil,
accessibility: Accessibility? = nil,
labelAccessibility: Accessibility? = nil,
height: CGFloat? = nil,
onTap: (() -> Void)? = nil
) -> Info {
return Info(
font: font ?? self.font,
message: message ?? self.message,
icon: icon ?? self.icon,
tintColor: tintColor ?? self.tintColor,
backgroundColor: backgroundColor ?? self.backgroundColor,
accessibility: accessibility ?? self.accessibility,
labelAccessibility: labelAccessibility ?? self.labelAccessibility,
height: height ?? self.height,
onTap: onTap ?? self.onTap
)
}
public func hash(into hasher: inout Hasher) {
font.hash(into: &hasher)
message.hash(into: &hasher)
icon.hash(into: &hasher)
tintColor.hash(into: &hasher)
backgroundColor.hash(into: &hasher)
accessibility.hash(into: &hasher)
labelAccessibility.hash(into: &hasher)
height.hash(into: &hasher)
}
public static func == (lhs: InfoBanner.Info, rhs: InfoBanner.Info) -> Bool {
return (
lhs.font == rhs.font &&
lhs.message == rhs.message &&
lhs.icon == rhs.icon &&
lhs.tintColor == rhs.tintColor &&
lhs.backgroundColor == rhs.backgroundColor &&
lhs.accessibility == rhs.accessibility &&
lhs.labelAccessibility == rhs.labelAccessibility &&
lhs.height == rhs.height
)
}
}
private lazy var stackView: UIStackView = {
let result: UIStackView = UIStackView()
result.axis = .horizontal
result.alignment = .center
result.distribution = .fill
result.spacing = Values.smallSpacing
return result
}()
private lazy var leftIconPadding: UIView = {
let result: UIView = UIView()
result.set(.width, to: 18)
result.set(.height, to: 18)
result.isHidden = true
return result
}()
private lazy var label: UILabel = {
let result: UILabel = UILabel()
result.textAlignment = .center
result.lineBreakMode = .byWordWrapping
result.numberOfLines = 0
result.isAccessibilityElement = true
return result
}()
private lazy var rightIconImageView: UIImageView = {
let result: UIImageView = UIImageView(
image: UIImage(systemName: "arrow.up.right.square")?.withRenderingMode(.alwaysTemplate)
)
result.set(.width, to: 18)
result.set(.height, to: 18)
result.isHidden = true
return result
}()
public var info: Info?
private var heightConstraint: NSLayoutConstraint?
// MARK: - Initialization
init(info: Info) {
super.init(frame: CGRect.zero)
addSubview(stackView)
stackView.addArrangedSubview(leftIconPadding)
stackView.addArrangedSubview(label)
stackView.addArrangedSubview(rightIconImageView)
stackView.pin(.top, to: .top, of: self, withInset: Values.verySmallSpacing)
stackView.pin(.bottom, to: .bottom, of: self, withInset: -Values.verySmallSpacing)
stackView.pin(.leading, to: .leading, of: self, withInset: Values.mediumSpacing)
stackView.pin(.trailing, to: .trailing, of: self, withInset: -Values.mediumSpacing)
self.update(with: info)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(bannerTapped))
self.addGestureRecognizer(tapGestureRecognizer)
}
override init(frame: CGRect) {
preconditionFailure("Use init(message:) instead.")
}
required init?(coder: NSCoder) {
preconditionFailure("Use init(coder:) instead.")
}
// MARK: - Interaction
@objc private func bannerTapped() {
info?.onTap?()
}
// MARK: - Update
public func update(
font: UIFont? = nil,
message: String? = nil,
icon: Icon = .none,
tintColor: ThemeValue? = nil,
backgroundColor: ThemeValue? = nil,
accessibility: Accessibility? = nil,
labelAccessibility: Accessibility? = nil,
height: CGFloat? = nil,
onTap: (() -> Void)? = nil
) {
guard let currentInfo: Info = self.info else { return }
self.update(
with: currentInfo.with(
font: font,
message: message,
icon: icon,
tintColor: tintColor,
backgroundColor: backgroundColor,
accessibility: accessibility,
labelAccessibility: labelAccessibility,
height: height,
onTap: onTap
)
)
}
public func update(with info: InfoBanner.Info) {
self.info = info
self.heightConstraint?.isActive = false // Calling 'set' below will enable it
switch info.height {
case .some(let fixedHeight): self.heightConstraint = self.set(.height, to: fixedHeight)
case .none: break
}
themeBackgroundColor = info.backgroundColor
isAccessibilityElement = (info.accessibility != nil)
accessibilityIdentifier = info.accessibility?.identifier
accessibilityLabel = info.accessibility?.label
label.font = info.font
label.text = info.message
label.themeTextColor = info.tintColor
label.accessibilityIdentifier = info.labelAccessibility?.identifier
label.accessibilityLabel = info.labelAccessibility?.label
rightIconImageView.image = info.icon.image
rightIconImageView.isHidden = (info.icon == .none)
rightIconImageView.themeTintColor = info.tintColor
}
}