Telegram-iOS/submodules/TelegramUI/Sources/WallpaperUploadManager.swift
isaac 2ac8a490b0 Postbox -> TelegramEngine waves 94-102 (squashed)
Adds AccountManagerResources facade in TelegramCore mirroring
TelegramEngine.Resources, drains the entire accountManager-side
mediaBox refactor across consumer modules, and validates the
Shape-C/D handle-migration pattern (account: Account ->
engine: TelegramEngine) on three classes.

Wave 94 (AccountManagerResources facade + 8 sites): new
AccountManagerResources class in TelegramCore with
storeResourceData, completedResourcePath (resource + id overloads),
status (with approximateSynchronousValue), moveResourceData,
fetch. Migrate EditThemeController (5 storeResourceData + 2
completedResourcePath) and WebBrowserSettingsController (1
storeResourceData).

Wave 95 (10 sites): WallpaperUtils.swift sweep, 5 distinct
storeResourceData patterns, replace_all on the two repeated ones.

Wave 96 (7 sites): theme controller triple — TPC (1 store + 2
moveResourceData), TPCN (1 status + 1 fetch + closure type
MediaResourceStatus -> EngineMediaResource.FetchStatus + rename
account local to engine), TAC (1 completedResourcePath + 1 store).

Wave 97 (9 sites): WallpaperGalleryController.swift, 4
completedResourcePath + 5 storeResourceData via replace_all.

Wave 98 (2 files): WallpaperGallery status-type cascade
Promise<MediaResourceStatus> -> Promise<EngineMediaResource.FetchStatus>
across WallpaperGalleryItem.swift + WallpaperGalleryController.swift.
Drops cosmetic `_asStatus()` unwrap.

Wave 99 (2 facade extensions + 7 sites): adds
AccountManagerResources.data() and approximateSynchronousValue
to engine.resources.status(). Migrates OpenResolvedUrl (data +
storeResourceData with .complete -> .isComplete cascade),
SettingsThemeWallpaperNode (paired engine + accountManager
status), WallpaperUploadManager, OpenChatMessage,
LiveChatReactionStreamView.

Wave 100 (Shape-C/D, free function): legacyWebSearchItem
account: Account -> engine: TelegramEngine. 4 files.

Wave 101 (Shape-C/D, small class): VerticalListContextResults
ChatInputPanelItem stored field migration. 2 files. Drops
`|> map(EngineMediaResource.FetchStatus.init)` redundancy.

Wave 102 (Shape-C/D, medium class with weak-context retention):
SharedMediaPlayer account: Account -> engine: TelegramEngine.
Engine field is intentionally a strong handle since the class
stores context as `weak var`. 2 files.

After this squash the accountManager-side mediaBox refactor is
essentially complete; only Postbox-protocol-blocked
cachedResourceRepresentation triple remains as the foundational
deferred item.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 02:46:44 +04:00

157 lines
8.4 KiB
Swift

import Foundation
import UIKit
import Postbox
import TelegramCore
import SwiftSignalKit
import TelegramPresentationData
import TelegramUIPreferences
import MediaResources
import AccountContext
private extension TelegramWallpaper {
var mainResource: MediaResource? {
switch self {
case let .image(representations, _):
return largestImageRepresentation(representations)?.resource
case let .file(file):
return file.file.resource
default:
return nil
}
}
}
private final class WallpaperUploadContext {
let wallpaper: TelegramWallpaper
private let disposable: Disposable
init(wallpaper: TelegramWallpaper, disposable: Disposable) {
self.wallpaper = wallpaper
self.disposable = disposable
}
deinit {
self.disposable.dispose()
}
}
private func areMediaResourcesEqual(_ lhs: MediaResource?, _ rhs: MediaResource?) -> Bool {
if let lhs = lhs, let rhs = rhs {
return lhs.isEqual(to: rhs)
} else {
return false
}
}
final class WallpaperUploadManagerImpl: WallpaperUploadManager {
private let sharedContext: SharedAccountContext
private let account: Account
private var context: WallpaperUploadContext?
private let presentationDataDisposable = MetaDisposable()
private var currentPresentationData: PresentationData?
private let statePromise = Promise<WallpaperUploadManagerStatus>(.none)
init(sharedContext: SharedAccountContext, account: Account, presentationData: Signal<PresentationData, NoError>) {
self.sharedContext = sharedContext
self.account = account
self.presentationDataDisposable.set(presentationData.startStrict(next: { [weak self] presentationData in
if let strongSelf = self {
strongSelf.presentationDataUpdated(presentationData)
}
}))
}
deinit {
self.presentationDataDisposable.dispose()
}
func stateSignal() -> Signal<WallpaperUploadManagerStatus, NoError> {
return self.statePromise.get()
}
func presentationDataUpdated(_ presentationData: PresentationData) {
let previousPresentationData = self.currentPresentationData
self.currentPresentationData = presentationData
let currentWallpaper = presentationData.chatWallpaper
if previousPresentationData?.chatWallpaper != currentWallpaper {
if case .image = presentationData.chatWallpaper, let currentResource = currentWallpaper.mainResource {
if areMediaResourcesEqual(self.context?.wallpaper.mainResource, currentResource) {
} else {
let disposable = MetaDisposable()
self.context = WallpaperUploadContext(wallpaper: currentWallpaper, disposable: disposable)
let statePromise = Promise<WallpaperUploadManagerStatus>(.uploading(currentWallpaper, 0.0))
self.statePromise.set(statePromise.get())
let sharedContext = self.sharedContext
let account = self.account
let uploadSignal = uploadWallpaper(account: account, resource: currentResource, settings: currentWallpaper.settings ?? WallpaperSettings(), forChat: false)
|> map { result -> UploadWallpaperStatus in
switch result {
case let .complete(wallpaper):
if case let .file(file) = wallpaper {
sharedContext.accountManager.resources.moveResourceData(from: EngineMediaResource.Id(currentResource.id), to: EngineMediaResource.Id(file.file.resource.id))
account.postbox.mediaBox.moveResourceData(from: currentResource.id, to: file.file.resource.id)
}
default:
break
}
return result
}
let autoNightModeTriggered = presentationData.autoNightModeTriggered
disposable.set(uploadSignal.start(next: { status in
if case let .complete(wallpaper) = status {
let updateWallpaper: (TelegramWallpaper) -> Void = { wallpaper in
if let resource = wallpaper.mainResource {
let _ = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start(completed: {})
let _ = sharedContext.accountManager.mediaBox.cachedResourceRepresentation(resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start(completed: {})
}
let _ = (updatePresentationThemeSettingsInteractively(accountManager: sharedContext.accountManager, { current in
let updatedWallpaper: TelegramWallpaper
if let currentSettings = currentWallpaper.settings {
updatedWallpaper = wallpaper.withUpdatedSettings(currentSettings)
} else {
updatedWallpaper = wallpaper
}
let themeReference: PresentationThemeReference
if autoNightModeTriggered {
themeReference = current.automaticThemeSwitchSetting.theme
} else {
themeReference = current.theme
}
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
themeSpecificChatWallpapers[themeReference.index] = updatedWallpaper
themeSpecificChatWallpapers[coloredThemeIndex(reference: themeReference, accentColor: current.themeSpecificAccentColors[themeReference.index])] = updatedWallpaper
return PresentationThemeSettings(theme: current.theme, themePreferredBaseTheme: current.themePreferredBaseTheme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, chatBubbleSettings: current.chatBubbleSettings, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, reduceMotion: current.reduceMotion)
})).start()
}
if case let .file(file) = wallpaper, file.settings.blur {
let _ = account.postbox.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
})
let _ = sharedContext.accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
updateWallpaper(wallpaper)
})
} else {
updateWallpaper(wallpaper)
}
statePromise.set(.single(.uploaded(currentWallpaper, wallpaper)))
} else if case let .progress(progress) = status {
statePromise.set(.single(.uploading(currentWallpaper, progress)))
}
}))
}
} else if previousPresentationData?.theme.name == presentationData.theme.name {
self.context = nil
}
}
}
}