diff --git a/apps/ios/Shared/Theme/ThemeManager.swift b/apps/ios/Shared/Theme/ThemeManager.swift index b9a35163cf..ae5072436a 100644 --- a/apps/ios/Shared/Theme/ThemeManager.swift +++ b/apps/ios/Shared/Theme/ThemeManager.swift @@ -80,7 +80,7 @@ class ThemeManager { if theme == nil && perUserTheme == nil && perChatTheme == nil && themeOverridesForType == nil { return ActiveTheme(name: themeName, base: baseTheme.base, colors: baseTheme.colors, appColors: baseTheme.appColors, wallpaper: baseTheme.wallpaper) } - let presetWallpaperTheme: ThemeColors? = if let themeOverridesForType, case let WallpaperType.preset(filename, _) = themeOverridesForType { + let presetWallpaperTheme: ResolvedColors? = if let themeOverridesForType, case let WallpaperType.preset(filename, _) = themeOverridesForType { PresetWallpaper.from(filename)?.colors[baseTheme.base] } else if let wallpaper = perChatTheme?.wallpaper { if let preset = wallpaper.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil } diff --git a/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift b/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift index 7acdedf88c..04824f9f3c 100644 --- a/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift +++ b/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift @@ -101,28 +101,28 @@ public enum PresetWallpaper: CaseIterable { } } - public var colors: [DefaultTheme: ThemeColors] { + public var colors: [DefaultTheme: ResolvedColors] { switch self { case .cats: [ - DefaultTheme.LIGHT: ThemeColors.from( + DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#fffffaed", sentQuote: "#fffaf0d6", receivedMessage: "#ffF8F7F4", receivedQuote: "#ffefede9" - ), - DefaultTheme.DARK: ThemeColors.from( + )), + DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff2f2919", sentQuote: "#ff473a1d", receivedMessage: "#ff272624", receivedQuote: "#ff373633" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( + )), + DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff41371b", sentQuote: "#ff654f1c", receivedMessage: "#ff272624", receivedQuote: "#ff373633" - ), - DefaultTheme.BLACK: ThemeColors.from( + )), + DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff41371b", sentQuote: "#ff654f1c", receivedMessage: "#ff1f1e1b", @@ -130,25 +130,25 @@ public enum PresetWallpaper: CaseIterable { ) ] case .flowers: [ - DefaultTheme.LIGHT: ThemeColors.from( + DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#fff1ffe5", sentQuote: "#ffdcf9c4", receivedMessage: "#ffF4F8F2", receivedQuote: "#ffe7ece7" - ), - DefaultTheme.DARK: ThemeColors.from( + )), + DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff163521", sentQuote: "#ff1B5330", receivedMessage: "#ff242523", receivedQuote: "#ff353733" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( + )), + DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff184739", sentQuote: "#ff1F6F4B", receivedMessage: "#ff242523", receivedQuote: "#ff353733" - ), - DefaultTheme.BLACK: ThemeColors.from( + )), + DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff184739", sentQuote: "#ff1F6F4B", receivedMessage: "#ff1c1f1a", @@ -156,25 +156,25 @@ public enum PresetWallpaper: CaseIterable { ) ] case .hearts: [ - DefaultTheme.LIGHT: ThemeColors.from( + DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#fffff4f4", sentQuote: "#ffffdfdf", receivedMessage: "#fff8f6f6", receivedQuote: "#ffefebeb" - ), - DefaultTheme.DARK: ThemeColors.from( + )), + DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff301515", sentQuote: "#ff4C1818", receivedMessage: "#ff242121", receivedQuote: "#ff3b3535" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( + )), + DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff491A28", sentQuote: "#ff761F29", receivedMessage: "#ff242121", receivedQuote: "#ff3b3535" - ), - DefaultTheme.BLACK: ThemeColors.from( + )), + DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff491A28", sentQuote: "#ff761F29", receivedMessage: "#ff1f1b1b", @@ -182,25 +182,25 @@ public enum PresetWallpaper: CaseIterable { ) ] case .kids: [ - DefaultTheme.LIGHT: ThemeColors.from( + DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ffeafeff", sentQuote: "#ffcbf4f7", receivedMessage: "#fff3fafa", receivedQuote: "#ffe4efef" - ), - DefaultTheme.DARK: ThemeColors.from( + )), + DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff16302F", sentQuote: "#ff1a4a49", receivedMessage: "#ff252626", receivedQuote: "#ff373A39" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( + )), + DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff1a4745", sentQuote: "#ff1d6b69", receivedMessage: "#ff252626", receivedQuote: "#ff373a39" - ), - DefaultTheme.BLACK: ThemeColors.from( + )), + DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff1a4745", sentQuote: "#ff1d6b69", receivedMessage: "#ff1e1f1f", @@ -208,51 +208,51 @@ public enum PresetWallpaper: CaseIterable { ) ] case .school: [ - DefaultTheme.LIGHT: ThemeColors.from( - sentMessage: "oklch(0.9756479, 0.01416295, 231.2013)", - sentQuote: "oklch(0.9331527, 0.03006113, 232.4212)", - receivedMessage: "oklch(0.9697657, 0.005748723, 264.5325)", - receivedQuote: "oklch(0.9296755, 0.00918803, 258.3366)" + DefaultTheme.LIGHT: ResolvedColors( + sentMessage: oklch(0.9756479, 0.01416295, 231.2013), + sentQuote: oklch(0.9331527, 0.03006113, 232.4212), + receivedMessage: oklch(0.9697657, 0.005748723, 264.5325), + receivedQuote: oklch(0.9296755, 0.00918803, 258.3366) ), - DefaultTheme.DARK: ThemeColors.from( - sentMessage: "oklch(0.267226, 0.03061943, 237.8609)", - sentQuote: "oklch(0.3464064, 0.04943852, 232.4005)", - receivedMessage: "oklch(0.2764251, 0.007910622, 264.4375)", - receivedQuote: "oklch(0.3548081, 0.008034593, 255.5451)" + DefaultTheme.DARK: ResolvedColors( + sentMessage: oklch(0.267226, 0.03061943, 237.8609), + sentQuote: oklch(0.3464064, 0.04943852, 232.4005), + receivedMessage: oklch(0.2764251, 0.007910622, 264.4375), + receivedQuote: oklch(0.3548081, 0.008034593, 255.5451) ), - DefaultTheme.SIMPLEX: ThemeColors.from( - sentMessage: "oklch(0.3481476, 0.07023845, 249.9259)", - sentQuote: "oklch(0.4520089, 0.08394516, 241.1934)", - receivedMessage: "oklch(0.2764251, 0.007910622, 264.4375)", - receivedQuote: "oklch(0.3548081, 0.008034593, 255.5451)" + DefaultTheme.SIMPLEX: ResolvedColors( + sentMessage: oklch(0.3481476, 0.07023845, 249.9259), + sentQuote: oklch(0.4520089, 0.08394516, 241.1934), + receivedMessage: oklch(0.2764251, 0.007910622, 264.4375), + receivedQuote: oklch(0.3548081, 0.008034593, 255.5451) ), - DefaultTheme.BLACK: ThemeColors.from( - sentMessage: "oklch(0.3481476, 0.07023845, 249.9259)", - sentQuote: "oklch(0.4520089, 0.08394516, 241.1934)", - receivedMessage: "oklch(0.2356588, 0.007789041, 274.6063)", - receivedQuote: "oklch(0.2886546, 0.007823012, 264.445)" + DefaultTheme.BLACK: ResolvedColors( + sentMessage: oklch(0.3481476, 0.07023845, 249.9259), + sentQuote: oklch(0.4520089, 0.08394516, 241.1934), + receivedMessage: oklch(0.2356588, 0.007789041, 274.6063), + receivedQuote: oklch(0.2886546, 0.007823012, 264.445) ) ] case .travel: [ - DefaultTheme.LIGHT: ThemeColors.from( + DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#fffcf6ff", sentQuote: "#fff2e0fc", receivedMessage: "#ffF6F4F7", receivedQuote: "#ffede9ee" - ), - DefaultTheme.DARK: ThemeColors.from( + )), + DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff33263B", sentQuote: "#ff53385E", receivedMessage: "#ff272528", receivedQuote: "#ff3B373E" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( + )), + DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff3C255D", sentQuote: "#ff623485", receivedMessage: "#ff26273B", receivedQuote: "#ff3A394F" - ), - DefaultTheme.BLACK: ThemeColors.from( + )), + DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from( sentMessage: "#ff3C255D", sentQuote: "#ff623485", receivedMessage: "#ff231f23", diff --git a/apps/ios/SimpleXChat/Theme/ThemeTypes.swift b/apps/ios/SimpleXChat/Theme/ThemeTypes.swift index c9c570ae91..f02430954b 100644 --- a/apps/ios/SimpleXChat/Theme/ThemeTypes.swift +++ b/apps/ios/SimpleXChat/Theme/ThemeTypes.swift @@ -226,6 +226,44 @@ public enum ThemeColor { } } +/// Resolved preset colors — Color objects, not strings. Used for presets defined in code. +public struct ResolvedColors { + public var primary: Color? = nil + public var primaryVariant: Color? = nil + public var secondary: Color? = nil + public var secondaryVariant: Color? = nil + public var background: Color? = nil + public var surface: Color? = nil + public var title: Color? = nil + public var primaryVariant2: Color? = nil + public var sentMessage: Color? = nil + public var sentQuote: Color? = nil + public var receivedMessage: Color? = nil + public var receivedQuote: Color? = nil + + public init(primary: Color? = nil, primaryVariant: Color? = nil, secondary: Color? = nil, secondaryVariant: Color? = nil, background: Color? = nil, surface: Color? = nil, title: Color? = nil, primaryVariant2: Color? = nil, sentMessage: Color? = nil, sentQuote: Color? = nil, receivedMessage: Color? = nil, receivedQuote: Color? = nil) { + self.primary = primary; self.primaryVariant = primaryVariant; self.secondary = secondary; self.secondaryVariant = secondaryVariant; self.background = background; self.surface = surface; self.title = title; self.primaryVariant2 = primaryVariant2; self.sentMessage = sentMessage; self.sentQuote = sentQuote; self.receivedMessage = receivedMessage; self.receivedQuote = receivedQuote + } + + /// Temporary: convert hex ThemeColors to ResolvedColors. + public static func fromThemeColors(_ tc: ThemeColors) -> ResolvedColors { + ResolvedColors( + primary: tc.primary?.colorFromReadableHex(), + primaryVariant: tc.primaryVariant?.colorFromReadableHex(), + secondary: tc.secondary?.colorFromReadableHex(), + secondaryVariant: tc.secondaryVariant?.colorFromReadableHex(), + background: tc.background?.colorFromReadableHex(), + surface: tc.surface?.colorFromReadableHex(), + title: tc.title?.colorFromReadableHex(), + primaryVariant2: tc.primaryVariant2?.colorFromReadableHex(), + sentMessage: tc.sentMessage?.colorFromReadableHex(), + sentQuote: tc.sentQuote?.colorFromReadableHex(), + receivedMessage: tc.receivedMessage?.colorFromReadableHex(), + receivedQuote: tc.receivedQuote?.colorFromReadableHex() + ) + } +} + // Spec: spec/services/theme.md#ThemeColors public struct ThemeColors: Codable, Equatable, Hashable { public var primary: String? = nil @@ -434,7 +472,7 @@ public struct ThemeOverrides: Codable, Equatable, Hashable { return ThemeOverrides(themeId: themeId, base: base, colors: c, wallpaper: w) } - public func toColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perUserTheme: ThemeColors?, _ presetWallpaperTheme: ThemeColors?) -> Colors { + public func toColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perUserTheme: ThemeColors?, _ presetWallpaperTheme: ResolvedColors?) -> Colors { let baseColors = switch base { case DefaultTheme.LIGHT: LightColorPalette case DefaultTheme.DARK: DarkColorPalette @@ -442,16 +480,16 @@ public struct ThemeOverrides: Codable, Equatable, Hashable { case DefaultTheme.BLACK: BlackColorPalette } let c = baseColors.clone() - c.primary = perChatTheme?.primary?.colorFromReadableHex() ?? perUserTheme?.primary?.colorFromReadableHex() ?? colors.primary?.colorFromReadableHex() ?? presetWallpaperTheme?.primary?.colorFromReadableHex() ?? baseColors.primary - c.primaryVariant = perChatTheme?.primaryVariant?.colorFromReadableHex() ?? perUserTheme?.primaryVariant?.colorFromReadableHex() ?? colors.primaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant?.colorFromReadableHex() ?? baseColors.primaryVariant - c.secondary = perChatTheme?.secondary?.colorFromReadableHex() ?? perUserTheme?.secondary?.colorFromReadableHex() ?? colors.secondary?.colorFromReadableHex() ?? presetWallpaperTheme?.secondary?.colorFromReadableHex() ?? baseColors.secondary - c.secondaryVariant = perChatTheme?.secondaryVariant?.colorFromReadableHex() ?? perUserTheme?.secondaryVariant?.colorFromReadableHex() ?? colors.secondaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.secondaryVariant?.colorFromReadableHex() ?? baseColors.secondaryVariant - c.background = perChatTheme?.background?.colorFromReadableHex() ?? perUserTheme?.background?.colorFromReadableHex() ?? colors.background?.colorFromReadableHex() ?? presetWallpaperTheme?.background?.colorFromReadableHex() ?? baseColors.background - c.surface = perChatTheme?.surface?.colorFromReadableHex() ?? perUserTheme?.surface?.colorFromReadableHex() ?? colors.surface?.colorFromReadableHex() ?? presetWallpaperTheme?.surface?.colorFromReadableHex() ?? baseColors.surface + c.primary = perChatTheme?.primary?.colorFromReadableHex() ?? perUserTheme?.primary?.colorFromReadableHex() ?? colors.primary?.colorFromReadableHex() ?? presetWallpaperTheme?.primary ?? baseColors.primary + c.primaryVariant = perChatTheme?.primaryVariant?.colorFromReadableHex() ?? perUserTheme?.primaryVariant?.colorFromReadableHex() ?? colors.primaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant ?? baseColors.primaryVariant + c.secondary = perChatTheme?.secondary?.colorFromReadableHex() ?? perUserTheme?.secondary?.colorFromReadableHex() ?? colors.secondary?.colorFromReadableHex() ?? presetWallpaperTheme?.secondary ?? baseColors.secondary + c.secondaryVariant = perChatTheme?.secondaryVariant?.colorFromReadableHex() ?? perUserTheme?.secondaryVariant?.colorFromReadableHex() ?? colors.secondaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.secondaryVariant ?? baseColors.secondaryVariant + c.background = perChatTheme?.background?.colorFromReadableHex() ?? perUserTheme?.background?.colorFromReadableHex() ?? colors.background?.colorFromReadableHex() ?? presetWallpaperTheme?.background ?? baseColors.background + c.surface = perChatTheme?.surface?.colorFromReadableHex() ?? perUserTheme?.surface?.colorFromReadableHex() ?? colors.surface?.colorFromReadableHex() ?? presetWallpaperTheme?.surface ?? baseColors.surface return c } - public func toAppColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ThemeColors?) -> AppColors { + public func toAppColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ResolvedColors?) -> AppColors { let baseColors = switch base { case DefaultTheme.LIGHT: LightColorPaletteApp case DefaultTheme.DARK: DarkColorPaletteApp @@ -459,14 +497,14 @@ public struct ThemeOverrides: Codable, Equatable, Hashable { case DefaultTheme.BLACK: BlackColorPaletteApp } - let sentMessageFallback = colors.sentMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.sentMessage?.colorFromReadableHex() ?? baseColors.sentMessage - let sentQuoteFallback = colors.sentQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.sentQuote?.colorFromReadableHex() ?? baseColors.sentQuote - let receivedMessageFallback = colors.receivedMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedMessage?.colorFromReadableHex() ?? baseColors.receivedMessage - let receivedQuoteFallback = colors.receivedQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedQuote?.colorFromReadableHex() ?? baseColors.receivedQuote - + let sentMessageFallback = colors.sentMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.sentMessage ?? baseColors.sentMessage + let sentQuoteFallback = colors.sentQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.sentQuote ?? baseColors.sentQuote + let receivedMessageFallback = colors.receivedMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedMessage ?? baseColors.receivedMessage + let receivedQuoteFallback = colors.receivedQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedQuote ?? baseColors.receivedQuote + let c = baseColors.clone() - c.title = perChatTheme?.title?.colorFromReadableHex() ?? perUserTheme?.title?.colorFromReadableHex() ?? colors.title?.colorFromReadableHex() ?? presetWallpaperTheme?.title?.colorFromReadableHex() ?? baseColors.title - c.primaryVariant2 = perChatTheme?.primaryVariant2?.colorFromReadableHex() ?? perUserTheme?.primaryVariant2?.colorFromReadableHex() ?? colors.primaryVariant2?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant2?.colorFromReadableHex() ?? baseColors.primaryVariant2 + c.title = perChatTheme?.title?.colorFromReadableHex() ?? perUserTheme?.title?.colorFromReadableHex() ?? colors.title?.colorFromReadableHex() ?? presetWallpaperTheme?.title ?? baseColors.title + c.primaryVariant2 = perChatTheme?.primaryVariant2?.colorFromReadableHex() ?? perUserTheme?.primaryVariant2?.colorFromReadableHex() ?? colors.primaryVariant2?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant2 ?? baseColors.primaryVariant2 c.sentMessage = if let c = perChatTheme?.sentMessage { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.sentMessage?.colorFromReadableHex() ?? sentMessageFallback } else { sentMessageFallback } c.sentQuote = if let c = perChatTheme?.sentQuote { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.sentQuote?.colorFromReadableHex() ?? sentQuoteFallback } else { sentQuoteFallback } c.receivedMessage = if let c = perChatTheme?.receivedMessage { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.receivedMessage?.colorFromReadableHex() ?? receivedMessageFallback } @@ -508,7 +546,7 @@ public struct ThemeOverrides: Codable, Equatable, Hashable { return AppWallpaper(background: background, tint: tint, type: wallpaper) } - public func withFilledColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ThemeColors?) -> ThemeColors { + public func withFilledColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ResolvedColors?) -> ThemeColors { let c = toColors(base, perChatTheme, perUserTheme, presetWallpaperTheme) let ac = toAppColors(base, perChatTheme, perChatWallpaperType, perUserTheme, perUserWallpaperType, presetWallpaperTheme) return ThemeColors(