import Foundation import UIKit import TelegramCore import TelegramUIPreferences private func decodeColor(_ values: KeyedDecodingContainer, _ key: Key, decoder: Decoder? = nil, fallbackKey: String? = nil) throws -> UIColor { if let decoder = decoder as? PresentationThemeDecoding, let fallbackKey = fallbackKey { var codingPath = decoder.codingPath.map { $0.stringValue } codingPath.append(key.stringValue) let key = codingPath.joined(separator: ".") decoder.fallbackKeys[key] = fallbackKey } let value = try values.decode(String.self, forKey: key) if value.lowercased() == "clear" { return UIColor.clear } else if let color = UIColor(hexString: value) { return color } else { throw PresentationThemeDecodingError.generic } } private func encodeColor(_ values: inout KeyedEncodingContainer, _ value: UIColor, _ key: Key) throws { if value == UIColor.clear { try values.encode("clear", forKey: key) } else if value.alpha < 1.0 { try values.encode(String(format: "%08x", value.argb), forKey: key) } else { try values.encode(String(format: "%06x", value.rgb), forKey: key) } } private func decodeColorList(_ values: KeyedDecodingContainer, _ key: Key) throws -> [UIColor] { let colorValues = try values.decode([String].self, forKey: key) var result: [UIColor] = [] for value in colorValues { if value.lowercased() == "clear" { result.append(UIColor.clear) } else if let color = UIColor(hexString: value) { result.append(color) } else { throw PresentationThemeDecodingError.generic } } return result } private func encodeColorList(_ values: inout KeyedEncodingContainer, _ colors: [UIColor], _ key: Key) throws { var stringList: [String] = [] for value in colors { if value == UIColor.clear { stringList.append("clear") } else if value.alpha < 1.0 { stringList.append(String(format: "%08x", value.argb)) } else { stringList.append(String(format: "%06x", value.rgb)) } } try values.encode(stringList, forKey: key) } struct TelegramWallpaperStandardizedCodable: Codable { let value: TelegramWallpaper init(_ value: TelegramWallpaper) { self.value = value } init(from decoder: Decoder) throws { let values = try decoder.singleValueContainer() if let value = try? values.decode(String.self) { switch value.lowercased() { case "builtin": self.value = .builtin(WallpaperSettings()) default: let optionKeys = ["motion", "blur"] if [6, 8].contains(value.count), let color = UIColor(hexString: value) { self.value = .color(color.argb) } else { let components = value.components(separatedBy: " ") var blur = false var motion = false if components.contains("motion") { motion = true } if components.contains("blur") { blur = true } var slug: String? var colors: [UIColor] = [] var intensity: Int32? var rotation: Int32? for i in 0 ..< components.count { let component = components[i] if optionKeys.contains(component) { continue } if i == 0 && component.count > 8 { slug = component } else if [6, 8].contains(component.count), let color = UIColor(hexString: component) { colors.append(color) } else if component.count <= 4, let value = Int32(component) { if intensity == nil { if value >= -100 && value <= 100 { intensity = value } else { intensity = 50 } } else if rotation == nil { if value >= 0 && value < 360 { rotation = value } } } } if let slug = slug { self.value = .file(TelegramWallpaper.File(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: !colors.isEmpty, isDark: false, slug: slug, file: TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 0), partialReference: nil, resource: WallpaperDataResource(slug: slug), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: [], alternativeRepresentations: []), settings: WallpaperSettings(blur: blur, motion: motion, colors: colors.map { $0.argb }, intensity: intensity, rotation: rotation))) } else if colors.count > 1 { self.value = .gradient(TelegramWallpaper.Gradient(id: nil, colors: colors.map { $0.argb }, settings: WallpaperSettings(blur: blur, motion: motion, rotation: rotation))) } else { throw PresentationThemeDecodingError.generic } } } } else { throw PresentationThemeDecodingError.generic } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self.value { case .builtin: try container.encode("builtin") case let .color(color): try container.encode(String(format: "%06x", color)) case let .gradient(gradient): var components: [String] = [] for color in gradient.colors { components.append(String(format: "%06x", color)) } if let rotation = gradient.settings.rotation { components.append("\(rotation)") } if gradient.settings.motion { components.append("motion") } if gradient.settings.blur { components.append("blur") } try container.encode(components.joined(separator: " ")) case let .file(file): var components: [String] = [] components.append(file.slug) if self.value.isPattern { if file.settings.colors.count >= 1 { components.append(String(format: "%06x", file.settings.colors[0])) } if let intensity = file.settings.intensity { components.append("\(intensity)") } if file.settings.colors.count >= 2 { components.append(String(format: "%06x", file.settings.colors[1])) } if file.settings.colors.count >= 3 { components.append(String(format: "%06x", file.settings.colors[2])) } if file.settings.colors.count >= 4 { components.append(String(format: "%06x", file.settings.colors[3])) } if let rotation = file.settings.rotation, rotation != 0 { components.append("\(rotation)") } } if file.settings.motion { components.append("motion") } if file.settings.blur { components.append("blur") } try container.encode(components.joined(separator: " ")) default: break } } } extension PresentationThemeStatusBarStyle: Codable { public init(from decoder: Decoder) throws { let values = try decoder.singleValueContainer() if let value = try? values.decode(String.self) { switch value.lowercased() { case "black": self = .black case "white": self = .white default: self = .black } } else { self = .black } } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .black: try container.encode("black") case .white: try container.encode("white") } } } extension PresentationThemeActionSheetBackgroundType: Codable { public init(from decoder: Decoder) throws { let values = try decoder.singleValueContainer() if let value = try? values.decode(String.self) { switch value.lowercased() { case "light": self = .light case "dark": self = .dark default: self = .light } } else { self = .light } } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .light: try container.encode("light") case .dark: try container.encode("dark") } } } extension PresentationThemeKeyboardColor: Codable { public init(from decoder: Decoder) throws { let values = try decoder.singleValueContainer() if let value = try? values.decode(String.self) { switch value.lowercased() { case "light": self = .light case "dark": self = .dark default: self = .light } } else { self = .light } } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .light: try container.encode("light") case .dark: try container.encode("dark") } } } extension PresentationThemeExpandedNotificationBackgroundType: Codable { public init(from decoder: Decoder) throws { let values = try decoder.singleValueContainer() if let value = try? values.decode(String.self) { switch value.lowercased() { case "light": self = .light case "dark": self = .dark default: self = .light } } else { self = .light } } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .light: try container.encode("light") case .dark: try container.encode("dark") } } } extension PresentationThemeGradientColors: Codable { enum CodingKeys: String, CodingKey { case top case bottom } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(topColor: try decodeColor(values, .top), bottomColor: try decodeColor(values, .bottom)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.topColor, .top) try encodeColor(&values, self.bottomColor, .bottom) } } extension PresentationThemeIntro: Codable { enum CodingKeys: String, CodingKey { case statusBar case primaryText case accentText case disabledText case startButton case dot } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(statusBarStyle: try values.decode(PresentationThemeStatusBarStyle.self, forKey: .statusBar), primaryTextColor: try decodeColor(values, .primaryText), accentTextColor: try decodeColor(values, .accentText), disabledTextColor: try decodeColor(values, .disabledText), startButtonColor: try decodeColor(values, .startButton), dotColor: try decodeColor(values, .dot)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.statusBarStyle, forKey: .statusBar) try encodeColor(&values, self.primaryTextColor, .primaryText) try encodeColor(&values, self.accentTextColor, .accentText) try encodeColor(&values, self.disabledTextColor, .disabledText) try encodeColor(&values, self.startButtonColor, .startButton) try encodeColor(&values, self.dotColor, .dot) } } extension PresentationThemePasscode: Codable { enum CodingKeys: String, CodingKey { case bg case button } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(backgroundColors: try values.decode(PresentationThemeGradientColors.self, forKey: .bg), buttonColor: try decodeColor(values, .button)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.backgroundColors, forKey: .bg) try encodeColor(&values, self.buttonColor, .button) } } extension PresentationThemeRootTabBar: Codable { enum CodingKeys: String, CodingKey { case background case separator case icon case selectedIcon case text case selectedText case badgeBackground case badgeStroke case badgeText } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(backgroundColor: try decodeColor(values, .background), separatorColor: try decodeColor(values, .separator), iconColor: try decodeColor(values, .icon), selectedIconColor: try decodeColor(values, .selectedIcon), textColor: try decodeColor(values, .text), selectedTextColor: try decodeColor(values, .selectedText), badgeBackgroundColor: try decodeColor(values, .badgeBackground), badgeStrokeColor: try decodeColor(values, .badgeStroke), badgeTextColor: try decodeColor(values, .badgeText)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.backgroundColor, .background) try encodeColor(&values, self.separatorColor, .separator) try encodeColor(&values, self.iconColor, .icon) try encodeColor(&values, self.selectedIconColor, .selectedIcon) try encodeColor(&values, self.textColor, .text) try encodeColor(&values, self.selectedTextColor, .selectedText) try encodeColor(&values, self.badgeBackgroundColor, .badgeBackground) try encodeColor(&values, self.badgeStrokeColor, .badgeStroke) try encodeColor(&values, self.badgeTextColor, .badgeText) } } extension PresentationThemeRootNavigationBar: Codable { enum CodingKeys: String, CodingKey { case button case disabledButton case primaryText case secondaryText case control case accentText case background case separator case badgeFill case badgeStroke case badgeText case segmentedBg case segmentedFg case segmentedText case segmentedDivider case clearButtonBackground case clearButtonForeground case opaqueBackground } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let blurredBackgroundColor = try decodeColor(values, .background) let opaqueBackgroundColor: UIColor if blurredBackgroundColor.alpha >= 0.99 { opaqueBackgroundColor = blurredBackgroundColor } else { opaqueBackgroundColor = (try? decodeColor(values, .opaqueBackground)) ?? blurredBackgroundColor } self.init( buttonColor: try decodeColor(values, .button), disabledButtonColor: try decodeColor(values, .disabledButton), primaryTextColor: try decodeColor(values, .primaryText), secondaryTextColor: try decodeColor(values, .secondaryText), controlColor: try decodeColor(values, .control), accentTextColor: try decodeColor(values, .accentText), blurredBackgroundColor: blurredBackgroundColor, opaqueBackgroundColor: opaqueBackgroundColor, separatorColor: try decodeColor(values, .separator), badgeBackgroundColor: try decodeColor(values, .badgeFill), badgeStrokeColor: try decodeColor(values, .badgeStroke), badgeTextColor: try decodeColor(values, .badgeText), segmentedBackgroundColor: try decodeColor(values, .segmentedBg, decoder: decoder, fallbackKey: "root.searchBar.inputFill"), segmentedForegroundColor: try decodeColor(values, .segmentedFg, decoder: decoder, fallbackKey: "root.navBar.background"), segmentedTextColor: try decodeColor(values, .segmentedText, decoder: decoder, fallbackKey: "root.navBar.primaryText"), segmentedDividerColor: try decodeColor(values, .segmentedDivider, decoder: decoder, fallbackKey: "list.freeInputField.stroke"), clearButtonBackgroundColor: try decodeColor(values, .clearButtonBackground, decoder: decoder, fallbackKey: "list.freeInputField.bg"), clearButtonForegroundColor: try decodeColor(values, .clearButtonForeground, decoder: decoder, fallbackKey: "list.freeInputField.primary") ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.buttonColor, .button) try encodeColor(&values, self.disabledButtonColor, .disabledButton) try encodeColor(&values, self.primaryTextColor, .primaryText) try encodeColor(&values, self.secondaryTextColor, .secondaryText) try encodeColor(&values, self.controlColor, .control) try encodeColor(&values, self.accentTextColor, .accentText) try encodeColor(&values, self.blurredBackgroundColor, .background) try encodeColor(&values, self.opaqueBackgroundColor, .opaqueBackground) try encodeColor(&values, self.separatorColor, .separator) try encodeColor(&values, self.badgeBackgroundColor, .badgeFill) try encodeColor(&values, self.badgeStrokeColor, .badgeStroke) try encodeColor(&values, self.badgeTextColor, .badgeText) try encodeColor(&values, self.segmentedBackgroundColor, .segmentedBg) try encodeColor(&values, self.segmentedForegroundColor, .segmentedFg) try encodeColor(&values, self.segmentedTextColor, .segmentedText) try encodeColor(&values, self.segmentedDividerColor, .segmentedDivider) } } extension PresentationThemeNavigationSearchBar: Codable { enum CodingKeys: String, CodingKey { case background case accent case inputFill case inputText case inputPlaceholderText case inputIcon case inputClearButton case separator } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(backgroundColor: try decodeColor(values, .background), accentColor: try decodeColor(values, .accent), inputFillColor: try decodeColor(values, .inputFill), inputTextColor: try decodeColor(values, .inputText), inputPlaceholderTextColor: try decodeColor(values, .inputPlaceholderText), inputIconColor: try decodeColor(values, .inputIcon), inputClearButtonColor: try decodeColor(values, .inputClearButton), separatorColor: try decodeColor(values, .separator)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.backgroundColor, .background) try encodeColor(&values, self.accentColor, .accent) try encodeColor(&values, self.inputFillColor, .inputFill) try encodeColor(&values, self.inputTextColor, .inputText) try encodeColor(&values, self.inputPlaceholderTextColor, .inputPlaceholderText) try encodeColor(&values, self.inputIconColor, .inputIcon) try encodeColor(&values, self.inputClearButtonColor, .inputClearButton) try encodeColor(&values, self.separatorColor, .separator) } } extension PresentationThemeRootController: Codable { enum CodingKeys: String, CodingKey { case statusBar case tabBar case navBar case searchBar case keyboard } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(statusBarStyle: try values.decode(PresentationThemeStatusBarStyle.self, forKey: .statusBar), tabBar: try values.decode(PresentationThemeRootTabBar.self, forKey: .tabBar), navigationBar: try values.decode(PresentationThemeRootNavigationBar.self, forKey: .navBar), navigationSearchBar: try values.decode(PresentationThemeNavigationSearchBar.self, forKey: .searchBar), keyboardColor: try values.decode(PresentationThemeKeyboardColor.self, forKey: .keyboard)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.statusBarStyle, forKey: .statusBar) try values.encode(self.tabBar, forKey: .tabBar) try values.encode(self.navigationBar, forKey: .navBar) try values.encode(self.navigationSearchBar, forKey: .searchBar) try values.encode(self.keyboardColor, forKey: .keyboard) } } extension PresentationThemeActionSheet: Codable { enum CodingKeys: String, CodingKey { case dim case bgType case opaqueItemBg case itemBg case opaqueItemHighlightedBg case itemHighlightedBg case opaqueItemSeparator case standardActionText case destructiveActionText case disabledActionText case primaryText case secondaryText case controlAccent case inputBg case inputHollowBg case inputBorder case inputPlaceholder case inputText case inputClearButton case checkContent } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(dimColor: try decodeColor(values, .dim), backgroundType: try values.decode(PresentationThemeActionSheetBackgroundType.self, forKey: .bgType), opaqueItemBackgroundColor: try decodeColor(values, .opaqueItemBg), itemBackgroundColor: try decodeColor(values, .itemBg), opaqueItemHighlightedBackgroundColor: try decodeColor(values, .opaqueItemHighlightedBg), itemHighlightedBackgroundColor: try decodeColor(values, .itemHighlightedBg), opaqueItemSeparatorColor: try decodeColor(values, .opaqueItemSeparator), standardActionTextColor: try decodeColor(values, .standardActionText), destructiveActionTextColor: try decodeColor(values, .destructiveActionText), disabledActionTextColor: try decodeColor(values, .disabledActionText), primaryTextColor: try decodeColor(values, .primaryText), secondaryTextColor: try decodeColor(values, .secondaryText), controlAccentColor: try decodeColor(values, .controlAccent), inputBackgroundColor: try decodeColor(values, .inputBg), inputHollowBackgroundColor: try decodeColor(values, .inputHollowBg), inputBorderColor: try decodeColor(values, .inputBorder), inputPlaceholderColor: try decodeColor(values, .inputPlaceholder), inputTextColor: try decodeColor(values, .inputText), inputClearButtonColor: try decodeColor(values, .inputClearButton), checkContentColor: try decodeColor(values, .checkContent)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.dimColor, .dim) try values.encode(self.backgroundType, forKey: .bgType) try encodeColor(&values, self.opaqueItemBackgroundColor, .opaqueItemBg) try encodeColor(&values, self.itemBackgroundColor, .itemBg) try encodeColor(&values, self.opaqueItemHighlightedBackgroundColor, .opaqueItemHighlightedBg) try encodeColor(&values, self.itemHighlightedBackgroundColor, .itemHighlightedBg) try encodeColor(&values, self.opaqueItemSeparatorColor, .opaqueItemSeparator) try encodeColor(&values, self.standardActionTextColor, .standardActionText) try encodeColor(&values, self.destructiveActionTextColor, .destructiveActionText) try encodeColor(&values, self.disabledActionTextColor, .disabledActionText) try encodeColor(&values, self.primaryTextColor, .primaryText) try encodeColor(&values, self.secondaryTextColor, .secondaryText) try encodeColor(&values, self.controlAccentColor, .controlAccent) try encodeColor(&values, self.inputBackgroundColor, .inputBg) try encodeColor(&values, self.inputHollowBackgroundColor, .inputHollowBg) try encodeColor(&values, self.inputBorderColor, .inputBorder) try encodeColor(&values, self.inputPlaceholderColor, .inputPlaceholder) try encodeColor(&values, self.inputTextColor, .inputText) try encodeColor(&values, self.inputClearButtonColor, .inputClearButton) try encodeColor(&values, self.checkContentColor, .checkContent) } } extension PresentationThemeSwitch: Codable { enum CodingKeys: String, CodingKey { case frame case handle case content case positive case negative } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(frameColor: try decodeColor(values, .frame), handleColor: try decodeColor(values, .handle), contentColor: try decodeColor(values, .content), positiveColor: try decodeColor(values, .positive), negativeColor: try decodeColor(values, .negative)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.frameColor, .frame) try encodeColor(&values, self.handleColor, .handle) try encodeColor(&values, self.contentColor, .content) try encodeColor(&values, self.positiveColor, .positive) try encodeColor(&values, self.negativeColor, .negative) } } extension PresentationThemeFillForeground: Codable { enum CodingKeys: String, CodingKey { case bg case fg } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(fillColor: try decodeColor(values, .bg), foregroundColor: try decodeColor(values, .fg)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.fillColor, .bg) try encodeColor(&values, self.foregroundColor, .fg) } } extension PresentationThemeItemDisclosureActions: Codable { enum CodingKeys: String, CodingKey { case neutral1 case neutral2 case destructive case constructive case accent case warning case inactive } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(neutral1: try values.decode(PresentationThemeFillForeground.self, forKey: .neutral1), neutral2: try values.decode(PresentationThemeFillForeground.self, forKey: .neutral2), destructive: try values.decode(PresentationThemeFillForeground.self, forKey: .destructive), constructive: try values.decode(PresentationThemeFillForeground.self, forKey: .constructive), accent: try values.decode(PresentationThemeFillForeground.self, forKey: .accent), warning: try values.decode(PresentationThemeFillForeground.self, forKey: .warning), inactive: try values.decode(PresentationThemeFillForeground.self, forKey: .inactive)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.neutral1, forKey: .neutral1) try values.encode(self.neutral2, forKey: .neutral2) try values.encode(self.destructive, forKey: .destructive) try values.encode(self.constructive, forKey: .constructive) try values.encode(self.accent, forKey: .accent) try values.encode(self.warning, forKey: .warning) try values.encode(self.inactive, forKey: .inactive) } } extension PresentationThemeItemBarChart: Codable { enum CodingKeys: String, CodingKey { case color1 case color2 case color3 } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(color1: try decodeColor(values, .color1), color2: try decodeColor(values, .color2), color3: try decodeColor(values, .color3)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.color1, .color1) try encodeColor(&values, self.color2, .color2) try encodeColor(&values, self.color3, .color3) } } extension PresentationThemeFillStrokeForeground: Codable { enum CodingKeys: String, CodingKey { case bg case stroke case fg } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(fillColor: try decodeColor(values, .bg), strokeColor: try decodeColor(values, .stroke), foregroundColor: try decodeColor(values, .fg)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.fillColor, .bg) try encodeColor(&values, self.strokeColor, .stroke) try encodeColor(&values, self.foregroundColor, .fg) } } extension PresentationInputFieldTheme: Codable { enum CodingKeys: String, CodingKey { case bg case stroke case placeholder case primary case control } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(backgroundColor: try decodeColor(values, .bg), strokeColor: try decodeColor(values, .stroke), placeholderColor: try decodeColor(values, .placeholder), primaryColor: try decodeColor(values, .primary), controlColor: try decodeColor(values, .control)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.backgroundColor, .bg) try encodeColor(&values, self.strokeColor, .stroke) try encodeColor(&values, self.placeholderColor, .placeholder) try encodeColor(&values, self.primaryColor, .primary) try encodeColor(&values, self.controlColor, .control) } } extension PresentationThemeList.PaymentOption: Codable { enum CodingKeys: String, CodingKey { case inactiveFill case inactiveForeground case activeFill case activeForeground } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init( inactiveFillColor: try decodeColor(values, .inactiveFill), inactiveForegroundColor: try decodeColor(values, .inactiveForeground), activeFillColor: try decodeColor(values, .activeFill), activeForegroundColor: try decodeColor(values, .activeForeground) ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.activeFillColor, .inactiveFill) try encodeColor(&values, self.activeForegroundColor, .inactiveForeground) try encodeColor(&values, self.activeFillColor, .activeFill) try encodeColor(&values, self.activeForegroundColor, .activeForeground) } } extension PresentationThemeList: Codable { enum CodingKeys: String, CodingKey { case blocksBg case modalBlocksBg case plainBg case modalPlainBg case primaryText case secondaryText case disabledText case accent case highlighted case destructive case placeholderText case itemBlocksBg case itemModalBlocksBg case itemHighlightedBg case blocksSeparator case plainSeparator case disclosureArrow case sectionHeaderText case freeText case freeTextError case freeTextSuccess case freeMonoIcon case `switch` case disclosureActions case check case controlSecondary case freeInputField case freePlainInputField case mediaPlaceholder case scrollIndicator case pageIndicatorInactive case inputClearButton case itemBarChart case itemInputField case paymentOption } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let freePlainInputField: PresentationInputFieldTheme if let value = try? values.decode(PresentationInputFieldTheme.self, forKey: .freePlainInputField) { freePlainInputField = value } else { freePlainInputField = try values.decode(PresentationInputFieldTheme.self, forKey: .freeInputField) } let freeTextSuccessColor = try decodeColor(values, .freeTextSuccess) self.init( blocksBackgroundColor: try decodeColor(values, .blocksBg), modalBlocksBackgroundColor: try decodeColor(values, .modalBlocksBg, decoder: decoder, fallbackKey: "list.blocksBg"), plainBackgroundColor: try decodeColor(values, .plainBg), modalPlainBackgroundColor: try decodeColor(values, .modalPlainBg, decoder: decoder, fallbackKey: "list.plainBg"), itemPrimaryTextColor: try decodeColor(values, .primaryText), itemSecondaryTextColor: try decodeColor(values, .secondaryText), itemDisabledTextColor: try decodeColor(values, .disabledText), itemAccentColor: try decodeColor(values, .accent), itemHighlightedColor: try decodeColor(values, .highlighted), itemDestructiveColor: try decodeColor(values, .destructive), itemPlaceholderTextColor: try decodeColor(values, .placeholderText), itemBlocksBackgroundColor: try decodeColor(values, .itemBlocksBg), itemModalBlocksBackgroundColor: try decodeColor(values, .itemModalBlocksBg, decoder: decoder, fallbackKey: "list.itemBlocksBg"), itemHighlightedBackgroundColor: try decodeColor(values, .itemHighlightedBg), itemBlocksSeparatorColor: try decodeColor(values, .blocksSeparator), itemPlainSeparatorColor: try decodeColor(values, .plainSeparator), disclosureArrowColor: try decodeColor(values, .disclosureArrow), sectionHeaderTextColor: try decodeColor(values, .sectionHeaderText), freeTextColor: try decodeColor(values, .freeText), freeTextErrorColor: try decodeColor(values, .freeTextError), freeTextSuccessColor: freeTextSuccessColor, freeMonoIconColor: try decodeColor(values, .freeMonoIcon), itemSwitchColors: try values.decode(PresentationThemeSwitch.self, forKey: .switch), itemDisclosureActions: try values.decode(PresentationThemeItemDisclosureActions.self, forKey: .disclosureActions), itemCheckColors: try values.decode(PresentationThemeFillStrokeForeground.self, forKey: .check), controlSecondaryColor: try decodeColor(values, .controlSecondary), freeInputField: try values.decode(PresentationInputFieldTheme.self, forKey: .freeInputField), freePlainInputField: freePlainInputField, mediaPlaceholderColor: try decodeColor(values, .mediaPlaceholder), scrollIndicatorColor: try decodeColor(values, .scrollIndicator), pageIndicatorInactiveColor: try decodeColor(values, .pageIndicatorInactive), inputClearButtonColor: try decodeColor(values, .inputClearButton), itemBarChart: try values.decode(PresentationThemeItemBarChart.self, forKey: .itemBarChart), itemInputField: try values.decode(PresentationInputFieldTheme.self, forKey: .itemInputField), paymentOption: (try? values.decode(PresentationThemeList.PaymentOption.self, forKey: .paymentOption)) ?? PresentationThemeList.PaymentOption( inactiveFillColor: freeTextSuccessColor.withMultipliedAlpha(0.3), inactiveForegroundColor: freeTextSuccessColor, activeFillColor: freeTextSuccessColor, activeForegroundColor: UIColor(rgb: 0xffffff) ) ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.blocksBackgroundColor, .blocksBg) try encodeColor(&values, self.plainBackgroundColor, .plainBg) try encodeColor(&values, self.itemPrimaryTextColor, .primaryText) try encodeColor(&values, self.itemSecondaryTextColor, .secondaryText) try encodeColor(&values, self.itemDisabledTextColor, .disabledText) try encodeColor(&values, self.itemAccentColor, .accent) try encodeColor(&values, self.itemHighlightedColor, .highlighted) try encodeColor(&values, self.itemDestructiveColor, .destructive) try encodeColor(&values, self.itemPlaceholderTextColor, .placeholderText) try encodeColor(&values, self.itemBlocksBackgroundColor, .itemBlocksBg) try encodeColor(&values, self.itemHighlightedBackgroundColor, .itemHighlightedBg) try encodeColor(&values, self.itemBlocksSeparatorColor, .blocksSeparator) try encodeColor(&values, self.itemPlainSeparatorColor, .plainSeparator) try encodeColor(&values, self.disclosureArrowColor, .disclosureArrow) try encodeColor(&values, self.sectionHeaderTextColor, .sectionHeaderText) try encodeColor(&values, self.freeTextColor, .freeText) try encodeColor(&values, self.freeTextErrorColor, .freeTextError) try encodeColor(&values, self.freeTextSuccessColor, .freeTextSuccess) try encodeColor(&values, self.freeMonoIconColor, .freeMonoIcon) try values.encode(self.itemSwitchColors, forKey: .`switch`) try values.encode(self.itemDisclosureActions, forKey: .disclosureActions) try values.encode(self.itemCheckColors, forKey: .check) try encodeColor(&values, self.controlSecondaryColor, .controlSecondary) try values.encode(self.freeInputField, forKey: .freeInputField) try encodeColor(&values, self.mediaPlaceholderColor, .mediaPlaceholder) try encodeColor(&values, self.scrollIndicatorColor, .scrollIndicator) try encodeColor(&values, self.pageIndicatorInactiveColor, .pageIndicatorInactive) try encodeColor(&values, self.inputClearButtonColor, .inputClearButton) try values.encode(self.itemBarChart, forKey: .itemBarChart) try values.encode(self.itemInputField, forKey: .itemInputField) } } extension PresentationThemeArchiveAvatarColors: Codable { enum CodingKeys: String, CodingKey { case background case foreground } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(backgroundColors: try values.decode(PresentationThemeGradientColors.self, forKey: .background), foregroundColor: try decodeColor(values, .foreground)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.backgroundColors, forKey: .background) try encodeColor(&values, self.foregroundColor, .foreground) } } extension PresentationThemeChatList: Codable { enum CodingKeys: String, CodingKey { case bg case itemSeparator case itemBg case pinnedItemBg case itemHighlightedBg case pinnedItemHighlightedBg case itemSelectedBg case title case secretTitle case dateText case authorName case messageText case messageHighlightedText case messageDraftText case checkmark case pendingIndicator case failedFill case failedFg case muteIcon case unreadBadgeActiveBg case unreadBadgeActiveText case unreadBadgeInactiveBg case unreadBadgeInactiveText case reactionBadgeActiveBg case pinnedBadge case pinnedSearchBar case regularSearchBar case sectionHeaderBg case sectionHeaderText case verifiedIconBg case verifiedIconFg case secretIcon case pinnedArchiveAvatar case unpinnedArchiveAvatar case onlineDot case storyUnseen case storyUnseenPrivate case storySeen } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") self.init( backgroundColor: try decodeColor(values, .bg), itemSeparatorColor: try decodeColor(values, .itemSeparator), itemBackgroundColor: try decodeColor(values, .itemBg), pinnedItemBackgroundColor: try decodeColor(values, .pinnedItemBg), itemHighlightedBackgroundColor: try decodeColor(values, .itemHighlightedBg), pinnedItemHighlightedBackgroundColor: try decodeColor(values, .pinnedItemHighlightedBg, fallbackKey: "\(codingPath).itemHighlightedBg"), itemSelectedBackgroundColor: try decodeColor(values, .itemSelectedBg), titleColor: try decodeColor(values, .title), secretTitleColor: try decodeColor(values, .secretTitle), dateTextColor: try decodeColor(values, .dateText), authorNameColor: try decodeColor(values, .authorName), messageTextColor: try decodeColor(values, .messageText), messageHighlightedTextColor: try decodeColor(values, .messageHighlightedText), messageDraftTextColor: try decodeColor(values, .messageDraftText), checkmarkColor: try decodeColor(values, .checkmark), pendingIndicatorColor: try decodeColor(values, .pendingIndicator), failedFillColor: try decodeColor(values, .failedFill), failedForegroundColor: try decodeColor(values, .failedFg), muteIconColor: try decodeColor(values, .muteIcon), unreadBadgeActiveBackgroundColor: try decodeColor(values, .unreadBadgeActiveBg), unreadBadgeActiveTextColor: try decodeColor(values, .unreadBadgeActiveText), unreadBadgeInactiveBackgroundColor: try decodeColor(values, .unreadBadgeInactiveBg), unreadBadgeInactiveTextColor: try decodeColor(values, .unreadBadgeInactiveText), reactionBadgeActiveBackgroundColor: try decodeColor(values, .reactionBadgeActiveBg, fallbackKey: "\(codingPath).unreadBadgeActiveBg"), pinnedBadgeColor: try decodeColor(values, .pinnedBadge), pinnedSearchBarColor: try decodeColor(values, .pinnedSearchBar), regularSearchBarColor: try decodeColor(values, .regularSearchBar), sectionHeaderFillColor: try decodeColor(values, .sectionHeaderBg), sectionHeaderTextColor: try decodeColor(values, .sectionHeaderText), verifiedIconFillColor: try decodeColor(values, .verifiedIconBg), verifiedIconForegroundColor: try decodeColor(values, .verifiedIconFg), secretIconColor: try decodeColor(values, .secretIcon), pinnedArchiveAvatarColor: try values.decode(PresentationThemeArchiveAvatarColors.self, forKey: .pinnedArchiveAvatar), unpinnedArchiveAvatarColor: try values.decode(PresentationThemeArchiveAvatarColors.self, forKey: .unpinnedArchiveAvatar), onlineDotColor: try decodeColor(values, .onlineDot), storyUnseenColors: (try? values.decode(PresentationThemeGradientColors.self, forKey: .storyUnseen)) ?? PresentationThemeGradientColors(topColor: UIColor(rgb: 0x34C76F), bottomColor: UIColor(rgb: 0x3DA1FD)), storyUnseenPrivateColors: (try? values.decode(PresentationThemeGradientColors.self, forKey: .storyUnseenPrivate)) ?? PresentationThemeGradientColors(topColor: UIColor(rgb: 0x7CD636), bottomColor: UIColor(rgb: 0x26B470)), storySeenColors: (try? values.decode(PresentationThemeGradientColors.self, forKey: .storySeen)) ?? PresentationThemeGradientColors(topColor: UIColor(rgb: 0xD8D8E1), bottomColor: UIColor(rgb: 0xD8D8E1)) ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.backgroundColor, .bg) try encodeColor(&values, self.itemSeparatorColor, .itemSeparator) try encodeColor(&values, self.itemBackgroundColor, .itemBg) try encodeColor(&values, self.pinnedItemBackgroundColor, .pinnedItemBg) try encodeColor(&values, self.itemHighlightedBackgroundColor, .itemHighlightedBg) try encodeColor(&values, self.pinnedItemHighlightedBackgroundColor, .pinnedItemHighlightedBg) try encodeColor(&values, self.itemSelectedBackgroundColor, .itemSelectedBg) try encodeColor(&values, self.titleColor, .title) try encodeColor(&values, self.secretTitleColor, .secretTitle) try encodeColor(&values, self.dateTextColor, .dateText) try encodeColor(&values, self.authorNameColor, .authorName) try encodeColor(&values, self.messageTextColor, .messageText) try encodeColor(&values, self.messageHighlightedTextColor, .messageHighlightedText) try encodeColor(&values, self.messageDraftTextColor, .messageDraftText) try encodeColor(&values, self.checkmarkColor, .checkmark) try encodeColor(&values, self.pendingIndicatorColor, .pendingIndicator) try encodeColor(&values, self.failedFillColor, .failedFill) try encodeColor(&values, self.failedForegroundColor, .failedFg) try encodeColor(&values, self.muteIconColor, .muteIcon) try encodeColor(&values, self.unreadBadgeActiveBackgroundColor, .unreadBadgeActiveBg) try encodeColor(&values, self.unreadBadgeActiveTextColor, .unreadBadgeActiveText) try encodeColor(&values, self.unreadBadgeInactiveBackgroundColor, .unreadBadgeInactiveBg) try encodeColor(&values, self.unreadBadgeInactiveTextColor, .unreadBadgeInactiveText) try encodeColor(&values, self.reactionBadgeActiveBackgroundColor, .reactionBadgeActiveBg) try encodeColor(&values, self.pinnedBadgeColor, .pinnedBadge) try encodeColor(&values, self.pinnedSearchBarColor, .pinnedSearchBar) try encodeColor(&values, self.regularSearchBarColor, .regularSearchBar) try encodeColor(&values, self.sectionHeaderFillColor, .sectionHeaderBg) try encodeColor(&values, self.sectionHeaderTextColor, .sectionHeaderText) try encodeColor(&values, self.verifiedIconFillColor, .verifiedIconBg) try encodeColor(&values, self.verifiedIconForegroundColor, .verifiedIconFg) try encodeColor(&values, self.secretIconColor, .secretIcon) try values.encode(self.pinnedArchiveAvatarColor, forKey: .pinnedArchiveAvatar) try values.encode(self.unpinnedArchiveAvatarColor, forKey: .unpinnedArchiveAvatar) try encodeColor(&values, self.onlineDotColor, .onlineDot) try values.encode(self.storyUnseenColors, forKey: .storyUnseen) try values.encode(self.storyUnseenPrivateColors, forKey: .storyUnseenPrivate) try values.encode(self.storySeenColors, forKey: .storySeen) } } extension PresentationThemeBubbleShadow: Codable { enum CodingKeys: String, CodingKey { case color case radius case verticalOffset } public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init( color: try decodeColor(values, .color), radius: try CGFloat(Double(truncating: values.decode(Decimal.self, forKey: .radius) as NSNumber)), verticalOffset: try CGFloat(Double(truncating: values.decode(Decimal.self, forKey: .verticalOffset) as NSNumber)) ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.color, .color) try values.encode(Decimal(Double(self.radius)), forKey: .radius) try values.encode(Decimal(Double(self.verticalOffset)), forKey: .verticalOffset) } } extension PresentationThemeBubbleColorComponents: Codable { enum CodingKeys: String, CodingKey { case bg case gradientBg case highlightedBg case stroke case shadow case bgList case reactionInactiveBg case reactionInactiveFg case reactionInactiveMediaPlaceholder case reactionActiveMediaPlaceholder case reactionActiveBg case reactionActiveFg case __workaroundNonexistingKey } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") let fill: [UIColor] if let bgList = try? decodeColorList(values, .bgList) { fill = bgList } else { var fillColor = try decodeColor(values, .bg) var gradientColor = try decodeColor(values, .gradientBg, decoder: decoder, fallbackKey: "\(codingPath).bg") if gradientColor.rgb != fillColor.rgb { fillColor = fillColor.withAlphaComponent(1.0) gradientColor = gradientColor.withAlphaComponent(1.0) } fill = [fillColor, gradientColor] } let fallbackKeyPrefix: String if codingPath.hasPrefix("chat.message.incoming.") { fallbackKeyPrefix = "chat.message.incoming." } else { fallbackKeyPrefix = "chat.message.outgoing." } let reactionInactiveBackground: UIColor if let color = try? decodeColor(values, .reactionInactiveBg) { reactionInactiveBackground = color } else { reactionInactiveBackground = (try decodeColor(values, .__workaroundNonexistingKey, fallbackKey: "\(fallbackKeyPrefix).accentControl")).withMultipliedAlpha(0.1) } let reactionInactiveMediaPlaceholder: UIColor if let color = try? decodeColor(values, .reactionInactiveMediaPlaceholder) { reactionInactiveMediaPlaceholder = color } else { reactionInactiveMediaPlaceholder = (try decodeColor(values, .__workaroundNonexistingKey, fallbackKey: "\(fallbackKeyPrefix).accentControl")).withMultipliedAlpha(0.1) } let reactionActiveMediaPlaceholder: UIColor if let color = try? decodeColor(values, .reactionActiveMediaPlaceholder) { reactionActiveMediaPlaceholder = color } else { reactionActiveMediaPlaceholder = (try decodeColor(values, .__workaroundNonexistingKey, fallbackKey: "\(fallbackKeyPrefix).accentControl")).withMultipliedAlpha(0.1) } let reactionInactiveForeground: UIColor if let color = try? decodeColor(values, .reactionInactiveFg) { reactionInactiveForeground = color } else { reactionInactiveForeground = try decodeColor(values, .__workaroundNonexistingKey, fallbackKey: "\(fallbackKeyPrefix).accentControl") } let reactionActiveBackground: UIColor if let color = try? decodeColor(values, .reactionActiveBg) { reactionActiveBackground = color } else { reactionActiveBackground = try decodeColor(values, .__workaroundNonexistingKey, fallbackKey: "\(fallbackKeyPrefix).accentControl") } let reactionActiveForeground: UIColor if let color = try? decodeColor(values, .reactionActiveFg) { reactionActiveForeground = color } else { reactionActiveForeground = .clear } self.init( fill: fill, highlightedFill: try decodeColor(values, .highlightedBg), stroke: try decodeColor(values, .stroke), shadow: try? values.decode(PresentationThemeBubbleShadow.self, forKey: .shadow), reactionInactiveBackground: reactionInactiveBackground, reactionInactiveForeground: reactionInactiveForeground, reactionActiveBackground: reactionActiveBackground, reactionActiveForeground: reactionActiveForeground, reactionStarsInactiveBackground: reactionInactiveBackground, reactionStarsInactiveForeground: reactionInactiveForeground, reactionStarsActiveBackground: reactionActiveBackground, reactionStarsActiveForeground: reactionActiveForeground, reactionInactiveMediaPlaceholder: reactionInactiveMediaPlaceholder, reactionActiveMediaPlaceholder: reactionActiveMediaPlaceholder ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) if self.fill.count <= 2 { if self.fill.count > 1 { try encodeColor(&values, self.fill[0], .bg) try encodeColor(&values, self.fill[1], .gradientBg) } else { try encodeColor(&values, self.fill[0], .bg) try encodeColor(&values, self.fill[0], .gradientBg) } } else { try encodeColorList(&values, self.fill, .bgList) } try encodeColor(&values, self.highlightedFill, .highlightedBg) try encodeColor(&values, self.stroke, .stroke) try encodeColor(&values, self.reactionInactiveBackground, .reactionInactiveBg) try encodeColor(&values, self.reactionInactiveForeground, .reactionInactiveFg) try encodeColor(&values, self.reactionActiveBackground, .reactionActiveBg) try encodeColor(&values, self.reactionActiveForeground, .reactionActiveFg) try encodeColor(&values, self.reactionInactiveMediaPlaceholder, .reactionInactiveMediaPlaceholder) try encodeColor(&values, self.reactionActiveMediaPlaceholder, .reactionActiveMediaPlaceholder) } } extension PresentationThemeBubbleColor: Codable { enum CodingKeys: String, CodingKey { case withWp case withoutWp } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(withWallpaper: try values.decode(PresentationThemeBubbleColorComponents.self, forKey: .withWp), withoutWallpaper: try values.decode(PresentationThemeBubbleColorComponents.self, forKey: .withoutWp)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.withWallpaper, forKey: .withWp) try values.encode(self.withoutWallpaper, forKey: .withoutWp) } } extension PresentationThemeVariableColor: Codable { enum CodingKeys: String, CodingKey { case withWp case withoutWp } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(withWallpaper: try decodeColor(values, .withWp), withoutWallpaper: try decodeColor(values, .withoutWp)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.withWallpaper, .withWp) try encodeColor(&values, self.withoutWallpaper, .withoutWp) } } extension PresentationThemeChatBubblePolls: Codable { enum CodingKeys: String, CodingKey { case radioButton case radioProgress case highlight case separator case bar case barIconForeground case barPositive case barNegative } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let bar = try decodeColor(values, .bar) self.init( radioButton: try decodeColor(values, .radioButton), radioProgress: try decodeColor(values, .radioProgress), highlight: try decodeColor(values, .highlight), separator: try decodeColor(values, .separator), bar: bar, barIconForeground: (try? decodeColor(values, .barIconForeground)) ?? .clear, barPositive: (try? decodeColor(values, .barPositive)) ?? bar, barNegative: (try? decodeColor(values, .barNegative)) ?? bar ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.radioButton, .radioButton) try encodeColor(&values, self.radioProgress, .radioProgress) try encodeColor(&values, self.highlight, .highlight) try encodeColor(&values, self.separator, .separator) try encodeColor(&values, self.bar, .bar) try encodeColor(&values, self.barIconForeground, .barIconForeground) try encodeColor(&values, self.barPositive, .barPositive) try encodeColor(&values, self.barNegative, .barNegative) } } extension PresentationThemePartedColors: Codable { enum CodingKeys: String, CodingKey { case bubble case primaryText case secondaryText case linkText case linkHighlight case scam case textHighlight case accentText case accentControl case mediaActiveControl case mediaInactiveControl case mediaControlInnerBg case pendingActivity case fileTitle case fileDescription case fileDuration case mediaPlaceholder case polls case actionButtonsBg case actionButtonsStroke case actionButtonsText case textSelection case textSelectionKnob case accentControlDisabled } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") let accentControlColor = try decodeColor(values, .accentControl) self.init( bubble: try values.decode(PresentationThemeBubbleColor.self, forKey: .bubble), primaryTextColor: try decodeColor(values, .primaryText), secondaryTextColor: try decodeColor(values, .secondaryText), linkTextColor: try decodeColor(values, .linkText), linkHighlightColor: try decodeColor(values, .linkHighlight), scamColor: try decodeColor(values, .scam), textHighlightColor: try decodeColor(values, .textHighlight), accentTextColor: try decodeColor(values, .accentText), accentControlColor: accentControlColor, accentControlDisabledColor: (try? decodeColor(values, .accentControlDisabled)) ?? accentControlColor.withAlphaComponent(0.5), mediaActiveControlColor: try decodeColor(values, .mediaActiveControl), mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl), mediaControlInnerBackgroundColor: try decodeColor(values, .mediaControlInnerBg, decoder: decoder, fallbackKey: "\(codingPath).bubble.withWp.bg"), pendingActivityColor: try decodeColor(values, .pendingActivity), fileTitleColor: try decodeColor(values, .fileTitle), fileDescriptionColor: try decodeColor(values, .fileDescription), fileDurationColor: try decodeColor(values, .fileDuration), mediaPlaceholderColor: try decodeColor(values, .mediaPlaceholder), polls: try values.decode(PresentationThemeChatBubblePolls.self, forKey: .polls), actionButtonsFillColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsBg), actionButtonsStrokeColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsStroke), actionButtonsTextColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsText), textSelectionColor: try decodeColor(values, .textSelection), textSelectionKnobColor: try decodeColor(values, .textSelectionKnob) ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.bubble, forKey: .bubble) try encodeColor(&values, self.primaryTextColor, .primaryText) try encodeColor(&values, self.secondaryTextColor, .secondaryText) try encodeColor(&values, self.linkTextColor, .linkText) try encodeColor(&values, self.linkHighlightColor, .linkHighlight) try encodeColor(&values, self.scamColor, .scam) try encodeColor(&values, self.textHighlightColor, .textHighlight) try encodeColor(&values, self.accentTextColor, .accentText) try encodeColor(&values, self.accentControlColor, .accentControl) try encodeColor(&values, self.mediaActiveControlColor, .mediaActiveControl) try encodeColor(&values, self.mediaInactiveControlColor, .mediaInactiveControl) try encodeColor(&values, self.mediaControlInnerBackgroundColor, .mediaControlInnerBg) try encodeColor(&values, self.pendingActivityColor, .pendingActivity) try encodeColor(&values, self.fileTitleColor, .fileTitle) try encodeColor(&values, self.fileDescriptionColor, .fileDescription) try encodeColor(&values, self.fileDurationColor, .fileDuration) try encodeColor(&values, self.mediaPlaceholderColor, .mediaPlaceholder) try values.encode(self.polls, forKey: .polls) try values.encode(self.actionButtonsFillColor, forKey: .actionButtonsBg) try values.encode(self.actionButtonsStrokeColor, forKey: .actionButtonsStroke) try values.encode(self.actionButtonsTextColor, forKey: .actionButtonsText) try encodeColor(&values, self.textSelectionColor, .textSelection) try encodeColor(&values, self.textSelectionKnobColor, .textSelectionKnob) } } extension PresentationThemeChatMessage: Codable { enum CodingKeys: String, CodingKey { case incoming case outgoing case freeform case infoPrimaryText case infoLinkText case outgoingCheck case mediaDateAndStatusBg case mediaDateAndStatusText case shareButtonBg case shareButtonStroke case shareButtonFg case mediaOverlayControl case selectionControl case deliveryFailed case mediaHighlightOverlay case stickerPlaceholder case stickerPlaceholderShimmer } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(incoming: try values.decode(PresentationThemePartedColors.self, forKey: .incoming), outgoing: try values.decode(PresentationThemePartedColors.self, forKey: .outgoing), freeform: try values.decode(PresentationThemeBubbleColor.self, forKey: .freeform), infoPrimaryTextColor: try decodeColor(values, .infoPrimaryText), infoLinkTextColor: try decodeColor(values, .infoLinkText), outgoingCheckColor: try decodeColor(values, .outgoingCheck), mediaDateAndStatusFillColor: try decodeColor(values, .mediaDateAndStatusBg), mediaDateAndStatusTextColor: try decodeColor(values, .mediaDateAndStatusText), shareButtonFillColor: try values.decode(PresentationThemeVariableColor.self, forKey: .shareButtonBg), shareButtonStrokeColor: try values.decode(PresentationThemeVariableColor.self, forKey: .shareButtonStroke), shareButtonForegroundColor: try values.decode(PresentationThemeVariableColor.self, forKey: .shareButtonFg), mediaOverlayControlColors: try values.decode(PresentationThemeFillForeground.self, forKey: .mediaOverlayControl), selectionControlColors: try values.decode(PresentationThemeFillStrokeForeground.self, forKey: .selectionControl), deliveryFailedColors: try values.decode(PresentationThemeFillForeground.self, forKey: .deliveryFailed), mediaHighlightOverlayColor: try decodeColor(values, .mediaHighlightOverlay), stickerPlaceholderColor: try values.decode(PresentationThemeVariableColor.self, forKey: .stickerPlaceholder), stickerPlaceholderShimmerColor: try values.decode(PresentationThemeVariableColor.self, forKey: .stickerPlaceholderShimmer) ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.incoming, forKey: .incoming) try values.encode(self.outgoing, forKey: .outgoing) try values.encode(self.freeform, forKey: .freeform) try encodeColor(&values, self.infoPrimaryTextColor, .infoPrimaryText) try encodeColor(&values, self.infoLinkTextColor, .infoLinkText) try encodeColor(&values, self.outgoingCheckColor, .outgoingCheck) try encodeColor(&values, self.mediaDateAndStatusFillColor, .mediaDateAndStatusBg) try encodeColor(&values, self.mediaDateAndStatusTextColor, .mediaDateAndStatusText) try values.encode(self.shareButtonFillColor, forKey: .shareButtonBg) try values.encode(self.shareButtonStrokeColor, forKey: .shareButtonStroke) try values.encode(self.shareButtonForegroundColor, forKey: .shareButtonFg) try values.encode(self.mediaOverlayControlColors, forKey: .mediaOverlayControl) try values.encode(self.selectionControlColors, forKey: .selectionControl) try values.encode(self.deliveryFailedColors, forKey: .deliveryFailed) try encodeColor(&values, self.mediaHighlightOverlayColor, .mediaHighlightOverlay) try values.encode(self.stickerPlaceholderColor, forKey: .stickerPlaceholder) try values.encode(self.stickerPlaceholderShimmerColor, forKey: .stickerPlaceholderShimmer) } } extension PresentationThemeServiceMessageColorComponents: Codable { enum CodingKeys: String, CodingKey { case bg case primaryText case linkHighlight case scam case dateFillStatic case dateFillFloat } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(fill: try decodeColor(values, .bg), primaryText: try decodeColor(values, .primaryText), linkHighlight: try decodeColor(values, .linkHighlight), scam: try decodeColor(values, .scam), dateFillStatic: try decodeColor(values, .dateFillStatic), dateFillFloating: try decodeColor(values, .dateFillFloat)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.fill, .bg) try encodeColor(&values, self.primaryText, .primaryText) try encodeColor(&values, self.linkHighlight, .linkHighlight) try encodeColor(&values, self.scam, .scam) try encodeColor(&values, self.dateFillStatic, .dateFillStatic) try encodeColor(&values, self.dateFillFloating, .dateFillFloat) } } extension PresentationThemeServiceMessageColor: Codable { enum CodingKeys: String, CodingKey { case withDefaultWp case withCustomWp } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(withDefaultWallpaper: try values.decode(PresentationThemeServiceMessageColorComponents.self, forKey: .withDefaultWp), withCustomWallpaper: try values.decode(PresentationThemeServiceMessageColorComponents.self, forKey: .withCustomWp)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.withDefaultWallpaper, forKey: .withDefaultWp) try values.encode(self.withCustomWallpaper, forKey: .withCustomWp) } } extension PresentationThemeServiceMessage: Codable { enum CodingKeys: String, CodingKey { case components case unreadBarBg case unreadBarStroke case unreadBarText case dateText } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(components: try values.decode(PresentationThemeServiceMessageColor.self, forKey: .components), unreadBarFillColor: try decodeColor(values, .unreadBarBg), unreadBarStrokeColor: try decodeColor(values, .unreadBarStroke), unreadBarTextColor: try decodeColor(values, .unreadBarText), dateTextColor: try values.decode(PresentationThemeVariableColor.self, forKey: .dateText)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.components, forKey: .components) try encodeColor(&values, self.unreadBarFillColor, .unreadBarBg) try encodeColor(&values, self.unreadBarStrokeColor, .unreadBarStroke) try encodeColor(&values, self.unreadBarTextColor, .unreadBarText) try values.encode(self.dateTextColor, forKey: .dateText) } } extension PresentationThemeChatInputPanelMediaRecordingControl: Codable { enum CodingKeys: String, CodingKey { case button case micLevel case activeIcon } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(buttonColor: try decodeColor(values, .button), micLevelColor: try decodeColor(values, .micLevel), activeIconColor: try decodeColor(values, .activeIcon)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.buttonColor, .button) try encodeColor(&values, self.micLevelColor, .micLevel) try encodeColor(&values, self.activeIconColor, .activeIcon) } } extension PresentationThemeChatInputPanel: Codable { enum CodingKeys: String, CodingKey { case panelBg case panelBgNoWallpaper case panelSeparator case panelControlAccent case panelControl case panelControlDisabled case panelControlDestructive case inputBg case inputStroke case inputPlaceholder case inputText case inputControl case actionControlBg case actionControlFg case primaryText case secondaryText case mediaRecordDot case mediaRecordControl } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") self.init(panelBackgroundColor: try decodeColor(values, .panelBg), panelBackgroundColorNoWallpaper: try decodeColor(values, .panelBg, decoder: decoder, fallbackKey: "\(codingPath).panelBgNoWallpaper"), panelSeparatorColor: try decodeColor(values, .panelSeparator), panelControlAccentColor: try decodeColor(values, .panelControlAccent), panelControlColor: try decodeColor(values, .panelControl), panelControlDisabledColor: try decodeColor(values, .panelControlDisabled), panelControlDestructiveColor: try decodeColor(values, .panelControlDestructive), inputBackgroundColor: try decodeColor(values, .inputBg), inputStrokeColor: try decodeColor(values, .inputStroke), inputPlaceholderColor: try decodeColor(values, .inputPlaceholder), inputTextColor: try decodeColor(values, .inputText), inputControlColor: try decodeColor(values, .inputControl), actionControlFillColor: try decodeColor(values, .actionControlBg), actionControlForegroundColor: try decodeColor(values, .actionControlFg), primaryTextColor: try decodeColor(values, .primaryText), secondaryTextColor: try decodeColor(values, .secondaryText), mediaRecordingDotColor: try decodeColor(values, .mediaRecordDot), mediaRecordingControl: try values.decode(PresentationThemeChatInputPanelMediaRecordingControl.self, forKey: .mediaRecordControl)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.panelBackgroundColor, .panelBg) try encodeColor(&values, self.panelSeparatorColor, .panelSeparator) try encodeColor(&values, self.panelControlAccentColor, .panelControlAccent) try encodeColor(&values, self.panelControlColor, .panelControl) try encodeColor(&values, self.panelControlDisabledColor, .panelControlDisabled) try encodeColor(&values, self.panelControlDestructiveColor, .panelControlDestructive) try encodeColor(&values, self.inputBackgroundColor, .inputBg) try encodeColor(&values, self.inputStrokeColor, .inputStroke) try encodeColor(&values, self.inputPlaceholderColor, .inputPlaceholder) try encodeColor(&values, self.inputTextColor, .inputText) try encodeColor(&values, self.inputControlColor, .inputControl) try encodeColor(&values, self.actionControlFillColor, .actionControlBg) try encodeColor(&values, self.actionControlForegroundColor, .actionControlFg) try encodeColor(&values, self.primaryTextColor, .primaryText) try encodeColor(&values, self.secondaryTextColor, .secondaryText) try encodeColor(&values, self.mediaRecordingDotColor, .mediaRecordDot) try values.encode(self.mediaRecordingControl, forKey: .mediaRecordControl) } } extension PresentationThemeInputMediaPanel: Codable { enum CodingKeys: String, CodingKey { case panelSeparator case panelIcon case panelHighlightedIconBg case panelHighlightedIcon case panelContentVibrantOverlay case panelContentControlVibrantOverlay case panelContentControlVibrantSelection case panelContentControlOpaqueOverlay case panelContentControlOpaqueSelection case stickersBg case stickersSectionText case stickersSearchBg case stickersSearchPlaceholder case stickersSearchPrimary case stickersSearchControl case gifsBg case bg case panelContentVibrantSearchOverlay case panelContentVibrantSearchOverlaySelected case panelContentVibrantSearchOverlayHighlight case panelContentOpaqueSearchOverlay case panelContentOpaqueSearchOverlaySelected case panelContentOpaqueSearchOverlayHighlight } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let backgroundColor: UIColor if let value = try? decodeColor(values, .bg) { backgroundColor = value } else { backgroundColor = try decodeColor(values, .gifsBg).withMultipliedAlpha(0.75) } let panelHighlightedIconColor: UIColor if let value = try? decodeColor(values, .panelHighlightedIcon) { panelHighlightedIconColor = value } else if let value = try? decodeColor(values, .panelHighlightedIcon, fallbackKey: "chat.inputPanel.inputText") { let defaultColor = try decodeColor(values, .panelIcon) panelHighlightedIconColor = defaultColor.mixedWith(value, alpha: 0.35) } else { panelHighlightedIconColor = try decodeColor(values, .panelIcon) } let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") self.init(panelSeparatorColor: try decodeColor(values, .panelSeparator), panelIconColor: try decodeColor(values, .panelIcon), panelHighlightedIconBackgroundColor: try decodeColor(values, .panelHighlightedIconBg), panelHighlightedIconColor: panelHighlightedIconColor, panelContentVibrantOverlayColor: try decodeColor(values, .panelContentVibrantOverlay, fallbackKey: "\(codingPath).stickersSectionText"), panelContentControlVibrantOverlayColor: try decodeColor(values, .panelContentControlVibrantOverlay, fallbackKey: "\(codingPath).stickersSectionText"), panelContentControlVibrantSelectionColor: try decodeColor(values, .panelContentControlVibrantSelection, fallbackKey: "\(codingPath).stickersSectionText"), panelContentControlOpaqueOverlayColor: try decodeColor(values, .panelContentControlOpaqueOverlay, fallbackKey: "\(codingPath).stickersSectionText"), panelContentControlOpaqueSelectionColor: try decodeColor(values, .panelContentControlOpaqueSelection, fallbackKey: "\(codingPath).stickersSectionText"), panelContentVibrantSearchOverlayColor: try decodeColor(values, .panelContentVibrantSearchOverlay, fallbackKey: "\(codingPath).stickersSectionText"), panelContentVibrantSearchOverlaySelectedColor: try decodeColor(values, .panelContentVibrantSearchOverlaySelected, fallbackKey: "\(codingPath).stickersSectionText"), panelContentVibrantSearchOverlayHighlightColor: try decodeColor(values, .panelContentVibrantSearchOverlayHighlight, fallbackKey: "\(codingPath).panelHighlightedIconBg"), panelContentOpaqueSearchOverlayColor: try decodeColor(values, .panelContentOpaqueSearchOverlay, fallbackKey: "\(codingPath).stickersSectionText"), panelContentOpaqueSearchOverlaySelectedColor: try decodeColor(values, .panelContentOpaqueSearchOverlaySelected, fallbackKey: "\(codingPath).stickersSectionText"), panelContentOpaqueSearchOverlayHighlightColor: try decodeColor(values, .panelContentOpaqueSearchOverlayHighlight, fallbackKey: "\(codingPath).panelHighlightedIconBg"), stickersBackgroundColor: try decodeColor(values, .stickersBg), stickersSectionTextColor: try decodeColor(values, .stickersSectionText), stickersSearchBackgroundColor: try decodeColor(values, .stickersSearchBg), stickersSearchPlaceholderColor: try decodeColor(values, .stickersSearchPlaceholder), stickersSearchPrimaryColor: try decodeColor(values, .stickersSearchPrimary), stickersSearchControlColor: try decodeColor(values, .stickersSearchControl), gifsBackgroundColor: try decodeColor(values, .gifsBg), backgroundColor: backgroundColor) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.panelSeparatorColor, .panelSeparator) try encodeColor(&values, self.panelIconColor, .panelIcon) try encodeColor(&values, self.panelHighlightedIconBackgroundColor, .panelHighlightedIconBg) try encodeColor(&values, self.panelHighlightedIconColor, .panelHighlightedIcon) try encodeColor(&values, self.panelContentVibrantOverlayColor, .panelContentVibrantOverlay) try encodeColor(&values, self.panelContentControlVibrantOverlayColor, .panelContentControlVibrantOverlay) try encodeColor(&values, self.panelContentControlVibrantSelectionColor, .panelContentControlVibrantSelection) try encodeColor(&values, self.panelContentControlOpaqueOverlayColor, .panelContentControlOpaqueOverlay) try encodeColor(&values, self.panelContentControlOpaqueSelectionColor, .panelContentControlOpaqueSelection) try encodeColor(&values, self.panelContentVibrantSearchOverlayColor, .panelContentVibrantSearchOverlay) try encodeColor(&values, self.panelContentVibrantSearchOverlaySelectedColor, .panelContentVibrantSearchOverlaySelected) try encodeColor(&values, self.panelContentVibrantSearchOverlayHighlightColor, .panelContentVibrantSearchOverlayHighlight) try encodeColor(&values, self.panelContentOpaqueSearchOverlayColor, .panelContentOpaqueSearchOverlay) try encodeColor(&values, self.panelContentOpaqueSearchOverlaySelectedColor, .panelContentOpaqueSearchOverlaySelected) try encodeColor(&values, self.panelContentOpaqueSearchOverlayHighlightColor, .panelContentOpaqueSearchOverlayHighlight) try encodeColor(&values, self.stickersBackgroundColor, .stickersBg) try encodeColor(&values, self.stickersSectionTextColor, .stickersSectionText) try encodeColor(&values, self.stickersSearchBackgroundColor, .stickersSearchBg) try encodeColor(&values, self.stickersSearchPlaceholderColor, .stickersSearchPlaceholder) try encodeColor(&values, self.stickersSearchPrimaryColor, .stickersSearchPrimary) try encodeColor(&values, self.stickersSearchControlColor, .stickersSearchControl) try encodeColor(&values, self.gifsBackgroundColor, .gifsBg) try encodeColor(&values, self.backgroundColor, .bg) } } extension PresentationThemeInputButtonPanel: Codable { enum CodingKeys: String, CodingKey { case panelBg case panelSeparator case buttonBg case buttonHighlight case buttonStroke case buttonHighlightedBg case buttonHighlightedStroke case buttonText } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(panelSeparatorColor: try decodeColor(values, .panelSeparator), panelBackgroundColor: try decodeColor(values, .panelBg), buttonFillColor: try decodeColor(values, .buttonBg), buttonHighlightColor: try decodeColor(values, .buttonHighlight), buttonStrokeColor: try decodeColor(values, .buttonStroke), buttonHighlightedFillColor: try decodeColor(values, .buttonHighlightedBg), buttonHighlightedStrokeColor: try decodeColor(values, .buttonHighlightedStroke), buttonTextColor: try decodeColor(values, .buttonText)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.panelBackgroundColor, .panelBg) try encodeColor(&values, self.panelSeparatorColor, .panelSeparator) try encodeColor(&values, self.buttonFillColor, .buttonBg) try encodeColor(&values, self.buttonHighlightColor, .buttonHighlight) try encodeColor(&values, self.buttonStrokeColor, .buttonStroke) try encodeColor(&values, self.buttonHighlightedFillColor, .buttonHighlightedBg) try encodeColor(&values, self.buttonHighlightedStrokeColor, .buttonHighlightedStroke) try encodeColor(&values, self.buttonTextColor, .buttonText) } } extension PresentationThemeChatHistoryNavigation: Codable { enum CodingKeys: String, CodingKey { case bg case stroke case fg case badgeBg case badgeStroke case badgeText } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(fillColor: try decodeColor(values, .bg), strokeColor: try decodeColor(values, .stroke), foregroundColor: try decodeColor(values, .fg), badgeBackgroundColor: try decodeColor(values, .badgeBg), badgeStrokeColor: try decodeColor(values, .badgeStroke), badgeTextColor: try decodeColor(values, .badgeText)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.fillColor, .bg) try encodeColor(&values, self.strokeColor, .stroke) try encodeColor(&values, self.foregroundColor, .fg) try encodeColor(&values, self.badgeBackgroundColor, .badgeBg) try encodeColor(&values, self.badgeStrokeColor, .badgeStroke) try encodeColor(&values, self.badgeTextColor, .badgeText) } } extension PresentationThemeChat: Codable { enum CodingKeys: String, CodingKey { case defaultWallpaper case message case animateMessageColors case serviceMessage case inputPanel case inputMediaPanel case inputButtonPanel case historyNav } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) var wallpaper = (try values.decode(TelegramWallpaperStandardizedCodable.self, forKey: .defaultWallpaper)).value if let decoder = decoder as? PresentationThemeDecoding { if case .file = wallpaper, let resolvedWallpaper = decoder.resolvedWallpaper { wallpaper = resolvedWallpaper } } self.init(defaultWallpaper: wallpaper, animateMessageColors: (try? values.decode(Bool.self, forKey: .animateMessageColors)) ?? false, message: try values.decode(PresentationThemeChatMessage.self, forKey: .message), serviceMessage: try values.decode(PresentationThemeServiceMessage.self, forKey: .serviceMessage), inputPanel: try values.decode(PresentationThemeChatInputPanel.self, forKey: .inputPanel), inputMediaPanel: try values.decode(PresentationThemeInputMediaPanel.self, forKey: .inputMediaPanel), inputButtonPanel: try values.decode(PresentationThemeInputButtonPanel.self, forKey: .inputButtonPanel), historyNavigation: try values.decode(PresentationThemeChatHistoryNavigation.self, forKey: .historyNav)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(TelegramWallpaperStandardizedCodable(self.defaultWallpaper), forKey: .defaultWallpaper) try values.encode(self.animateMessageColors, forKey: .animateMessageColors) try values.encode(self.message, forKey: .message) try values.encode(self.serviceMessage, forKey: .serviceMessage) try values.encode(self.inputPanel, forKey: .inputPanel) try values.encode(self.inputMediaPanel, forKey: .inputMediaPanel) try values.encode(self.inputButtonPanel, forKey: .inputButtonPanel) try values.encode(self.historyNavigation, forKey: .historyNav) } } extension PresentationThemeExpandedNotificationNavigationBar: Codable { enum CodingKeys: String, CodingKey { case background case primaryText case control case separator } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(backgroundColor: try decodeColor(values, .background), primaryTextColor: try decodeColor(values, .primaryText), controlColor: try decodeColor(values, .control), separatorColor: try decodeColor(values, .separator)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.backgroundColor, .background) try encodeColor(&values, self.primaryTextColor, .primaryText) try encodeColor(&values, self.controlColor, .control) try encodeColor(&values, self.separatorColor, .separator) } } extension PresentationThemeExpandedNotification: Codable { enum CodingKeys: String, CodingKey { case bgType case navBar } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(backgroundType: try values.decode(PresentationThemeExpandedNotificationBackgroundType.self, forKey: .bgType), navigationBar: try values.decode(PresentationThemeExpandedNotificationNavigationBar.self, forKey: .navBar)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try values.encode(self.backgroundType, forKey: .bgType) try values.encode(self.navigationBar, forKey: .navBar) } } extension PresentationThemeContextMenu: Codable { enum CodingKeys: String, CodingKey { case dim case background case itemSeparator case sectionSeparator case itemBg case itemHighlightedBg case primary case secondary case destructive case badgeFill case badgeForeground case badgeInactiveFill case badgeInactiveForeground case extractedTint } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let destructiveColor = try decodeColor(values, .destructive) let backgroundColor = try decodeColor(values, .background) self.init( dimColor: try decodeColor(values, .dim), backgroundColor: backgroundColor, itemSeparatorColor: try decodeColor(values, .itemSeparator), sectionSeparatorColor: try decodeColor(values, .sectionSeparator), itemBackgroundColor: try decodeColor(values, .itemBg), itemHighlightedBackgroundColor: try decodeColor(values, .itemHighlightedBg), primaryColor: try decodeColor(values, .primary), secondaryColor: try decodeColor(values, .secondary), destructiveColor: destructiveColor, badgeFillColor: (try? decodeColor(values, .badgeFill)) ?? destructiveColor, badgeForegroundColor: (try? decodeColor(values, .badgeForeground)) ?? backgroundColor, badgeInactiveFillColor: (try? decodeColor(values, .badgeInactiveFill)) ?? destructiveColor, badgeInactiveForegroundColor: (try? decodeColor(values, .badgeInactiveForeground)) ?? backgroundColor, extractedContentTintColor: (try? decodeColor(values, .extractedTint)) ?? backgroundColor ) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.dimColor, .dim) try encodeColor(&values, self.backgroundColor, .background) try encodeColor(&values, self.itemSeparatorColor, .itemSeparator) try encodeColor(&values, self.sectionSeparatorColor, .sectionSeparator) try encodeColor(&values, self.itemBackgroundColor, .itemBg) try encodeColor(&values, self.itemHighlightedBackgroundColor, .itemHighlightedBg) try encodeColor(&values, self.primaryColor, .primary) try encodeColor(&values, self.secondaryColor, .secondary) try encodeColor(&values, self.destructiveColor, .destructive) } } extension PresentationThemeInAppNotification: Codable { enum CodingKeys: String, CodingKey { case bg case primaryText case expanded } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(fillColor: try decodeColor(values, .bg), primaryTextColor: try decodeColor(values, .primaryText), expandedNotification: try values.decode(PresentationThemeExpandedNotification.self, forKey: .expanded)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.fillColor, .bg) try encodeColor(&values, self.primaryTextColor, .primaryText) try values.encode(self.expandedNotification, forKey: .expanded) } } extension PresentationThemeChart: Codable { enum CodingKeys: String, CodingKey { case labels case helperLines case strongLines case barStrongLines case detailsText case detailsArrow case detailsView case rangeViewFrame case rangeViewMarker } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.init(labelsColor: try decodeColor(values, .labels), helperLinesColor: try decodeColor(values, .helperLines), strongLinesColor: try decodeColor(values, .strongLines), barStrongLinesColor: try decodeColor(values, .barStrongLines), detailsTextColor: try decodeColor(values, .detailsText), detailsArrowColor: try decodeColor(values, .detailsArrow), detailsViewColor: try decodeColor(values, .detailsView), rangeViewFrameColor: try decodeColor(values, .rangeViewFrame), rangeViewMarkerColor: try decodeColor(values, .rangeViewMarker)) } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) try encodeColor(&values, self.labelsColor, .labels) try encodeColor(&values, self.helperLinesColor, .helperLines) try encodeColor(&values, self.strongLinesColor, .strongLines) try encodeColor(&values, self.barStrongLinesColor, .barStrongLines) try encodeColor(&values, self.detailsTextColor, .detailsText) try encodeColor(&values, self.detailsArrowColor, .detailsArrow) try encodeColor(&values, self.detailsViewColor, .detailsView) try encodeColor(&values, self.rangeViewFrameColor, .rangeViewFrame) try encodeColor(&values, self.rangeViewMarkerColor, .rangeViewMarker) } } extension PresentationThemeName: Codable { public init(from decoder: Decoder) throws { let value = try decoder.singleValueContainer() self = .custom(try value.decode(String.self)) } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case let .builtin(name): switch name { case .day: try container.encode("Day") case .dayClassic: try container.encode("Classic") case .nightAccent: try container.encode("Night Tinted") case .night: try container.encode("Night") } case let .custom(name): try container.encode(name) } } } extension PresentationBuiltinThemeReference: Codable { public init(from decoder: Decoder) throws { let values = try decoder.singleValueContainer() if let value = try? values.decode(String.self) { switch value.lowercased() { case "day": self = .day case "classic": self = .dayClassic case "nighttinted": self = .nightAccent case "night": self = .night default: self = .dayClassic } } else { self = .dayClassic } } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .day: try container.encode("day") case .dayClassic: try container.encode("classic") case .nightAccent: try container.encode("nighttinted") case .night: try container.encode("night") } } } extension PresentationTheme: Codable { enum CodingKeys: String, CodingKey { case name case basedOn case dark case intro case passcode case root case list case chatList case chat case actionSheet case contextMenu case notification case chart } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let referenceTheme: PresentationBuiltinThemeReference if let theme = try? values.decode(PresentationBuiltinThemeReference.self, forKey: .basedOn) { referenceTheme = theme } else { referenceTheme = .dayClassic } let index: Int64 if let decoder = decoder as? PresentationThemeDecoding { let serviceBackgroundColor = decoder.serviceBackgroundColor ?? defaultServiceBackgroundColor decoder.referenceTheme = makeDefaultPresentationTheme(reference: referenceTheme, serviceBackgroundColor: serviceBackgroundColor) index = decoder.reference?.index ?? Int64.random(in: Int64.min ... Int64.max) } else { index = Int64.random(in: Int64.min ... Int64.max) } self.init(name: (try? values.decode(PresentationThemeName.self, forKey: .name)) ?? .custom("Untitled"), index: index, referenceTheme: referenceTheme, overallDarkAppearance: (try? values.decode(Bool.self, forKey: .dark)) ?? false, intro: try values.decode(PresentationThemeIntro.self, forKey: .intro), passcode: try values.decode(PresentationThemePasscode.self, forKey: .passcode), rootController: try values.decode(PresentationThemeRootController.self, forKey: .root), list: try values.decode(PresentationThemeList.self, forKey: .list), chatList: try values.decode(PresentationThemeChatList.self, forKey: .chatList), chat: try values.decode(PresentationThemeChat.self, forKey: .chat), actionSheet: try values.decode(PresentationThemeActionSheet.self, forKey: .actionSheet), contextMenu: try values.decode(PresentationThemeContextMenu.self, forKey: .contextMenu), inAppNotification: try values.decode(PresentationThemeInAppNotification.self, forKey: .notification), chart: try values.decode(PresentationThemeChart.self, forKey: .chart) ) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.name, forKey: .name) try container.encode(self.referenceTheme, forKey: .basedOn) try container.encode(self.overallDarkAppearance, forKey: .dark) try container.encode(self.intro, forKey: .intro) try container.encode(self.passcode, forKey: .passcode) try container.encode(self.rootController, forKey: .root) try container.encode(self.list, forKey: .list) try container.encode(self.chatList, forKey: .chatList) try container.encode(self.chat, forKey: .chat) try container.encode(self.actionSheet, forKey: .actionSheet) try container.encode(self.contextMenu, forKey: .contextMenu) try container.encode(self.inAppNotification, forKey: .notification) try container.encode(self.chart, forKey: .chart) } }