Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "TelegramUIPreferences",
module_name = "TelegramUIPreferences",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/TelegramCore:TelegramCore",
"//submodules/Postbox:Postbox",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
],
visibility = [
"//visibility:public",
],
)
@@ -0,0 +1,67 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public struct CallListSettings: Codable, Equatable {
public var _showTab: Bool?
public static var defaultSettings: CallListSettings {
return CallListSettings(showTab: nil)
}
public var showTab: Bool {
get {
if let value = self._showTab {
return value
} else {
return true
}
} set {
self._showTab = newValue
}
}
public init(showTab: Bool?) {
self._showTab = showTab
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
if let value = try container.decodeIfPresent(Int32.self, forKey: "showTab") {
self._showTab = value != 0
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
if let showTab = self._showTab {
try container.encode((showTab ? 1 : 0) as Int32, forKey: "showTab")
} else {
try container.encodeNil(forKey: "showTab")
}
}
public static func ==(lhs: CallListSettings, rhs: CallListSettings) -> Bool {
return lhs._showTab == rhs._showTab
}
public func withUpdatedShowTab(_ showTab: Bool) -> CallListSettings {
return CallListSettings(showTab: showTab)
}
}
public func updateCallListSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (CallListSettings) -> CallListSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.callListSettings, { entry in
let currentSettings: CallListSettings
if let entry = entry?.get(CallListSettings.self) {
currentSettings = entry
} else {
currentSettings = CallListSettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,47 @@
import Foundation
import SwiftSignalKit
import TelegramCore
public struct ChatArchiveSettings: Equatable, Codable {
public var isHiddenByDefault: Bool
public var hiddenPsaPeerId: EnginePeer.Id?
public static var `default`: ChatArchiveSettings {
return ChatArchiveSettings(isHiddenByDefault: false, hiddenPsaPeerId: nil)
}
public init(isHiddenByDefault: Bool, hiddenPsaPeerId: EnginePeer.Id?) {
self.isHiddenByDefault = isHiddenByDefault
self.hiddenPsaPeerId = hiddenPsaPeerId
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.isHiddenByDefault = (try container.decode(Int32.self, forKey: "isHiddenByDefault")) != 0
self.hiddenPsaPeerId = (try container.decodeIfPresent(Int64.self, forKey: "hiddenPsaPeerId")).flatMap(EnginePeer.Id.init)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.isHiddenByDefault ? 1 : 0) as Int32, forKey: "isHiddenByDefault")
if let hiddenPsaPeerId = self.hiddenPsaPeerId {
try container.encode(hiddenPsaPeerId.toInt64(), forKey: "hiddenPsaPeerId")
} else {
try container.encodeNil(forKey: "hiddenPsaPeerId")
}
}
}
public func updateChatArchiveSettings(engine: TelegramEngine, _ f: @escaping (ChatArchiveSettings) -> ChatArchiveSettings) -> Signal<Never, NoError> {
return engine.preferences.update(id: ApplicationSpecificPreferencesKeys.chatArchiveSettings, { entry in
let currentSettings: ChatArchiveSettings
if let entry = entry?.get(ChatArchiveSettings.self) {
currentSettings = entry
} else {
currentSettings = .default
}
return SharedPreferencesEntry(f(currentSettings))
})
}
@@ -0,0 +1,60 @@
import Foundation
import Postbox
import TelegramCore
import SwiftSignalKit
public enum ContactsSortOrder: Int32 {
case presence
case natural
}
public enum PresentationPersonNameOrder: Int32 {
case firstLast = 0
case lastFirst = 1
}
public struct ContactSynchronizationSettings: Equatable, Codable {
public var _legacySynchronizeDeviceContacts: Bool
public var nameDisplayOrder: PresentationPersonNameOrder
public var sortOrder: ContactsSortOrder
public static var defaultSettings: ContactSynchronizationSettings {
return ContactSynchronizationSettings(_legacySynchronizeDeviceContacts: true, nameDisplayOrder: .firstLast, sortOrder: .presence)
}
public init(_legacySynchronizeDeviceContacts: Bool, nameDisplayOrder: PresentationPersonNameOrder, sortOrder: ContactsSortOrder) {
self._legacySynchronizeDeviceContacts = _legacySynchronizeDeviceContacts
self.nameDisplayOrder = nameDisplayOrder
self.sortOrder = sortOrder
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self._legacySynchronizeDeviceContacts = (try container.decode(Int32.self, forKey: "synchronizeDeviceContacts")) != 0
self.nameDisplayOrder = PresentationPersonNameOrder(rawValue: try container.decode(Int32.self, forKey: "nameDisplayOrder")) ?? .firstLast
self.sortOrder = ContactsSortOrder(rawValue: try container.decode(Int32.self, forKey: "sortOrder")) ?? .presence
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self._legacySynchronizeDeviceContacts ? 1 : 0) as Int32, forKey: "synchronizeDeviceContacts")
try container.encode(self.nameDisplayOrder.rawValue, forKey: "nameDisplayOrder")
try container.encode(self.sortOrder.rawValue, forKey: "sortOrder")
}
}
public func updateContactSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (ContactSynchronizationSettings) -> ContactSynchronizationSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings, { entry in
let currentSettings: ContactSynchronizationSettings
if let entry = entry?.get(ContactSynchronizationSettings.self) {
currentSettings = entry
} else {
currentSettings = .defaultSettings
}
return PreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,36 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public struct ExperimentalSettings: Codable, Equatable {
public static var defaultSettings: ExperimentalSettings {
return ExperimentalSettings()
}
public init() {
}
public init(from decoder: Decoder) throws {
}
public func encode(to encoder: Encoder) throws {
}
public static func ==(lhs: ExperimentalSettings, rhs: ExperimentalSettings) -> Bool {
return true
}
}
public func updateExperimentalSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (ExperimentalSettings) -> ExperimentalSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalSettings, { entry in
let currentSettings: ExperimentalSettings
if let entry = entry?.get(ExperimentalSettings.self) {
currentSettings = entry
} else {
currentSettings = ExperimentalSettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,331 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public struct ExperimentalUISettings: Codable, Equatable {
public struct AccountReactionOverrides: Equatable, Codable {
public struct Item: Equatable, Codable {
public var key: MessageReaction.Reaction
public var messageId: EngineMessage.Id
public var mediaId: EngineMedia.Id
public init(key: MessageReaction.Reaction, messageId: EngineMessage.Id, mediaId: EngineMedia.Id) {
self.key = key
self.messageId = messageId
self.mediaId = mediaId
}
}
public var accountId: Int64
public var items: [Item]
public init(accountId: Int64, items: [Item]) {
self.accountId = accountId
self.items = items
}
}
public var keepChatNavigationStack: Bool
public var skipReadHistory: Bool
public var alwaysDisplayTyping: Bool
public var crashOnLongQueries: Bool
public var chatListPhotos: Bool
public var knockoutWallpaper: Bool
public var foldersTabAtBottom: Bool
public var playerEmbedding: Bool
public var preferredVideoCodec: String?
public var disableVideoAspectScaling: Bool
public var enableVoipTcp: Bool
public var experimentalCompatibility: Bool
public var enableDebugDataDisplay: Bool
public var fakeGlass: Bool
public var compressedEmojiCache: Bool
public var localTranscription: Bool
public var enableReactionOverrides: Bool
public var browserExperiment: Bool
public var accountReactionEffectOverrides: [AccountReactionOverrides]
public var accountStickerEffectOverrides: [AccountReactionOverrides]
public var disableQuickReaction: Bool
public var disableLanguageRecognition: Bool
public var disableImageContentAnalysis: Bool
public var disableBackgroundAnimation: Bool
public var logLanguageRecognition: Bool
public var storiesExperiment: Bool
public var storiesJpegExperiment: Bool
public var crashOnMemoryPressure: Bool
public var dustEffect: Bool
public var disableCallV2: Bool
public var experimentalCallMute: Bool
public var allowWebViewInspection: Bool
public var disableReloginTokens: Bool
public var liveStreamV2: Bool
public var dynamicStreaming: Bool
public var enableLocalTranslation: Bool
public var autoBenchmarkReflectors: Bool?
public var playerV2: Bool
public var devRequests: Bool
public var fakeAds: Bool
public var conferenceDebug: Bool
public var checkSerializedData: Bool
public var allForumsHaveTabs: Bool
public var debugRatingLayout: Bool
public var enableUpdates: Bool
public static var defaultSettings: ExperimentalUISettings {
return ExperimentalUISettings(
keepChatNavigationStack: false,
skipReadHistory: false,
alwaysDisplayTyping: false,
crashOnLongQueries: false,
chatListPhotos: false,
knockoutWallpaper: false,
foldersTabAtBottom: false,
playerEmbedding: false,
preferredVideoCodec: nil,
disableVideoAspectScaling: false,
enableVoipTcp: false,
experimentalCompatibility: false,
enableDebugDataDisplay: false,
fakeGlass: false,
compressedEmojiCache: false,
localTranscription: false,
enableReactionOverrides: false,
browserExperiment: false,
accountReactionEffectOverrides: [],
accountStickerEffectOverrides: [],
disableQuickReaction: false,
disableLanguageRecognition: false,
disableImageContentAnalysis: false,
disableBackgroundAnimation: false,
logLanguageRecognition: false,
storiesExperiment: false,
storiesJpegExperiment: false,
crashOnMemoryPressure: false,
dustEffect: false,
disableCallV2: false,
experimentalCallMute: false,
allowWebViewInspection: false,
disableReloginTokens: false,
liveStreamV2: false,
dynamicStreaming: false,
enableLocalTranslation: false,
autoBenchmarkReflectors: nil,
playerV2: false,
devRequests: false,
fakeAds: false,
conferenceDebug: false,
checkSerializedData: false,
allForumsHaveTabs: false,
debugRatingLayout: false,
enableUpdates: false
)
}
public init(
keepChatNavigationStack: Bool,
skipReadHistory: Bool,
alwaysDisplayTyping: Bool,
crashOnLongQueries: Bool,
chatListPhotos: Bool,
knockoutWallpaper: Bool,
foldersTabAtBottom: Bool,
playerEmbedding: Bool,
preferredVideoCodec: String?,
disableVideoAspectScaling: Bool,
enableVoipTcp: Bool,
experimentalCompatibility: Bool,
enableDebugDataDisplay: Bool,
fakeGlass: Bool,
compressedEmojiCache: Bool,
localTranscription: Bool,
enableReactionOverrides: Bool,
browserExperiment: Bool,
accountReactionEffectOverrides: [AccountReactionOverrides],
accountStickerEffectOverrides: [AccountReactionOverrides],
disableQuickReaction: Bool,
disableLanguageRecognition: Bool,
disableImageContentAnalysis: Bool,
disableBackgroundAnimation: Bool,
logLanguageRecognition: Bool,
storiesExperiment: Bool,
storiesJpegExperiment: Bool,
crashOnMemoryPressure: Bool,
dustEffect: Bool,
disableCallV2: Bool,
experimentalCallMute: Bool,
allowWebViewInspection: Bool,
disableReloginTokens: Bool,
liveStreamV2: Bool,
dynamicStreaming: Bool,
enableLocalTranslation: Bool,
autoBenchmarkReflectors: Bool?,
playerV2: Bool,
devRequests: Bool,
fakeAds: Bool,
conferenceDebug: Bool,
checkSerializedData: Bool,
allForumsHaveTabs: Bool,
debugRatingLayout: Bool,
enableUpdates: Bool
) {
self.keepChatNavigationStack = keepChatNavigationStack
self.skipReadHistory = skipReadHistory
self.alwaysDisplayTyping = alwaysDisplayTyping
self.crashOnLongQueries = crashOnLongQueries
self.chatListPhotos = chatListPhotos
self.knockoutWallpaper = knockoutWallpaper
self.foldersTabAtBottom = foldersTabAtBottom
self.playerEmbedding = playerEmbedding
self.preferredVideoCodec = preferredVideoCodec
self.disableVideoAspectScaling = disableVideoAspectScaling
self.enableVoipTcp = enableVoipTcp
self.experimentalCompatibility = experimentalCompatibility
self.enableDebugDataDisplay = enableDebugDataDisplay
self.fakeGlass = fakeGlass
self.compressedEmojiCache = compressedEmojiCache
self.localTranscription = localTranscription
self.enableReactionOverrides = enableReactionOverrides
self.browserExperiment = browserExperiment
self.accountReactionEffectOverrides = accountReactionEffectOverrides
self.accountStickerEffectOverrides = accountStickerEffectOverrides
self.disableQuickReaction = disableQuickReaction
self.disableLanguageRecognition = disableLanguageRecognition
self.disableImageContentAnalysis = disableImageContentAnalysis
self.disableBackgroundAnimation = disableBackgroundAnimation
self.logLanguageRecognition = logLanguageRecognition
self.storiesExperiment = storiesExperiment
self.storiesJpegExperiment = storiesJpegExperiment
self.crashOnMemoryPressure = crashOnMemoryPressure
self.dustEffect = dustEffect
self.disableCallV2 = disableCallV2
self.experimentalCallMute = experimentalCallMute
self.allowWebViewInspection = allowWebViewInspection
self.disableReloginTokens = disableReloginTokens
self.liveStreamV2 = liveStreamV2
self.dynamicStreaming = dynamicStreaming
self.enableLocalTranslation = enableLocalTranslation
self.autoBenchmarkReflectors = autoBenchmarkReflectors
self.playerV2 = playerV2
self.devRequests = devRequests
self.fakeAds = fakeAds
self.conferenceDebug = conferenceDebug
self.checkSerializedData = checkSerializedData
self.allForumsHaveTabs = allForumsHaveTabs
self.debugRatingLayout = debugRatingLayout
self.enableUpdates = enableUpdates
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.keepChatNavigationStack = (try container.decodeIfPresent(Int32.self, forKey: "keepChatNavigationStack") ?? 0) != 0
self.skipReadHistory = (try container.decodeIfPresent(Int32.self, forKey: "skipReadHistory") ?? 0) != 0
self.alwaysDisplayTyping = (try container.decodeIfPresent(Int32.self, forKey: "alwaysDisplayTyping") ?? 0) != 0
self.crashOnLongQueries = (try container.decodeIfPresent(Int32.self, forKey: "crashOnLongQueries") ?? 0) != 0
self.chatListPhotos = (try container.decodeIfPresent(Int32.self, forKey: "chatListPhotos") ?? 0) != 0
self.knockoutWallpaper = (try container.decodeIfPresent(Int32.self, forKey: "knockoutWallpaper") ?? 0) != 0
self.foldersTabAtBottom = (try container.decodeIfPresent(Int32.self, forKey: "foldersTabAtBottom") ?? 0) != 0
self.playerEmbedding = (try container.decodeIfPresent(Int32.self, forKey: "playerEmbedding") ?? 0) != 0
self.preferredVideoCodec = try container.decodeIfPresent(String.self.self, forKey: "preferredVideoCodec")
self.disableVideoAspectScaling = (try container.decodeIfPresent(Int32.self, forKey: "disableVideoAspectScaling") ?? 0) != 0
self.enableVoipTcp = (try container.decodeIfPresent(Int32.self, forKey: "enableVoipTcp") ?? 0) != 0
self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0
self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0
self.fakeGlass = (try container.decodeIfPresent(Int32.self, forKey: "fakeGlass") ?? 0) != 0
self.compressedEmojiCache = (try container.decodeIfPresent(Int32.self, forKey: "compressedEmojiCache") ?? 0) != 0
self.localTranscription = (try container.decodeIfPresent(Int32.self, forKey: "localTranscription") ?? 0) != 0
self.enableReactionOverrides = try container.decodeIfPresent(Bool.self, forKey: "enableReactionOverrides") ?? false
self.browserExperiment = try container.decodeIfPresent(Bool.self, forKey: "browserExperiment") ?? false
self.accountReactionEffectOverrides = (try? container.decodeIfPresent([AccountReactionOverrides].self, forKey: "accountReactionEffectOverrides")) ?? []
self.accountStickerEffectOverrides = (try? container.decodeIfPresent([AccountReactionOverrides].self, forKey: "accountStickerEffectOverrides")) ?? []
self.disableQuickReaction = try container.decodeIfPresent(Bool.self, forKey: "disableQuickReaction") ?? false
self.disableLanguageRecognition = try container.decodeIfPresent(Bool.self, forKey: "disableLanguageRecognition") ?? false
self.disableImageContentAnalysis = try container.decodeIfPresent(Bool.self, forKey: "disableImageContentAnalysis") ?? false
self.disableBackgroundAnimation = try container.decodeIfPresent(Bool.self, forKey: "disableBackgroundAnimation") ?? false
self.logLanguageRecognition = try container.decodeIfPresent(Bool.self, forKey: "logLanguageRecognition") ?? false
self.storiesExperiment = try container.decodeIfPresent(Bool.self, forKey: "storiesExperiment") ?? false
self.storiesJpegExperiment = try container.decodeIfPresent(Bool.self, forKey: "storiesJpegExperiment") ?? false
self.crashOnMemoryPressure = try container.decodeIfPresent(Bool.self, forKey: "crashOnMemoryPressure") ?? false
self.dustEffect = try container.decodeIfPresent(Bool.self, forKey: "dustEffect") ?? false
self.disableCallV2 = try container.decodeIfPresent(Bool.self, forKey: "disableCallV2") ?? false
self.experimentalCallMute = try container.decodeIfPresent(Bool.self, forKey: "experimentalCallMute") ?? false
self.allowWebViewInspection = try container.decodeIfPresent(Bool.self, forKey: "allowWebViewInspection") ?? false
self.disableReloginTokens = try container.decodeIfPresent(Bool.self, forKey: "disableReloginTokens") ?? false
self.liveStreamV2 = try container.decodeIfPresent(Bool.self, forKey: "liveStreamV2") ?? false
self.dynamicStreaming = try container.decodeIfPresent(Bool.self, forKey: "dynamicStreaming_v2") ?? false
self.enableLocalTranslation = try container.decodeIfPresent(Bool.self, forKey: "enableLocalTranslation") ?? false
self.autoBenchmarkReflectors = try container.decodeIfPresent(Bool.self, forKey: "autoBenchmarkReflectors")
self.playerV2 = try container.decodeIfPresent(Bool.self, forKey: "playerV2") ?? false
self.devRequests = try container.decodeIfPresent(Bool.self, forKey: "devRequests") ?? false
self.fakeAds = try container.decodeIfPresent(Bool.self, forKey: "fakeAds") ?? false
self.conferenceDebug = try container.decodeIfPresent(Bool.self, forKey: "conferenceDebug") ?? false
self.checkSerializedData = try container.decodeIfPresent(Bool.self, forKey: "checkSerializedData") ?? false
self.allForumsHaveTabs = try container.decodeIfPresent(Bool.self, forKey: "allForumsHaveTabs") ?? false
self.debugRatingLayout = try container.decodeIfPresent(Bool.self, forKey: "debugRatingLayout") ?? false
self.enableUpdates = try container.decodeIfPresent(Bool.self, forKey: "enableUpdates") ?? false
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.keepChatNavigationStack ? 1 : 0) as Int32, forKey: "keepChatNavigationStack")
try container.encode((self.skipReadHistory ? 1 : 0) as Int32, forKey: "skipReadHistory")
try container.encode((self.alwaysDisplayTyping ? 1 : 0) as Int32, forKey: "alwaysDisplayTyping")
try container.encode((self.crashOnLongQueries ? 1 : 0) as Int32, forKey: "crashOnLongQueries")
try container.encode((self.chatListPhotos ? 1 : 0) as Int32, forKey: "chatListPhotos")
try container.encode((self.knockoutWallpaper ? 1 : 0) as Int32, forKey: "knockoutWallpaper")
try container.encode((self.foldersTabAtBottom ? 1 : 0) as Int32, forKey: "foldersTabAtBottom")
try container.encode((self.playerEmbedding ? 1 : 0) as Int32, forKey: "playerEmbedding")
try container.encodeIfPresent(self.preferredVideoCodec, forKey: "preferredVideoCodec")
try container.encode((self.disableVideoAspectScaling ? 1 : 0) as Int32, forKey: "disableVideoAspectScaling")
try container.encode((self.enableVoipTcp ? 1 : 0) as Int32, forKey: "enableVoipTcp")
try container.encode((self.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility")
try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
try container.encode((self.fakeGlass ? 1 : 0) as Int32, forKey: "fakeGlass")
try container.encode((self.compressedEmojiCache ? 1 : 0) as Int32, forKey: "compressedEmojiCache")
try container.encode((self.localTranscription ? 1 : 0) as Int32, forKey: "localTranscription")
try container.encode(self.enableReactionOverrides, forKey: "enableReactionOverrides")
try container.encode(self.browserExperiment, forKey: "browserExperiment")
try container.encode(self.accountReactionEffectOverrides, forKey: "accountReactionEffectOverrides")
try container.encode(self.accountStickerEffectOverrides, forKey: "accountStickerEffectOverrides")
try container.encode(self.disableQuickReaction, forKey: "disableQuickReaction")
try container.encode(self.disableLanguageRecognition, forKey: "disableLanguageRecognition")
try container.encode(self.disableImageContentAnalysis, forKey: "disableImageContentAnalysis")
try container.encode(self.disableBackgroundAnimation, forKey: "disableBackgroundAnimation")
try container.encode(self.logLanguageRecognition, forKey: "logLanguageRecognition")
try container.encode(self.storiesExperiment, forKey: "storiesExperiment")
try container.encode(self.storiesJpegExperiment, forKey: "storiesJpegExperiment")
try container.encode(self.crashOnMemoryPressure, forKey: "crashOnMemoryPressure")
try container.encode(self.dustEffect, forKey: "dustEffect")
try container.encode(self.disableCallV2, forKey: "disableCallV2")
try container.encode(self.experimentalCallMute, forKey: "experimentalCallMute")
try container.encode(self.allowWebViewInspection, forKey: "allowWebViewInspection")
try container.encode(self.disableReloginTokens, forKey: "disableReloginTokens")
try container.encode(self.liveStreamV2, forKey: "liveStreamV2")
try container.encode(self.dynamicStreaming, forKey: "dynamicStreaming")
try container.encode(self.enableLocalTranslation, forKey: "enableLocalTranslation")
try container.encodeIfPresent(self.autoBenchmarkReflectors, forKey: "autoBenchmarkReflectors")
try container.encodeIfPresent(self.playerV2, forKey: "playerV2")
try container.encodeIfPresent(self.devRequests, forKey: "devRequests")
try container.encodeIfPresent(self.fakeAds, forKey: "fakeAds")
try container.encodeIfPresent(self.conferenceDebug, forKey: "conferenceDebug")
try container.encodeIfPresent(self.checkSerializedData, forKey: "checkSerializedData")
try container.encodeIfPresent(self.allForumsHaveTabs, forKey: "allForumsHaveTabs")
try container.encodeIfPresent(self.debugRatingLayout, forKey: "debugRatingLayout")
try container.encodeIfPresent(self.enableUpdates, forKey: "enableUpdates")
}
}
public func updateExperimentalUISettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (ExperimentalUISettings) -> ExperimentalUISettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { entry in
let currentSettings: ExperimentalUISettings
if let entry = entry?.get(ExperimentalUISettings.self) {
currentSettings = entry
} else {
currentSettings = .defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,53 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public struct GeneratedMediaStoreSettings: Codable, Equatable {
public let storeEditedPhotos: Bool
public let storeCapturedMedia: Bool
public static var defaultSettings: GeneratedMediaStoreSettings {
return GeneratedMediaStoreSettings(storeEditedPhotos: true, storeCapturedMedia: true)
}
public init(storeEditedPhotos: Bool, storeCapturedMedia: Bool) {
self.storeEditedPhotos = storeEditedPhotos
self.storeCapturedMedia = storeCapturedMedia
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.storeEditedPhotos = (try container.decode(Int32.self, forKey: "eph")) != 0
self.storeCapturedMedia = (try container.decode(Int32.self, forKey: "cpm")) != 0
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.storeEditedPhotos ? 1 : 0) as Int32, forKey: "eph")
try container.encode((self.storeCapturedMedia ? 1 : 0) as Int32, forKey: "cpm")
}
public static func ==(lhs: GeneratedMediaStoreSettings, rhs: GeneratedMediaStoreSettings) -> Bool {
return lhs.storeEditedPhotos == rhs.storeEditedPhotos && lhs.storeCapturedMedia == rhs.storeCapturedMedia
}
public func withUpdatedStoreEditedPhotos(_ storeEditedPhotos: Bool) -> GeneratedMediaStoreSettings {
return GeneratedMediaStoreSettings(storeEditedPhotos: storeEditedPhotos, storeCapturedMedia: self.storeCapturedMedia)
}
}
public func updateGeneratedMediaStoreSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (GeneratedMediaStoreSettings) -> GeneratedMediaStoreSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings, { entry in
let currentSettings: GeneratedMediaStoreSettings
if let entry = entry?.get(GeneratedMediaStoreSettings.self) {
currentSettings = entry
} else {
currentSettings = GeneratedMediaStoreSettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,119 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramCore
public enum TotalUnreadCountDisplayStyle: Int32 {
case filtered = 0
public var category: ChatListTotalUnreadStateCategory {
switch self {
case .filtered:
return .filtered
}
}
}
public enum TotalUnreadCountDisplayCategory: Int32 {
case chats = 0
case messages = 1
public var statsType: ChatListTotalUnreadStateStats {
switch self {
case .chats:
return .chats
case .messages:
return .messages
}
}
}
public struct InAppNotificationSettings: Codable, Equatable {
public var playSounds: Bool
public var vibrate: Bool
public var displayPreviews: Bool
public var totalUnreadCountDisplayStyle: TotalUnreadCountDisplayStyle
public var totalUnreadCountDisplayCategory: TotalUnreadCountDisplayCategory
public var totalUnreadCountIncludeTags: PeerSummaryCounterTags
public var displayNameOnLockscreen: Bool
public var displayNotificationsFromAllAccounts: Bool
public var customSound: String?
public static var defaultSettings: InAppNotificationSettings {
return InAppNotificationSettings(playSounds: true, vibrate: false, displayPreviews: true, totalUnreadCountDisplayStyle: .filtered, totalUnreadCountDisplayCategory: .messages, totalUnreadCountIncludeTags: .all, displayNameOnLockscreen: true, displayNotificationsFromAllAccounts: true, customSound: nil)
}
public init(playSounds: Bool, vibrate: Bool, displayPreviews: Bool, totalUnreadCountDisplayStyle: TotalUnreadCountDisplayStyle, totalUnreadCountDisplayCategory: TotalUnreadCountDisplayCategory, totalUnreadCountIncludeTags: PeerSummaryCounterTags, displayNameOnLockscreen: Bool, displayNotificationsFromAllAccounts: Bool, customSound: String?) {
self.playSounds = playSounds
self.vibrate = vibrate
self.displayPreviews = displayPreviews
self.totalUnreadCountDisplayStyle = totalUnreadCountDisplayStyle
self.totalUnreadCountDisplayCategory = totalUnreadCountDisplayCategory
self.totalUnreadCountIncludeTags = totalUnreadCountIncludeTags
self.displayNameOnLockscreen = displayNameOnLockscreen
self.displayNotificationsFromAllAccounts = displayNotificationsFromAllAccounts
self.customSound = customSound
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.playSounds = (try container.decode(Int32.self, forKey: "s")) != 0
self.vibrate = (try container.decode(Int32.self, forKey: "v")) != 0
self.displayPreviews = (try container.decode(Int32.self, forKey: "p")) != 0
self.totalUnreadCountDisplayStyle = TotalUnreadCountDisplayStyle(rawValue: try container.decode(Int32.self, forKey: "cds")) ?? .filtered
self.totalUnreadCountDisplayCategory = TotalUnreadCountDisplayCategory(rawValue: try container.decodeIfPresent(Int32.self, forKey: "totalUnreadCountDisplayCategory") ?? 1) ?? .messages
if let value = try container.decodeIfPresent(Int32.self, forKey: "totalUnreadCountIncludeTags_2") {
self.totalUnreadCountIncludeTags = PeerSummaryCounterTags(rawValue: value)
} else if let value = try container.decodeIfPresent(Int32.self, forKey: "totalUnreadCountIncludeTags") {
var resultTags: PeerSummaryCounterTags = []
for legacyTag in LegacyPeerSummaryCounterTags(rawValue: value) {
if legacyTag == .regularChatsAndPrivateGroups {
resultTags.insert(.contact)
resultTags.insert(.nonContact)
resultTags.insert(.bot)
resultTags.insert(.group)
} else if legacyTag == .publicGroups {
resultTags.insert(.group)
} else if legacyTag == .channels {
resultTags.insert(.channel)
}
}
self.totalUnreadCountIncludeTags = resultTags
} else {
self.totalUnreadCountIncludeTags = .all
}
self.displayNameOnLockscreen = (try container.decodeIfPresent(Int32.self, forKey: "displayNameOnLockscreen") ?? 1) != 0
self.displayNotificationsFromAllAccounts = (try container.decodeIfPresent(Int32.self, forKey: "displayNotificationsFromAllAccounts") ?? 1) != 0
self.customSound = try container.decodeIfPresent(String.self, forKey: "customSound")
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.playSounds ? 1 : 0) as Int32, forKey: "s")
try container.encode((self.vibrate ? 1 : 0) as Int32, forKey: "v")
try container.encode((self.displayPreviews ? 1 : 0) as Int32, forKey: "p")
try container.encode(self.totalUnreadCountDisplayStyle.rawValue, forKey: "cds")
try container.encode(self.totalUnreadCountDisplayCategory.rawValue, forKey: "totalUnreadCountDisplayCategory")
try container.encode(self.totalUnreadCountIncludeTags.rawValue, forKey: "totalUnreadCountIncludeTags_2")
try container.encode((self.displayNameOnLockscreen ? 1 : 0) as Int32, forKey: "displayNameOnLockscreen")
try container.encode((self.displayNotificationsFromAllAccounts ? 1 : 0) as Int32, forKey: "displayNotificationsFromAllAccounts")
try container.encodeIfPresent(self.customSound, forKey: "customSound")
}
}
public func updateInAppNotificationSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (InAppNotificationSettings) -> InAppNotificationSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings, { entry in
let currentSettings: InAppNotificationSettings
if let entry = entry?.get(InAppNotificationSettings.self) {
currentSettings = entry
} else {
currentSettings = InAppNotificationSettings.defaultSettings
}
return PreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,112 @@
import Foundation
import Postbox
import TelegramCore
import SwiftSignalKit
public enum InstantPageThemeType: Int32 {
case light = 0
case dark = 1
case sepia = 2
case gray = 3
}
public enum InstantPagePresentationFontSize: Int32 {
case xxsmall = -2
case xsmall = -1
case small = 0
case standard = 1
case large = 2
case xlarge = 3
case xxlarge = 4
}
public final class InstantPagePresentationSettings: Codable, Equatable {
public static var defaultSettings = InstantPagePresentationSettings(themeType: .light, fontSize: .standard, forceSerif: false, autoNightMode: true, ignoreAutoNightModeUntil: 0)
public var themeType: InstantPageThemeType
public var fontSize: InstantPagePresentationFontSize
public var forceSerif: Bool
public var autoNightMode: Bool
public var ignoreAutoNightModeUntil: Int32
public init(themeType: InstantPageThemeType, fontSize: InstantPagePresentationFontSize, forceSerif: Bool, autoNightMode: Bool, ignoreAutoNightModeUntil: Int32) {
self.themeType = themeType
self.fontSize = fontSize
self.forceSerif = forceSerif
self.autoNightMode = autoNightMode
self.ignoreAutoNightModeUntil = ignoreAutoNightModeUntil
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.themeType = InstantPageThemeType(rawValue: try container.decode(Int32.self, forKey: "themeType"))!
self.fontSize = InstantPagePresentationFontSize(rawValue: try container.decode(Int32.self, forKey: "fontSize"))!
self.forceSerif = try container.decode(Int32.self, forKey: "forceSerif") != 0
self.autoNightMode = try container.decode(Int32.self, forKey: "autoNightMode") != 0
self.ignoreAutoNightModeUntil = try container.decode(Int32.self, forKey: "ignoreAutoNightModeUntil")
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.themeType.rawValue, forKey: "themeType")
try container.encode(self.fontSize.rawValue, forKey: "fontSize")
try container.encode((self.forceSerif ? 1 : 0) as Int32, forKey: "forceSerif")
try container.encode((self.autoNightMode ? 1 : 0) as Int32, forKey: "autoNightMode")
try container.encode(self.ignoreAutoNightModeUntil, forKey: "ignoreAutoNightModeUntil")
}
public static func ==(lhs: InstantPagePresentationSettings, rhs: InstantPagePresentationSettings) -> Bool {
if lhs.themeType != rhs.themeType {
return false
}
if lhs.fontSize != rhs.fontSize {
return false
}
if lhs.forceSerif != rhs.forceSerif {
return false
}
if lhs.autoNightMode != rhs.autoNightMode {
return false
}
if lhs.ignoreAutoNightModeUntil != rhs.ignoreAutoNightModeUntil {
return false
}
return true
}
public func withUpdatedThemeType(_ themeType: InstantPageThemeType) -> InstantPagePresentationSettings {
return InstantPagePresentationSettings(themeType: themeType, fontSize: self.fontSize, forceSerif: self.forceSerif, autoNightMode: self.autoNightMode, ignoreAutoNightModeUntil: self.ignoreAutoNightModeUntil)
}
public func withUpdatedFontSize(_ fontSize: InstantPagePresentationFontSize) -> InstantPagePresentationSettings {
return InstantPagePresentationSettings(themeType: self.themeType, fontSize: fontSize, forceSerif: self.forceSerif, autoNightMode: self.autoNightMode, ignoreAutoNightModeUntil: self.ignoreAutoNightModeUntil)
}
public func withUpdatedForceSerif(_ forceSerif: Bool) -> InstantPagePresentationSettings {
return InstantPagePresentationSettings(themeType: self.themeType, fontSize: self.fontSize, forceSerif: forceSerif, autoNightMode: self.autoNightMode, ignoreAutoNightModeUntil: self.ignoreAutoNightModeUntil)
}
public func withUpdatedAutoNightMode(_ autoNightMode: Bool) -> InstantPagePresentationSettings {
return InstantPagePresentationSettings(themeType: self.themeType, fontSize: self.fontSize, forceSerif: self.forceSerif, autoNightMode: autoNightMode, ignoreAutoNightModeUntil: self.ignoreAutoNightModeUntil)
}
public func withUpdatedIgnoreAutoNightModeUntil(_ ignoreAutoNightModeUntil: Int32) -> InstantPagePresentationSettings {
return InstantPagePresentationSettings(themeType: self.themeType, fontSize: self.fontSize, forceSerif: self.forceSerif, autoNightMode: autoNightMode, ignoreAutoNightModeUntil: ignoreAutoNightModeUntil)
}
}
public func updateInstantPagePresentationSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (InstantPagePresentationSettings) -> InstantPagePresentationSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.instantPagePresentationSettings, { entry in
let currentSettings: InstantPagePresentationSettings
if let entry = entry?.get(InstantPagePresentationSettings.self) {
currentSettings = entry
} else {
currentSettings = InstantPagePresentationSettings.defaultSettings
}
return PreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,100 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public struct IntentsSettings: Codable, Equatable {
public let initiallyReset: Bool
public let account: EnginePeer.Id?
public let contacts: Bool
public let privateChats: Bool
public let savedMessages: Bool
public let groups: Bool
public let onlyShared: Bool
public static var defaultSettings: IntentsSettings {
return IntentsSettings(initiallyReset: false, account: nil, contacts: true, privateChats: false, savedMessages: true, groups: false, onlyShared: false)
}
public init(initiallyReset: Bool, account: EnginePeer.Id?, contacts: Bool, privateChats: Bool, savedMessages: Bool, groups: Bool, onlyShared: Bool) {
self.initiallyReset = initiallyReset
self.account = account
self.contacts = contacts
self.privateChats = privateChats
self.savedMessages = savedMessages
self.groups = groups
self.onlyShared = onlyShared
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.initiallyReset = try container.decodeIfPresent(Bool.self, forKey: "initiallyReset_v2") ?? false
self.account = (try container.decodeIfPresent(Int64.self, forKey: "account")).flatMap { EnginePeer.Id($0) }
self.contacts = try container.decodeIfPresent(Bool.self, forKey: "contacts") ?? true
self.privateChats = try container.decodeIfPresent(Bool.self, forKey: "privateChats") ?? false
self.savedMessages = try container.decodeIfPresent(Bool.self, forKey: "savedMessages") ?? true
self.groups = try container.decodeIfPresent(Bool.self, forKey: "groups") ?? false
self.onlyShared = try container.decodeIfPresent(Bool.self, forKey: "onlyShared") ?? false
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.initiallyReset, forKey: "initiallyReset_v2")
try container.encodeIfPresent(self.account?.toInt64(), forKey: "account")
try container.encode(self.contacts, forKey: "contacts")
try container.encode(self.privateChats, forKey: "privateChats")
try container.encode(self.savedMessages, forKey: "savedMessages")
try container.encode(self.groups, forKey: "groups")
try container.encode(self.onlyShared, forKey: "onlyShared")
}
public static func ==(lhs: IntentsSettings, rhs: IntentsSettings) -> Bool {
return lhs.initiallyReset == rhs.initiallyReset && lhs.account == rhs.account && lhs.contacts == rhs.contacts && lhs.privateChats == rhs.privateChats && lhs.savedMessages == rhs.savedMessages && lhs.groups == rhs.groups && lhs.onlyShared == rhs.onlyShared
}
public func withUpdatedAccount(_ account: EnginePeer.Id?) -> IntentsSettings {
return IntentsSettings(initiallyReset: self.initiallyReset, account: account, contacts: self.contacts, privateChats: self.privateChats, savedMessages: self.savedMessages, groups: self.groups, onlyShared: self.onlyShared)
}
public func withUpdatedContacts(_ contacts: Bool) -> IntentsSettings {
return IntentsSettings(initiallyReset: self.initiallyReset, account: self.account, contacts: contacts, privateChats: self.privateChats, savedMessages: self.savedMessages, groups: self.groups, onlyShared: self.onlyShared)
}
public func withUpdatedPrivateChats(_ privateChats: Bool) -> IntentsSettings {
return IntentsSettings(initiallyReset: self.initiallyReset, account: self.account, contacts: self.contacts, privateChats: privateChats, savedMessages: self.savedMessages, groups: self.groups, onlyShared: self.onlyShared)
}
public func withUpdatedSavedMessages(_ savedMessages: Bool) -> IntentsSettings {
return IntentsSettings(initiallyReset: self.initiallyReset, account: self.account, contacts: self.contacts, privateChats: self.privateChats, savedMessages: savedMessages, groups: self.groups, onlyShared: self.onlyShared)
}
public func withUpdatedGroups(_ groups: Bool) -> IntentsSettings {
return IntentsSettings(initiallyReset: self.initiallyReset, account: self.account, contacts: self.contacts, privateChats: self.privateChats, savedMessages: self.savedMessages, groups: groups, onlyShared: self.onlyShared)
}
public func withUpdatedOnlyShared(_ onlyShared: Bool) -> IntentsSettings {
return IntentsSettings(initiallyReset: self.initiallyReset, account: self.account, contacts: self.contacts, privateChats: self.privateChats, savedMessages: self.savedMessages, groups: self.groups, onlyShared: onlyShared)
}
}
public func updateIntentsSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (IntentsSettings) -> IntentsSettings) -> Signal<(IntentsSettings?, IntentsSettings?), NoError> {
return accountManager.transaction { transaction -> (IntentsSettings?, IntentsSettings?) in
var previousSettings: IntentsSettings? = nil
var updatedSettings: IntentsSettings? = nil
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.intentsSettings, { entry in
let currentSettings: IntentsSettings
if let entry = entry?.get(IntentsSettings.self) {
currentSettings = entry
} else {
currentSettings = IntentsSettings.defaultSettings
}
previousSettings = currentSettings
updatedSettings = f(currentSettings)
return SharedPreferencesEntry(updatedSettings)
})
return (previousSettings, updatedSettings)
}
}
@@ -0,0 +1,637 @@
import Foundation
import UIKit
import Postbox
import SwiftSignalKit
import TelegramCore
public enum MediaAutoDownloadNetworkType {
case wifi
case cellular
}
public extension MediaAutoDownloadNetworkType {
init(_ networkType: NetworkType) {
switch networkType {
case .none, .cellular:
self = .cellular
case .wifi:
self = .wifi
}
}
}
public enum MediaAutoDownloadPreset: Int32 {
case low
case medium
case high
case custom
}
public struct MediaAutoDownloadPresets: Codable, Equatable {
public var low: MediaAutoDownloadCategories
public var medium: MediaAutoDownloadCategories
public var high: MediaAutoDownloadCategories
public init(low: MediaAutoDownloadCategories, medium: MediaAutoDownloadCategories, high: MediaAutoDownloadCategories) {
self.low = low
self.medium = medium
self.high = high
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.low = try container.decode(MediaAutoDownloadCategories.self, forKey: "low")
self.medium = try container.decode(MediaAutoDownloadCategories.self, forKey: "medium")
self.high = try container.decode(MediaAutoDownloadCategories.self, forKey: "high")
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.low, forKey: "low")
try container.encode(self.medium, forKey: "medium")
try container.encode(self.high, forKey: "high")
}
}
public struct MediaAutoDownloadConnection: Codable, Equatable {
public var enabled: Bool
public var preset: MediaAutoDownloadPreset
public var custom: MediaAutoDownloadCategories?
public init(enabled: Bool, preset: MediaAutoDownloadPreset, custom: MediaAutoDownloadCategories?) {
self.enabled = enabled
self.preset = preset
self.custom = custom
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.enabled = try container.decode(Int32.self, forKey: "enabled") != 0
self.preset = MediaAutoDownloadPreset(rawValue: try container.decode(Int32.self, forKey: "preset")) ?? .medium
self.custom = try container.decodeIfPresent(MediaAutoDownloadCategories.self, forKey: "custom")
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.enabled ? 1 : 0) as Int32, forKey: "enabled")
try container.encode(self.preset.rawValue, forKey: "preset")
try container.encodeIfPresent(self.custom, forKey: "custom")
}
}
public struct MediaAutoDownloadCategories: Codable, Equatable, Comparable {
public var basePreset: MediaAutoDownloadPreset
public var photo: MediaAutoDownloadCategory
public var video: MediaAutoDownloadCategory
public var file: MediaAutoDownloadCategory
public var stories: MediaAutoDownloadCategory
public init(basePreset: MediaAutoDownloadPreset, photo: MediaAutoDownloadCategory, video: MediaAutoDownloadCategory, file: MediaAutoDownloadCategory, stories: MediaAutoDownloadCategory) {
self.basePreset = basePreset
self.photo = photo
self.video = video
self.file = file
self.stories = stories
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.basePreset = MediaAutoDownloadPreset(rawValue: try container.decode(Int32.self, forKey: "preset")) ?? .medium
self.photo = try container.decode(MediaAutoDownloadCategory.self, forKey: "photo")
self.video = try container.decode(MediaAutoDownloadCategory.self, forKey: "video")
self.file = try container.decode(MediaAutoDownloadCategory.self, forKey: "file")
self.stories = try container.decodeIfPresent(MediaAutoDownloadCategory.self, forKey: "stories") ?? MediaAutoDownloadSettings.defaultSettings.presets.high.stories
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.basePreset.rawValue, forKey: "preset")
try container.encode(self.photo, forKey: "photo")
try container.encode(self.video, forKey: "video")
try container.encode(self.file, forKey: "file")
try container.encode(self.stories, forKey: "stories")
}
public static func < (lhs: MediaAutoDownloadCategories, rhs: MediaAutoDownloadCategories) -> Bool {
let lhsSizeLimit: Int64 = Int64((isAutodownloadEnabledForAnyPeerType(category: lhs.video) ? lhs.video.sizeLimit : 0)) + Int64((isAutodownloadEnabledForAnyPeerType(category: lhs.file) ? lhs.file.sizeLimit : 0))
let rhsSizeLimit: Int64 = Int64((isAutodownloadEnabledForAnyPeerType(category: rhs.video) ? rhs.video.sizeLimit : 0)) + Int64((isAutodownloadEnabledForAnyPeerType(category: rhs.file) ? rhs.file.sizeLimit : 0))
return lhsSizeLimit < rhsSizeLimit
}
}
public struct MediaAutoDownloadCategory: Codable, Equatable {
public var contacts: Bool
public var otherPrivate: Bool
public var groups: Bool
public var channels: Bool
public var sizeLimit: Int64
public var predownload: Bool
public init(contacts: Bool, otherPrivate: Bool, groups: Bool, channels: Bool, sizeLimit: Int64, predownload: Bool) {
self.contacts = contacts
self.otherPrivate = otherPrivate
self.groups = groups
self.channels = channels
self.sizeLimit = sizeLimit
self.predownload = predownload
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.contacts = try container.decode(Int32.self, forKey: "contacts") != 0
self.otherPrivate = try container.decode(Int32.self, forKey: "otherPrivate") != 0
self.groups = try container.decode(Int32.self, forKey: "groups") != 0
self.channels = try container.decode(Int32.self, forKey: "channels") != 0
if let sizeLimit = try container.decodeIfPresent(Int64.self, forKey: "size64") {
self.sizeLimit = sizeLimit
} else {
self.sizeLimit = Int64(try container.decode(Int32.self, forKey: "size"))
}
self.predownload = try container.decode(Int32.self, forKey: "predownload") != 0
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.contacts ? 1 : 0) as Int32, forKey: "contacts")
try container.encode((self.otherPrivate ? 1 : 0) as Int32, forKey: "otherPrivate")
try container.encode((self.groups ? 1 : 0) as Int32, forKey: "groups")
try container.encode((self.channels ? 1 : 0) as Int32, forKey: "channels")
try container.encode(self.sizeLimit, forKey: "size64")
try container.encode((self.predownload ? 1 : 0) as Int32, forKey: "predownload")
}
}
public struct MediaAutoSaveConfiguration: Codable, Equatable {
public var photo: Bool
public var video: Bool
public var maximumVideoSize: Int64
public static var `default` = MediaAutoSaveConfiguration(
photo: false,
video: false,
maximumVideoSize: 100 * 1024 * 1024
)
public init(photo: Bool, video: Bool, maximumVideoSize: Int64) {
self.photo = photo
self.video = video
self.maximumVideoSize = maximumVideoSize
}
}
public struct MediaAutoSaveSettings: Codable, Equatable {
private enum CodingKeys: String, CodingKey {
case configurations
case exceptions
}
public enum PeerType: String, Codable {
case users = "users"
case groups = "groups"
case channels = "channels"
}
private struct ConfigurationItem: Codable {
var peerType: PeerType
var configuration: MediaAutoSaveConfiguration
}
public struct ExceptionItem: Codable, Equatable {
public var id: PeerId
public var configuration: MediaAutoSaveConfiguration
public init(id: PeerId, configuration: MediaAutoSaveConfiguration) {
self.id = id
self.configuration = configuration
}
}
public var configurations: [PeerType: MediaAutoSaveConfiguration]
public var exceptions: [ExceptionItem]
public static let `default` = MediaAutoSaveSettings(configurations: [:], exceptions: [])
public init(configurations: [PeerType: MediaAutoSaveConfiguration], exceptions: [ExceptionItem]) {
self.configurations = configurations
self.exceptions = exceptions
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.configurations = [:]
if let data = try container.decodeIfPresent(Data.self, forKey: .configurations) {
if let value = try? JSONDecoder().decode([ConfigurationItem].self, from: data) {
self.configurations = [:]
for item in value {
self.configurations[item.peerType] = item.configuration
}
}
}
self.exceptions = []
if let data = try container.decodeIfPresent(Data.self, forKey: .exceptions) {
if let value = try? JSONDecoder().decode([ExceptionItem].self, from: data) {
self.exceptions = value
}
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var configurations: [ConfigurationItem] = []
for (key, value) in self.configurations {
configurations.append(ConfigurationItem(peerType: key, configuration: value))
}
configurations.sort(by: { $0.peerType.rawValue < $1.peerType.rawValue })
let jsonConfigurations = try JSONEncoder().encode(configurations)
try container.encode(jsonConfigurations, forKey: .configurations)
let jsonExceptions = try JSONEncoder().encode(self.exceptions)
try container.encode(jsonExceptions, forKey: .exceptions)
}
}
public struct EnergyUsageSettings: Codable, Equatable {
private enum CodingKeys: CodingKey {
case activationThreshold
case autoplayVideo
case autoplayGif
case loopStickers
case loopEmoji
case fullTranslucency
case extendBackgroundWork
case autodownloadInBackground
}
public static let `default`: EnergyUsageSettings = {
var length: Int = 4
var cpuCount: UInt32 = 0
sysctlbyname("hw.ncpu", &cpuCount, &length, nil, 0)
let isCapable = cpuCount >= 4
return EnergyUsageSettings(
activationThreshold: 15,
autoplayVideo: true,
autoplayGif: true,
loopStickers: true,
loopEmoji: isCapable,
fullTranslucency: isCapable,
extendBackgroundWork: true,
autodownloadInBackground: true
)
}()
public static var powerSavingDefault: EnergyUsageSettings {
return EnergyUsageSettings(
activationThreshold: 15,
autoplayVideo: false,
autoplayGif: false,
loopStickers: false,
loopEmoji: false,
fullTranslucency: false,
extendBackgroundWork: false,
autodownloadInBackground: false
)
}
public var activationThreshold: Int32
public var autoplayVideo: Bool
public var autoplayGif: Bool
public var loopStickers: Bool
public var loopEmoji: Bool
public var fullTranslucency: Bool
public var extendBackgroundWork: Bool
public var autodownloadInBackground: Bool
public init(
activationThreshold: Int32,
autoplayVideo: Bool,
autoplayGif: Bool,
loopStickers: Bool,
loopEmoji: Bool,
fullTranslucency: Bool,
extendBackgroundWork: Bool,
autodownloadInBackground: Bool
) {
self.activationThreshold = activationThreshold
self.autoplayVideo = autoplayVideo
self.autoplayGif = autoplayGif
self.loopStickers = loopStickers
self.loopEmoji = loopEmoji
self.fullTranslucency = fullTranslucency
self.extendBackgroundWork = extendBackgroundWork
self.autodownloadInBackground = autodownloadInBackground
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.activationThreshold = try container.decodeIfPresent(Int32.self, forKey: .activationThreshold) ?? EnergyUsageSettings.default.activationThreshold
self.autoplayVideo = try container.decodeIfPresent(Bool.self, forKey: .autoplayVideo) ?? EnergyUsageSettings.default.autoplayVideo
self.autoplayGif = try container.decodeIfPresent(Bool.self, forKey: .autoplayGif) ?? EnergyUsageSettings.default.autoplayGif
self.loopStickers = try container.decodeIfPresent(Bool.self, forKey: .loopStickers) ?? EnergyUsageSettings.default.loopStickers
self.loopEmoji = try container.decodeIfPresent(Bool.self, forKey: .loopEmoji) ?? EnergyUsageSettings.default.loopEmoji
self.fullTranslucency = try container.decodeIfPresent(Bool.self, forKey: .fullTranslucency) ?? EnergyUsageSettings.default.fullTranslucency
self.extendBackgroundWork = try container.decodeIfPresent(Bool.self, forKey: .extendBackgroundWork) ?? EnergyUsageSettings.default.extendBackgroundWork
self.autodownloadInBackground = try container.decodeIfPresent(Bool.self, forKey: .autodownloadInBackground) ?? EnergyUsageSettings.default.autodownloadInBackground
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.activationThreshold, forKey: .activationThreshold)
try container.encode(self.autoplayVideo, forKey: .autoplayVideo)
try container.encode(self.autoplayGif, forKey: .autoplayGif)
try container.encode(self.loopEmoji, forKey: .loopEmoji)
try container.encode(self.loopStickers, forKey: .loopStickers)
try container.encode(self.fullTranslucency, forKey: .fullTranslucency)
try container.encode(self.extendBackgroundWork, forKey: .extendBackgroundWork)
try container.encode(self.autodownloadInBackground, forKey: .autodownloadInBackground)
}
}
public struct MediaAutoDownloadSettings: Codable, Equatable {
public var presets: MediaAutoDownloadPresets
public var cellular: MediaAutoDownloadConnection
public var wifi: MediaAutoDownloadConnection
public var downloadInBackground: Bool
public var energyUsageSettings: EnergyUsageSettings
public var highQualityStories: Bool
public static var defaultSettings: MediaAutoDownloadSettings {
let mb: Int64 = 1024 * 1024
let presets = MediaAutoDownloadPresets(low:
MediaAutoDownloadCategories(
basePreset: .low,
photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
video: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false),
file: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false),
stories: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 20 * mb, predownload: false)
),
medium: MediaAutoDownloadCategories(
basePreset: .medium,
photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: Int64(2.5 * CGFloat(mb)), predownload: false),
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
stories: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 20 * mb, predownload: false)
),
high: MediaAutoDownloadCategories(
basePreset: .high,
photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 10 * mb, predownload: true),
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 3 * mb, predownload: false),
stories: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 20 * mb, predownload: false)
)
)
return MediaAutoDownloadSettings(presets: presets, cellular: MediaAutoDownloadConnection(enabled: true, preset: .medium, custom: nil), wifi: MediaAutoDownloadConnection(enabled: true, preset: .high, custom: nil), downloadInBackground: true, energyUsageSettings: EnergyUsageSettings.default, highQualityStories: false)
}
public init(presets: MediaAutoDownloadPresets, cellular: MediaAutoDownloadConnection, wifi: MediaAutoDownloadConnection, downloadInBackground: Bool, energyUsageSettings: EnergyUsageSettings, highQualityStories: Bool) {
self.presets = presets
self.cellular = cellular
self.wifi = wifi
self.downloadInBackground = downloadInBackground
self.energyUsageSettings = energyUsageSettings
self.highQualityStories = highQualityStories
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
let defaultSettings = MediaAutoDownloadSettings.defaultSettings
self.presets = defaultSettings.presets
self.cellular = (try? container.decodeIfPresent(MediaAutoDownloadConnection.self, forKey: "cellular")) ?? defaultSettings.cellular
self.wifi = (try? container.decodeIfPresent(MediaAutoDownloadConnection.self, forKey: "wifi")) ?? defaultSettings.wifi
self.downloadInBackground = try container.decode(Int32.self, forKey: "downloadInBackground") != 0
self.energyUsageSettings = (try container.decodeIfPresent(EnergyUsageSettings.self, forKey: "energyUsageSettings")) ?? EnergyUsageSettings.default
self.highQualityStories = try container.decodeIfPresent(Bool.self, forKey: "highQualityStories") ?? false
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.cellular, forKey: "cellular")
try container.encode(self.wifi, forKey: "wifi")
try container.encode((self.downloadInBackground ? 1 : 0) as Int32, forKey: "downloadInBackground")
try container.encode(self.energyUsageSettings, forKey: "energyUsageSettings")
try container.encode(self.highQualityStories, forKey: "highQualityStories")
}
public func connectionSettings(for networkType: MediaAutoDownloadNetworkType) -> MediaAutoDownloadConnection {
switch networkType {
case .cellular:
return self.cellular
case .wifi:
return self.wifi
}
}
public func updatedWithAutodownloadSettings(_ autodownloadSettings: AutodownloadSettings) -> MediaAutoDownloadSettings {
var settings = self
settings.presets = presetsWithAutodownloadSettings(autodownloadSettings)
return settings
}
}
private func categoriesWithAutodownloadPreset(_ autodownloadPreset: AutodownloadPresetSettings, preset: MediaAutoDownloadPreset) -> MediaAutoDownloadCategories {
let videoEnabled = autodownloadPreset.videoSizeMax > 0
let videoSizeMax = autodownloadPreset.videoSizeMax > 0 ? autodownloadPreset.videoSizeMax : 1 * 1024 * 1024
let fileEnabled = autodownloadPreset.fileSizeMax > 0
let fileSizeMax = autodownloadPreset.fileSizeMax > 0 ? autodownloadPreset.fileSizeMax : 1 * 1024 * 1024
return MediaAutoDownloadCategories(
basePreset: preset,
photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: autodownloadPreset.photoSizeMax, predownload: false),
video: MediaAutoDownloadCategory(contacts: videoEnabled, otherPrivate: videoEnabled, groups: videoEnabled, channels: videoEnabled, sizeLimit: videoSizeMax, predownload: autodownloadPreset.preloadLargeVideo),
file: MediaAutoDownloadCategory(contacts: fileEnabled, otherPrivate: fileEnabled, groups: fileEnabled, channels: fileEnabled, sizeLimit: fileSizeMax, predownload: false),
stories: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: autodownloadPreset.photoSizeMax, predownload: false)
)
}
private func presetsWithAutodownloadSettings(_ autodownloadSettings: AutodownloadSettings) -> MediaAutoDownloadPresets {
return MediaAutoDownloadPresets(low: categoriesWithAutodownloadPreset(autodownloadSettings.lowPreset, preset: .low), medium: categoriesWithAutodownloadPreset(autodownloadSettings.mediumPreset, preset: .medium), high: categoriesWithAutodownloadPreset(autodownloadSettings.highPreset, preset: .high))
}
public func updateMediaDownloadSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (MediaAutoDownloadSettings) -> MediaAutoDownloadSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings, { entry in
let currentSettings: MediaAutoDownloadSettings
if let entry = entry?.get(MediaAutoDownloadSettings.self) {
currentSettings = entry
} else {
currentSettings = MediaAutoDownloadSettings.defaultSettings
}
let updated = f(currentSettings)
return PreferencesEntry(updated)
})
}
}
public enum MediaAutoDownloadPeerType {
case contact
case otherPrivate
case group
case channel
}
public struct InstantPageSourceLocation {
public var userLocation: MediaResourceUserLocation
public var peerType: MediaAutoDownloadPeerType
public init(userLocation: MediaResourceUserLocation, peerType: MediaAutoDownloadPeerType) {
self.userLocation = userLocation
self.peerType = peerType
}
}
public func effectiveAutodownloadCategories(settings: MediaAutoDownloadSettings, networkType: MediaAutoDownloadNetworkType) -> MediaAutoDownloadCategories {
let connection = settings.connectionSettings(for: networkType)
switch connection.preset {
case .custom:
return connection.custom ?? settings.presets.medium
case .low:
return settings.presets.low
case .medium:
return settings.presets.medium
case .high:
return settings.presets.high
}
}
private func categoryAndSizeForMedia(_ media: Media?, isStory: Bool, categories: MediaAutoDownloadCategories) -> (MediaAutoDownloadCategory, Int32?)? {
if isStory {
return (categories.stories, 0)
}
guard let media = media else {
return (categories.photo, 0)
}
if media is TelegramMediaImage || media is TelegramMediaWebFile {
return (categories.photo, 0)
} else if let file = media as? TelegramMediaFile {
for attribute in file.attributes {
switch attribute {
case .Video:
return (categories.video, file.size.flatMap(Int32.init))
case let .Audio(isVoice, _, _, _, _):
if isVoice {
return (categories.file, file.size.flatMap(Int32.init))
}
case .Animated:
return (categories.video, file.size.flatMap(Int32.init))
default:
break
}
}
return (categories.file, file.size.flatMap(Int32.init))
} else {
return nil
}
}
public func isAutodownloadEnabledForPeerType(_ peerType: MediaAutoDownloadPeerType, category: MediaAutoDownloadCategory) -> Bool {
switch peerType {
case .contact:
return category.contacts
case .otherPrivate:
return category.otherPrivate
case .group:
return category.groups
case .channel:
return category.channels
}
}
public func isAutodownloadEnabledForAnyPeerType(category: MediaAutoDownloadCategory) -> Bool {
return category.contacts || category.otherPrivate || category.groups || category.channels
}
public func shouldDownloadMediaAutomatically(settings: MediaAutoDownloadSettings, peerType: MediaAutoDownloadPeerType, networkType: MediaAutoDownloadNetworkType, authorPeerId: PeerId? = nil, contactsPeerIds: Set<PeerId> = Set(), media: Media?, isStory: Bool = false, isAd: Bool = false) -> Bool {
if isAd {
return true
}
if (networkType == .cellular && !settings.cellular.enabled) || (networkType == .wifi && !settings.wifi.enabled) {
return false
}
if let file = media as? TelegramMediaFile, file.isSticker {
return true
}
var peerType = peerType
if case .group = peerType, let authorPeerId = authorPeerId, contactsPeerIds.contains(authorPeerId) {
peerType = .contact
}
if let (category, size) = categoryAndSizeForMedia(media, isStory: isStory, categories: effectiveAutodownloadCategories(settings: settings, networkType: networkType)) {
if let size = size {
var sizeLimit = category.sizeLimit
if let file = media as? TelegramMediaFile, file.isVoice {
sizeLimit = max(2 * 1024 * 1024, sizeLimit)
} else if !isAutodownloadEnabledForPeerType(peerType, category: category) {
return false
}
return size <= sizeLimit
} else if media?.id?.namespace == Namespaces.Media.LocalFile {
return true
} else if category.sizeLimit == Int32.max {
return true
} else {
return false
}
} else {
return false
}
}
public func shouldPredownloadMedia(settings: MediaAutoDownloadSettings, peerType: MediaAutoDownloadPeerType, networkType: MediaAutoDownloadNetworkType, media: Media) -> Bool {
if #available(iOSApplicationExtension 10.3, *) {
if (networkType == .cellular && !settings.cellular.enabled) || (networkType == .wifi && !settings.wifi.enabled) {
return false
}
if let (category, _) = categoryAndSizeForMedia(media, isStory: false, categories: effectiveAutodownloadCategories(settings: settings, networkType: networkType)) {
guard isAutodownloadEnabledForPeerType(peerType, category: category) else {
return false
}
return category.sizeLimit > 2 * 1024 * 1024 && category.predownload
} else {
return false
}
} else {
return false
}
}
public func updateMediaAutoSaveSettingsInteractively(account: Account, _ f: @escaping (MediaAutoSaveSettings) -> MediaAutoSaveSettings) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Void in
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.mediaAutoSaveSettings, { entry in
let currentSettings: MediaAutoSaveSettings
if let entry = entry?.get(MediaAutoSaveSettings.self) {
currentSettings = entry
} else {
currentSettings = .default
}
let updated = f(currentSettings)
return PreferencesEntry(updated)
})
}
|> ignoreValues
}
@@ -0,0 +1,49 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public struct MediaDisplaySettings: Codable, Equatable {
public let showNextMediaOnTap: Bool
public static var defaultSettings: MediaDisplaySettings {
return MediaDisplaySettings(showNextMediaOnTap: true)
}
public init(showNextMediaOnTap: Bool) {
self.showNextMediaOnTap = showNextMediaOnTap
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.showNextMediaOnTap = (try container.decode(Int32.self, forKey: "showNextMediaOnTap")) != 0
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.showNextMediaOnTap ? 1 : 0) as Int32, forKey: "showNextMediaOnTap")
}
public static func ==(lhs: MediaDisplaySettings, rhs: MediaDisplaySettings) -> Bool {
return lhs.showNextMediaOnTap == rhs.showNextMediaOnTap
}
public func withUpdatedShowNextMediaOnTap(_ showNextMediaOnTap: Bool) -> MediaDisplaySettings {
return MediaDisplaySettings(showNextMediaOnTap: showNextMediaOnTap)
}
}
public func updateMediaDisplaySettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (MediaDisplaySettings) -> MediaDisplaySettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.mediaDisplaySettings, { entry in
let currentSettings: MediaDisplaySettings
if let entry = entry?.get(MediaDisplaySettings.self) {
currentSettings = entry
} else {
currentSettings = MediaDisplaySettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,57 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public struct MediaInputSettings: Codable, Equatable {
public let enableRaiseToSpeak: Bool
public let pauseMusicOnRecording: Bool
public static var defaultSettings: MediaInputSettings {
return MediaInputSettings(enableRaiseToSpeak: true, pauseMusicOnRecording: true)
}
public init(enableRaiseToSpeak: Bool, pauseMusicOnRecording: Bool) {
self.enableRaiseToSpeak = enableRaiseToSpeak
self.pauseMusicOnRecording = pauseMusicOnRecording
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.enableRaiseToSpeak = (try container.decode(Int32.self, forKey: "enableRaiseToSpeak")) != 0
self.pauseMusicOnRecording = (try container.decodeIfPresent(Int32.self, forKey: "pauseMusicOnRecording_v2") ?? 1) != 0
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.enableRaiseToSpeak ? 1 : 0) as Int32, forKey: "enableRaiseToSpeak")
try container.encode((self.pauseMusicOnRecording ? 1 : 0) as Int32, forKey: "pauseMusicOnRecording_v2")
}
public static func ==(lhs: MediaInputSettings, rhs: MediaInputSettings) -> Bool {
return lhs.enableRaiseToSpeak == rhs.enableRaiseToSpeak && lhs.pauseMusicOnRecording == rhs.pauseMusicOnRecording
}
public func withUpdatedEnableRaiseToSpeak(_ enableRaiseToSpeak: Bool) -> MediaInputSettings {
return MediaInputSettings(enableRaiseToSpeak: enableRaiseToSpeak, pauseMusicOnRecording: self.pauseMusicOnRecording)
}
public func withUpdatedPauseMusicOnRecording(_ pauseMusicOnRecording: Bool) -> MediaInputSettings {
return MediaInputSettings(enableRaiseToSpeak: self.enableRaiseToSpeak, pauseMusicOnRecording: pauseMusicOnRecording)
}
}
public func updateMediaInputSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (MediaInputSettings) -> MediaInputSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.mediaInputSettings, { entry in
let currentSettings: MediaInputSettings
if let entry = entry?.get(MediaInputSettings.self) {
currentSettings = entry
} else {
currentSettings = MediaInputSettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,155 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public enum MusicPlaybackSettingsOrder: Int32 {
case regular = 0
case reversed = 1
case random = 2
}
public enum MusicPlaybackSettingsLooping: Int32 {
case none = 0
case item = 1
case all = 2
}
public enum AudioPlaybackRate: Equatable {
case x0_5
case x1
case x1_5
case x2
case x4
case x8
case x16
case custom(Int32)
public var isPreset: Bool {
switch self {
case .x1, .x1_5, .x2:
return true
default:
return false
}
}
public var doubleValue: Double {
return Double(self.rawValue) / 1000.0
}
public var rawValue: Int32 {
switch self {
case .x0_5:
return 500
case .x1:
return 1000
case .x1_5:
return 1500
case .x2:
return 2000
case .x4:
return 4000
case .x8:
return 8000
case .x16:
return 16000
case let .custom(value):
return value
}
}
public init(_ value: Double) {
self.init(rawValue: Int32(value * 1000.0))
}
public init(rawValue: Int32) {
switch rawValue {
case 500:
self = .x0_5
case 1000:
self = .x1
case 1500:
self = .x1_5
case 2000:
self = .x2
case 4000:
self = .x4
case 8000:
self = .x8
case 16000:
self = .x16
default:
self = .custom(rawValue)
}
}
public var stringValue: String {
var stringValue = String(format: "%.1fx", self.doubleValue)
if stringValue.hasSuffix(".0x") {
stringValue = stringValue.replacingOccurrences(of: ".0x", with: "x")
}
return stringValue
}
}
public struct MusicPlaybackSettings: Codable, Equatable {
public var order: MusicPlaybackSettingsOrder
public var looping: MusicPlaybackSettingsLooping
public var voicePlaybackRate: AudioPlaybackRate
public static var defaultSettings: MusicPlaybackSettings {
return MusicPlaybackSettings(order: .regular, looping: .none, voicePlaybackRate: .x1)
}
public init(order: MusicPlaybackSettingsOrder, looping: MusicPlaybackSettingsLooping, voicePlaybackRate: AudioPlaybackRate) {
self.order = order
self.looping = looping
self.voicePlaybackRate = voicePlaybackRate
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.order = MusicPlaybackSettingsOrder(rawValue: try container.decode(Int32.self, forKey: "order")) ?? .regular
self.looping = MusicPlaybackSettingsLooping(rawValue: try container.decode(Int32.self, forKey: "looping")) ?? .none
self.voicePlaybackRate = AudioPlaybackRate(rawValue: try container.decodeIfPresent(Int32.self, forKey: "voicePlaybackRate") ?? AudioPlaybackRate.x1.rawValue)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.order.rawValue, forKey: "order")
try container.encode(self.looping.rawValue, forKey: "looping")
try container.encode(self.voicePlaybackRate.rawValue, forKey: "voicePlaybackRate")
}
public static func ==(lhs: MusicPlaybackSettings, rhs: MusicPlaybackSettings) -> Bool {
return lhs.order == rhs.order && lhs.looping == rhs.looping && lhs.voicePlaybackRate == rhs.voicePlaybackRate
}
public func withUpdatedOrder(_ order: MusicPlaybackSettingsOrder) -> MusicPlaybackSettings {
return MusicPlaybackSettings(order: order, looping: self.looping, voicePlaybackRate: self.voicePlaybackRate)
}
public func withUpdatedLooping(_ looping: MusicPlaybackSettingsLooping) -> MusicPlaybackSettings {
return MusicPlaybackSettings(order: self.order, looping: looping, voicePlaybackRate: self.voicePlaybackRate)
}
public func withUpdatedVoicePlaybackRate(_ voicePlaybackRate: AudioPlaybackRate) -> MusicPlaybackSettings {
return MusicPlaybackSettings(order: self.order, looping: self.looping, voicePlaybackRate: voicePlaybackRate)
}
}
public func updateMusicPlaybackSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (MusicPlaybackSettings) -> MusicPlaybackSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { entry in
let currentSettings: MusicPlaybackSettings
if let entry = entry?.get(MusicPlaybackSettings.self) {
currentSettings = entry
} else {
currentSettings = MusicPlaybackSettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,124 @@
import Foundation
import TelegramCore
import Postbox
private enum ApplicationSpecificPreferencesKeyValues: Int32 {
case voipDerivedState = 16
case chatArchiveSettings = 17
case chatListFilterSettings = 18
case widgetSettings = 19
case mediaAutoSaveSettings = 20
case ageVerificationState = 21
}
public struct ApplicationSpecificPreferencesKeys {
public static let voipDerivedState = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.voipDerivedState.rawValue)
public static let chatArchiveSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.chatArchiveSettings.rawValue)
public static let chatListFilterSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.chatListFilterSettings.rawValue)
public static let widgetSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.widgetSettings.rawValue)
public static let mediaAutoSaveSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.mediaAutoSaveSettings.rawValue)
public static let ageVerificationState = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.ageVerificationState.rawValue)
}
private enum ApplicationSpecificSharedDataKeyValues: Int32 {
case inAppNotificationSettings = 0
case presentationPasscodeSettings = 1
case automaticMediaDownloadSettings = 2
case generatedMediaStoreSettings = 3
case voiceCallSettings = 4
case presentationThemeSettings = 5
case instantPagePresentationSettings = 6
case callListSettings = 7
case experimentalSettings = 8
case musicPlaybackSettings = 9
case mediaInputSettings = 10
case experimentalUISettings = 11
case stickerSettings = 12
case watchPresetSettings = 13
case webSearchSettings = 14
case contactSynchronizationSettings = 15
case webBrowserSettings = 16
case intentsSettings = 17
case translationSettings = 18
case drawingSettings = 19
case mediaDisplaySettings = 20
case updateSettings = 21
}
public struct ApplicationSpecificSharedDataKeys {
public static let inAppNotificationSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.inAppNotificationSettings.rawValue)
public static let presentationPasscodeSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.presentationPasscodeSettings.rawValue)
public static let automaticMediaDownloadSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.automaticMediaDownloadSettings.rawValue)
public static let generatedMediaStoreSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.generatedMediaStoreSettings.rawValue)
public static let voiceCallSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.voiceCallSettings.rawValue)
public static let presentationThemeSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.presentationThemeSettings.rawValue)
public static let instantPagePresentationSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.instantPagePresentationSettings.rawValue)
public static let callListSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.callListSettings.rawValue)
public static let experimentalSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.experimentalSettings.rawValue)
public static let musicPlaybackSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.musicPlaybackSettings.rawValue)
public static let mediaInputSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.mediaInputSettings.rawValue)
public static let experimentalUISettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.experimentalUISettings.rawValue)
public static let stickerSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.stickerSettings.rawValue)
public static let watchPresetSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.watchPresetSettings.rawValue)
public static let webSearchSettings = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.webSearchSettings.rawValue)
public static let contactSynchronizationSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.contactSynchronizationSettings.rawValue)
public static let webBrowserSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.webBrowserSettings.rawValue)
public static let intentsSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.intentsSettings.rawValue)
public static let translationSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.translationSettings.rawValue)
public static let drawingSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.drawingSettings.rawValue)
public static let mediaDisplaySettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.mediaDisplaySettings.rawValue)
public static let updateSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.updateSettings.rawValue)
}
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
case instantPageStoredState = 0
case cachedInstantPages = 1
case cachedWallpapers = 2
case mediaPlaybackStoredState = 3
case cachedGeocodes = 4
case visualMediaStoredState = 5
case cachedImageRecognizedContent = 6
case pendingInAppPurchaseState = 7
case translationState = 10
case storySource = 11
case mediaEditorState = 12
case shareWithPeersState = 13
case webAppPermissionsState = 14
}
public struct ApplicationSpecificItemCacheCollectionId {
public static let instantPageStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.instantPageStoredState.rawValue)
public static let cachedInstantPages = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedInstantPages.rawValue)
public static let cachedWallpapers = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedWallpapers.rawValue)
public static let cachedGeocodes = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedGeocodes.rawValue)
public static let visualMediaStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.visualMediaStoredState.rawValue)
public static let cachedImageRecognizedContent = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedImageRecognizedContent.rawValue)
public static let pendingInAppPurchaseState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.pendingInAppPurchaseState.rawValue)
public static let translationState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.translationState.rawValue)
public static let storySource = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.storySource.rawValue)
public static let mediaEditorState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.mediaEditorState.rawValue)
public static let shareWithPeersState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.shareWithPeersState.rawValue)
public static let webAppPermissionsState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.webAppPermissionsState.rawValue)
}
private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 {
case webSearchRecentQueries = 0
case wallpaperSearchRecentQueries = 1
case settingsSearchRecentItems = 2
case localThemes = 3
case storyDrafts = 4
case storySources = 5
case hashtagSearchRecentQueries = 6
case browserRecentlyVisited = 7
}
public struct ApplicationSpecificOrderedItemListCollectionId {
public static let webSearchRecentQueries = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.webSearchRecentQueries.rawValue)
public static let wallpaperSearchRecentQueries = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.wallpaperSearchRecentQueries.rawValue)
public static let settingsSearchRecentItems = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.settingsSearchRecentItems.rawValue)
public static let localThemes = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.localThemes.rawValue)
public static let storyDrafts = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.storyDrafts.rawValue)
public static let storySources = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.storySources.rawValue)
public static let hashtagSearchRecentQueries = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.hashtagSearchRecentQueries.rawValue)
public static let browserRecentlyVisited = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.browserRecentlyVisited.rawValue)
}
@@ -0,0 +1,77 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public struct PresentationPasscodeSettings: Codable, Equatable {
public var enableBiometrics: Bool
public var autolockTimeout: Int32?
public var biometricsDomainState: Data?
public var shareBiometricsDomainState: Data?
public static var defaultSettings: PresentationPasscodeSettings {
return PresentationPasscodeSettings(enableBiometrics: false, autolockTimeout: nil, biometricsDomainState: nil, shareBiometricsDomainState: nil)
}
public init(enableBiometrics: Bool, autolockTimeout: Int32?, biometricsDomainState: Data?, shareBiometricsDomainState: Data?) {
self.enableBiometrics = enableBiometrics
self.autolockTimeout = autolockTimeout
self.biometricsDomainState = biometricsDomainState
self.shareBiometricsDomainState = shareBiometricsDomainState
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.enableBiometrics = (try container.decode(Int32.self, forKey: "s")) != 0
self.autolockTimeout = try container.decodeIfPresent(Int32.self, forKey: "al")
self.biometricsDomainState = try container.decodeIfPresent(Data.self, forKey: "ds")
self.shareBiometricsDomainState = try container.decodeIfPresent(Data.self, forKey: "sds")
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.enableBiometrics ? 1 : 0) as Int32, forKey: "s")
try container.encodeIfPresent(self.autolockTimeout, forKey: "al")
try container.encodeIfPresent(self.biometricsDomainState, forKey: "ds")
try container.encodeIfPresent(self.shareBiometricsDomainState, forKey: "sds")
}
public static func ==(lhs: PresentationPasscodeSettings, rhs: PresentationPasscodeSettings) -> Bool {
return lhs.enableBiometrics == rhs.enableBiometrics && lhs.autolockTimeout == rhs.autolockTimeout && lhs.biometricsDomainState == rhs.biometricsDomainState && lhs.shareBiometricsDomainState == rhs.shareBiometricsDomainState
}
public func withUpdatedEnableBiometrics(_ enableBiometrics: Bool) -> PresentationPasscodeSettings {
return PresentationPasscodeSettings(enableBiometrics: enableBiometrics, autolockTimeout: self.autolockTimeout, biometricsDomainState: self.biometricsDomainState, shareBiometricsDomainState: self.shareBiometricsDomainState)
}
public func withUpdatedAutolockTimeout(_ autolockTimeout: Int32?) -> PresentationPasscodeSettings {
return PresentationPasscodeSettings(enableBiometrics: self.enableBiometrics, autolockTimeout: autolockTimeout, biometricsDomainState: self.biometricsDomainState, shareBiometricsDomainState: self.shareBiometricsDomainState)
}
public func withUpdatedBiometricsDomainState(_ biometricsDomainState: Data?) -> PresentationPasscodeSettings {
return PresentationPasscodeSettings(enableBiometrics: self.enableBiometrics, autolockTimeout: autolockTimeout, biometricsDomainState: biometricsDomainState, shareBiometricsDomainState: self.shareBiometricsDomainState)
}
public func withUpdatedShareBiometricsDomainState(_ shareBiometricsDomainState: Data?) -> PresentationPasscodeSettings {
return PresentationPasscodeSettings(enableBiometrics: self.enableBiometrics, autolockTimeout: autolockTimeout, biometricsDomainState: self.biometricsDomainState, shareBiometricsDomainState: shareBiometricsDomainState)
}
}
public func updatePresentationPasscodeSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (PresentationPasscodeSettings) -> PresentationPasscodeSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
updatePresentationPasscodeSettingsInternal(transaction: transaction, f)
}
}
public func updatePresentationPasscodeSettingsInternal(transaction: AccountManagerModifier<TelegramAccountManagerTypes>, _ f: @escaping (PresentationPasscodeSettings) -> PresentationPasscodeSettings) {
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationPasscodeSettings, { entry in
let currentSettings: PresentationPasscodeSettings
if let entry = entry?.get(PresentationPasscodeSettings.self) {
currentSettings = entry
} else {
currentSettings = PresentationPasscodeSettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
@@ -0,0 +1,808 @@
import Foundation
import UIKit
import Postbox
import TelegramCore
import SwiftSignalKit
private extension UIColor {
convenience init(rgb: UInt32) {
self.init(red: CGFloat((rgb >> 16) & 0xff) / 255.0, green: CGFloat((rgb >> 8) & 0xff) / 255.0, blue: CGFloat(rgb & 0xff) / 255.0, alpha: 1.0)
}
}
public enum PresentationBuiltinThemeReference: Int32 {
case dayClassic = 0
case night = 1
case day = 2
case nightAccent = 3
public init(baseTheme: TelegramBaseTheme) {
switch baseTheme {
case .classic:
self = .dayClassic
case .day:
self = .day
case .night:
self = .night
case .tinted:
self = .nightAccent
}
}
public var baseTheme: TelegramBaseTheme {
switch self {
case .dayClassic:
return .classic
case .day:
return .day
case .night:
return .night
case .nightAccent:
return .tinted
}
}
}
public struct WallpaperPresentationOptions: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let motion = WallpaperPresentationOptions(rawValue: 1 << 0)
public static let blur = WallpaperPresentationOptions(rawValue: 1 << 1)
}
public struct PresentationLocalTheme: PostboxCoding, Equatable {
public let title: String
public let resource: LocalFileMediaResource
public let resolvedWallpaper: TelegramWallpaper?
public init(title: String, resource: LocalFileMediaResource, resolvedWallpaper: TelegramWallpaper?) {
self.title = title
self.resource = resource
self.resolvedWallpaper = resolvedWallpaper
}
public init(decoder: PostboxDecoder) {
self.title = decoder.decodeStringForKey("title", orElse: "")
self.resource = decoder.decodeObjectForKey("resource", decoder: { LocalFileMediaResource(decoder: $0) }) as! LocalFileMediaResource
self.resolvedWallpaper = decoder.decode(TelegramWallpaperNativeCodable.self, forKey: "wallpaper")?.value
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeString(self.title, forKey: "title")
encoder.encodeObject(self.resource, forKey: "resource")
if let resolvedWallpaper = self.resolvedWallpaper {
encoder.encode(TelegramWallpaperNativeCodable(resolvedWallpaper), forKey: "wallpaper")
} else {
encoder.encodeNil(forKey: "wallpaper")
}
}
public static func ==(lhs: PresentationLocalTheme, rhs: PresentationLocalTheme) -> Bool {
if lhs.title != rhs.title {
return false
}
if !lhs.resource.isEqual(to: rhs.resource) {
return false
}
if lhs.resolvedWallpaper != rhs.resolvedWallpaper {
return false
}
return true
}
}
public struct PresentationCloudTheme: Codable, Equatable {
public let theme: TelegramTheme
public let resolvedWallpaper: TelegramWallpaper?
public let creatorAccountId: AccountRecordId?
public init(theme: TelegramTheme, resolvedWallpaper: TelegramWallpaper?, creatorAccountId: AccountRecordId?) {
self.theme = theme
self.resolvedWallpaper = resolvedWallpaper
self.creatorAccountId = creatorAccountId
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.theme = (try container.decode(TelegramThemeNativeCodable.self, forKey: "theme")).value
self.resolvedWallpaper = (try container.decodeIfPresent(TelegramWallpaperNativeCodable.self, forKey: "wallpaper"))?.value
self.creatorAccountId = (try container.decodeIfPresent(Int64.self, forKey: "account")).flatMap(AccountRecordId.init(rawValue:))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(TelegramThemeNativeCodable(self.theme), forKey: "theme")
try container.encodeIfPresent(self.resolvedWallpaper.flatMap(TelegramWallpaperNativeCodable.init), forKey: "wallpaper")
try container.encodeIfPresent(self.creatorAccountId?.int64, forKey: "account")
}
public static func ==(lhs: PresentationCloudTheme, rhs: PresentationCloudTheme) -> Bool {
if lhs.theme != rhs.theme {
return false
}
if lhs.resolvedWallpaper != rhs.resolvedWallpaper {
return false
}
return true
}
}
public enum PresentationThemeReference: PostboxCoding, Equatable {
case builtin(PresentationBuiltinThemeReference)
case local(PresentationLocalTheme)
case cloud(PresentationCloudTheme)
public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("v", orElse: 0) {
case 0:
self = .builtin(PresentationBuiltinThemeReference(rawValue: decoder.decodeInt32ForKey("t", orElse: 0))!)
case 1:
if let localTheme = decoder.decodeObjectForKey("localTheme", decoder: { PresentationLocalTheme(decoder: $0) }) as? PresentationLocalTheme {
self = .local(localTheme)
} else {
self = .builtin(.dayClassic)
}
case 2:
if let cloudTheme = decoder.decode(PresentationCloudTheme.self, forKey: "cloudTheme") {
self = .cloud(cloudTheme)
} else {
self = .builtin(.dayClassic)
}
case 3:
self = .builtin(.dayClassic)
default:
assertionFailure()
self = .builtin(.dayClassic)
}
}
public func encode(_ encoder: PostboxEncoder) {
switch self {
case let .builtin(reference):
encoder.encodeInt32(0, forKey: "v")
encoder.encodeInt32(reference.rawValue, forKey: "t")
case let .local(theme):
encoder.encodeInt32(1, forKey: "v")
encoder.encodeObject(theme, forKey: "localTheme")
case let .cloud(theme):
encoder.encodeInt32(2, forKey: "v")
encoder.encode(theme, forKey: "cloudTheme")
}
}
public static func ==(lhs: PresentationThemeReference, rhs: PresentationThemeReference) -> Bool {
switch lhs {
case let .builtin(reference):
if case .builtin(reference) = rhs {
return true
} else {
return false
}
case let .local(lhsTheme):
if case let .local(rhsTheme) = rhs, lhsTheme == rhsTheme {
return true
} else {
return false
}
case let .cloud(lhsTheme):
if case let .cloud(rhsTheme) = rhs, lhsTheme == rhsTheme {
return true
} else {
return false
}
}
}
public var index: Int64 {
let namespace: Int32
let id: Int32
func themeId(for id: Int64) -> Int32 {
var acc: UInt32 = 0
let low = UInt32(UInt64(bitPattern: id) & (0xffffffff as UInt64))
let high = UInt32((UInt64(bitPattern: id) >> 32) & (0xffffffff as UInt64))
acc = (acc &* 20261) &+ high
acc = (acc &* 20261) &+ low
return Int32(bitPattern: acc & UInt32(0x7fffffff))
}
switch self {
case let .builtin(reference):
namespace = 0
id = reference.rawValue
case let .local(theme):
namespace = 1
id = themeId(for: theme.resource.fileId)
case let .cloud(theme):
namespace = 2
id = themeId(for: theme.theme.id)
}
return (Int64(namespace) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: id)))
}
public var generalThemeReference: PresentationThemeReference {
let generalThemeReference: PresentationThemeReference
if case let .cloud(theme) = self, let settings = theme.theme.settings?.first {
generalThemeReference = .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme))
} else {
generalThemeReference = self
}
return generalThemeReference
}
public var emoticon: String? {
switch self {
case .builtin(.dayClassic):
return "🏠"
case let .cloud(theme):
return theme.theme.emoticon
default:
return nil
}
}
}
public func coloredThemeIndex(reference: PresentationThemeReference, accentColor: PresentationThemeAccentColor?) -> Int64 {
if let accentColor = accentColor {
if case .builtin = reference {
return reference.index * 1000 &+ Int64(accentColor.index)
} else {
return reference.index &+ Int64(accentColor.index)
}
} else {
return reference.index
}
}
public enum PresentationFontSize: Int32, CaseIterable {
case extraSmall = 0
case small = 1
case regular = 2
case large = 3
case extraLarge = 4
case extraLargeX2 = 5
case medium = 6
}
public enum AutomaticThemeSwitchTimeBasedSetting: Codable, Equatable {
case manual(fromSeconds: Int32, toSeconds: Int32)
case automatic(latitude: Double, longitude: Double, localizedName: String)
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
switch try container.decode(Int32.self, forKey: "_t") {
case 0:
self = .manual(fromSeconds: try container.decode(Int32.self, forKey: "fromSeconds"), toSeconds: try container.decode(Int32.self, forKey: "toSeconds"))
case 1:
self = .automatic(latitude: try container.decode(Double.self, forKey: "latitude"), longitude: try container.decode(Double.self, forKey: "longitude"), localizedName: try container.decode(String.self, forKey: "localizedName"))
default:
assertionFailure()
self = .manual(fromSeconds: 0, toSeconds: 1)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
switch self {
case let .manual(fromSeconds, toSeconds):
try container.encode(0 as Int32, forKey: "_t")
try container.encode(fromSeconds, forKey: "fromSeconds")
try container.encode(toSeconds, forKey: "toSeconds")
case let .automatic(latitude, longitude, localizedName):
try container.encode(1 as Int32, forKey: "_t")
try container.encode(latitude, forKey: "latitude")
try container.encode(longitude, forKey: "longitude")
try container.encode(localizedName, forKey: "localizedName")
}
}
}
public enum AutomaticThemeSwitchTrigger: Codable, Equatable {
case system
case explicitNone
case timeBased(setting: AutomaticThemeSwitchTimeBasedSetting)
case brightness(threshold: Double)
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
switch try container.decode(Int32.self, forKey: "_t") {
case 0:
self = .system
case 1:
self = .timeBased(setting: try container.decode(AutomaticThemeSwitchTimeBasedSetting.self, forKey: "setting"))
case 2:
self = .brightness(threshold: try container.decode(Double.self, forKey: "threshold"))
case 3:
self = .explicitNone
default:
assertionFailure()
self = .system
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
switch self {
case .system:
try container.encode(0 as Int32, forKey: "_t")
case let .timeBased(setting):
try container.encode(1 as Int32, forKey: "_t")
try container.encode(setting, forKey: "setting")
case let .brightness(threshold):
try container.encode(2 as Int32, forKey: "_t")
try container.encode(threshold, forKey: "threshold")
case .explicitNone:
try container.encode(3 as Int32, forKey: "_t")
}
}
}
public struct AutomaticThemeSwitchSetting: Codable, Equatable {
public var force: Bool
public var trigger: AutomaticThemeSwitchTrigger
public var theme: PresentationThemeReference
public init(force: Bool, trigger: AutomaticThemeSwitchTrigger, theme: PresentationThemeReference) {
self.force = force
self.trigger = trigger
self.theme = theme
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.force = try container.decodeIfPresent(Bool.self, forKey: "force") ?? false
self.trigger = try container.decode(AutomaticThemeSwitchTrigger.self, forKey: "trigger")
if let themeData = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: "theme_v2") {
self.theme = PresentationThemeReference(decoder: PostboxDecoder(buffer: MemoryBuffer(data: themeData.data)))
} else {
self.theme = .builtin(.night)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.force, forKey: "force")
try container.encode(self.trigger, forKey: "trigger")
let themeData = PostboxEncoder().encodeObjectToRawData(self.theme)
try container.encode(themeData, forKey: "theme_v2")
}
}
public enum PresentationThemeBaseColor: Int32, CaseIterable {
case blue
case cyan
case green
case pink
case orange
case purple
case red
case yellow
case gray
case black
case white
case custom
case preset
case theme
public var color: UIColor {
let value: UInt32
switch self {
case .blue:
value = 0x0088ff
case .cyan:
value = 0x00c2ed
case .green:
value = 0x29b327
case .pink:
value = 0xeb6ca4
case .orange:
value = 0xf08200
case .purple:
value = 0x9472ee
case .red:
value = 0xd33213
case .yellow:
value = 0xedb400
case .gray:
value = 0x6d839e
case .black:
value = 0x000000
case .white:
value = 0xffffff
case .custom, .preset, .theme:
return .clear
}
return UIColor(rgb: value)
}
}
public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
public static func == (lhs: PresentationThemeAccentColor, rhs: PresentationThemeAccentColor) -> Bool {
return lhs.index == rhs.index && lhs.baseColor == rhs.baseColor && lhs.accentColor == rhs.accentColor && lhs.bubbleColors == rhs.bubbleColors
}
public var index: Int32
public var baseColor: PresentationThemeBaseColor
public var accentColor: UInt32?
public var bubbleColors: [UInt32]
public var wallpaper: TelegramWallpaper?
public var themeIndex: Int64?
public init(baseColor: PresentationThemeBaseColor) {
if baseColor != .preset && baseColor != .custom {
self.index = baseColor.rawValue + 10
} else {
self.index = -1
}
self.baseColor = baseColor
self.accentColor = nil
self.bubbleColors = []
self.wallpaper = nil
}
public init(index: Int32, baseColor: PresentationThemeBaseColor, accentColor: UInt32? = nil, bubbleColors: [UInt32] = [], wallpaper: TelegramWallpaper? = nil) {
self.index = index
self.baseColor = baseColor
self.accentColor = accentColor
self.bubbleColors = bubbleColors
self.wallpaper = wallpaper
}
public init(themeIndex: Int64) {
self.index = -1
self.baseColor = .theme
self.accentColor = nil
self.bubbleColors = []
self.wallpaper = nil
self.themeIndex = themeIndex
}
public init(decoder: PostboxDecoder) {
self.index = decoder.decodeInt32ForKey("i", orElse: -1)
self.baseColor = PresentationThemeBaseColor(rawValue: decoder.decodeInt32ForKey("b", orElse: 0)) ?? .blue
self.accentColor = decoder.decodeOptionalInt32ForKey("c").flatMap { UInt32(bitPattern: $0) }
let bubbleColors = decoder.decodeInt32ArrayForKey("bubbleColors")
if !bubbleColors.isEmpty {
self.bubbleColors = bubbleColors.map(UInt32.init(bitPattern:))
} else {
if let bubbleTopColor = decoder.decodeOptionalInt32ForKey("bt") {
if let bubbleBottomColor = decoder.decodeOptionalInt32ForKey("bb") {
self.bubbleColors = [UInt32(bitPattern: bubbleTopColor), UInt32(bitPattern: bubbleBottomColor)]
} else {
self.bubbleColors = [UInt32(bitPattern: bubbleTopColor)]
}
} else {
self.bubbleColors = []
}
}
self.wallpaper = decoder.decode(TelegramWallpaperNativeCodable.self, forKey: "w")?.value
self.themeIndex = decoder.decodeOptionalInt64ForKey("t")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.index, forKey: "i")
encoder.encodeInt32(self.baseColor.rawValue, forKey: "b")
if let value = self.accentColor {
encoder.encodeInt32(Int32(bitPattern: value), forKey: "c")
} else {
encoder.encodeNil(forKey: "c")
}
encoder.encodeInt32Array(self.bubbleColors.map(Int32.init(bitPattern:)), forKey: "bubbleColors")
if let wallpaper = self.wallpaper {
encoder.encode(TelegramWallpaperNativeCodable(wallpaper), forKey: "w")
} else {
encoder.encodeNil(forKey: "w")
}
if let themeIndex = self.themeIndex {
encoder.encodeInt64(themeIndex, forKey: "t")
} else {
encoder.encodeNil(forKey: "t")
}
}
public var color: UIColor {
if let value = self.accentColor {
return UIColor(rgb: UInt32(bitPattern: value))
} else {
return self.baseColor.color
}
}
public func colorFor(baseTheme: TelegramBaseTheme) -> UIColor {
if let value = self.accentColor {
return UIColor(rgb: UInt32(bitPattern: value))
} else {
if baseTheme == .night && self.baseColor == .blue {
return UIColor(rgb: 0x3e88f7)
} else {
return self.baseColor.color
}
}
}
public var customBubbleColors: [UInt32] {
return self.bubbleColors
}
public var plainBubbleColors: [UInt32] {
return self.bubbleColors
}
public func withUpdatedWallpaper(_ wallpaper: TelegramWallpaper?) -> PresentationThemeAccentColor {
return PresentationThemeAccentColor(index: self.index, baseColor: self.baseColor, accentColor: self.accentColor, bubbleColors: self.bubbleColors, wallpaper: wallpaper)
}
}
public struct PresentationChatBubbleSettings: Codable, Equatable {
public var mainRadius: Int32
public var auxiliaryRadius: Int32
public var mergeBubbleCorners: Bool
public static var `default`: PresentationChatBubbleSettings = PresentationChatBubbleSettings(mainRadius: 16, auxiliaryRadius: 8, mergeBubbleCorners: true)
public init(mainRadius: Int32, auxiliaryRadius: Int32, mergeBubbleCorners: Bool) {
self.mainRadius = mainRadius
self.auxiliaryRadius = auxiliaryRadius
self.mergeBubbleCorners = mergeBubbleCorners
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.mainRadius = try container.decodeIfPresent(Int32.self, forKey: "mainRadius") ?? 16
self.auxiliaryRadius = try container.decodeIfPresent(Int32.self, forKey: "auxiliaryRadius") ?? 8
self.mergeBubbleCorners = (try container.decodeIfPresent(Int32.self, forKey: "mergeBubbleCorners") ?? 1) != 0
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.mainRadius, forKey: "mainRadius")
try container.encode(self.auxiliaryRadius, forKey: "auxiliaryRadius")
try container.encode((self.mergeBubbleCorners ? 1 : 0) as Int32, forKey: "mergeBubbleCorners")
}
}
public struct PresentationThemeSettings: Codable {
private struct DictionaryKey: Codable, Hashable {
var key: Int64
init(_ key: Int64) {
self.key = key
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.key = try container.decode(Int64.self, forKey: "k")
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.key, forKey: "k")
}
}
public var theme: PresentationThemeReference
public var themePreferredBaseTheme: [Int64: TelegramBaseTheme]
public var themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
public var themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
public var useSystemFont: Bool
public var fontSize: PresentationFontSize
public var listsFontSize: PresentationFontSize
public var chatBubbleSettings: PresentationChatBubbleSettings
public var automaticThemeSwitchSetting: AutomaticThemeSwitchSetting
public var largeEmoji: Bool
public var reduceMotion: Bool
private func wallpaperResources(_ wallpaper: TelegramWallpaper) -> [MediaResourceId] {
switch wallpaper {
case let .image(representations, _):
return representations.map { $0.resource.id }
case let .file(file):
var resources: [MediaResourceId] = []
resources.append(file.file.resource.id)
resources.append(contentsOf: file.file.previewRepresentations.map { $0.resource.id })
return resources
default:
return []
}
}
public var relatedResources: [MediaResourceId] {
var resources: [MediaResourceId] = []
for (_, chatWallpaper) in self.themeSpecificChatWallpapers {
resources.append(contentsOf: wallpaperResources(chatWallpaper))
}
switch self.theme {
case .builtin:
break
case let .local(theme):
resources.append(theme.resource.id)
case let .cloud(theme):
if let file = theme.theme.file {
resources.append(file.resource.id)
}
if let chatWallpaper = theme.resolvedWallpaper {
resources.append(contentsOf: wallpaperResources(chatWallpaper))
}
}
return resources
}
public static var defaultSettings: PresentationThemeSettings {
return PresentationThemeSettings(theme: .builtin(.dayClassic), themePreferredBaseTheme: [:], themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], useSystemFont: true, fontSize: .regular, listsFontSize: .regular, chatBubbleSettings: .default, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(force: false, trigger: .system, theme: .builtin(.night)), largeEmoji: true, reduceMotion: false)
}
public init(theme: PresentationThemeReference, themePreferredBaseTheme: [Int64: TelegramBaseTheme], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], useSystemFont: Bool, fontSize: PresentationFontSize, listsFontSize: PresentationFontSize, chatBubbleSettings: PresentationChatBubbleSettings, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, reduceMotion: Bool) {
self.theme = theme
self.themePreferredBaseTheme = themePreferredBaseTheme
self.themeSpecificAccentColors = themeSpecificAccentColors
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
self.useSystemFont = useSystemFont
self.fontSize = fontSize
self.listsFontSize = listsFontSize
self.chatBubbleSettings = chatBubbleSettings
self.automaticThemeSwitchSetting = automaticThemeSwitchSetting
self.largeEmoji = largeEmoji
self.reduceMotion = reduceMotion
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
if let themeData = try container.decodeIfPresent(AdaptedPostboxDecoder.RawObjectData.self, forKey: "t") {
self.theme = PresentationThemeReference(decoder: PostboxDecoder(buffer: MemoryBuffer(data: themeData.data)))
} else {
self.theme = .builtin(.dayClassic)
}
var mappedThemePreferredBaseTheme: [Int64: TelegramBaseTheme] = [:]
let themePreferredBaseThemeDict = try container.decodeIfPresent([Int64: Int64].self, forKey: "themePreferredBaseTheme") ?? [:]
for (key, value) in themePreferredBaseThemeDict {
if let baseTheme = TelegramBaseTheme(rawValue: Int32(clamping: value)) {
mappedThemePreferredBaseTheme[key] = baseTheme
}
}
self.themePreferredBaseTheme = mappedThemePreferredBaseTheme
let themeSpecificChatWallpapersDict = try container.decode([DictionaryKey: TelegramWallpaperNativeCodable].self, forKey: "themeSpecificChatWallpapers")
var mappedThemeSpecificChatWallpapers: [Int64: TelegramWallpaper] = [:]
for (key, value) in themeSpecificChatWallpapersDict {
mappedThemeSpecificChatWallpapers[key.key] = value.value
}
self.themeSpecificChatWallpapers = mappedThemeSpecificChatWallpapers
let themeSpecificAccentColorsDict = try container.decode([DictionaryKey: AdaptedPostboxDecoder.RawObjectData].self, forKey: "themeSpecificAccentColors")
var mappedThemeSpecificAccentColors: [Int64: PresentationThemeAccentColor] = [:]
for (key, value) in themeSpecificAccentColorsDict {
let innerDecoder = PostboxDecoder(buffer: MemoryBuffer(data: value.data))
mappedThemeSpecificAccentColors[key.key] = PresentationThemeAccentColor(decoder: innerDecoder)
}
self.themeSpecificAccentColors = mappedThemeSpecificAccentColors
self.useSystemFont = (try container.decodeIfPresent(Int32.self, forKey: "useSystemFont") ?? 1) != 0
let fontSize = PresentationFontSize(rawValue: try container.decodeIfPresent(Int32.self, forKey: "f") ?? PresentationFontSize.regular.rawValue) ?? .regular
self.fontSize = fontSize
self.listsFontSize = PresentationFontSize(rawValue: try container.decodeIfPresent(Int32.self, forKey: "lf") ?? PresentationFontSize.regular.rawValue) ?? fontSize
self.chatBubbleSettings = try container.decodeIfPresent(PresentationChatBubbleSettings.self, forKey: "chatBubbleSettings") ?? PresentationChatBubbleSettings.default
self.automaticThemeSwitchSetting = try container.decodeIfPresent(AutomaticThemeSwitchSetting.self, forKey: "automaticThemeSwitchSetting") ?? AutomaticThemeSwitchSetting(force: false, trigger: .system, theme: .builtin(.night))
self.largeEmoji = try container.decodeIfPresent(Bool.self, forKey: "largeEmoji") ?? true
self.reduceMotion = try container.decodeIfPresent(Bool.self, forKey: "reduceMotion") ?? false
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(PostboxEncoder().encodeObjectToRawData(self.theme), forKey: "t")
var mappedThemePreferredBaseTheme: [Int64: Int64] = [:]
for (key, value) in self.themePreferredBaseTheme {
mappedThemePreferredBaseTheme[key] = Int64(value.rawValue)
}
try container.encode(mappedThemePreferredBaseTheme, forKey: "themePreferredBaseTheme")
var mappedThemeSpecificAccentColors: [DictionaryKey: AdaptedPostboxEncoder.RawObjectData] = [:]
for (key, value) in self.themeSpecificAccentColors {
mappedThemeSpecificAccentColors[DictionaryKey(key)] = PostboxEncoder().encodeObjectToRawData(value)
}
try container.encode(mappedThemeSpecificAccentColors, forKey: "themeSpecificAccentColors")
var mappedThemeSpecificChatWallpapers: [DictionaryKey: TelegramWallpaperNativeCodable] = [:]
for (key, value) in self.themeSpecificChatWallpapers {
mappedThemeSpecificChatWallpapers[DictionaryKey(key)] = TelegramWallpaperNativeCodable(value)
}
try container.encode(mappedThemeSpecificChatWallpapers, forKey: "themeSpecificChatWallpapers")
try container.encode((self.useSystemFont ? 1 : 0) as Int32, forKey: "useSystemFont")
try container.encode(self.fontSize.rawValue, forKey: "f")
try container.encode(self.listsFontSize.rawValue, forKey: "lf")
try container.encode(self.chatBubbleSettings, forKey: "chatBubbleSettings")
try container.encode(self.automaticThemeSwitchSetting, forKey: "automaticThemeSwitchSetting")
try container.encode(self.largeEmoji, forKey: "largeEmoji")
try container.encode(self.reduceMotion, forKey: "reduceMotion")
}
public static func ==(lhs: PresentationThemeSettings, rhs: PresentationThemeSettings) -> Bool {
return lhs.theme == rhs.theme && lhs.themePreferredBaseTheme == rhs.themePreferredBaseTheme && lhs.themeSpecificAccentColors == rhs.themeSpecificAccentColors && lhs.themeSpecificChatWallpapers == rhs.themeSpecificChatWallpapers && lhs.useSystemFont == rhs.useSystemFont && lhs.fontSize == rhs.fontSize && lhs.listsFontSize == rhs.listsFontSize && lhs.chatBubbleSettings == rhs.chatBubbleSettings && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.largeEmoji == rhs.largeEmoji && lhs.reduceMotion == rhs.reduceMotion
}
public func withUpdatedTheme(_ theme: PresentationThemeReference) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: theme, themePreferredBaseTheme: self.themePreferredBaseTheme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, reduceMotion: self.reduceMotion)
}
public func withUpdatedThemePreferredBaseTheme(_ themePreferredBaseTheme: [Int64: TelegramBaseTheme]) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: self.theme, themePreferredBaseTheme: themePreferredBaseTheme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, reduceMotion: self.reduceMotion)
}
public func withUpdatedThemeSpecificAccentColors(_ themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: self.theme, themePreferredBaseTheme: self.themePreferredBaseTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, reduceMotion: self.reduceMotion)
}
public func withUpdatedThemeSpecificChatWallpapers(_ themeSpecificChatWallpapers: [Int64: TelegramWallpaper]) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: self.theme, themePreferredBaseTheme: self.themePreferredBaseTheme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, reduceMotion: self.reduceMotion)
}
public func withUpdatedUseSystemFont(_ useSystemFont: Bool) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: self.theme, themePreferredBaseTheme: self.themePreferredBaseTheme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, reduceMotion: self.reduceMotion)
}
public func withUpdatedFontSizes(fontSize: PresentationFontSize, listsFontSize: PresentationFontSize) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: self.theme, themePreferredBaseTheme: self.themePreferredBaseTheme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: fontSize, listsFontSize: listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, reduceMotion: self.reduceMotion)
}
public func withUpdatedChatBubbleSettings(_ chatBubbleSettings: PresentationChatBubbleSettings) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: self.theme, themePreferredBaseTheme: self.themePreferredBaseTheme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, reduceMotion: self.reduceMotion)
}
public func withUpdatedAutomaticThemeSwitchSetting(_ automaticThemeSwitchSetting: AutomaticThemeSwitchSetting) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: self.theme, themePreferredBaseTheme: self.themePreferredBaseTheme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, reduceMotion: self.reduceMotion)
}
public func withUpdatedLargeEmoji(_ largeEmoji: Bool) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: self.theme, themePreferredBaseTheme: self.themePreferredBaseTheme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: largeEmoji, reduceMotion: self.reduceMotion)
}
public func withUpdatedReduceMotion(_ reduceMotion: Bool) -> PresentationThemeSettings {
return PresentationThemeSettings(theme: self.theme, themePreferredBaseTheme: self.themePreferredBaseTheme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, reduceMotion: reduceMotion)
}
}
public func updatePresentationThemeSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (PresentationThemeSettings) -> PresentationThemeSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
let currentSettings: PresentationThemeSettings
if let entry = entry?.get(PresentationThemeSettings.self) {
currentSettings = entry
} else {
currentSettings = PresentationThemeSettings.defaultSettings
}
return PreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,49 @@
import Foundation
import Postbox
import TelegramCore
import SwiftSignalKit
public enum RenderedTotalUnreadCountType {
case raw
case filtered
}
public func renderedTotalUnreadCount(inAppNotificationSettings: InAppNotificationSettings, transaction: Transaction) -> (Int32, RenderedTotalUnreadCountType) {
let totalUnreadState = transaction.getTotalUnreadState(groupId: .root)
return renderedTotalUnreadCount(inAppSettings: inAppNotificationSettings, totalUnreadState: totalUnreadState)
}
public func renderedTotalUnreadCount(inAppSettings: InAppNotificationSettings, totalUnreadState: ChatListTotalUnreadState) -> (Int32, RenderedTotalUnreadCountType) {
let type: RenderedTotalUnreadCountType
switch inAppSettings.totalUnreadCountDisplayStyle {
case .filtered:
type = .filtered
}
return (totalUnreadState.count(for: inAppSettings.totalUnreadCountDisplayStyle.category, in: inAppSettings.totalUnreadCountDisplayCategory.statsType, with: inAppSettings.totalUnreadCountIncludeTags), type)
}
public func renderedTotalUnreadCount(accountManager: AccountManager<TelegramAccountManagerTypes>, engine: TelegramEngine) -> Signal<(Int32, RenderedTotalUnreadCountType), NoError> {
return combineLatest(
accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings]),
engine.data.subscribe(
TelegramEngine.EngineData.Item.Messages.TotalReadCounters()
)
)
|> map { sharedData, totalReadCounters -> (Int32, RenderedTotalUnreadCountType) in
let inAppSettings: InAppNotificationSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings]?.get(InAppNotificationSettings.self) {
inAppSettings = value
} else {
inAppSettings = .defaultSettings
}
let type: RenderedTotalUnreadCountType
switch inAppSettings.totalUnreadCountDisplayStyle {
case .filtered:
type = .filtered
}
return (totalReadCounters.count(for: inAppSettings.totalUnreadCountDisplayStyle.category, in: inAppSettings.totalUnreadCountDisplayCategory.statsType, with: inAppSettings.totalUnreadCountIncludeTags), type)
}
|> distinctUntilChanged(isEqual: { lhs, rhs in
return lhs == rhs
})
}
@@ -0,0 +1,75 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public enum EmojiStickerSuggestionMode: Int32 {
case none
case all
case installed
}
public struct StickerSettings: Codable, Equatable {
public var emojiStickerSuggestionMode: EmojiStickerSuggestionMode
public var suggestAnimatedEmoji: Bool
public var dynamicPackOrder: Bool
public static var defaultSettings: StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: .all, suggestAnimatedEmoji: true, dynamicPackOrder: true)
}
init(emojiStickerSuggestionMode: EmojiStickerSuggestionMode, suggestAnimatedEmoji: Bool, dynamicPackOrder: Bool) {
self.emojiStickerSuggestionMode = emojiStickerSuggestionMode
self.suggestAnimatedEmoji = suggestAnimatedEmoji
self.dynamicPackOrder = dynamicPackOrder
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.emojiStickerSuggestionMode = EmojiStickerSuggestionMode(rawValue: try container.decode(Int32.self, forKey: "emojiStickerSuggestionMode"))!
self.suggestAnimatedEmoji = try container.decodeIfPresent(Bool.self, forKey: "suggestAnimatedEmoji") ?? true
self.dynamicPackOrder = try container.decodeIfPresent(Bool.self, forKey: "dynamicPackOrder") ?? true
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.emojiStickerSuggestionMode.rawValue, forKey: "emojiStickerSuggestionMode")
try container.encode(self.suggestAnimatedEmoji, forKey: "suggestAnimatedEmoji")
try container.encode(self.dynamicPackOrder, forKey: "dynamicPackOrder")
}
public static func ==(lhs: StickerSettings, rhs: StickerSettings) -> Bool {
return lhs.emojiStickerSuggestionMode == rhs.emojiStickerSuggestionMode && lhs.suggestAnimatedEmoji == rhs.suggestAnimatedEmoji && lhs.dynamicPackOrder == rhs.dynamicPackOrder
}
public func withUpdatedEmojiStickerSuggestionMode(_ emojiStickerSuggestionMode: EmojiStickerSuggestionMode) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: emojiStickerSuggestionMode, suggestAnimatedEmoji: self.suggestAnimatedEmoji, dynamicPackOrder: self.dynamicPackOrder)
}
public func withUpdatedLoopAnimatedStickers(_ loopAnimatedStickers: Bool) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, suggestAnimatedEmoji: self.suggestAnimatedEmoji, dynamicPackOrder: self.dynamicPackOrder)
}
public func withUpdatedSuggestAnimatedEmoji(_ suggestAnimatedEmoji: Bool) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, suggestAnimatedEmoji: suggestAnimatedEmoji, dynamicPackOrder: self.dynamicPackOrder)
}
public func withUpdatedDynamicPackOrder(_ dynamicPackOrder: Bool) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, suggestAnimatedEmoji: self.suggestAnimatedEmoji, dynamicPackOrder: dynamicPackOrder)
}
}
public func updateStickerSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (StickerSettings) -> StickerSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.stickerSettings, { entry in
let currentSettings: StickerSettings
if let entry = entry?.get(StickerSettings.self) {
currentSettings = entry
} else {
currentSettings = StickerSettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,19 @@
//
// TelegramUIPreferences.h
// TelegramUIPreferences
//
// Created by Peter on 6/13/19.
// Copyright © 2019 Telegram LLP. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for TelegramUIPreferences.
FOUNDATION_EXPORT double TelegramUIPreferencesVersionNumber;
//! Project version string for TelegramUIPreferences.
FOUNDATION_EXPORT const unsigned char TelegramUIPreferencesVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <TelegramUIPreferences/PublicHeader.h>
@@ -0,0 +1,65 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public struct TranslationSettings: Codable, Equatable {
public var showTranslate: Bool
public var translateChats: Bool
public var ignoredLanguages: [String]?
public static var defaultSettings: TranslationSettings {
return TranslationSettings(showTranslate: false, translateChats: true, ignoredLanguages: nil)
}
init(showTranslate: Bool, translateChats: Bool, ignoredLanguages: [String]?) {
self.showTranslate = showTranslate
self.translateChats = translateChats
self.ignoredLanguages = ignoredLanguages
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.showTranslate = try container.decodeIfPresent(Bool.self, forKey: "showTranslate") ?? false
self.translateChats = try container.decodeIfPresent(Bool.self, forKey: "translateChats") ?? true
self.ignoredLanguages = try container.decodeIfPresent([String].self, forKey: "ignoredLanguages")
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.showTranslate, forKey: "showTranslate")
try container.encode(self.translateChats, forKey: "translateChats")
try container.encodeIfPresent(self.ignoredLanguages, forKey: "ignoredLanguages")
}
public static func ==(lhs: TranslationSettings, rhs: TranslationSettings) -> Bool {
return lhs.showTranslate == rhs.showTranslate && lhs.translateChats == rhs.translateChats && lhs.ignoredLanguages == rhs.ignoredLanguages
}
public func withUpdatedShowTranslate(_ showTranslate: Bool) -> TranslationSettings {
return TranslationSettings(showTranslate: showTranslate, translateChats: self.translateChats, ignoredLanguages: self.ignoredLanguages)
}
public func withUpdatedTranslateChats(_ translateChats: Bool) -> TranslationSettings {
return TranslationSettings(showTranslate: self.showTranslate, translateChats: translateChats, ignoredLanguages: self.ignoredLanguages)
}
public func withUpdatedIgnoredLanguages(_ ignoredLanguages: [String]?) -> TranslationSettings {
return TranslationSettings(showTranslate: self.showTranslate, translateChats: self.translateChats, ignoredLanguages: ignoredLanguages)
}
}
public func updateTranslationSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (TranslationSettings) -> TranslationSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.translationSettings, { entry in
let currentSettings: TranslationSettings
if let entry = entry?.get(TranslationSettings.self) {
currentSettings = entry
} else {
currentSettings = TranslationSettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,81 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public func effectiveDataSaving(for settings: VoiceCallSettings?, autodownloadSettings: AutodownloadSettings) -> VoiceCallDataSaving {
if let settings = settings {
if case .default = settings.dataSaving {
switch (autodownloadSettings.mediumPreset.lessDataForPhoneCalls, autodownloadSettings.highPreset.lessDataForPhoneCalls) {
case (true, true):
return .always
case (true, false):
return .cellular
default:
return .never
}
} else {
return settings.dataSaving
}
} else {
return .never
}
}
public enum VoiceCallDataSaving: Int32 {
case never
case cellular
case always
case `default`
}
public struct VoiceCallSettings: Codable, Equatable {
public var dataSaving: VoiceCallDataSaving
public var enableSystemIntegration: Bool
public static var defaultSettings: VoiceCallSettings {
return VoiceCallSettings(dataSaving: .default, enableSystemIntegration: true)
}
public init(dataSaving: VoiceCallDataSaving, enableSystemIntegration: Bool) {
self.dataSaving = dataSaving
self.enableSystemIntegration = enableSystemIntegration
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.dataSaving = VoiceCallDataSaving(rawValue: try container.decode(Int32.self, forKey: "ds")) ?? .default
self.enableSystemIntegration = (try container.decode(Int32.self, forKey: "enableSystemIntegration")) != 0
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.dataSaving.rawValue, forKey: "ds")
try container.encode((self.enableSystemIntegration ? 1 : 0) as Int32, forKey: "enableSystemIntegration")
}
public static func ==(lhs: VoiceCallSettings, rhs: VoiceCallSettings) -> Bool {
if lhs.dataSaving != rhs.dataSaving {
return false
}
if lhs.enableSystemIntegration != rhs.enableSystemIntegration {
return false
}
return true
}
}
public func updateVoiceCallSettingsSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (VoiceCallSettings) -> VoiceCallSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.voiceCallSettings, { entry in
let currentSettings: VoiceCallSettings
if let entry = entry?.get(VoiceCallSettings.self) {
currentSettings = entry
} else {
currentSettings = VoiceCallSettings.defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,65 @@
import Foundation
import Postbox
import TelegramCore
import SwiftSignalKit
public struct WatchPresetSettings: Codable, Equatable {
public var customPresets: [String : String]
public static var defaultSettings: WatchPresetSettings {
return WatchPresetSettings(presets: [:])
}
public init(presets: [String : String]) {
self.customPresets = presets
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
let keys = try container.decode([String].self, forKey: "presetKeys")
let values = try container.decode([String].self, forKey: "presetValues")
if keys.count == values.count {
var presets: [String : String] = [:]
for i in 0 ..< keys.count {
presets[keys[i]] = values[i]
}
self.customPresets = presets
} else {
self.customPresets = [:]
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
let keys = self.customPresets.keys.sorted()
let values = keys.reduce([String]()) { (values, index) in
var values = values
if let value = self.customPresets[index] {
values.append(value)
}
return values
}
try container.encode(keys, forKey: "presetKeys")
try container.encode(values, forKey: "presetValues")
}
public static func ==(lhs: WatchPresetSettings, rhs: WatchPresetSettings) -> Bool {
return lhs.customPresets == rhs.customPresets
}
}
public func updateWatchPresetSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (WatchPresetSettings) -> WatchPresetSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.watchPresetSettings, { entry in
let currentSettings: WatchPresetSettings
if let entry = entry?.get(WatchPresetSettings.self) {
currentSettings = entry
} else {
currentSettings = WatchPresetSettings.defaultSettings
}
return PreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,96 @@
import Foundation
import Postbox
import TelegramCore
import SwiftSignalKit
public struct WebBrowserException: Codable, Equatable {
public let domain: String
public let title: String
public let icon: TelegramMediaImage?
public init(domain: String, title: String, icon: TelegramMediaImage?) {
self.domain = domain
self.title = title
self.icon = icon
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.domain = try container.decode(String.self, forKey: "domain")
self.title = try container.decode(String.self, forKey: "title")
self.icon = try container.decodeIfPresent(TelegramMediaImage.self, forKey: "icon")
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.domain, forKey: "domain")
try container.encode(self.title, forKey: "title")
if let icon = self.icon {
try container.encode(icon, forKey: "icon")
} else {
try container.encodeNil(forKey: "icon")
}
}
}
public struct WebBrowserSettings: Codable, Equatable {
public let defaultWebBrowser: String?
public let exceptions: [WebBrowserException]
public static var defaultSettings: WebBrowserSettings {
return WebBrowserSettings(defaultWebBrowser: nil, exceptions: [])
}
public init(defaultWebBrowser: String?, exceptions: [WebBrowserException]) {
self.defaultWebBrowser = defaultWebBrowser
self.exceptions = exceptions
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.defaultWebBrowser = try? container.decodeIfPresent(String.self, forKey: "defaultWebBrowser")
self.exceptions = (try? container.decodeIfPresent([WebBrowserException].self, forKey: "exceptions")) ?? []
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encodeIfPresent(self.defaultWebBrowser, forKey: "defaultWebBrowser")
try container.encode(self.exceptions, forKey: "exceptions")
}
public static func ==(lhs: WebBrowserSettings, rhs: WebBrowserSettings) -> Bool {
if lhs.defaultWebBrowser != rhs.defaultWebBrowser {
return false
}
if lhs.exceptions != rhs.exceptions {
return false
}
return true
}
public func withUpdatedDefaultWebBrowser(_ defaultWebBrowser: String?) -> WebBrowserSettings {
return WebBrowserSettings(defaultWebBrowser: defaultWebBrowser, exceptions: self.exceptions)
}
public func withUpdatedExceptions(_ exceptions: [WebBrowserException]) -> WebBrowserSettings {
return WebBrowserSettings(defaultWebBrowser: self.defaultWebBrowser, exceptions: exceptions)
}
}
public func updateWebBrowserSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (WebBrowserSettings) -> WebBrowserSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.webBrowserSettings, { entry in
let currentSettings: WebBrowserSettings
if let entry = entry?.get(WebBrowserSettings.self) {
currentSettings = entry
} else {
currentSettings = WebBrowserSettings.defaultSettings
}
return PreferencesEntry(f(currentSettings))
})
}
}
@@ -0,0 +1,47 @@
import Foundation
import Postbox
import TelegramCore
import SwiftSignalKit
public enum WebSearchScope: Int32 {
case images
case gifs
}
public struct WebSearchSettings: Codable, Equatable {
public var scope: WebSearchScope
public static var defaultSettings: WebSearchSettings {
return WebSearchSettings(scope: .images)
}
public init(scope: WebSearchScope) {
self.scope = scope
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.scope = WebSearchScope(rawValue: try container.decode(Int32.self, forKey: "scope")) ?? .images
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.scope.rawValue, forKey: "scope")
}
}
public func updateWebSearchSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (WebSearchSettings) -> WebSearchSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.webSearchSettings, { entry in
let currentSettings: WebSearchSettings
if let entry = entry?.get(WebSearchSettings.self) {
currentSettings = entry
} else {
currentSettings = .defaultSettings
}
return PreferencesEntry(f(currentSettings))
})
}
}