session-ios/Session/Shared/Types/SessionCell+Accessory.swift

465 lines
18 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import GRDB
import SessionUIKit
import SessionMessagingKit
import SessionUtilitiesKit
extension SessionCell {
public enum Accessory: Hashable, Equatable {
case icon(
UIImage?,
size: IconSize,
customTint: ThemeValue?,
shouldFill: Bool,
accessibility: SessionCell.Accessibility?
)
case iconAsync(
size: IconSize,
customTint: ThemeValue?,
shouldFill: Bool,
accessibility: SessionCell.Accessibility?,
setter: (UIImageView) -> Void
)
case toggle(
DataSource,
accessibility: SessionCell.Accessibility?
)
case dropDown(
DataSource,
accessibility: SessionCell.Accessibility?
)
case radio(
size: RadioSize,
isSelected: () -> Bool,
storedSelection: Bool,
accessibility: SessionCell.Accessibility?
)
case highlightingBackgroundLabel(
title: String,
accessibility: SessionCell.Accessibility?
)
case profile(
id: String,
size: IconSize,
threadVariant: SessionThread.Variant,
customImageData: Data?,
profile: Profile?,
additionalProfile: Profile?,
cornerIcon: UIImage?,
accessibility: SessionCell.Accessibility?
)
case search(
placeholder: String,
accessibility: SessionCell.Accessibility?,
searchTermChanged: (String?) -> Void
)
case button(
style: SessionButton.Style,
title: String,
accessibility: SessionCell.Accessibility?,
run: (SessionButton?) -> Void
)
case customView(
hashValue: AnyHashable,
viewGenerator: () -> UIView
)
// MARK: - Convenience Vatiables
var shouldFitToEdge: Bool {
switch self {
case .icon(_, _, _, let shouldFill, _), .iconAsync(_, _, let shouldFill, _, _):
return shouldFill
default: return false
}
}
var currentBoolValue: Bool {
switch self {
case .toggle(let dataSource, _), .dropDown(let dataSource, _): return dataSource.currentBoolValue
case .radio(_, let isSelected, _, _): return isSelected()
default: return false
}
}
// MARK: - Conformance
public func hash(into hasher: inout Hasher) {
switch self {
case .icon(let image, let size, let customTint, let shouldFill, let accessibility):
image.hash(into: &hasher)
size.hash(into: &hasher)
customTint.hash(into: &hasher)
shouldFill.hash(into: &hasher)
accessibility.hash(into: &hasher)
case .iconAsync(let size, let customTint, let shouldFill, let accessibility, _):
size.hash(into: &hasher)
customTint.hash(into: &hasher)
shouldFill.hash(into: &hasher)
accessibility.hash(into: &hasher)
case .toggle(let dataSource, let accessibility):
dataSource.hash(into: &hasher)
accessibility.hash(into: &hasher)
case .dropDown(let dataSource, let accessibility):
dataSource.hash(into: &hasher)
accessibility.hash(into: &hasher)
case .radio(let size, let isSelected, let storedSelection, let accessibility):
size.hash(into: &hasher)
isSelected().hash(into: &hasher)
storedSelection.hash(into: &hasher)
accessibility.hash(into: &hasher)
case .highlightingBackgroundLabel(let title, let accessibility):
title.hash(into: &hasher)
accessibility.hash(into: &hasher)
case .profile(
let profileId,
let size,
let threadVariant,
let customImageData,
let profile,
let additionalProfile,
let cornerIcon,
let accessibility
):
profileId.hash(into: &hasher)
size.hash(into: &hasher)
threadVariant.hash(into: &hasher)
customImageData.hash(into: &hasher)
profile.hash(into: &hasher)
additionalProfile.hash(into: &hasher)
cornerIcon.hash(into: &hasher)
accessibility.hash(into: &hasher)
case .search(let placeholder, let accessibility, _):
placeholder.hash(into: &hasher)
accessibility.hash(into: &hasher)
case .button(let style, let title, let accessibility, _):
style.hash(into: &hasher)
title.hash(into: &hasher)
accessibility.hash(into: &hasher)
case .customView(let hashValue, _):
hashValue.hash(into: &hasher)
}
}
public static func == (lhs: Accessory, rhs: Accessory) -> Bool {
switch (lhs, rhs) {
case (.icon(let lhsImage, let lhsSize, let lhsCustomTint, let lhsShouldFill, let lhsAccessibility), .icon(let rhsImage, let rhsSize, let rhsCustomTint, let rhsShouldFill, let rhsAccessibility)):
return (
lhsImage == rhsImage &&
lhsSize == rhsSize &&
lhsCustomTint == rhsCustomTint &&
lhsShouldFill == rhsShouldFill &&
lhsAccessibility == rhsAccessibility
)
case (.iconAsync(let lhsSize, let lhsCustomTint, let lhsShouldFill, let lhsAccessibility, _), .iconAsync(let rhsSize, let rhsCustomTint, let rhsShouldFill, let rhsAccessibility, _)):
return (
lhsSize == rhsSize &&
lhsCustomTint == rhsCustomTint &&
lhsShouldFill == rhsShouldFill &&
lhsAccessibility == rhsAccessibility
)
case (.toggle(let lhsDataSource, let lhsAccessibility), .toggle(let rhsDataSource, let rhsAccessibility)):
return (
lhsDataSource == rhsDataSource &&
lhsAccessibility == rhsAccessibility
)
case (.dropDown(let lhsDataSource, let lhsAccessibility), .dropDown(let rhsDataSource, let rhsAccessibility)):
return (
lhsDataSource == rhsDataSource &&
lhsAccessibility == rhsAccessibility
)
case (.radio(let lhsSize, let lhsIsSelected, let lhsStoredSelection, let lhsAccessibility), .radio(let rhsSize, let rhsIsSelected, let rhsStoredSelection, let rhsAccessibility)):
return (
lhsSize == rhsSize &&
lhsIsSelected() == rhsIsSelected() &&
lhsStoredSelection == rhsStoredSelection &&
lhsAccessibility == rhsAccessibility
)
case (.highlightingBackgroundLabel(let lhsTitle, let lhsAccessibility), .highlightingBackgroundLabel(let rhsTitle, let rhsAccessibility)):
return (
lhsTitle == rhsTitle &&
lhsAccessibility == rhsAccessibility
)
case (
.profile(
let lhsProfileId,
let lhsSize,
let lhsThreadVariant,
let lhsProfile,
let lhsAdditionalProfile,
let lhsCustomImageData,
let lhsCornerIcon,
let lhsAccessibility
),
.profile(
let rhsProfileId,
let rhsSize,
let rhsThreadVariant,
let rhsProfile,
let rhsAdditionalProfile,
let rhsCustomImageData,
let rhsCornerIcon,
let rhsAccessibility
)
):
return (
lhsProfileId == rhsProfileId &&
lhsSize == rhsSize &&
lhsThreadVariant == rhsThreadVariant &&
lhsProfile == rhsProfile &&
lhsAdditionalProfile == rhsAdditionalProfile &&
lhsCustomImageData == rhsCustomImageData &&
lhsCornerIcon == rhsCornerIcon &&
lhsAccessibility == rhsAccessibility
)
case (.search(let lhsPlaceholder, let lhsAccessibility, _), .search(let rhsPlaceholder, let rhsAccessibility, _)):
return (
lhsPlaceholder == rhsPlaceholder &&
lhsAccessibility == rhsAccessibility
)
case (.button(let lhsStyle, let lhsTitle, let lhsAccessibility, _), .button(let rhsStyle, let rhsTitle, let rhsAccessibility, _)):
return (
lhsStyle == rhsStyle &&
lhsTitle == rhsTitle &&
lhsAccessibility == rhsAccessibility
)
case (.customView(let lhsHashValue, _), .customView(let rhsHashValue, _)):
return (
lhsHashValue.hashValue == rhsHashValue.hashValue
)
default: return false
}
}
}
}
// MARK: - Convenience Types
/// These are here because XCode doesn't realy like default values within enums so auto-complete and syntax
/// highlighting don't work properly
extension SessionCell.Accessory {
// MARK: - .icon Variants
public static func icon(_ image: UIImage?) -> SessionCell.Accessory {
return .icon(image, size: .medium, customTint: nil, shouldFill: false, accessibility: nil)
}
public static func icon(_ image: UIImage?, customTint: ThemeValue) -> SessionCell.Accessory {
return .icon(image, size: .medium, customTint: customTint, shouldFill: false, accessibility: nil)
}
public static func icon(_ image: UIImage?, size: IconSize) -> SessionCell.Accessory {
return .icon(image, size: size, customTint: nil, shouldFill: false, accessibility: nil)
}
public static func icon(_ image: UIImage?, size: IconSize, customTint: ThemeValue) -> SessionCell.Accessory {
return .icon(image, size: size, customTint: customTint, shouldFill: false, accessibility: nil)
}
public static func icon(_ image: UIImage?, shouldFill: Bool) -> SessionCell.Accessory {
return .icon(image, size: .medium, customTint: nil, shouldFill: shouldFill, accessibility: nil)
}
public static func icon(_ image: UIImage?, accessibility: SessionCell.Accessibility) -> SessionCell.Accessory {
return .icon(image, size: .medium, customTint: nil, shouldFill: false, accessibility: accessibility)
}
// MARK: - .iconAsync Variants
public static func iconAsync(_ setter: @escaping (UIImageView) -> Void) -> SessionCell.Accessory {
return .iconAsync(size: .medium, customTint: nil, shouldFill: false, accessibility: nil, setter: setter)
}
public static func iconAsync(customTint: ThemeValue, _ setter: @escaping (UIImageView) -> Void) -> SessionCell.Accessory {
return .iconAsync(size: .medium, customTint: customTint, shouldFill: false, accessibility: nil, setter: setter)
}
public static func iconAsync(size: IconSize, setter: @escaping (UIImageView) -> Void) -> SessionCell.Accessory {
return .iconAsync(size: size, customTint: nil, shouldFill: false, accessibility: nil, setter: setter)
}
public static func iconAsync(shouldFill: Bool, setter: @escaping (UIImageView) -> Void) -> SessionCell.Accessory {
return .iconAsync(size: .medium, customTint: nil, shouldFill: shouldFill, accessibility: nil, setter: setter)
}
public static func iconAsync(size: IconSize, customTint: ThemeValue, setter: @escaping (UIImageView) -> Void) -> SessionCell.Accessory {
return .iconAsync(size: size, customTint: customTint, shouldFill: false, accessibility: nil, setter: setter)
}
public static func iconAsync(size: IconSize, shouldFill: Bool, setter: @escaping (UIImageView) -> Void) -> SessionCell.Accessory {
return .iconAsync(size: size, customTint: nil, shouldFill: shouldFill, accessibility: nil, setter: setter)
}
// MARK: - .toggle Variants
public static func toggle(_ dataSource: DataSource) -> SessionCell.Accessory {
return .toggle(dataSource, accessibility: nil)
}
// MARK: - .dropDown Variants
public static func dropDown(_ dataSource: DataSource) -> SessionCell.Accessory {
return .dropDown(dataSource, accessibility: nil)
}
// MARK: - .radio Variants
public static func radio(isSelected: @escaping () -> Bool) -> SessionCell.Accessory {
return .radio(size: .medium, isSelected: isSelected, storedSelection: false, accessibility: nil)
}
public static func radio(isSelected: @escaping () -> Bool, storedSelection: Bool) -> SessionCell.Accessory {
return .radio(size: .medium, isSelected: isSelected, storedSelection: storedSelection, accessibility: nil)
}
// MARK: - .highlightingBackgroundLabel Variants
public static func highlightingBackgroundLabel(title: String) -> SessionCell.Accessory {
return .highlightingBackgroundLabel(title: title, accessibility: nil)
}
// MARK: - .profile Variants
public static func profile(id: String, profile: Profile?) -> SessionCell.Accessory {
return .profile(
id: id,
size: .veryLarge,
threadVariant: .contact,
customImageData: nil,
profile: profile,
additionalProfile: nil,
cornerIcon: nil,
accessibility: nil
)
}
public static func profile(id: String, size: IconSize, profile: Profile?) -> SessionCell.Accessory {
return .profile(
id: id,
size: size,
threadVariant: .contact,
customImageData: nil,
profile: profile,
additionalProfile: nil,
cornerIcon: nil,
accessibility: nil
)
}
// MARK: - .search Variants
public static func search(placeholder: String, searchTermChanged: @escaping (String?) -> Void) -> SessionCell.Accessory {
return .search(placeholder: placeholder, accessibility: nil, searchTermChanged: searchTermChanged)
}
// MARK: - .button Variants
public static func button(style: SessionButton.Style, title: String, run: @escaping (SessionButton?) -> Void) -> SessionCell.Accessory {
return .button(style: style, title: title, accessibility: nil, run: run)
}
}
// MARK: - SessionCell.Accessory.DataSource
extension SessionCell.Accessory {
public enum DataSource: Hashable, Equatable {
case boolValue(Bool)
case dynamicString(() -> String?)
case userDefaults(UserDefaults, key: String)
case settingBool(key: Setting.BoolKey)
// MARK: - Convenience
public var currentBoolValue: Bool {
switch self {
case .boolValue(let value): return value
case .dynamicString: return false
case .userDefaults(let defaults, let key): return defaults.bool(forKey: key)
case .settingBool(let key): return Storage.shared[key]
}
}
public var currentStringValue: String? {
switch self {
case .dynamicString(let value): return value()
default: return nil
}
}
// MARK: - Conformance
public func hash(into hasher: inout Hasher) {
switch self {
case .boolValue(let value): value.hash(into: &hasher)
case .dynamicString(let generator): generator().hash(into: &hasher)
case .userDefaults(_, let key): key.hash(into: &hasher)
case .settingBool(let key): key.hash(into: &hasher)
}
}
public static func == (lhs: DataSource, rhs: DataSource) -> Bool {
switch (lhs, rhs) {
case (.boolValue(let lhsValue), .boolValue(let rhsValue)):
return (lhsValue == rhsValue)
case (.dynamicString(let lhsGenerator), .dynamicString(let rhsGenerator)):
return (lhsGenerator() == rhsGenerator())
case (.userDefaults(_, let lhsKey), .userDefaults(_, let rhsKey)):
return (lhsKey == rhsKey)
case (.settingBool(let lhsKey), .settingBool(let rhsKey)):
return (lhsKey == rhsKey)
default: return false
}
}
}
}
// MARK: - SessionCell.Accessory.RadioSize
extension SessionCell.Accessory {
public enum RadioSize {
case small
case medium
var borderSize: CGFloat {
switch self {
case .small: return 20
case .medium: return 26
}
}
var selectionSize: CGFloat {
switch self {
case .small: return 15
case .medium: return 20
}
}
}
}