divided types per target

This commit is contained in:
Avently
2024-06-14 16:08:58 +07:00
committed by Evgeny Poberezkin
parent 2023464a13
commit fb4927ca87
14 changed files with 826 additions and 774 deletions
+1 -1
View File
@@ -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
}
+3 -3
View File
@@ -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
+121
View File
@@ -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
)
}
}
@@ -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)
}
}
-37
View File
@@ -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 }
+40 -411
View File
@@ -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
)
}
}
@@ -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 {
@@ -7,6 +7,7 @@
//
import SwiftUI
import SimpleXChat
let defaultAccentColor = CGColor.init(red: 0, green: 0.533, blue: 1, alpha: 1)
+3 -10
View File
@@ -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
}
+30 -22
View File
@@ -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 = "<group>"; };
8C05382D2B39887E006436DC /* VideoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUtils.swift; sourceTree = "<group>"; };
8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
8C74C3EB2C1B92A900039E77 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = "<group>"; };
8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = "<group>"; };
8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = "<group>"; };
8C7E3CE32C0DEAC400BFF63A /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeTypes.swift; sourceTree = "<group>"; };
8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = "<group>"; };
8C804B1D2C11F966007A63C8 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = "<group>"; };
8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaperTypes.swift; sourceTree = "<group>"; };
8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioDevicePicker.swift; sourceTree = "<group>"; };
8C852B072C1086D100BA61E8 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; };
@@ -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 = "<group>";
@@ -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 = "<group>";
};
8C74C3E92C1B909200039E77 /* Theme */ = {
isa = PBXGroup;
children = (
8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */,
8C74C3EB2C1B92A900039E77 /* Theme.swift */,
);
path = Theme;
sourceTree = "<group>";
};
8C7D94982B8894D300B7B9E1 /* Migration */ = {
isa = PBXGroup;
children = (
@@ -935,20 +949,12 @@
path = Migration;
sourceTree = "<group>";
};
8C86EBE22C0DAE0A00E12243 /* UI */ = {
isa = PBXGroup;
children = (
8C86EBE32C0DAE3700E12243 /* Theme */,
);
path = UI;
sourceTree = "<group>";
};
8C86EBE32C0DAE3700E12243 /* Theme */ = {
isa = PBXGroup;
children = (
8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */,
8C7E3CE32C0DEAC400BFF63A /* Theme.swift */,
8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */,
8C852B072C1086D100BA61E8 /* Color.swift */,
8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */,
);
path = Theme;
sourceTree = "<group>";
@@ -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;
+4 -4
View File
@@ -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
}
@@ -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
}
}
}
+101
View File
@@ -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
)
}
}
@@ -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())