Files
Morgan Pretty 81bca6815b Merge remote-tracking branch 'upstream/dev' into higher-character-limits
# Conflicts:
#	Session.xcodeproj/project.pbxproj
#	Session/Calls/CallVC.swift
#	Session/Conversations/Input View/MentionSelectionView.swift
#	Session/Conversations/Message Cells/Content Views/MediaView.swift
#	Session/Conversations/Message Cells/Content Views/QuoteView.swift
#	Session/Conversations/Settings/ThreadSettingsViewModel.swift
#	Session/Meta/AppDelegate.swift
#	Session/Settings/SettingsViewModel.swift
#	Session/Utilities/ImageLoading+Convenience.swift
#	SessionMessagingKit/Configuration.swift
#	SessionMessagingKit/Database/Models/Interaction.swift
#	SessionMessagingKit/Database/Models/Profile.swift
#	SessionMessagingKit/Jobs/DisplayPictureDownloadJob.swift
#	SessionMessagingKit/LibSession/LibSession+SessionMessagingKit.swift
#	SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift
#	SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift
#	SessionMessagingKit/Utilities/DisplayPictureManager.swift
#	SessionMessagingKit/Utilities/ProfilePictureView+Convenience.swift
#	SessionMessagingKitTests/Jobs/DisplayPictureDownloadJobSpec.swift
#	SessionMessagingKitTests/Jobs/MessageSendJobSpec.swift
#	SessionMessagingKitTests/LibSession/LibSessionGroupInfoSpec.swift
#	SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift
#	SessionMessagingKitTests/Sending & Receiving/MessageReceiverGroupsSpec.swift
#	SessionMessagingKitTests/_TestUtilities/CommonSMKMockExtensions.swift
#	SessionMessagingKitTests/_TestUtilities/MockImageDataManager.swift
#	SessionTests/Conversations/Settings/ThreadNotificationSettingsViewModelSpec.swift
#	SessionTests/Conversations/Settings/ThreadSettingsViewModelSpec.swift
#	SessionTests/Settings/NotificationContentViewModelSpec.swift
#	SessionUIKit/Components/SessionHostingViewController.swift
#	SessionUIKit/Components/SwiftUI/AttributedText.swift
#	SessionUIKit/Style Guide/ThemeManager.swift
#	SessionUIKit/Style Guide/Themes/SwiftUI+Theme.swift
#	SessionUIKit/Style Guide/Themes/Theme.swift
#	SessionUIKit/Style Guide/Themes/UIKit+Theme.swift
#	SessionUIKit/Types/ImageDataManager.swift
#	SessionUIKit/Utilities/MentionUtilities.swift
#	SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentApprovalViewController.swift
2025-08-05 14:53:35 +10:00

261 lines
7.6 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit.UIColor
import SwiftUI
// MARK: - Theme
public enum Theme: Int, Sendable, CaseIterable, Codable {
public static var defaultTheme: Theme = .classicDark
case classicDark
case classicLight
case oceanDark
case oceanLight
// MARK: - Properties
public var title: String {
switch self {
case .classicDark: return "appearanceThemesClassicDark".localized()
case .classicLight: return "appearanceThemesClassicLight".localized()
case .oceanDark: return "appearanceThemesOceanDark".localized()
case .oceanLight: return "appearanceThemesOceanLight".localized()
}
}
public var interfaceStyle: UIUserInterfaceStyle {
switch self {
case .classicDark, .oceanDark: return .dark
case .classicLight, .oceanLight: return .light
}
}
public var statusBarStyle: UIStatusBarStyle {
switch self {
case .classicDark, .oceanDark: return .lightContent
case .classicLight, .oceanLight: return .darkContent
}
}
public var keyboardAppearance: UIKeyboardAppearance {
switch self {
case .classicDark, .oceanDark: return .dark
case .classicLight, .oceanLight: return .default
}
}
public var blurStyle: UIBlurEffect.Style {
switch self {
case .classicDark, .oceanDark: return .dark
case .classicLight, .oceanLight: return .light
}
}
fileprivate var colors: [ThemeValue: UIColor] {
switch self {
case .classicDark: return Theme_ClassicDark.theme
case .classicLight: return Theme_ClassicLight.theme
case .oceanDark: return Theme_OceanDark.theme
case .oceanLight: return Theme_OceanLight.theme
}
}
fileprivate var colorsSwiftUI: [ThemeValue: Color] {
switch self {
case .classicDark: return Theme_ClassicDark.themeSwiftUI
case .classicLight: return Theme_ClassicLight.themeSwiftUI
case .oceanDark: return Theme_OceanDark.themeSwiftUI
case .oceanLight: return Theme_OceanLight.themeSwiftUI
}
}
internal var defaultPrimary: Theme.PrimaryColor {
let maybeTargetColor: Color? = {
switch self {
case .classicDark: return Theme_ClassicDark.themeSwiftUI[.defaultPrimary]
case .classicLight: return Theme_ClassicLight.themeSwiftUI[.defaultPrimary]
case .oceanDark: return Theme_OceanDark.themeSwiftUI[.defaultPrimary]
case .oceanLight: return Theme_OceanLight.themeSwiftUI[.defaultPrimary]
}
}()
guard let targetColor: Color = maybeTargetColor else { return .green }
return (Theme.PrimaryColor.allCases.first(where: { $0.colorSwiftUI == targetColor }) ?? .green)
}
}
// MARK: - ColorType Convenience
internal extension ColorType {
static func resolve(_ value: ThemeValue, for theme: Theme) -> Self? {
switch Self.self {
case is UIColor.Type: return theme.colors[value] as? Self
case is Color.Type: return theme.colorsSwiftUI[value] as? Self
default: return nil
}
}
}
// MARK: - ThemeColors
public protocol ThemeColors {
static var theme: [ThemeValue: UIColor] { get }
static var themeSwiftUI: [ThemeValue: Color] { get }
}
// MARK: - ThemedNavigation
public protocol ThemedNavigation {
var navigationBackground: ThemeValue? { get }
}
// MARK: - ThemeValue
public indirect enum ThemeValue: Hashable, Equatable {
case value(ThemeValue, alpha: CGFloat)
case explicitPrimary(Theme.PrimaryColor)
case dynamicForInterfaceStyle(light: ThemeValue, dark: ThemeValue)
case dynamicForPrimary(Theme.PrimaryColor, use: ThemeValue, otherwise: ThemeValue)
// The 'highlighted' state of a color will automatically lighten/darken a ThemeValue
// by a fixed amount depending on wither the theme is dark/light mode
case highlighted(ThemeValue, alwaysDarken: Bool)
public static func highlighted(_ value: ThemeValue) -> ThemeValue {
return .highlighted(value, alwaysDarken: false)
}
// General
case white
case black
case clear
case primary
case defaultPrimary
case warning
case danger
case disabled
case backgroundPrimary
case backgroundSecondary
case textPrimary
case textSecondary
case borderSeparator
// Path
case path_connected
case path_connecting
case path_error
case path_unknown
// TextBox
case textBox_background
case textBox_border
// MessageBubble
case messageBubble_outgoingBackground
case messageBubble_incomingBackground
case messageBubble_outgoingText
case messageBubble_incomingText
case messageBubble_overlay
case messageBubble_deliveryStatus
// MenuButton
case menuButton_background
case menuButton_icon
case menuButton_outerShadow
case menuButton_innerShadow
// RadioButton
case radioButton_selectedBackground
case radioButton_unselectedBackground
case radioButton_selectedBorder
case radioButton_unselectedBorder
case radioButton_disabledSelectedBackground
case radioButton_disabledUnselectedBackground
case radioButton_disabledBorder
// SessionButton
case sessionButton_text
case sessionButton_background
case sessionButton_highlight
case sessionButton_border
case sessionButton_filledText
case sessionButton_filledBackground
case sessionButton_filledHighlight
case sessionButton_destructiveText
case sessionButton_destructiveBackground
case sessionButton_destructiveHighlight
case sessionButton_destructiveBorder
case sessionButton_primaryFilledText
case sessionButton_primaryFilledBackground
// SolidButton
case solidButton_background
// Settings
case settings_tertiaryAction
case settings_tabBackground
case settings_glowingBackground
// Appearance
case appearance_sectionBackground
case appearance_buttonBackground
// Alert
case alert_text
case alert_background
case alert_buttonBackground
case toast_background
// ConversationButton
case conversationButton_background
case conversationButton_unreadBackground
case conversationButton_unreadStripBackground
case conversationButton_unreadBubbleBackground
case conversationButton_unreadBubbleText
case conversationButton_swipeDestructive
case conversationButton_swipeSecondary
case conversationButton_swipeTertiary
case conversationButton_swipeRead
// InputButton
case inputButton_background
// ContextMenu
case contextMenu_background
case contextMenu_highlight
case contextMenu_text
case contextMenu_textHighlight
// Call
case callAccept_background
case callDecline_background
// Reactions
case reactions_contextBackground
case reactions_contextMoreBackground
// NewConversation
case newConversation_background
// Profile
case profileIcon
case profileIcon_greenPrimaryColor
case profileIcon_background
// Unread Marker
case unreadMarker
}
// MARK: - ForcedThemeValue
public enum ForcedThemeValue {
case color(UIColor)
case theme(Theme, color: ThemeValue, alpha: CGFloat?)
public static func theme(_ theme: Theme, color: ThemeValue) -> ForcedThemeValue {
return .theme(theme, color: color, alpha: nil)
}
}