Swiftgram/submodules/TelegramUI/Sources/ThemeUpdateManager.swift
2023-09-21 00:12:53 +02:00

158 lines
9.1 KiB
Swift

import Foundation
import UIKit
import Postbox
import TelegramCore
import SwiftSignalKit
import TelegramPresentationData
import TelegramUIPreferences
import MediaResources
import WallpaperResources
import AccountContext
private final class ThemeUpdateManagerContext {
let themeReference: PresentationThemeReference
private let disposable: Disposable
let isAutoNight: Bool
init(themeReference: PresentationThemeReference, disposable: Disposable, isAutoNight: Bool) {
self.themeReference = themeReference
self.disposable = disposable
self.isAutoNight = isAutoNight
}
deinit {
self.disposable.dispose()
}
}
final class ThemeUpdateManagerImpl: ThemeUpdateManager {
private let sharedContext: SharedAccountContext
private let account: Account
private var contexts: [Int64: ThemeUpdateManagerContext] = [:]
private let queue = Queue()
private var disposable: Disposable?
private var currentThemeSettings: PresentationThemeSettings?
init(sharedContext: SharedAccountContext, account: Account) {
self.sharedContext = sharedContext
self.account = account
self.disposable = (sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings])
|> map { sharedData -> PresentationThemeSettings in
return sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) ?? PresentationThemeSettings.defaultSettings
}
|> deliverOn(queue)).startStrict(next: { [weak self] themeSettings in
self?.presentationThemeSettingsUpdated(themeSettings)
})
}
deinit {
self.disposable?.dispose()
}
private func presentationThemeSettingsUpdated(_ themeSettings: PresentationThemeSettings) {
let previousThemeSettings = self.currentThemeSettings
self.currentThemeSettings = themeSettings
var previousIds = Set<Int64>()
if let previousThemeSettings = previousThemeSettings {
previousIds.insert(previousThemeSettings.theme.index)
}
var validIds = Set<Int64>()
var themes: [Int64: (PresentationThemeReference, Bool)] = [:]
if case .cloud = themeSettings.theme {
validIds.insert(themeSettings.theme.index)
themes[themeSettings.theme.index] = (themeSettings.theme, false)
}
if case .cloud = themeSettings.automaticThemeSwitchSetting.theme, themeSettings.automaticThemeSwitchSetting.trigger != .explicitNone {
validIds.insert(themeSettings.automaticThemeSwitchSetting.theme.index)
themes[themeSettings.automaticThemeSwitchSetting.theme.index] = (themeSettings.automaticThemeSwitchSetting.theme, true)
}
if previousIds != validIds {
for id in validIds {
if let _ = self.contexts[id] {
} else if let (theme, isAutoNight) = themes[id], case let .cloud(info) = theme {
var currentTheme = theme
let account = self.account
let accountManager = self.sharedContext.accountManager
let disposable = (actualizedTheme(account: account, accountManager: accountManager, theme: info.theme)
|> mapToSignal { theme -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
guard let file = theme.file else {
return .complete()
}
return telegramThemeData(account: account, accountManager: accountManager, reference: .standalone(resource: file.resource))
|> mapToSignal { data -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
guard let data = data, let presentationTheme = makePresentationTheme(data: data) else {
return .complete()
}
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
resolvedWallpaper = cachedWallpaper(account: account, slug: file.slug, settings: file.settings)
|> map { wallpaper in
return wallpaper?.wallpaper
}
} else {
resolvedWallpaper = .single(nil)
}
return resolvedWallpaper
|> mapToSignal { wallpaper -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
if let wallpaper = wallpaper, case let .file(file) = wallpaper {
var convertedRepresentations: [ImageRepresentationWithReference] = []
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|> mapToSignal { _, fullSizeData, complete -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
guard complete, let fullSizeData = fullSizeData else {
return .complete()
}
accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData, synchronous: true)
return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper, creatorAccountId: theme.isCreator ? account.id : nil)), presentationTheme))
}
} else {
return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: theme.isCreator ? account.id : nil)), presentationTheme))
}
}
}
}).start(next: { updatedTheme, presentationTheme in
if updatedTheme != currentTheme {
currentTheme = updatedTheme
let _ = (accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
let current: PresentationThemeSettings
if let entry = entry?.get(PresentationThemeSettings.self) {
current = entry
} else {
current = PresentationThemeSettings.defaultSettings
}
var theme = current.theme
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
if isAutoNight {
automaticThemeSwitchSetting.theme = updatedTheme
} else {
theme = updatedTheme
}
return PreferencesEntry(PresentationThemeSettings(theme: theme, themePreferredBaseTheme: current.themePreferredBaseTheme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, chatBubbleSettings: current.chatBubbleSettings, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, reduceMotion: current.reduceMotion))
})
}).start()
}
})
self.contexts[id] = ThemeUpdateManagerContext(themeReference: theme, disposable: disposable, isAutoNight: isAutoNight)
}
}
for id in previousIds {
if !validIds.contains(id) {
self.contexts[id] = nil
}
}
}
}
}