From fb4927ca878391da9e19d99b67808a919f194f3f Mon Sep 17 00:00:00 2001 From: Avently <7953703+avently@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:08:58 +0700 Subject: [PATCH] divided types per target --- apps/ios/Shared/Model/ChatModel.swift | 2 +- apps/ios/Shared/Model/SimpleXAPI.swift | 6 +- apps/ios/Shared/Theme/Theme.swift | 121 +++++ .../Shared/{UI => }/Theme/ThemeManager.swift | 88 +--- apps/ios/Shared/UI/Theme/Color.swift | 37 -- .../Shared/Views/Helpers/ChatWallpaper.swift | 451 ++---------------- .../Views/UserSettings/AppSettings.swift | 2 +- .../UserSettings/AppearanceSettings.swift | 1 + .../ios/SimpleX NSE/NotificationService.swift | 13 +- apps/ios/SimpleX.xcodeproj/project.pbxproj | 52 +- apps/ios/SimpleXChat/APITypes.swift | 8 +- .../Theme/ChatWallpaperTypes.swift | 374 +++++++++++++++ apps/ios/SimpleXChat/Theme/Color.swift | 101 ++++ .../Theme/ThemeTypes.swift} | 344 ++++++------- 14 files changed, 826 insertions(+), 774 deletions(-) create mode 100644 apps/ios/Shared/Theme/Theme.swift rename apps/ios/Shared/{UI => }/Theme/ThemeManager.swift (80%) delete mode 100644 apps/ios/Shared/UI/Theme/Color.swift create mode 100644 apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift create mode 100644 apps/ios/SimpleXChat/Theme/Color.swift rename apps/ios/{Shared/UI/Theme/Theme.swift => SimpleXChat/Theme/ThemeTypes.swift} (72%) diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 8b8dc90a3b..dc226b90ee 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -425,7 +425,7 @@ final class ChatModel: ObservableObject { func updateCurrentUserUiThemes(uiThemes: ThemeModeOverrides?) { guard var current = currentUser else { return } current.uiThemes = uiThemes - let i = users.index(where: { $0.user.userId == current.userId }) + let i = users.firstIndex(where: { $0.user.userId == current.userId }) if let i { users[i].user = current } diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 11986f1a55..2047c6ff6c 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -825,15 +825,15 @@ func apiSetConnectionAlias(connId: Int64, localAlias: String) async throws -> Pe throw r } -func apiSetUserUIThemes(userId: Int64, themes: ThemeModeOverrides?) -> Bool { +func apiSetUserUIThemes(userId: Int64, themes: ThemeModeOverrides?) async -> Bool { let r = await chatSendCmd(.apiSetUserUIThemes(userId: userId, themes: themes)) if case .cmdOk = r { return true } logger.error("apiSetUserUIThemes bad response: \(String(describing: r))") return false } -func apiSetChatUIThemes(chatId: ChatId, themes: ThemeModeOverrides?) -> Bool { - let r = chatSendCmd(.apiSetChatUIThemes(chatId: chatId, themes: themes)) +func apiSetChatUIThemes(chatId: ChatId, themes: ThemeModeOverrides?) async -> Bool { + let r = await chatSendCmd(.apiSetChatUIThemes(chatId: chatId, themes: themes)) if case .cmdOk = r { return true } logger.error("apiSetChatUIThemes bad response: \(String(describing: r))") return false diff --git a/apps/ios/Shared/Theme/Theme.swift b/apps/ios/Shared/Theme/Theme.swift new file mode 100644 index 0000000000..1ba4dd4ee7 --- /dev/null +++ b/apps/ios/Shared/Theme/Theme.swift @@ -0,0 +1,121 @@ +// +// ThemeShared.swift +// SimpleX (iOS) +// +// Created by Avently on 14.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI +import SimpleXChat + +var CurrentColors: ThemeManager.ActiveTheme = ThemeManager.currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) { + didSet { + AppTheme.shared.name = CurrentColors.name + AppTheme.shared.base = CurrentColors.base + AppTheme.shared.colors.updateColorsFrom(CurrentColors.colors) + AppTheme.shared.appColors.updateColorsFrom(CurrentColors.appColors) + AppTheme.shared.wallpaper.updateWallpaperFrom(CurrentColors.wallpaper) + AppTheme.shared.objectWillChange.send() + } +} + +var MenuTextColor: Color { if isInDarkTheme() { AppTheme.shared.colors.onBackground.opacity(0.8) } else { Color.black } } +var NoteFolderIconColor: Color { AppTheme.shared.appColors.primaryVariant2 } + +func isInDarkTheme() -> Bool { !CurrentColors.colors.isLight } + +class AppTheme: ObservableObject { + static let shared = AppTheme(name: CurrentColors.name, base: CurrentColors.base, colors: CurrentColors.colors, appColors: CurrentColors.appColors, wallpaper: CurrentColors.wallpaper) + + var name: String + var base: DefaultTheme + @ObservedObject var colors: Colors + @ObservedObject var appColors: AppColors + @ObservedObject var wallpaper: AppWallpaper + + init(name: String, base: DefaultTheme, colors: Colors, appColors: AppColors, wallpaper: AppWallpaper) { + self.name = name + self.base = base + self.colors = colors + self.appColors = appColors + self.wallpaper = wallpaper + } +} + +struct ThemedBackground: ViewModifier { + @EnvironmentObject var theme: AppTheme + + func body(content: Content) -> some View { + content + .background( + theme.base == DefaultTheme.SIMPLEX + ? LinearGradient( + colors: [ + theme.colors.background.lighter(0.4), + theme.colors.background.darker(0.4) + ], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + : LinearGradient( + colors: [], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + ) + .background( + theme.base == DefaultTheme.SIMPLEX + ? Color.clear + : theme.colors.background + ) + } +} + +func reactOnDarkThemeChanges(_ isDark: Bool) { + systemInDarkThemeCurrently = isDark + //sceneDelegate.window?.overrideUserInterfaceStyle == .unspecified + if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME && CurrentColors.colors.isLight == isDark { + // Change active colors from light to dark and back based on system theme + ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME) + } +} + +extension ThemeWallpaper { + public func importFromString() -> ThemeWallpaper { + if preset == nil, let image { + // Need to save image from string and to save its path + if let data = Data(base64Encoded: dropImagePrefix(image)), + let parsed = UIImage(data: data), + let filename = saveWallpaperFile(image: parsed) { + var copy = self + copy.image = nil + copy.imageFile = filename + return copy + } else { + return ThemeWallpaper() + } + } else { + return self + } + } + + func withFilledWallpaperBase64() -> ThemeWallpaper { + let aw = toAppWallpaper() + let type = aw.type + let preset: String? = if case let WallpaperType.Preset(filename, _) = type { filename } else { nil } + let scale: Float? = if case let WallpaperType.Preset(_, scale) = type { scale } else { if case let WallpaperType.Image(_, scale, _) = type { scale } else { 1.0 } } + let scaleType: WallpaperScaleType? = if case let WallpaperType.Image(_, _, scaleType) = type { scaleType } else { nil } + let image: String? = if case WallpaperType.Image = type, let image = type.uiImage { resizeImageToStrSize(image, maxDataSize: 5_000_000) } else { nil } + return ThemeWallpaper ( + preset: preset, + scale: scale, + scaleType: scaleType, + background: aw.background?.toReadableHex(), + tint: aw.tint?.toReadableHex(), + image: image, + imageFile: nil + ) + } +} diff --git a/apps/ios/Shared/UI/Theme/ThemeManager.swift b/apps/ios/Shared/Theme/ThemeManager.swift similarity index 80% rename from apps/ios/Shared/UI/Theme/ThemeManager.swift rename to apps/ios/Shared/Theme/ThemeManager.swift index b6d5b6e38f..f40d772c1c 100644 --- a/apps/ios/Shared/UI/Theme/ThemeManager.swift +++ b/apps/ios/Shared/Theme/ThemeManager.swift @@ -10,8 +10,8 @@ import Foundation import SwiftUI import SimpleXChat -public class ThemeManager { - public struct ActiveTheme { +class ThemeManager { + struct ActiveTheme { let name: String let base: DefaultTheme let colors: Colors @@ -49,7 +49,7 @@ public class ThemeManager { return perUserTheme } let defaultTheme = defaultActiveTheme(appSettingsTheme) - return ThemeModeOverride(colors: defaultTheme?.colors ?? ThemeColors(), wallpaper: defaultTheme?.wallpaper) + return ThemeModeOverride(mode: CurrentColors.base.mode, colors: defaultTheme?.colors ?? ThemeColors(), wallpaper: defaultTheme?.wallpaper) } static func currentColors(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ActiveTheme { @@ -95,7 +95,7 @@ public class ThemeManager { let wType = current.wallpaper.type let wBackground = current.wallpaper.background let wTint = current.wallpaper.tint - let w: ThemeWallpaper? = if case WallpaperType.Empty = wType { + let w: ThemeWallpaper? = if case WallpaperType.Empty = wType { nil } else { ThemeWallpaper.from(wType, wBackground?.toReadableHex(), wTint?.toReadableHex()).withFilledWallpaperBase64() @@ -176,19 +176,17 @@ public class ThemeManager { var w: ThemeWallpaper = ThemeWallpaper.from(type, nil, nil) w.scale = nil w.scaleType = nil - pref.wrappedValue = ThemeModeOverride(wallpaper: w) + pref.wrappedValue = ThemeModeOverride(mode: CurrentColors.base.mode, wallpaper: w) } else { // Make an empty wallpaper to override any top level ones - pref.wrappedValue = ThemeModeOverride(wallpaper: ThemeWallpaper()) + pref.wrappedValue = ThemeModeOverride(mode: CurrentColors.base.mode, wallpaper: ThemeWallpaper()) } return true } var type = sameWallpaper.toAppWallpaper().type if case let WallpaperType.Image(filename, scale, scaleType) = type, sameWallpaper.imageFile == filename { // same image file. Needs to be copied first in order to be able to remove the file once it's not needed anymore without affecting main theme override - // LALAL - let filename: String? = "LALAL"//saveWallpaperFile(File(getWallpaperFilePath(filename)).toURI()) - if let filename { + if let filename = saveWallpaperFile(url: getWallpaperFilePath(filename)) { type = WallpaperType.Image(filename, scale, scaleType) } else { logger.error("Error while copying wallpaper from global overrides to chat overrides") @@ -221,9 +219,8 @@ public class ThemeManager { let pref: CodableDefault<[ThemeOverrides]> = pref ?? themeOverridesDefault let overrides = pref.get() var prevValue = overrides.getTheme(nil, wallpaper?.toAppWallpaper().type, theme.base) ?? ThemeOverrides(base: theme.base) - if prevValue.wallpaper?.imageFile != nil { - // LALAL - //File(getWallpaperFilePath(prevValue.wallpaper.imageFile)).delete() + if let imageFile = prevValue.wallpaper?.imageFile { + try? FileManager.default.removeItem(at: getWallpaperFilePath(imageFile)) } prevValue.base = theme.base prevValue.colors = theme.colors @@ -264,70 +261,3 @@ public class ThemeManager { themeOverridesDefault.set(themes) } } - -extension String { - func colorFromReadableHex() -> Color { - // https://stackoverflow.com/a/56874327 - let hex = self.trimmingCharacters(in: ["#", " "]) - var int: UInt64 = 0 - Scanner(string: hex).scanHexInt64(&int) - let a, r, g, b: UInt64 - switch hex.count { - case 3: // RGB (12-bit) - (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) - case 6: // RGB (24-bit) - (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) - case 8: // ARGB (32-bit) - (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) - default: - (a, r, g, b) = (1, 1, 1, 0) - } - - return Color( - .sRGB, - red: Double(r) / 255, - green: Double(g) / 255, - blue: Double(b) / 255, - opacity: Double(a) / 255 - ) - } -} - -extension Color { - init(_ argb: Int64) { - let a = Double((argb & 0xFF000000) >> 24) / 255.0 - let r = Double((argb & 0xFF0000) >> 16) / 255.0 - let g = Double((argb & 0xFF00) >> 8) / 255.0 - let b = Double((argb & 0xFF)) / 255.0 - self.init(.sRGB, red: r, green: g, blue: b, opacity: a) - } - - init(_ r: Int, _ g: Int, _ b: Int, a: Int) { - self.init(.sRGB, red: Double(r) / 255.0, green: Double(g) / 255.0, blue: Double(b) / 255.0, opacity: Double(a) / 255.0) - } - - func toReadableHex() -> String { - let uiColor: UIColor = .init(self) - var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) - uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) - return String(format: "#%02x%02x%02x%02x", - Int(a * 255), - Int(r * 255), - Int(g * 255), - Int(b * 255) - ) - } - - func darker(_ factor: CGFloat = 0.1) -> Color { - var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) - UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &a) - return Color(.sRGB, red: max(r * (1 - factor), 0), green: max(g * (1 - factor), 0), blue: max(b * (1 - factor), 0), opacity: a) - } - - func lighter(_ factor: CGFloat = 0.1) -> Color { - var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) - UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &a) - return Color(.sRGB, red: min(r * (1 + factor), 1), green: min(g * (1 + factor), 1), blue: min(b * (1 + factor), 1), opacity: a) - } - -} diff --git a/apps/ios/Shared/UI/Theme/Color.swift b/apps/ios/Shared/UI/Theme/Color.swift deleted file mode 100644 index cb8712e1c8..0000000000 --- a/apps/ios/Shared/UI/Theme/Color.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// Color.swift -// SimpleX (iOS) -// -// Created by Avently on 05.06.2024. -// Copyright © 2024 SimpleX Chat. All rights reserved. -// - -import Foundation -import SwiftUI - -let Purple200 = Color(0xFFBB86FC) -let Purple500 = Color(0xFF6200EE) -let Purple700 = Color(0xFF3700B3) -let Teal200 = Color(0xFF03DAC5) -let Gray = Color(0x22222222) -let Indigo = Color(0xFF9966FF) -let SimplexBlue = Color(0, 136, 255, a: 255) -let SimplexGreen = Color(77, 218, 103, a: 255) -let SecretColor = Color(0x40808080) -let LightGray = Color(241, 242, 246, a: 255) -let DarkGray = Color(43, 44, 46, a: 255) -let HighOrLowlight = Color(139, 135, 134, a: 255) -let MessagePreviewDark = Color(179, 175, 174, a: 255) -let MessagePreviewLight = Color(49, 45, 44, a: 255) -let ToolbarLight = Color(220, 220, 220, a: 12) -let ToolbarDark = Color(80, 80, 80, a: 12) -let SettingsSecondaryLight = Color(200, 196, 195, a: 90) -let GroupDark = Color(80, 80, 80, a: 60) -let IncomingCallLight = Color(239, 237, 236, a: 255) -let WarningOrange = Color(255, 127, 0, a: 255) -let WarningYellow = Color(255, 192, 0, a: 255) -let FileLight = Color(183, 190, 199, a: 255) -let FileDark = Color(101, 101, 106, a: 255) - -var MenuTextColor: Color { if isInDarkTheme() { AppTheme.shared.colors.onBackground.opacity(0.8) } else { Color.black } } -var NoteFolderIconColor: Color { AppTheme.shared.appColors.primaryVariant2 } diff --git a/apps/ios/Shared/Views/Helpers/ChatWallpaper.swift b/apps/ios/Shared/Views/Helpers/ChatWallpaper.swift index a739dd5271..10023290ff 100644 --- a/apps/ios/Shared/Views/Helpers/ChatWallpaper.swift +++ b/apps/ios/Shared/Views/Helpers/ChatWallpaper.swift @@ -2,7 +2,7 @@ // ChatWallpaper.swift // SimpleX (iOS) // -// Created by Avently on 06.06.2024. +// Created by Avently on 14.06.2024. // Copyright © 2024 SimpleX Chat. All rights reserved. // @@ -10,385 +10,6 @@ import Foundation import SwiftUI import SimpleXChat -public enum PresetWallpaper { - case cats - case flowers - case hearts - case kids - case school - case travel - - var res: UIImage { - UIImage(named: "wallpaper_\(filename)")! - } - - var filename: String { - switch self { - case .cats: "cats" - case .flowers: "flowers" - case .hearts: "hearts" - case .kids: "kids" - case .school: "school" - case .travel: "travel" - } - } - - var scale: Float { - switch self { - case .cats: 0.63 - case .flowers: 0.53 - case .hearts: 0.59 - case .kids: 0.53 - case .school: 0.53 - case .travel: 0.68 - } - } - - var background: [DefaultTheme: Color] { - switch self { - case .cats: wallpaperBackgrounds(light: "#ffF8F6EA") - case .flowers: wallpaperBackgrounds(light: "#ffE2FFE4") - case .hearts: wallpaperBackgrounds(light: "#ffFDECEC") - case .kids: wallpaperBackgrounds(light: "#ffdbfdfb") - case .school: wallpaperBackgrounds(light: "#ffE7F5FF") - case .travel: wallpaperBackgrounds(light: "#fff9eeff") - } - } - - var tint: [DefaultTheme: Color] { - switch self { - case .cats: [ - DefaultTheme.LIGHT: "#ffefdca6".colorFromReadableHex(), - DefaultTheme.DARK: "#ff4b3b0e".colorFromReadableHex(), - DefaultTheme.SIMPLEX: "#ff51400f".colorFromReadableHex(), - DefaultTheme.BLACK: "#ff4b3b0e".colorFromReadableHex() - ] - case .flowers: [ - DefaultTheme.LIGHT: "#ff9CEA59".colorFromReadableHex(), - DefaultTheme.DARK: "#ff31560D".colorFromReadableHex(), - DefaultTheme.SIMPLEX: "#ff36600f".colorFromReadableHex(), - DefaultTheme.BLACK: "#ff31560D".colorFromReadableHex() - ] - case .hearts: [ - DefaultTheme.LIGHT: "#fffde0e0".colorFromReadableHex(), - DefaultTheme.DARK: "#ff3c0f0f".colorFromReadableHex(), - DefaultTheme.SIMPLEX: "#ff411010".colorFromReadableHex(), - DefaultTheme.BLACK: "#ff3C0F0F".colorFromReadableHex() - ] - case .kids: [ - DefaultTheme.LIGHT: "#ffadeffc".colorFromReadableHex(), - DefaultTheme.DARK: "#ff16404B".colorFromReadableHex(), - DefaultTheme.SIMPLEX: "#ff184753".colorFromReadableHex(), - DefaultTheme.BLACK: "#ff16404B".colorFromReadableHex() - ] - case .school: [ - DefaultTheme.LIGHT: "#ffCEEBFF".colorFromReadableHex(), - DefaultTheme.DARK: "#ff0F293B".colorFromReadableHex(), - DefaultTheme.SIMPLEX: "#ff112f43".colorFromReadableHex(), - DefaultTheme.BLACK: "#ff0F293B".colorFromReadableHex() - ] - case .travel: [ - DefaultTheme.LIGHT: "#ffeedbfe".colorFromReadableHex(), - DefaultTheme.DARK: "#ff311E48".colorFromReadableHex(), - DefaultTheme.SIMPLEX: "#ff35204e".colorFromReadableHex(), - DefaultTheme.BLACK: "#ff311E48".colorFromReadableHex() - ] - } - } - - var colors: [DefaultTheme: ThemeColors] { - switch self { - case .cats: [ - DefaultTheme.LIGHT: ThemeColors.from( - sentMessage: "#fffffaed", - sentQuote: "#fffaf0d6", - receivedMessage: "#ffF8F7F4", - receivedQuote: "#ffefede9" - ), - DefaultTheme.DARK: ThemeColors.from( - sentMessage: "#ff2f2919", - sentQuote: "#ff473a1d", - receivedMessage: "#ff272624", - receivedQuote: "#ff373633" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( - sentMessage: "#ff41371b", - sentQuote: "#ff654f1c", - receivedMessage: "#ff272624", - receivedQuote: "#ff373633" - ), - DefaultTheme.BLACK: ThemeColors.from( - sentMessage: "#ff41371b", - sentQuote: "#ff654f1c", - receivedMessage: "#ff1f1e1b", - receivedQuote: "#ff2f2d27" - ) - ] - case .flowers: [ - DefaultTheme.LIGHT: ThemeColors.from( - sentMessage: "#fff1ffe5", - sentQuote: "#ffdcf9c4", - receivedMessage: "#ffF4F8F2", - receivedQuote: "#ffe7ece7" - ), - DefaultTheme.DARK: ThemeColors.from( - sentMessage: "#ff163521", - sentQuote: "#ff1B5330", - receivedMessage: "#ff242523", - receivedQuote: "#ff353733" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( - sentMessage: "#ff184739", - sentQuote: "#ff1F6F4B", - receivedMessage: "#ff242523", - receivedQuote: "#ff353733" - ), - DefaultTheme.BLACK: ThemeColors.from( - sentMessage: "#ff184739", - sentQuote: "#ff1F6F4B", - receivedMessage: "#ff1c1f1a", - receivedQuote: "#ff282b25" - ) - ] - case .hearts: [ - DefaultTheme.LIGHT: ThemeColors.from( - sentMessage: "#fffff4f4", - sentQuote: "#ffffdfdf", - receivedMessage: "#fff8f6f6", - receivedQuote: "#ffefebeb" - ), - DefaultTheme.DARK: ThemeColors.from( - sentMessage: "#ff301515", - sentQuote: "#ff4C1818", - receivedMessage: "#ff242121", - receivedQuote: "#ff3b3535" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( - sentMessage: "#ff491A28", - sentQuote: "#ff761F29", - receivedMessage: "#ff242121", - receivedQuote: "#ff3b3535" - ), - DefaultTheme.BLACK: ThemeColors.from( - sentMessage: "#ff491A28", - sentQuote: "#ff761F29", - receivedMessage: "#ff1f1b1b", - receivedQuote: "#ff2e2626" - ) - ] - case .kids: [ - DefaultTheme.LIGHT: ThemeColors.from( - sentMessage: "#ffeafeff", - sentQuote: "#ffcbf4f7", - receivedMessage: "#fff3fafa", - receivedQuote: "#ffe4efef" - ), - DefaultTheme.DARK: ThemeColors.from( - sentMessage: "#ff16302F", - sentQuote: "#ff1a4a49", - receivedMessage: "#ff252626", - receivedQuote: "#ff373A39" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( - sentMessage: "#ff1a4745", - sentQuote: "#ff1d6b69", - receivedMessage: "#ff252626", - receivedQuote: "#ff373a39" - ), - DefaultTheme.BLACK: ThemeColors.from( - sentMessage: "#ff1a4745", - sentQuote: "#ff1d6b69", - receivedMessage: "#ff1e1f1f", - receivedQuote: "#ff262b29" - ) - ] - case .school: [ - DefaultTheme.LIGHT: ThemeColors.from( - sentMessage: "#ffeef9ff", - sentQuote: "#ffD6EDFA", - receivedMessage: "#ffF3F5F9", - receivedQuote: "#ffe4e8ee" - ), - DefaultTheme.DARK: ThemeColors.from( - sentMessage: "#ff172833", - sentQuote: "#ff1C3E4F", - receivedMessage: "#ff26282c", - receivedQuote: "#ff393c40" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( - sentMessage: "#ff1A3C5D", - sentQuote: "#ff235b80", - receivedMessage: "#ff26282c", - receivedQuote: "#ff393c40" - ), - DefaultTheme.BLACK: ThemeColors.from( - sentMessage: "#ff1A3C5D", - sentQuote: "#ff235b80", - receivedMessage: "#ff1d1e22", - receivedQuote: "#ff292b2f" - ) - ] - case .travel: [ - DefaultTheme.LIGHT: ThemeColors.from( - sentMessage: "#fffcf6ff", - sentQuote: "#fff2e0fc", - receivedMessage: "#ffF6F4F7", - receivedQuote: "#ffede9ee" - ), - DefaultTheme.DARK: ThemeColors.from( - sentMessage: "#ff33263B", - sentQuote: "#ff53385E", - receivedMessage: "#ff272528", - receivedQuote: "#ff3B373E" - ), - DefaultTheme.SIMPLEX: ThemeColors.from( - sentMessage: "#ff3C255D", - sentQuote: "#ff623485", - receivedMessage: "#ff26273B", - receivedQuote: "#ff3A394F" - ), - DefaultTheme.BLACK: ThemeColors.from( - sentMessage: "#ff3C255D", - sentQuote: "#ff623485", - receivedMessage: "#ff231f23", - receivedQuote: "#ff2c2931" - ) - ] - } - } - - func toType(_ base: DefaultTheme, _ scale: Float? = nil) -> WallpaperType { - WallpaperType.Preset( - filename, - scale ?? themeOverridesDefault.get().first { $0.wallpaper != nil && $0.wallpaper!.preset == filename && $0.base == base }?.wallpaper?.scale ?? 1 - ) - } - - static func from(_ filename: String) -> PresetWallpaper? { - switch filename { - case PresetWallpaper.cats.filename: PresetWallpaper.cats - case PresetWallpaper.flowers.filename: PresetWallpaper.flowers - case PresetWallpaper.hearts.filename: PresetWallpaper.hearts - case PresetWallpaper.kids.filename: PresetWallpaper.kids - case PresetWallpaper.school.filename: PresetWallpaper.school - case PresetWallpaper.travel.filename: PresetWallpaper.travel - default: nil - } - } -} - -func wallpaperBackgrounds(light: String) -> [DefaultTheme : Color] { - [ - DefaultTheme.LIGHT: light.colorFromReadableHex(), - DefaultTheme.DARK: "#ff121212".colorFromReadableHex(), - DefaultTheme.SIMPLEX: "#ff111528".colorFromReadableHex(), - DefaultTheme.BLACK: "#ff070707".colorFromReadableHex() - ] -} - -public enum WallpaperScaleType/*(val contentScale: ContentScale)*/: Codable { - case fill/* (ContentScale.Crop)*/ - case fit/* (ContentScale.Fit)*/ - case `repeat`/* (ContentScale.Fit)*/ - - var text: String { - switch self { - case .fill: "Fill" - case .fit: "Fit" - case .repeat: "Repeat" - } - } -} - -public enum WallpaperType { - var image: SwiftUI.Image? { - if let uiImage { - return SwiftUI.Image(uiImage: uiImage) - } - return nil - } - - var uiImage: UIImage? { - let filename: String - switch self { - case let .Preset(f, _): filename = f - case let .Image(f, _, _): filename = f - default: return nil - } - if filename == "" { return nil } - if let image = WallpaperType.cachedImages[filename] { - return image - } else { - let res: UIImage? - if case let .Preset(filename, _) = self { - res = (PresetWallpaper.from(filename) ?? PresetWallpaper.cats).res - } else { - do { - // LALAL REMOVE - res = nil - // In case of unintentional image deletion don't crash the app - //File(getWallpaperFilePath(filename)).inputStream().use { loadImageBitmap(it) } - } catch let e { - logger.error("Error while loading wallpaper file: \(e)") - res = nil - } - } - if let res { - WallpaperType.cachedImages[filename] = res - } - return res - } - } - - func sameType(_ other: WallpaperType?) -> Bool { - if case let .Preset(filename, _) = self, case let .Preset(otherFilename, _) = other { filename == otherFilename } - else if case .Image = self, case .Image = other { true } - else if case .Empty = self, case .Empty = other { true } - else { false } - } - - func samePreset(other: PresetWallpaper?) -> Bool { if case let .Preset(filename, _) = self, filename == other?.filename { true } else { false } } - - case Preset(_ filename: String, _ scale: Float?) - - case Image(_ filename: String, _ scale: Float?, _ scaleType: WallpaperScaleType?) - - case Empty - - func defaultBackgroundColor(_ theme: DefaultTheme, _ themeBackground: Color) -> Color { - if case let .Preset(filename, _) = self { - (PresetWallpaper.from(filename) ?? PresetWallpaper.cats).background[theme]! - } else { - themeBackground - } - } - - func defaultTintColor(_ theme: DefaultTheme) -> Color { - if case let .Preset(filename, _) = self { - (PresetWallpaper.from(filename) ?? PresetWallpaper.cats).tint[theme]! - } else if case let .Image(_, _, scaleType) = self, scaleType == WallpaperScaleType.repeat { - Color.clear - } else { - Color.clear - } - } - - static var cachedImages: [String: UIImage] = [:] - - static func from(_ wallpaper: ThemeWallpaper?) -> WallpaperType? { - if wallpaper == nil { - return nil - } else if let preset = wallpaper?.preset { - return WallpaperType.Preset(preset, wallpaper?.scale) - } else if let imageFile = wallpaper?.imageFile { - return WallpaperType.Image(imageFile, wallpaper?.scale, wallpaper?.scaleType) - } else { - return WallpaperType.Empty - } - } -} - - struct ChatViewBackground: ViewModifier { @EnvironmentObject var theme: AppTheme var image: Image @@ -420,37 +41,37 @@ struct ChatViewBackground: ViewModifier { case WallpaperScaleType.repeat: repeatDraw(CGFloat(scale ?? 1)) case WallpaperScaleType.fill: fallthrough case WallpaperScaleType.fit: -// let scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height)) -// let scaledWidth = (image.width * scale.scaleX).roundToInt() -// let scaledHeight = (image.height * scale.scaleY).roundToInt() -// drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) -// if (scaleType == WallpaperScaleType.FIT) { -// if (scaledWidth < size.width) { -// // has black lines at left and right sides -// var x = (size.width - scaledWidth) / 2 -// while (x > 0) { -// drawImage(image, dstOffset = IntOffset(x = (x - scaledWidth).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) -// x -= scaledWidth -// } -// x = size.width - (size.width - scaledWidth) / 2 -// while (x < size.width) { -// drawImage(image, dstOffset = IntOffset(x = x.roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) -// x += scaledWidth -// } -// } else { -// // has black lines at top and bottom sides -// var y = (size.height - scaledHeight) / 2 -// while (y > 0) { -// drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = (y - scaledHeight).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) -// y -= scaledHeight -// } -// y = size.height - (size.height - scaledHeight) / 2 -// while (y < size.height) { -// drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = y.roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) -// y += scaledHeight -// } -// } -// } + // let scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height)) + // let scaledWidth = (image.width * scale.scaleX).roundToInt() + // let scaledHeight = (image.height * scale.scaleY).roundToInt() + // drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) + // if (scaleType == WallpaperScaleType.FIT) { + // if (scaledWidth < size.width) { + // // has black lines at left and right sides + // var x = (size.width - scaledWidth) / 2 + // while (x > 0) { + // drawImage(image, dstOffset = IntOffset(x = (x - scaledWidth).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) + // x -= scaledWidth + // } + // x = size.width - (size.width - scaledWidth) / 2 + // while (x < size.width) { + // drawImage(image, dstOffset = IntOffset(x = x.roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) + // x += scaledWidth + // } + // } else { + // // has black lines at top and bottom sides + // var y = (size.height - scaledHeight) / 2 + // while (y > 0) { + // drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = (y - scaledHeight).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) + // y -= scaledHeight + // } + // y = size.height - (size.height - scaledHeight) / 2 + // while (y < size.height) { + // drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = y.roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality) + // y += scaledHeight + // } + // } + // } context.fill(Path(rect), with: .color(tint)) } case WallpaperType.Empty: () @@ -460,3 +81,11 @@ struct ChatViewBackground: ViewModifier { } } +extension PresetWallpaper { + public func toType(_ base: DefaultTheme, _ scale: Float? = nil) -> WallpaperType { + WallpaperType.Preset( + filename, + scale ?? themeOverridesDefault.get().first { $0.wallpaper != nil && $0.wallpaper!.preset == filename && $0.base == base }?.wallpaper?.scale ?? 1 + ) + } +} diff --git a/apps/ios/Shared/Views/UserSettings/AppSettings.swift b/apps/ios/Shared/Views/UserSettings/AppSettings.swift index eb1ce23b46..8c68d70526 100644 --- a/apps/ios/Shared/Views/UserSettings/AppSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/AppSettings.swift @@ -48,7 +48,7 @@ extension AppSettings { if let val = uiColorScheme { def.setValue(val, forKey: DEFAULT_CURRENT_THEME) } if let val = uiDarkColorScheme { def.setValue(val, forKey: DEFAULT_SYSTEM_DARK_THEME) } if let val = uiCurrentThemeIds { def.setValue(val, forKey: DEFAULT_CURRENT_THEME_IDS) } - if let val = uiThemes { def.setValue(val, forKey: DEFAULT_THEME_OVERRIDES) } + if let val = uiThemes { def.setValue(val.skipDuplicates(), forKey: DEFAULT_THEME_OVERRIDES) } } public static var current: AppSettings { diff --git a/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift b/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift index 5fc3987fb9..76f7526b92 100644 --- a/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift @@ -7,6 +7,7 @@ // import SwiftUI +import SimpleXChat let defaultAccentColor = CGColor.init(red: 0, green: 0.533, blue: 1, alpha: 1) diff --git a/apps/ios/SimpleX NSE/NotificationService.swift b/apps/ios/SimpleX NSE/NotificationService.swift index 12094c7053..764415b1aa 100644 --- a/apps/ios/SimpleX NSE/NotificationService.swift +++ b/apps/ios/SimpleX NSE/NotificationService.swift @@ -439,8 +439,7 @@ func doStartChat() -> DBMigrationResult? { logger.debug("NotificationService active user \(String(describing: user))") do { try setNetworkConfig(networkConfig) - try apiSetTempFolder(tempFolder: getTempFilesDirectory().path) - try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path) + try apiSetAppFilePaths(filesFolder: getAppFilesDirectory().path, tempFolder: getTempFilesDirectory().path, assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path) try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get()) // prevent suspension while starting chat suspendLock.wait() @@ -660,14 +659,8 @@ func apiSuspendChat(timeoutMicroseconds: Int) -> Bool { return false } -func apiSetTempFolder(tempFolder: String) throws { - let r = sendSimpleXCmd(.setTempFolder(tempFolder: tempFolder)) - if case .cmdOk = r { return } - throw r -} - -func apiSetFilesFolder(filesFolder: String) throws { - let r = sendSimpleXCmd(.setFilesFolder(filesFolder: filesFolder)) +func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws { + let r = sendSimpleXCmd(.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder)) if case .cmdOk = r { return } throw r } diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index f0d87d4114..71132653a6 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -186,14 +186,16 @@ 64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; }; 8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C05382D2B39887E006436DC /* VideoUtils.swift */; }; 8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; }; + 8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */; }; + 8C74C3E72C1B901900039E77 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C852B072C1086D100BA61E8 /* Color.swift */; }; + 8C74C3E82C1B905B00039E77 /* ChatWallpaperTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */; }; + 8C74C3EA2C1B90AF00039E77 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */; }; + 8C74C3EC2C1B92A900039E77 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C74C3EB2C1B92A900039E77 /* Theme.swift */; }; + 8C74C3EE2C1B942300039E77 /* ChatWallpaper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */; }; 8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */; }; 8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */; }; - 8C7E3CE42C0DEAC400BFF63A /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7E3CE32C0DEAC400BFF63A /* Theme.swift */; }; 8C7F8F0E2C19C0C100D16888 /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */; }; - 8C804B1E2C11F966007A63C8 /* ChatWallpaper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C804B1D2C11F966007A63C8 /* ChatWallpaper.swift */; }; 8C81482C2BD91CD4002CBEC3 /* AudioDevicePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */; }; - 8C852B082C1086D100BA61E8 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C852B072C1086D100BA61E8 /* Color.swift */; }; - 8C86EBE52C0DAE4F00E12243 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */; }; 8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */; }; 8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; }; D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; }; @@ -488,11 +490,13 @@ 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = ""; }; 8C05382D2B39887E006436DC /* VideoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUtils.swift; sourceTree = ""; }; 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; + 8C74C3EB2C1B92A900039E77 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; + 8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = ""; }; 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = ""; }; 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = ""; }; - 8C7E3CE32C0DEAC400BFF63A /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; + 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeTypes.swift; sourceTree = ""; }; 8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = ""; }; - 8C804B1D2C11F966007A63C8 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = ""; }; + 8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaperTypes.swift; sourceTree = ""; }; 8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioDevicePicker.swift; sourceTree = ""; }; 8C852B072C1086D100BA61E8 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; 8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = ""; }; @@ -670,8 +674,8 @@ 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */, 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */, 8C05382D2B39887E006436DC /* VideoUtils.swift */, - 8C804B1D2C11F966007A63C8 /* ChatWallpaper.swift */, 8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */, + 8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */, ); path = Helpers; sourceTree = ""; @@ -696,7 +700,7 @@ 5CA059C2279559F40002BEB4 /* Shared */ = { isa = PBXGroup; children = ( - 8C86EBE22C0DAE0A00E12243 /* UI */, + 8C74C3E92C1B909200039E77 /* Theme */, 5CA059C3279559F40002BEB4 /* SimpleXApp.swift */, 5C36027227F47AD5009F19D9 /* AppDelegate.swift */, 5CA059C4279559F40002BEB4 /* ContentView.swift */, @@ -833,6 +837,7 @@ 5CE2BA692845308900EC33A6 /* SimpleXChat */ = { isa = PBXGroup; children = ( + 8C86EBE32C0DAE3700E12243 /* Theme */, 5CDCAD5228186F9500503DA2 /* AppGroup.swift */, 5CDCAD7228188CFF00503DA2 /* ChatTypes.swift */, 5CDCAD7428188D2900503DA2 /* APITypes.swift */, @@ -926,6 +931,15 @@ path = Group; sourceTree = ""; }; + 8C74C3E92C1B909200039E77 /* Theme */ = { + isa = PBXGroup; + children = ( + 8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */, + 8C74C3EB2C1B92A900039E77 /* Theme.swift */, + ); + path = Theme; + sourceTree = ""; + }; 8C7D94982B8894D300B7B9E1 /* Migration */ = { isa = PBXGroup; children = ( @@ -935,20 +949,12 @@ path = Migration; sourceTree = ""; }; - 8C86EBE22C0DAE0A00E12243 /* UI */ = { - isa = PBXGroup; - children = ( - 8C86EBE32C0DAE3700E12243 /* Theme */, - ); - path = UI; - sourceTree = ""; - }; 8C86EBE32C0DAE3700E12243 /* Theme */ = { isa = PBXGroup; children = ( - 8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */, - 8C7E3CE32C0DEAC400BFF63A /* Theme.swift */, + 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */, 8C852B072C1086D100BA61E8 /* Color.swift */, + 8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */, ); path = Theme; sourceTree = ""; @@ -1183,6 +1189,7 @@ 5C93293129239BED0090FFF9 /* ProtocolServerView.swift in Sources */, 5C9CC7AD28C55D7800BEF955 /* DatabaseEncryptionView.swift in Sources */, 5CBD285A295711D700EC2CF4 /* ImageUtils.swift in Sources */, + 8C74C3EC2C1B92A900039E77 /* Theme.swift in Sources */, 6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */, 5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */, 8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */, @@ -1190,7 +1197,6 @@ 5C029EAA283942EA004A9677 /* CallController.swift in Sources */, 5CBE6C142944CC12002D9531 /* ScanCodeView.swift in Sources */, 5CC036E029C488D500C0EF20 /* HiddenProfileView.swift in Sources */, - 8C7E3CE42C0DEAC400BFF63A /* Theme.swift in Sources */, 5C5346A827B59A6A004DF848 /* ChatHelp.swift in Sources */, 5CB634A829E437960066AD6B /* PasscodeEntry.swift in Sources */, 5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */, @@ -1229,7 +1235,6 @@ 64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */, 5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */, 5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */, - 8C86EBE52C0DAE4F00E12243 /* ThemeManager.swift in Sources */, 5CB9250D27A9432000ACCCDD /* ChatListNavLink.swift in Sources */, 649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */, 5CA059ED279559F40002BEB4 /* ContentView.swift in Sources */, @@ -1242,6 +1247,7 @@ 5CB346E72868D76D001FD2EF /* NotificationsView.swift in Sources */, 647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */, 646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */, + 8C74C3EA2C1B90AF00039E77 /* ThemeManager.swift in Sources */, 5C7505A227B65FDB00BE3227 /* CIMetaView.swift in Sources */, 5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */, 5C35CFC827B2782E00FB6C6D /* BGManager.swift in Sources */, @@ -1259,8 +1265,8 @@ 6454036F2822A9750090DDFF /* ComposeFileView.swift in Sources */, 5C5DB70E289ABDD200730FFF /* AppearanceSettings.swift in Sources */, 5C5F2B6D27EBC3FE006A9D5F /* ImagePicker.swift in Sources */, + 8C74C3EE2C1B942300039E77 /* ChatWallpaper.swift in Sources */, 5C3CCFCC2AE6BD3100C3F0C3 /* ConnectDesktopView.swift in Sources */, - 8C804B1E2C11F966007A63C8 /* ChatWallpaper.swift in Sources */, 5C9C2DA92899DA6F00CC63B1 /* NetworkAndServers.swift in Sources */, 5C6BA667289BD954009B8ECC /* DismissSheets.swift in Sources */, 5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */, @@ -1287,7 +1293,6 @@ 5C5E5D3B2824468B00B0488A /* ActiveCallView.swift in Sources */, 5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */, 6440CA00288857A10062C672 /* CIEventView.swift in Sources */, - 8C852B082C1086D100BA61E8 /* Color.swift in Sources */, 5CB0BA92282713FD00B3292C /* CreateProfile.swift in Sources */, 5C5F2B7027EBC704006A9D5F /* ProfileImage.swift in Sources */, 5C9329412929248A0090FFF9 /* ScanProtocolServer.swift in Sources */, @@ -1345,6 +1350,7 @@ 5C00168128C4FE760094D739 /* KeyChain.swift in Sources */, 5CE2BA97284537A800EC33A6 /* dummy.m in Sources */, 5CE2BA922845340900EC33A6 /* FileUtils.swift in Sources */, + 8C74C3E72C1B901900039E77 /* Color.swift in Sources */, 5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */, 5CE2BA91284533A300EC33A6 /* Notifications.swift in Sources */, 5CE2BA79284530CC00EC33A6 /* SimpleXChat.docc in Sources */, @@ -1353,7 +1359,9 @@ 5CE2BA8F284533A300EC33A6 /* APITypes.swift in Sources */, 5C9D811A2AA8727A001D49FD /* CryptoFile.swift in Sources */, 5CE2BA8C284533A300EC33A6 /* AppGroup.swift in Sources */, + 8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */, 5CE2BA8D284533A300EC33A6 /* CallTypes.swift in Sources */, + 8C74C3E82C1B905B00039E77 /* ChatWallpaperTypes.swift in Sources */, 5CE2BA8E284533A300EC33A6 /* API.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 37e4d9c7b1..be9ff8cde7 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -170,7 +170,7 @@ public enum ChatCommand { case .apiStopChat: return "/_stop" case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))" case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)" - case let .apiSetAppFilePaths(filesFolder, tempFolder, assetsFolder): return "/set file paths \(encodeJSON(AppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder)))" + case let .apiSetAppFilePaths(filesFolder, tempFolder, assetsFolder): return "/set file paths \(encodeJSON(AppFilePaths(appFilesFolder: filesFolder, appTempFolder: tempFolder, appAssetsFolder: assetsFolder)))" case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))" case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))" case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))" @@ -2244,7 +2244,7 @@ public enum MsgType: String, Codable { } public struct AppFilePaths: Encodable { - public let filesFolder: String - public let tempFolder: String - public let assetsFolder: String + public let appFilesFolder: String + public let appTempFolder: String + public let appAssetsFolder: String } diff --git a/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift b/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift new file mode 100644 index 0000000000..3f5416546c --- /dev/null +++ b/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift @@ -0,0 +1,374 @@ +// +// ChatWallpaper.swift +// SimpleX (iOS) +// +// Created by Avently on 06.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI + +public enum PresetWallpaper { + case cats + case flowers + case hearts + case kids + case school + case travel + + var res: UIImage { + UIImage(named: "wallpaper_\(filename)")! + } + + public var filename: String { + switch self { + case .cats: "cats" + case .flowers: "flowers" + case .hearts: "hearts" + case .kids: "kids" + case .school: "school" + case .travel: "travel" + } + } + + public var scale: Float { + switch self { + case .cats: 0.63 + case .flowers: 0.53 + case .hearts: 0.59 + case .kids: 0.53 + case .school: 0.53 + case .travel: 0.68 + } + } + + public var background: [DefaultTheme: Color] { + switch self { + case .cats: wallpaperBackgrounds(light: "#ffF8F6EA") + case .flowers: wallpaperBackgrounds(light: "#ffE2FFE4") + case .hearts: wallpaperBackgrounds(light: "#ffFDECEC") + case .kids: wallpaperBackgrounds(light: "#ffdbfdfb") + case .school: wallpaperBackgrounds(light: "#ffE7F5FF") + case .travel: wallpaperBackgrounds(light: "#fff9eeff") + } + } + + public var tint: [DefaultTheme: Color] { + switch self { + case .cats: [ + DefaultTheme.LIGHT: "#ffefdca6".colorFromReadableHex(), + DefaultTheme.DARK: "#ff4b3b0e".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff51400f".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff4b3b0e".colorFromReadableHex() + ] + case .flowers: [ + DefaultTheme.LIGHT: "#ff9CEA59".colorFromReadableHex(), + DefaultTheme.DARK: "#ff31560D".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff36600f".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff31560D".colorFromReadableHex() + ] + case .hearts: [ + DefaultTheme.LIGHT: "#fffde0e0".colorFromReadableHex(), + DefaultTheme.DARK: "#ff3c0f0f".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff411010".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff3C0F0F".colorFromReadableHex() + ] + case .kids: [ + DefaultTheme.LIGHT: "#ffadeffc".colorFromReadableHex(), + DefaultTheme.DARK: "#ff16404B".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff184753".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff16404B".colorFromReadableHex() + ] + case .school: [ + DefaultTheme.LIGHT: "#ffCEEBFF".colorFromReadableHex(), + DefaultTheme.DARK: "#ff0F293B".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff112f43".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff0F293B".colorFromReadableHex() + ] + case .travel: [ + DefaultTheme.LIGHT: "#ffeedbfe".colorFromReadableHex(), + DefaultTheme.DARK: "#ff311E48".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff35204e".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff311E48".colorFromReadableHex() + ] + } + } + + public var colors: [DefaultTheme: ThemeColors] { + switch self { + case .cats: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#fffffaed", + sentQuote: "#fffaf0d6", + receivedMessage: "#ffF8F7F4", + receivedQuote: "#ffefede9" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff2f2919", + sentQuote: "#ff473a1d", + receivedMessage: "#ff272624", + receivedQuote: "#ff373633" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff41371b", + sentQuote: "#ff654f1c", + receivedMessage: "#ff272624", + receivedQuote: "#ff373633" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff41371b", + sentQuote: "#ff654f1c", + receivedMessage: "#ff1f1e1b", + receivedQuote: "#ff2f2d27" + ) + ] + case .flowers: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#fff1ffe5", + sentQuote: "#ffdcf9c4", + receivedMessage: "#ffF4F8F2", + receivedQuote: "#ffe7ece7" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff163521", + sentQuote: "#ff1B5330", + receivedMessage: "#ff242523", + receivedQuote: "#ff353733" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff184739", + sentQuote: "#ff1F6F4B", + receivedMessage: "#ff242523", + receivedQuote: "#ff353733" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff184739", + sentQuote: "#ff1F6F4B", + receivedMessage: "#ff1c1f1a", + receivedQuote: "#ff282b25" + ) + ] + case .hearts: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#fffff4f4", + sentQuote: "#ffffdfdf", + receivedMessage: "#fff8f6f6", + receivedQuote: "#ffefebeb" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff301515", + sentQuote: "#ff4C1818", + receivedMessage: "#ff242121", + receivedQuote: "#ff3b3535" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff491A28", + sentQuote: "#ff761F29", + receivedMessage: "#ff242121", + receivedQuote: "#ff3b3535" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff491A28", + sentQuote: "#ff761F29", + receivedMessage: "#ff1f1b1b", + receivedQuote: "#ff2e2626" + ) + ] + case .kids: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#ffeafeff", + sentQuote: "#ffcbf4f7", + receivedMessage: "#fff3fafa", + receivedQuote: "#ffe4efef" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff16302F", + sentQuote: "#ff1a4a49", + receivedMessage: "#ff252626", + receivedQuote: "#ff373A39" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff1a4745", + sentQuote: "#ff1d6b69", + receivedMessage: "#ff252626", + receivedQuote: "#ff373a39" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff1a4745", + sentQuote: "#ff1d6b69", + receivedMessage: "#ff1e1f1f", + receivedQuote: "#ff262b29" + ) + ] + case .school: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#ffeef9ff", + sentQuote: "#ffD6EDFA", + receivedMessage: "#ffF3F5F9", + receivedQuote: "#ffe4e8ee" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff172833", + sentQuote: "#ff1C3E4F", + receivedMessage: "#ff26282c", + receivedQuote: "#ff393c40" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff1A3C5D", + sentQuote: "#ff235b80", + receivedMessage: "#ff26282c", + receivedQuote: "#ff393c40" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff1A3C5D", + sentQuote: "#ff235b80", + receivedMessage: "#ff1d1e22", + receivedQuote: "#ff292b2f" + ) + ] + case .travel: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#fffcf6ff", + sentQuote: "#fff2e0fc", + receivedMessage: "#ffF6F4F7", + receivedQuote: "#ffede9ee" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff33263B", + sentQuote: "#ff53385E", + receivedMessage: "#ff272528", + receivedQuote: "#ff3B373E" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff3C255D", + sentQuote: "#ff623485", + receivedMessage: "#ff26273B", + receivedQuote: "#ff3A394F" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff3C255D", + sentQuote: "#ff623485", + receivedMessage: "#ff231f23", + receivedQuote: "#ff2c2931" + ) + ] + } + } + + public static func from(_ filename: String) -> PresetWallpaper? { + switch filename { + case PresetWallpaper.cats.filename: PresetWallpaper.cats + case PresetWallpaper.flowers.filename: PresetWallpaper.flowers + case PresetWallpaper.hearts.filename: PresetWallpaper.hearts + case PresetWallpaper.kids.filename: PresetWallpaper.kids + case PresetWallpaper.school.filename: PresetWallpaper.school + case PresetWallpaper.travel.filename: PresetWallpaper.travel + default: nil + } + } +} + +func wallpaperBackgrounds(light: String) -> [DefaultTheme : Color] { + [ + DefaultTheme.LIGHT: light.colorFromReadableHex(), + DefaultTheme.DARK: "#ff121212".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff111528".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff070707".colorFromReadableHex() + ] +} + +public enum WallpaperScaleType/*(val contentScale: ContentScale)*/: Codable { + case fill/* (ContentScale.Crop)*/ + case fit/* (ContentScale.Fit)*/ + case `repeat`/* (ContentScale.Fit)*/ + + public var text: String { + switch self { + case .fill: "Fill" + case .fit: "Fit" + case .repeat: "Repeat" + } + } +} + +public enum WallpaperType { + public var image: SwiftUI.Image? { + if let uiImage { + return SwiftUI.Image(uiImage: uiImage) + } + return nil + } + + public var uiImage: UIImage? { + let filename: String + switch self { + case let .Preset(f, _): filename = f + case let .Image(f, _, _): filename = f + default: return nil + } + if filename == "" { return nil } + if let image = WallpaperType.cachedImages[filename] { + return image + } else { + let res: UIImage? + if case let .Preset(filename, _) = self { + res = (PresetWallpaper.from(filename) ?? PresetWallpaper.cats).res + } else { + // In case of unintentional image deletion don't crash the app + res = UIImage(contentsOfFile: getWallpaperFilePath(filename).path) + } + if let res { + WallpaperType.cachedImages[filename] = res + } + return res + } + } + + public func sameType(_ other: WallpaperType?) -> Bool { + if case let .Preset(filename, _) = self, case let .Preset(otherFilename, _) = other { filename == otherFilename } + else if case .Image = self, case .Image = other { true } + else if case .Empty = self, case .Empty = other { true } + else { false } + } + + public func samePreset(other: PresetWallpaper?) -> Bool { if case let .Preset(filename, _) = self, filename == other?.filename { true } else { false } } + + case Preset(_ filename: String, _ scale: Float?) + + case Image(_ filename: String, _ scale: Float?, _ scaleType: WallpaperScaleType?) + + case Empty + + public func defaultBackgroundColor(_ theme: DefaultTheme, _ themeBackground: Color) -> Color { + if case let .Preset(filename, _) = self { + (PresetWallpaper.from(filename) ?? PresetWallpaper.cats).background[theme]! + } else { + themeBackground + } + } + + public func defaultTintColor(_ theme: DefaultTheme) -> Color { + if case let .Preset(filename, _) = self { + (PresetWallpaper.from(filename) ?? PresetWallpaper.cats).tint[theme]! + } else if case let .Image(_, _, scaleType) = self, scaleType == WallpaperScaleType.repeat { + Color.clear + } else { + Color.clear + } + } + + public static var cachedImages: [String: UIImage] = [:] + + public static func from(_ wallpaper: ThemeWallpaper?) -> WallpaperType? { + if wallpaper == nil { + return nil + } else if let preset = wallpaper?.preset { + return WallpaperType.Preset(preset, wallpaper?.scale) + } else if let imageFile = wallpaper?.imageFile { + return WallpaperType.Image(imageFile, wallpaper?.scale, wallpaper?.scaleType) + } else { + return WallpaperType.Empty + } + } +} diff --git a/apps/ios/SimpleXChat/Theme/Color.swift b/apps/ios/SimpleXChat/Theme/Color.swift new file mode 100644 index 0000000000..00b1b8622c --- /dev/null +++ b/apps/ios/SimpleXChat/Theme/Color.swift @@ -0,0 +1,101 @@ +// +// Color.swift +// SimpleX (iOS) +// +// Created by Avently on 05.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI + +let Purple200 = Color(0xFFBB86FC) +let Purple500 = Color(0xFF6200EE) +let Purple700 = Color(0xFF3700B3) +let Teal200 = Color(0xFF03DAC5) +let Gray = Color(0x22222222) +let Indigo = Color(0xFF9966FF) +let SimplexBlue = Color(0, 136, 255, a: 255) +let SimplexGreen = Color(77, 218, 103, a: 255) +let SecretColor = Color(0x40808080) +let LightGray = Color(241, 242, 246, a: 255) +let DarkGray = Color(43, 44, 46, a: 255) +let HighOrLowlight = Color(139, 135, 134, a: 255) +let MessagePreviewDark = Color(179, 175, 174, a: 255) +let MessagePreviewLight = Color(49, 45, 44, a: 255) +let ToolbarLight = Color(220, 220, 220, a: 12) +let ToolbarDark = Color(80, 80, 80, a: 12) +let SettingsSecondaryLight = Color(200, 196, 195, a: 90) +let GroupDark = Color(80, 80, 80, a: 60) +let IncomingCallLight = Color(239, 237, 236, a: 255) +let WarningOrange = Color(255, 127, 0, a: 255) +let WarningYellow = Color(255, 192, 0, a: 255) +let FileLight = Color(183, 190, 199, a: 255) +let FileDark = Color(101, 101, 106, a: 255) + +extension Color { + public init(_ argb: Int64) { + let a = Double((argb & 0xFF000000) >> 24) / 255.0 + let r = Double((argb & 0xFF0000) >> 16) / 255.0 + let g = Double((argb & 0xFF00) >> 8) / 255.0 + let b = Double((argb & 0xFF)) / 255.0 + self.init(.sRGB, red: r, green: g, blue: b, opacity: a) + } + + public init(_ r: Int, _ g: Int, _ b: Int, a: Int) { + self.init(.sRGB, red: Double(r) / 255.0, green: Double(g) / 255.0, blue: Double(b) / 255.0, opacity: Double(a) / 255.0) + } + + public func toReadableHex() -> String { + let uiColor: UIColor = .init(self) + var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) + uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) + return String(format: "#%02x%02x%02x%02x", + Int(a * 255), + Int(r * 255), + Int(g * 255), + Int(b * 255) + ) + } + + public func darker(_ factor: CGFloat = 0.1) -> Color { + var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) + UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &a) + return Color(.sRGB, red: max(r * (1 - factor), 0), green: max(g * (1 - factor), 0), blue: max(b * (1 - factor), 0), opacity: a) + } + + public func lighter(_ factor: CGFloat = 0.1) -> Color { + var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) + UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &a) + return Color(.sRGB, red: min(r * (1 + factor), 1), green: min(g * (1 + factor), 1), blue: min(b * (1 + factor), 1), opacity: a) + } + +} + +extension String { + func colorFromReadableHex() -> Color { + // https://stackoverflow.com/a/56874327 + let hex = self.trimmingCharacters(in: ["#", " "]) + var int: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (1, 1, 1, 0) + } + + return Color( + .sRGB, + red: Double(r) / 255, + green: Double(g) / 255, + blue: Double(b) / 255, + opacity: Double(a) / 255 + ) + } +} diff --git a/apps/ios/Shared/UI/Theme/Theme.swift b/apps/ios/SimpleXChat/Theme/ThemeTypes.swift similarity index 72% rename from apps/ios/Shared/UI/Theme/Theme.swift rename to apps/ios/SimpleXChat/Theme/ThemeTypes.swift index 5cc4e074b0..2b8383828c 100644 --- a/apps/ios/Shared/UI/Theme/Theme.swift +++ b/apps/ios/SimpleXChat/Theme/ThemeTypes.swift @@ -7,26 +7,25 @@ // import Foundation -import SimpleXChat import SwiftUI -enum DefaultTheme: String, Codable { +public enum DefaultTheme: String, Codable { case LIGHT case DARK case SIMPLEX case BLACK - static let SYSTEM_THEME_NAME: String = "SYSTEM" + public static let SYSTEM_THEME_NAME: String = "SYSTEM" - var themeName: String { self.rawValue } + public var themeName: String { self.rawValue } - var mode: DefaultThemeMode { + public var mode: DefaultThemeMode { self == .LIGHT ? DefaultThemeMode.light : DefaultThemeMode.dark } - func hasChangedAnyColor(_ overrides: ThemeOverrides?) -> Bool { + public func hasChangedAnyColor(_ overrides: ThemeOverrides?) -> Bool { if let overrides { overrides.colors != ThemeColors() || overrides.wallpaper != nil && (overrides.wallpaper?.background != nil || overrides.wallpaper?.tint != nil) } else { @@ -35,24 +34,24 @@ enum DefaultTheme: String, Codable { } } -enum DefaultThemeMode: String, Codable { +public enum DefaultThemeMode: String, Codable { case light case dark } -class Colors: ObservableObject, NSCopying { - @Published var primary: Color - @Published var primaryVariant: Color - @Published var secondary: Color - @Published var secondaryVariant: Color - @Published var background: Color - @Published var surface: Color - @Published var error: Color - @Published var onBackground: Color - @Published var onSurface: Color - @Published var isLight: Bool +public class Colors: ObservableObject, NSCopying { + @Published public var primary: Color + @Published public var primaryVariant: Color + @Published public var secondary: Color + @Published public var secondaryVariant: Color + @Published public var background: Color + @Published public var surface: Color + @Published public var error: Color + @Published public var onBackground: Color + @Published public var onSurface: Color + @Published public var isLight: Bool - init(primary: Color, primaryVariant: Color, secondary: Color, secondaryVariant: Color, background: Color, surface: Color, error: Color, onBackground: Color, onSurface: Color, isLight: Bool) { + public init(primary: Color, primaryVariant: Color, secondary: Color, secondaryVariant: Color, background: Color, surface: Color, error: Color, onBackground: Color, onSurface: Color, isLight: Bool) { self.primary = primary self.primaryVariant = primaryVariant self.secondary = secondary @@ -64,22 +63,22 @@ class Colors: ObservableObject, NSCopying { self.onSurface = onSurface self.isLight = isLight } - func copy(with zone: NSZone? = nil) -> Any { + public func copy(with zone: NSZone? = nil) -> Any { Colors(primary: self.primary, primaryVariant: self.primaryVariant, secondary: self.secondary, secondaryVariant: self.secondaryVariant, background: self.background, surface: self.surface, error: self.error, onBackground: self.onBackground, onSurface: self.onSurface, isLight: self.isLight) } - func clone() -> Colors { copy() as! Colors } + public func clone() -> Colors { copy() as! Colors } } -class AppColors: ObservableObject, NSCopying { - @Published var title: Color - @Published var primaryVariant2: Color - @Published var sentMessage: Color - @Published var sentQuote: Color - @Published var receivedMessage: Color - @Published var receivedQuote: Color +public class AppColors: ObservableObject, NSCopying { + @Published public var title: Color + @Published public var primaryVariant2: Color + @Published public var sentMessage: Color + @Published public var sentQuote: Color + @Published public var receivedMessage: Color + @Published public var receivedQuote: Color - init(title: Color, primaryVariant2: Color, sentMessage: Color, sentQuote: Color, receivedMessage: Color, receivedQuote: Color) { + public init(title: Color, primaryVariant2: Color, sentMessage: Color, sentQuote: Color, receivedMessage: Color, receivedQuote: Color) { self.title = title self.primaryVariant2 = primaryVariant2 self.sentMessage = sentMessage @@ -88,13 +87,13 @@ class AppColors: ObservableObject, NSCopying { self.receivedQuote = receivedQuote } - func copy(with zone: NSZone? = nil) -> Any { + public func copy(with zone: NSZone? = nil) -> Any { AppColors(title: self.title, primaryVariant2: self.primaryVariant2, sentMessage: self.sentMessage, sentQuote: self.sentQuote, receivedMessage: self.receivedMessage, receivedQuote: self.receivedQuote) } - func clone() -> AppColors { copy() as! AppColors } + public func clone() -> AppColors { copy() as! AppColors } - func copy( + public func copy( title: Color?, primaryVariant2: Color?, sentMessage: Color?, @@ -113,24 +112,24 @@ class AppColors: ObservableObject, NSCopying { } } -class AppWallpaper: ObservableObject, NSCopying { - @Published var background: Color? = nil - @Published var tint: Color? = nil - @Published var type: WallpaperType = WallpaperType.Empty +public class AppWallpaper: ObservableObject, NSCopying { + @Published public var background: Color? = nil + @Published public var tint: Color? = nil + @Published public var type: WallpaperType = WallpaperType.Empty - init(background: Color?, tint: Color?, type: WallpaperType) { + public init(background: Color?, tint: Color?, type: WallpaperType) { self.background = background self.tint = tint self.type = type } - func copy(with zone: NSZone? = nil) -> Any { + public func copy(with zone: NSZone? = nil) -> Any { AppWallpaper(background: self.background, tint: self.tint, type: self.type) } - func clone() -> AppWallpaper { copy() as! AppWallpaper } + public func clone() -> AppWallpaper { copy() as! AppWallpaper } - func copyWithoutDefault(_ background: Color?, _ tint: Color?, _ type: WallpaperType) -> AppWallpaper { + public func copyWithoutDefault(_ background: Color?, _ tint: Color?, _ type: WallpaperType) -> AppWallpaper { AppWallpaper( background: background, tint: tint, @@ -139,7 +138,7 @@ class AppWallpaper: ObservableObject, NSCopying { } } -enum ThemeColor { +public enum ThemeColor { case PRIMARY case PRIMARY_VARIANT case SECONDARY @@ -155,7 +154,7 @@ enum ThemeColor { case WALLPAPER_BACKGROUND case WALLPAPER_TINT - func fromColors(_ colors: Colors, _ appColors: AppColors, _ appWallpaper: AppWallpaper) -> Color? { + public func fromColors(_ colors: Colors, _ appColors: AppColors, _ appWallpaper: AppWallpaper) -> Color? { switch (self) { case .PRIMARY: colors.primary case .PRIMARY_VARIANT: colors.primaryVariant @@ -174,7 +173,7 @@ enum ThemeColor { } } - var text: LocalizedStringKey { + public var text: LocalizedStringKey { switch (self) { case .PRIMARY: "Accent" case .PRIMARY_VARIANT: "Additional accent" @@ -194,19 +193,34 @@ enum ThemeColor { } } -struct ThemeColors: Codable, Equatable{ - var primary: String? = nil - var primaryVariant: String? = nil - var secondary: String? = nil - var secondaryVariant: String? = nil - var background: String? = nil - var surface: String? = nil - var title: String? = nil - var primaryVariant2: String? = nil - var sentMessage: String? = nil - var sentQuote: String? = nil - var receivedMessage: String? = nil - var receivedQuote: String? = nil +public struct ThemeColors: Codable, Equatable{ + public var primary: String? = nil + public var primaryVariant: String? = nil + public var secondary: String? = nil + public var secondaryVariant: String? = nil + public var background: String? = nil + public var surface: String? = nil + public var title: String? = nil + public var primaryVariant2: String? = nil + public var sentMessage: String? = nil + public var sentQuote: String? = nil + public var receivedMessage: String? = nil + public var receivedQuote: String? = nil + + public init(primary: String? = nil, primaryVariant: String? = nil, secondary: String? = nil, secondaryVariant: String? = nil, background: String? = nil, surface: String? = nil, title: String? = nil, primaryVariant2: String? = nil, sentMessage: String? = nil, sentQuote: String? = nil, receivedMessage: String? = nil, receivedQuote: String? = 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 + } enum CodingKeys: String, CodingKey { case primary = "accent" @@ -223,7 +237,7 @@ struct ThemeColors: Codable, Equatable{ case receivedQuote = "receivedReply" } - static func from(sentMessage: String, sentQuote: String, receivedMessage: String, receivedQuote: String) -> ThemeColors { + public static func from(sentMessage: String, sentQuote: String, receivedMessage: String, receivedQuote: String) -> ThemeColors { var c = ThemeColors() c.sentMessage = sentMessage c.sentQuote = sentQuote @@ -232,7 +246,7 @@ struct ThemeColors: Codable, Equatable{ return c } - static func from(_ colors: Colors, _ appColors: AppColors) -> ThemeColors { + public static func from(_ colors: Colors, _ appColors: AppColors) -> ThemeColors { ThemeColors( primary: colors.primary.toReadableHex(), primaryVariant: colors.primaryVariant.toReadableHex(), @@ -250,7 +264,7 @@ struct ThemeColors: Codable, Equatable{ } } -public struct ThemeWallpaper: Codable { +public struct ThemeWallpaper: Codable, Equatable { public var preset: String? public var scale: Float? public var scaleType: WallpaperScaleType? @@ -259,7 +273,17 @@ public struct ThemeWallpaper: Codable { public var image: String? public var imageFile: String? - func toAppWallpaper() -> AppWallpaper { + public init(preset: String? = nil, scale: Float? = nil, scaleType: WallpaperScaleType? = nil, background: String? = nil, tint: String? = nil, image: String? = nil, imageFile: String? = nil) { + self.preset = preset + self.scale = scale + self.scaleType = scaleType + self.background = background + self.tint = tint + self.image = image + self.imageFile = imageFile + } + + public func toAppWallpaper() -> AppWallpaper { AppWallpaper ( background: background?.colorFromReadableHex(), tint: tint?.colorFromReadableHex(), @@ -267,25 +291,7 @@ public struct ThemeWallpaper: Codable { ) } - func withFilledWallpaperBase64() -> ThemeWallpaper { - let aw = toAppWallpaper() - let type = aw.type - let preset: String? = if case let WallpaperType.Preset(filename, _) = type { filename } else { nil } - let scale: Float? = if case let WallpaperType.Preset(_, scale) = type { scale } else { if case let WallpaperType.Image(_, scale, _) = type { scale } else { 1.0 } } - let scaleType: WallpaperScaleType? = if case let WallpaperType.Image(_, _, scaleType) = type { scaleType } else { nil } - let image: String? = if case WallpaperType.Image = type, let image = type.uiImage { resizeImageToStrSize(image, maxDataSize: 5_000_000) } else { nil } - return ThemeWallpaper ( - preset: preset, - scale: scale, - scaleType: scaleType, - background: aw.background?.toReadableHex(), - tint: aw.tint?.toReadableHex(), - image: image, - imageFile: nil - ) - } - - func withFilledWallpaperPath() -> ThemeWallpaper { + public func withFilledWallpaperPath() -> ThemeWallpaper { let aw = toAppWallpaper() let type = aw.type let preset: String? = if case let WallpaperType.Preset(filename, _) = type { filename } else { nil } @@ -311,25 +317,7 @@ public struct ThemeWallpaper: Codable { ) } - func importFromString() -> ThemeWallpaper { - self - // LALAL - //if preset == nil, let image { - // Need to save image from string and to save its path -// do { -// let parsed = base64ToBitmap(image) -// let filename = saveWallpaperFile(parsed) -// return copy(image = nil, imageFile = filename) -// } catch let e { -// logger.error("Error while parsing/copying the image: \(e)") -// return ThemeWallpaper() -// } -// } else { -// self -// } - } - - static func from(_ type: WallpaperType, _ background: String?, _ tint: String?) -> ThemeWallpaper { + public static func from(_ type: WallpaperType, _ background: String?, _ tint: String?) -> ThemeWallpaper { let preset: String? = if case let WallpaperType.Preset(filename, _) = type { filename } else { nil } let scale: Float? = if case let WallpaperType.Preset(_, scale) = type { scale } else if case let WallpaperType.Image(_, scale, _) = type { scale } else { nil } let scaleType: WallpaperScaleType? = if case let WallpaperType.Image(_, _, scaleType) = type { scaleType } else { nil } @@ -346,13 +334,20 @@ public struct ThemeWallpaper: Codable { } } -public struct ThemeOverrides: Codable { - var themeId: String = UUID().uuidString - var base: DefaultTheme - var colors: ThemeColors = ThemeColors() - var wallpaper: ThemeWallpaper? = nil +public struct ThemeOverrides: Codable, Equatable { + public var themeId: String = UUID().uuidString + public var base: DefaultTheme + public var colors: ThemeColors = ThemeColors() + public var wallpaper: ThemeWallpaper? = nil - func isSame(_ type: WallpaperType?, _ themeName: String) -> Bool { + public init(themeId: String = UUID().uuidString, base: DefaultTheme, colors: ThemeColors = ThemeColors(), wallpaper: ThemeWallpaper? = nil) { + self.themeId = themeId + self.base = base + self.colors = colors + self.wallpaper = wallpaper + } + + public func isSame(_ type: WallpaperType?, _ themeName: String) -> Bool { if base.themeName != themeName { return false } @@ -369,7 +364,7 @@ public struct ThemeOverrides: Codable { } } - func withUpdatedColor(_ name: ThemeColor, _ color: String?) -> ThemeOverrides { + public func withUpdatedColor(_ name: ThemeColor, _ color: String?) -> ThemeOverrides { var c = colors var w = wallpaper switch name { @@ -391,7 +386,7 @@ public struct ThemeOverrides: Codable { return ThemeOverrides(themeId: themeId, base: base, colors: c, wallpaper: w) } - func toColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perUserTheme: ThemeColors?, _ presetWallpaperTheme: ThemeColors?) -> Colors { + public func toColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perUserTheme: ThemeColors?, _ presetWallpaperTheme: ThemeColors?) -> Colors { let baseColors = switch base { case DefaultTheme.LIGHT: LightColorPalette case DefaultTheme.DARK: DarkColorPalette @@ -408,7 +403,7 @@ public struct ThemeOverrides: Codable { return c } - 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: ThemeColors?) -> AppColors { let baseColors = switch base { case DefaultTheme.LIGHT: LightColorPaletteApp case DefaultTheme.DARK: DarkColorPaletteApp @@ -432,7 +427,7 @@ public struct ThemeOverrides: Codable { return c } - func toAppWallpaper(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverride?, _ themeBackgroundColor: Color) -> AppWallpaper { + public func toAppWallpaper(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverride?, _ themeBackgroundColor: Color) -> AppWallpaper { let mainType: WallpaperType if let t = themeOverridesForType { mainType = t } // type can be nil if override is empty `"wallpaper": "{}"`, in this case no wallpaper is needed, empty. @@ -464,7 +459,7 @@ public struct ThemeOverrides: Codable { return AppWallpaper(background: background, tint: tint, type: wallpaper) } - 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: ThemeColors?) -> ThemeColors { let c = toColors(base, perChatTheme, perUserTheme, presetWallpaperTheme) let ac = toAppColors(base, perChatTheme, perChatWallpaperType, perUserTheme, perUserWallpaperType, presetWallpaperTheme) return ThemeColors( @@ -485,15 +480,15 @@ public struct ThemeOverrides: Codable { } extension [ThemeOverrides] { - func getTheme(_ themeId: String?) -> ThemeOverrides? { + public func getTheme(_ themeId: String?) -> ThemeOverrides? { self.first { $0.themeId == themeId } } - func getTheme(_ themeId: String?, _ type: WallpaperType?, _ base: DefaultTheme) -> ThemeOverrides? { + public func getTheme(_ themeId: String?, _ type: WallpaperType?, _ base: DefaultTheme) -> ThemeOverrides? { self.first { $0.themeId == themeId || $0.isSame(type, base.themeName) } } - func replace(_ theme: ThemeOverrides) -> [ThemeOverrides] { + public func replace(_ theme: ThemeOverrides) -> [ThemeOverrides] { let index = self.firstIndex { $0.themeId == theme.themeId || // prevent situation when two themes has the same type but different theme id (maybe something was changed in prefs by hand) $0.isSame(WallpaperType.from(theme.wallpaper), theme.base.themeName) @@ -507,9 +502,9 @@ extension [ThemeOverrides] { return a } - func sameTheme(_ type: WallpaperType?, _ themeName: String) -> ThemeOverrides? { first { $0.isSame(type, themeName) } } + public func sameTheme(_ type: WallpaperType?, _ themeName: String) -> ThemeOverrides? { first { $0.isSame(type, themeName) } } - func skipDuplicates() -> [ThemeOverrides] { + public func skipDuplicates() -> [ThemeOverrides] { var res: [ThemeOverrides] = [] self.forEach { theme in let themeType = WallpaperType.from(theme.wallpaper) @@ -522,23 +517,29 @@ extension [ThemeOverrides] { } -struct ThemeModeOverrides: Codable { - var light: ThemeModeOverride? = nil - var dark: ThemeModeOverride? = nil +public struct ThemeModeOverrides: Codable { + public var light: ThemeModeOverride? = nil + public var dark: ThemeModeOverride? = nil - func preferredMode(_ darkTheme: Bool) -> ThemeModeOverride? { + public func preferredMode(_ darkTheme: Bool) -> ThemeModeOverride? { darkTheme ? dark : light } } -struct ThemeModeOverride: Codable { - var mode: DefaultThemeMode = CurrentColors.base.mode - var colors: ThemeColors = ThemeColors() - var wallpaper: ThemeWallpaper? = nil +public struct ThemeModeOverride: Codable { + public var mode: DefaultThemeMode// = CurrentColors.base.mode + public var colors: ThemeColors = ThemeColors() + public var wallpaper: ThemeWallpaper? = nil - var type: WallpaperType? { WallpaperType.from(wallpaper) } + public init(mode: DefaultThemeMode, colors: ThemeColors = ThemeColors(), wallpaper: ThemeWallpaper? = nil) { + self.mode = mode + self.colors = colors + self.wallpaper = wallpaper + } - func withUpdatedColor(_ name: ThemeColor, _ color: String?) -> ThemeModeOverride { + public var type: WallpaperType? { WallpaperType.from(wallpaper) } + + public func withUpdatedColor(_ name: ThemeColor, _ color: String?) -> ThemeModeOverride { var c = colors var w = wallpaper switch (name) { @@ -560,7 +561,7 @@ struct ThemeModeOverride: Codable { return ThemeModeOverride(mode: mode, colors: c, wallpaper: w) } - static func withFilledAppDefaults(_ mode: DefaultThemeMode, _ base: DefaultTheme) -> ThemeModeOverride { + public static func withFilledAppDefaults(_ mode: DefaultThemeMode, _ base: DefaultTheme) -> ThemeModeOverride { ThemeModeOverride( mode: mode, colors: ThemeOverrides(base: base).withFilledColors(base, nil, nil, nil, nil, nil), @@ -569,36 +570,7 @@ struct ThemeModeOverride: Codable { } } -struct ThemedBackground: ViewModifier { - @EnvironmentObject var theme: AppTheme - - func body(content: Content) -> some View { - content - .background( - theme.base == DefaultTheme.SIMPLEX - ? LinearGradient( - colors: [ - theme.colors.background.lighter(0.4), - theme.colors.background.darker(0.4) - ], - startPoint: .topLeading, - endPoint: .bottomTrailing - ) - : LinearGradient( - colors: [], - startPoint: .topLeading, - endPoint: .bottomTrailing - ) - ) - .background( - theme.base == DefaultTheme.SIMPLEX - ? Color.clear - : theme.colors.background - ) - } -} - -let DarkColorPalette = Colors( +public let DarkColorPalette = Colors( primary: SimplexBlue, primaryVariant: SimplexBlue, secondary: HighOrLowlight, @@ -610,7 +582,7 @@ let DarkColorPalette = Colors( onSurface: Color(0xFFFFFBFA), isLight: false ) -let DarkColorPaletteApp = AppColors( +public let DarkColorPaletteApp = AppColors( title: SimplexBlue, primaryVariant2: Color(0xFF18262E), sentMessage: Color(0xFF18262E), @@ -619,7 +591,7 @@ let DarkColorPaletteApp = AppColors( receivedQuote: Color(0xff373739) ) -let LightColorPalette = Colors ( +public let LightColorPalette = Colors ( primary: SimplexBlue, primaryVariant: SimplexBlue, secondary: HighOrLowlight, @@ -631,7 +603,7 @@ let LightColorPalette = Colors ( onSurface: Color.black, isLight: true ) -let LightColorPaletteApp = AppColors( +public let LightColorPaletteApp = AppColors( title: SimplexBlue, primaryVariant2: Color(0xFFE9F7FF), sentMessage: Color(0xFFE9F7FF), @@ -640,7 +612,7 @@ let LightColorPaletteApp = AppColors( receivedQuote: Color(0xffececee) ) -let SimplexColorPalette = Colors( +public let SimplexColorPalette = Colors( primary: Color(0xFF70F0F9), primaryVariant: Color(0xFF1298A5), secondary: HighOrLowlight, @@ -652,7 +624,7 @@ let SimplexColorPalette = Colors( onSurface: Color(0xFFFFFBFA), isLight: false ) -let SimplexColorPaletteApp = AppColors( +public let SimplexColorPaletteApp = AppColors( title: Color(0xFF267BE5), primaryVariant2: Color(0xFF172941), sentMessage: Color(0xFF172941), @@ -661,7 +633,7 @@ let SimplexColorPaletteApp = AppColors( receivedQuote: Color(0xff36394a) ) -let BlackColorPalette = Colors( +public let BlackColorPalette = Colors( primary: Color(0xff0077e0), primaryVariant: Color(0xff0077e0), secondary: HighOrLowlight, @@ -673,7 +645,7 @@ let BlackColorPalette = Colors( onSurface: Color(0xFFFFFBFA), isLight: false ) -let BlackColorPaletteApp = AppColors( +public let BlackColorPaletteApp = AppColors( title: Color(0xff0077e0), primaryVariant2: Color(0xff243747), sentMessage: Color(0xFF18262E), @@ -682,43 +654,12 @@ let BlackColorPaletteApp = AppColors( receivedQuote: Color(0xff29292b) ) -var systemInDarkThemeCurrently: Bool = false - -var CurrentColors: ThemeManager.ActiveTheme = ThemeManager.currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) { - didSet { - AppTheme.shared.name = CurrentColors.name - AppTheme.shared.base = CurrentColors.base - AppTheme.shared.colors.updateColorsFrom(CurrentColors.colors) - AppTheme.shared.appColors.updateColorsFrom(CurrentColors.appColors) - AppTheme.shared.wallpaper.updateWallpaperFrom(CurrentColors.wallpaper) - AppTheme.shared.objectWillChange.send() - } -} - -func isInDarkTheme() -> Bool { !CurrentColors.colors.isLight } +public var systemInDarkThemeCurrently: Bool = false //func isSystemInDarkTheme(): Bool -class AppTheme: ObservableObject { - static let shared = AppTheme(name: CurrentColors.name, base: CurrentColors.base, colors: CurrentColors.colors, appColors: CurrentColors.appColors, wallpaper: CurrentColors.wallpaper) - - var name: String - var base: DefaultTheme - @ObservedObject var colors: Colors - @ObservedObject var appColors: AppColors - @ObservedObject var wallpaper: AppWallpaper - - init(name: String, base: DefaultTheme, colors: Colors, appColors: AppColors, wallpaper: AppWallpaper) { - self.name = name - self.base = base - self.colors = colors - self.appColors = appColors - self.wallpaper = wallpaper - } -} - extension Colors { - func updateColorsFrom(_ other: Colors) { + public func updateColorsFrom(_ other: Colors) { primary = other.primary primaryVariant = other.primaryVariant secondary = other.secondary @@ -733,7 +674,7 @@ extension Colors { } extension AppColors { - func updateColorsFrom(_ other: AppColors) { + public func updateColorsFrom(_ other: AppColors) { title = other.title primaryVariant2 = other.primaryVariant2 sentMessage = other.sentMessage @@ -744,22 +685,13 @@ extension AppColors { } extension AppWallpaper { - func updateWallpaperFrom(_ other: AppWallpaper) { + public func updateWallpaperFrom(_ other: AppWallpaper) { background = other.background tint = other.tint type = other.type } } -func reactOnDarkThemeChanges(_ isDark: Bool) { - systemInDarkThemeCurrently = isDark - //sceneDelegate.window?.overrideUserInterfaceStyle == .unspecified - if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME && CurrentColors.colors.isLight == isDark { - // Change active colors from light to dark and back based on system theme - ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME) - } -} - //@Composable //func SimpleXTheme(darkTheme: Bool? = nil, content: @Composable () -> Void) { // val systemDark = rememberUpdatedState(isSystemInDarkTheme())