diff --git a/Telegram-iOS/Info.plist b/Telegram-iOS/Info.plist
index 4de303bfb9..0c3c9d00ae 100644
--- a/Telegram-iOS/Info.plist
+++ b/Telegram-iOS/Info.plist
@@ -8,11 +8,6 @@
en
CFBundleDisplayName
${APP_NAME}
- CFBundleDocumentTypes
-
-
-
-
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIcons
diff --git a/submodules/AccountContext/AccountContext_Xcode.xcodeproj/project.pbxproj b/submodules/AccountContext/AccountContext_Xcode.xcodeproj/project.pbxproj
index 811ee00cc6..2f1efe6069 100644
--- a/submodules/AccountContext/AccountContext_Xcode.xcodeproj/project.pbxproj
+++ b/submodules/AccountContext/AccountContext_Xcode.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 095214F12318D4F5008CDD87 /* ThemeUpdateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095214F02318D4F5008CDD87 /* ThemeUpdateManager.swift */; };
D03E4228230565790049C28B /* ChatListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E4227230565790049C28B /* ChatListController.swift */; };
D03E422A230567900049C28B /* ContactMultiselectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E4229230567900049C28B /* ContactMultiselectionController.swift */; };
D03E487C230770000049C28B /* PeerSelectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E487B230770000049C28B /* PeerSelectionController.swift */; };
@@ -49,6 +50,7 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
+ 095214F02318D4F5008CDD87 /* ThemeUpdateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeUpdateManager.swift; sourceTree = ""; };
D03E4227230565790049C28B /* ChatListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListController.swift; sourceTree = ""; };
D03E4229230567900049C28B /* ContactMultiselectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactMultiselectionController.swift; sourceTree = ""; };
D03E487B230770000049C28B /* PeerSelectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerSelectionController.swift; sourceTree = ""; };
@@ -155,6 +157,7 @@
D0879B6A22F7B04400C4D6B3 /* DeviceContactData.swift */,
D0879B6E22F83F6600C4D6B3 /* DownloadedMediaStoreManager.swift */,
D0879B7022F8425B00C4D6B3 /* WallpaperUploadManager.swift */,
+ 095214F02318D4F5008CDD87 /* ThemeUpdateManager.swift */,
D0879B7222F842FF00C4D6B3 /* WatchManager.swift */,
D0C9C12222FE414700FAB518 /* PresentationSurfaceLevels.swift */,
D0C9C3D62300CC2400FAB518 /* FetchMediaUtils.swift */,
@@ -271,6 +274,7 @@
D0879B6122F7A7A600C4D6B3 /* UniversalVideoNode.swift in Sources */,
D0879A4E22F65B2A00C4D6B3 /* MediaManager.swift in Sources */,
D0879A4A22F6584B00C4D6B3 /* SharedMediaPlayer.swift in Sources */,
+ 095214F12318D4F5008CDD87 /* ThemeUpdateManager.swift in Sources */,
D0879B7122F8425B00C4D6B3 /* WallpaperUploadManager.swift in Sources */,
D03E422A230567900049C28B /* ContactMultiselectionController.swift in Sources */,
D03E487C230770000049C28B /* PeerSelectionController.swift in Sources */,
diff --git a/submodules/AccountContext/Sources/ThemeUpdateManager.swift b/submodules/AccountContext/Sources/ThemeUpdateManager.swift
new file mode 100644
index 0000000000..1e2f7bef56
--- /dev/null
+++ b/submodules/AccountContext/Sources/ThemeUpdateManager.swift
@@ -0,0 +1,7 @@
+import Foundation
+import SwiftSignalKit
+import TelegramCore
+import TelegramPresentationData
+
+public protocol ThemeUpdateManager: class {
+}
diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift
index abdb484974..4e8a7dd749 100644
--- a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift
+++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift
@@ -132,7 +132,7 @@ private enum EditThemeControllerEntry: ItemListNodeEntry {
func item(_ arguments: EditThemeControllerArguments) -> ListViewItem {
switch self {
case let .title(theme, strings, title, text, done):
- return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(), text: text, placeholder: title, type: .regular(capitalization: true, autocorrection: false), returnKeyType: done ? .done : .next, clearType: .onFocus, tag: EditThemeEntryTag.title, sectionId: self.section, textUpdated: { value in
+ return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(), text: text, placeholder: title, type: .regular(capitalization: true, autocorrection: false), returnKeyType: .default, clearType: .onFocus, tag: EditThemeEntryTag.title, sectionId: self.section, textUpdated: { value in
arguments.updateState { current in
var state = current
state.title = value
@@ -268,9 +268,13 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
var convertedRepresentations: [ImageRepresentationWithReference] = []
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: CGSize(width: 100.0, height: 100.0), resource: file.file.resource), reference: .media(media: .standalone(media: file.file), resource: file.file.resource)))
- return wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
- |> map { _ -> TelegramWallpaper? in
- return wallpaper.wallpaper
+ return wallpaperDatas(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
+ |> mapToSignal { _, fullSizeData, complete -> Signal in
+ guard complete, let fullSizeData = fullSizeData else {
+ return .complete()
+ }
+ context.sharedContext.accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData)
+ return .single(wallpaper.wallpaper)
}
} else {
return .single(nil)
@@ -422,11 +426,11 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
}
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper))
- var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
+ var chatWallpaper = current.chatWallpaper
if let theme = theme {
- themeSpecificChatWallpapers[themeReference.index] = theme.chat.defaultWallpaper
+ chatWallpaper = resolvedWallpaper ?? theme.chat.defaultWallpaper
}
- return PresentationThemeSettings(chatWallpaper: theme?.chat.defaultWallpaper ?? current.chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
+ return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
})
} |> deliverOnMainQueue).start(completed: {
if !hasCustomFile {
@@ -459,18 +463,15 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
} else {
current = PresentationThemeSettings.defaultSettings
}
-
if let resource = resultTheme.file?.resource, let data = themeData {
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
}
-
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper))
- var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
+ var chatWallpaper = current.chatWallpaper
if let theme = theme {
- themeSpecificChatWallpapers[themeReference.index] = theme.chat.defaultWallpaper
+ chatWallpaper = resolvedWallpaper ?? theme.chat.defaultWallpaper
}
-
- return PresentationThemeSettings(chatWallpaper: theme?.chat.defaultWallpaper ?? current.chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
+ return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
})
} |> deliverOnMainQueue).start(completed: {
if let themeResource = themeResource, !hasCustomFile {
diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift
index 2e0519c277..55b0411bd7 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift
@@ -139,15 +139,15 @@ public final class ThemePreviewController: ViewController {
switch self.source {
case .theme, .slug:
theme = combineLatest(self.theme.get() |> take(1), wallpaperPromise.get() |> take(1))
- |> map { theme, wallpaper in
+ |> mapToSignal { theme, wallpaper -> Signal in
if let theme = theme {
if case let .file(file) = wallpaper, file.id != 0 {
- return .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper))
+ return .single(.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper)))
} else {
- return .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil))
+ return .single(.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil)))
}
} else {
- return nil
+ return .complete()
}
}
case .media:
@@ -169,72 +169,73 @@ public final class ThemePreviewController: ViewController {
return .complete()
}
switch theme {
- case .cloud:
+ case let .cloud(info):
+ resolvedWallpaper = info.resolvedWallpaper
return .single(theme)
case let .local(info):
return wallpaperPromise.get()
+ |> take(1)
+ |> mapToSignal { currentWallpaper -> Signal in
+ if case let .file(file) = currentWallpaper, file.id != 0 {
+ resolvedWallpaper = currentWallpaper
+ }
+
+ var wallpaperImage: UIImage?
+ if case .file = currentWallpaper {
+ wallpaperImage = chatControllerBackgroundImage(theme: previewTheme, wallpaper: currentWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false)
+ }
+ let themeThumbnail = generateImage(CGSize(width: 213, height: 320.0), contextGenerator: { size, context in
+ if let image = generateImage(CGSize(width: 194.0, height: 291.0), contextGenerator: { size, c in
+ drawThemeImage(context: c, theme: previewTheme, wallpaperImage: wallpaperImage, size: size)
+ })?.cgImage {
+ context.draw(image, in: CGRect(origin: CGPoint(), size: size))
+ }
+ }, scale: 1.0)
+ let themeThumbnailData = themeThumbnail?.jpegData(compressionQuality: 0.6)
+
+ return telegramThemes(postbox: context.account.postbox, network: context.account.network, accountManager: context.sharedContext.accountManager)
|> take(1)
- |> mapToSignal { currentWallpaper -> Signal in
- if case let .file(file) = currentWallpaper, file.id != 0 {
- resolvedWallpaper = currentWallpaper
- }
-
- var wallpaperImage: UIImage?
- if case .file = currentWallpaper {
- wallpaperImage = chatControllerBackgroundImage(theme: previewTheme, wallpaper: currentWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false)
- }
- let themeThumbnail = generateImage(CGSize(width: 213, height: 320.0), contextGenerator: { size, context in
- if let image = generateImage(CGSize(width: 194.0, height: 291.0), contextGenerator: { size, c in
- drawThemeImage(context: c, theme: previewTheme, wallpaperImage: wallpaperImage, size: size)
- })?.cgImage {
- context.draw(image, in: CGRect(origin: CGPoint(), size: size))
+ |> mapToSignal { themes -> Signal in
+ let similarTheme = themes.filter { $0.isCreator && $0.title == info.title }.first
+ if let similarTheme = similarTheme {
+ return updateTheme(account: context.account, theme: similarTheme, title: nil, slug: nil, resource: info.resource, thumbnailData: themeThumbnailData)
+ |> map(Optional.init)
+ |> `catch` { _ -> Signal in
+ return .single(nil)
}
- }, scale: 1.0)
- let themeThumbnailData = themeThumbnail?.jpegData(compressionQuality: 0.6)
-
- return telegramThemes(postbox: context.account.postbox, network: context.account.network)
- |> take(1)
- |> mapToSignal { themes -> Signal in
- let similarTheme = themes.filter { $0.isCreator && $0.title == info.title }.first
- if let similarTheme = similarTheme {
- return updateTheme(account: context.account, theme: similarTheme, title: nil, slug: nil, resource: info.resource, thumbnailData: themeThumbnailData)
- |> map(Optional.init)
- |> `catch` { _ -> Signal in
- return .single(nil)
- }
- |> mapToSignal { result -> Signal in
- guard let result = result else {
- let updatedTheme = PresentationLocalTheme(title: info.title, resource: info.resource, resolvedWallpaper: resolvedWallpaper)
- return .single(.local(updatedTheme))
- }
- if case let .result(theme) = result, let file = theme.file {
- context.sharedContext.accountManager.mediaBox.moveResourceData(from: info.resource.id, to: file.resource.id)
- return .single(.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: resolvedWallpaper)))
- } else {
- return .complete()
- }
- }
-
- } else {
- return createTheme(account: context.account, title: info.title, resource: info.resource, thumbnailData: themeThumbnailData)
- |> map(Optional.init)
- |> `catch` { _ -> Signal in
- return .single(nil)
- }
- |> mapToSignal { result -> Signal in
- guard let result = result else {
- let updatedTheme = PresentationLocalTheme(title: info.title, resource: info.resource, resolvedWallpaper: resolvedWallpaper)
- return .single(.local(updatedTheme))
- }
- if case let .result(updatedTheme) = result, let file = updatedTheme.file {
- context.sharedContext.accountManager.mediaBox.moveResourceData(from: info.resource.id, to: file.resource.id)
- return .single(.cloud(PresentationCloudTheme(theme: updatedTheme, resolvedWallpaper: resolvedWallpaper)))
- } else {
- return .complete()
- }
- }
+ |> mapToSignal { result -> Signal in
+ guard let result = result else {
+ let updatedTheme = PresentationLocalTheme(title: info.title, resource: info.resource, resolvedWallpaper: resolvedWallpaper)
+ return .single(.local(updatedTheme))
}
+ if case let .result(theme) = result, let file = theme.file {
+ context.sharedContext.accountManager.mediaBox.moveResourceData(from: info.resource.id, to: file.resource.id)
+ return .single(.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: resolvedWallpaper)))
+ } else {
+ return .complete()
+ }
+ }
+
+ } else {
+ return createTheme(account: context.account, title: info.title, resource: info.resource, thumbnailData: themeThumbnailData)
+ |> map(Optional.init)
+ |> `catch` { _ -> Signal in
+ return .single(nil)
+ }
+ |> mapToSignal { result -> Signal in
+ guard let result = result else {
+ let updatedTheme = PresentationLocalTheme(title: info.title, resource: info.resource, resolvedWallpaper: resolvedWallpaper)
+ return .single(.local(updatedTheme))
+ }
+ if case let .result(updatedTheme) = result, let file = updatedTheme.file {
+ context.sharedContext.accountManager.mediaBox.moveResourceData(from: info.resource.id, to: file.resource.id)
+ return .single(.cloud(PresentationCloudTheme(theme: updatedTheme, resolvedWallpaper: resolvedWallpaper)))
+ } else {
+ return .complete()
+ }
+ }
}
+ }
}
case .builtin:
return .single(theme)
@@ -243,7 +244,7 @@ public final class ThemePreviewController: ViewController {
|> mapToSignal { theme -> Signal in
if case let .cloud(info) = theme {
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: info.theme).start()
- let _ = saveThemeInteractively(account: context.account, theme: info.theme).start()
+ let _ = saveThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: info.theme).start()
}
return context.sharedContext.accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
index a74f41952d..0555305f43 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
@@ -175,6 +175,12 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let fileReference = FileMediaReference.standalone(media: file.file)
let signal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: fileReference, representations: convertedRepresentations, alwaysShowThumbnailFirst: false, autoFetchFullSize: false)
+ |> afterNext { next in
+ if let _ = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.file.resource) {
+ } else if let path = context.account.postbox.mediaBox.completedResourcePath(file.file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
+ context.sharedContext.accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data)
+ }
+ }
strongSelf.remoteChatBackgroundNode.setSignal(signal)
strongSelf.fetchDisposable.set(freeMediaFileInteractiveFetched(account: context.account, fileReference: .standalone(media: file.file)).start())
diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift
index 4b147ac8d1..1f8d4f9ccb 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift
@@ -8,6 +8,7 @@ import TelegramPresentationData
import TelegramUIPreferences
import ItemListUI
import AlertUI
+import WallpaperResources
import ShareController
import AccountContext
@@ -401,31 +402,11 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
currentAppIconName.set(currentAppIcon?.name ?? "Blue")
let cloudThemes = Promise<[TelegramTheme]>()
- let updatedCloudThemes = telegramThemes(postbox: context.account.postbox, network: context.account.network)
+ let updatedCloudThemes = telegramThemes(postbox: context.account.postbox, network: context.account.network, accountManager: context.sharedContext.accountManager)
cloudThemes.set(updatedCloudThemes)
let arguments = ThemeSettingsControllerArguments(context: context, selectTheme: { theme in
- let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in
- transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
- let current: PresentationThemeSettings
- if let entry = entry as? PresentationThemeSettings {
- current = entry
- } else {
- current = PresentationThemeSettings.defaultSettings
- }
-
- let chatWallpaper: TelegramWallpaper
- if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[theme.index] {
- chatWallpaper = themeSpecificWallpaper
- } else {
- let accentColor = current.themeSpecificAccentColors[theme.index]?.color
- let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: current.themeSpecificAccentColors[theme.index]?.baseColor ?? .blue)
- chatWallpaper = theme.chat.defaultWallpaper
- }
-
- return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
- })
- }).start()
+ selectThemeImpl?(theme)
}, selectFontSize: { size in
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
@@ -510,7 +491,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
selectThemeImpl?(newTheme)
}
- let _ = deleteThemeInteractively(account: context.account, theme: theme.theme).start()
+ let _ = deleteThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: theme.theme).start()
})
}))
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
@@ -585,7 +566,52 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
return controller?.navigationController as? NavigationController
}
selectThemeImpl = { theme in
- arguments.selectTheme(theme)
+ let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, serviceBackgroundColor: .black, baseColor: nil)
+
+ let resolvedWallpaper: Signal
+ if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
+ resolvedWallpaper = cachedWallpaper(account: context.account, slug: file.slug)
+ |> map { wallpaper -> TelegramWallpaper? in
+ return wallpaper?.wallpaper
+ }
+ } else {
+ resolvedWallpaper = .single(nil)
+ }
+
+ var cloudTheme: TelegramTheme?
+ if case let .cloud(theme) = theme {
+ cloudTheme = theme.theme
+ }
+ let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: cloudTheme).start()
+
+ let _ = (resolvedWallpaper
+ |> mapToSignal { resolvedWallpaper -> Signal in
+ var updatedTheme = theme
+ if case let .cloud(info) = theme {
+ updatedTheme = .cloud(PresentationCloudTheme(theme: info.theme, resolvedWallpaper: resolvedWallpaper))
+ }
+
+ return (context.sharedContext.accountManager.transaction { transaction -> Void in
+ transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
+ let current: PresentationThemeSettings
+ if let entry = entry as? PresentationThemeSettings {
+ current = entry
+ } else {
+ current = PresentationThemeSettings.defaultSettings
+ }
+
+ let chatWallpaper: TelegramWallpaper
+ if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[updatedTheme.index] {
+ chatWallpaper = themeSpecificWallpaper
+ } else {
+ let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: updatedTheme, accentColor: current.themeSpecificAccentColors[updatedTheme.index]?.color, serviceBackgroundColor: .black, baseColor: nil)
+ chatWallpaper = resolvedWallpaper ?? presentationTheme.chat.defaultWallpaper
+ }
+
+ return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: updatedTheme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
+ })
+ })
+ }).start()
}
moreImpl = {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
@@ -615,7 +641,7 @@ public final class ThemeSettingsCrossfadeController: ViewController {
private let snapshotView: UIView?
public init() {
- self.snapshotView = UIScreen.main.snapshotView(afterScreenUpdates: false)
+ self.snapshotView = UIScreen.main.snapshotView(afterScreenUpdates: true)
super.init(navigationBarPresentationData: nil)
@@ -631,6 +657,7 @@ public final class ThemeSettingsCrossfadeController: ViewController {
self.displayNode.backgroundColor = nil
self.displayNode.isOpaque = false
+ self.displayNode.isUserInteractionEnabled = false
if let snapshotView = self.snapshotView {
self.displayNode.view.addSubview(snapshotView)
}
@@ -639,7 +666,7 @@ public final class ThemeSettingsCrossfadeController: ViewController {
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
- self.displayNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
+ self.displayNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak self] _ in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
})
}
diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift
index bac5203446..650577c8c4 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift
@@ -11,34 +11,43 @@ import ItemListUI
import WallpaperResources
import AccountContext
+private var borderImages: [String: UIImage] = [:]
+
private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? {
- return generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in
- let bounds = CGRect(origin: CGPoint(), size: size)
- context.setFillColor(theme.list.itemBlocksBackgroundColor.cgColor)
- context.fill(bounds)
-
- context.setBlendMode(.clear)
- context.fillEllipse(in: bounds)
- context.setBlendMode(.normal)
-
- let lineWidth: CGFloat
- if selected {
- var accentColor = theme.list.itemAccentColor
- if accentColor.rgb == 0xffffff {
- accentColor = UIColor(rgb: 0x999999)
+ let key = "\(theme.list.itemBlocksBackgroundColor.hexString)_\(selected ? "s" + theme.list.itemAccentColor.hexString : theme.list.disclosureArrowColor.hexString)"
+ if let image = borderImages[key] {
+ return image
+ } else {
+ let image = generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in
+ let bounds = CGRect(origin: CGPoint(), size: size)
+ context.setFillColor(theme.list.itemBlocksBackgroundColor.cgColor)
+ context.fill(bounds)
+
+ context.setBlendMode(.clear)
+ context.fillEllipse(in: bounds)
+ context.setBlendMode(.normal)
+
+ let lineWidth: CGFloat
+ if selected {
+ var accentColor = theme.list.itemAccentColor
+ if accentColor.rgb == 0xffffff {
+ accentColor = UIColor(rgb: 0x999999)
+ }
+ context.setStrokeColor(accentColor.cgColor)
+ lineWidth = 2.0
+ } else {
+ context.setStrokeColor(theme.list.disclosureArrowColor.withAlphaComponent(0.4).cgColor)
+ lineWidth = 1.0
}
- context.setStrokeColor(accentColor.cgColor)
- lineWidth = 2.0
- } else {
- context.setStrokeColor(theme.list.disclosureArrowColor.withAlphaComponent(0.4).cgColor)
- lineWidth = 1.0
- }
-
- if bordered || selected {
- context.setLineWidth(lineWidth)
- context.strokeEllipse(in: bounds.insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0))
- }
- })?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
+
+ if bordered || selected {
+ context.setLineWidth(lineWidth)
+ context.strokeEllipse(in: bounds.insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0))
+ }
+ })?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
+ borderImages[key] = image
+ return image
+ }
}
private func createThemeImage(theme: PresentationTheme) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
@@ -66,78 +75,6 @@ private func createThemeImage(theme: PresentationTheme) -> Signal<(TransformImag
}
}
-private func themeIconImage(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
- let signal: Signal<(UIColor, UIColor, UIColor), NoError>
- if case let .builtin(theme) = theme {
- switch theme {
- case .dayClassic:
- signal = .single((UIColor(rgb: 0xd6e2ee), UIColor(rgb: 0xffffff), UIColor(rgb: 0xe1ffc7)))
- case .day:
- signal = .single((.white, UIColor(rgb: 0xd5dde6), accentColor ?? UIColor(rgb: 0x007aff)))
- case .night:
- signal = .single((.black, UIColor(rgb: 0x1f1f1f), accentColor ?? UIColor(rgb: 0x313131)))
- case .nightAccent:
- let accentColor = accentColor ?? UIColor(rgb: 0x007aff)
- signal = .single((accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18), accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25), accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59)))
- }
- } else {
- var resource: MediaResource?
- if case let .local(theme) = theme {
- resource = theme.resource
- } else if case let .cloud(theme) = theme {
- resource = theme.theme.file?.resource
- }
- if let resource = resource {
- signal = telegramThemeData(account: context.account, accountManager: context.sharedContext.accountManager, resource: resource, synchronousLoad: false)
- |> mapToSignal { data -> Signal<(UIColor, UIColor, UIColor), NoError> in
- if let data = data, let theme = makePresentationTheme(data: data) {
- let backgroundColor: UIColor
- switch theme.chat.defaultWallpaper {
- case .builtin:
- backgroundColor = UIColor(rgb: 0xd6e2ee)
- case let .color(color):
- backgroundColor = UIColor(rgb: UInt32(bitPattern: color))
- default:
- backgroundColor = theme.chatList.backgroundColor
- }
- return .single((backgroundColor, theme.chat.message.incoming.bubble.withoutWallpaper.fill ,theme.chat.message.outgoing.bubble.withoutWallpaper.fill))
- } else {
- return .complete()
- }
- }
- } else {
- signal = .never()
- }
- }
- return signal
- |> map { colors in
- return { arguments in
- let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil)
- let drawingRect = arguments.drawingRect
-
- context.withContext { c in
- c.setFillColor(colors.0.cgColor)
- c.fill(drawingRect)
-
- let incoming = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: colors.1)
- let outgoing = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: colors.2)
-
- c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
- c.scaleBy(x: 1.0, y: -1.0)
- c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
-
- c.draw(incoming!.cgImage!, in: CGRect(x: 9.0, y: 34.0, width: 57.0, height: 16.0))
-
- c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
- c.scaleBy(x: -1.0, y: 1.0)
- c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
- c.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0))
- }
-
- return context
- }
- }
-}
class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
var sectionId: ItemListSectionId
@@ -206,6 +143,11 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
private var action: (() -> Void)?
private var longTapAction: (() -> Void)?
+ private var theme: PresentationThemeReference?
+ private var currentTheme: PresentationTheme?
+ private var bordered: Bool?
+ private var selected: Bool?
+
override init() {
self.imageNode = TransformImageNode()
self.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 98.0, height: 62.0))
@@ -228,12 +170,23 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, longTapAction: @escaping () -> Void) {
if case let .cloud(theme) = theme, theme.theme.file == nil {
- self.imageNode.setSignal(createThemeImage(theme: currentTheme))
+ if self.currentTheme == nil || currentTheme !== self.currentTheme! {
+ self.imageNode.setSignal(createThemeImage(theme: currentTheme))
+ self.currentTheme = currentTheme
+ }
} else {
- self.imageNode.setSignal(themeIconImage(context: context, theme: theme, accentColor: accentColor))
+ if theme != self.theme {
+ self.imageNode.setSignal(themeIconImage(account: context.account, accountManager: context.sharedContext.accountManager, theme: theme, accentColor: accentColor))
+ self.theme = theme
+ }
+ }
+ if self.currentTheme == nil || currentTheme !== self.currentTheme! || bordered != self.bordered || selected != self.selected {
+ self.overlayNode.image = generateBorderImage(theme: currentTheme, bordered: bordered, selected: selected)
+ self.currentTheme = currentTheme
+ self.bordered = bordered
+ self.selected = selected
}
self.textNode.attributedText = title
- self.overlayNode.image = generateBorderImage(theme: currentTheme, bordered: bordered, selected: selected)
self.action = {
action()
}
diff --git a/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift b/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift
index af4f8c2a57..d541d33a8a 100644
--- a/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift
+++ b/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift
@@ -2955,14 +2955,14 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
updatedEntries.append(OrderedItemListEntry(id: id, contents: theme))
}
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
- accountManager.transaction { transaction in
+ let _ = accountManager.transaction { transaction in
transaction.updateSharedData(SharedDataKeys.themeSettings, { current in
if let current = current as? ThemeSettings, let theme = current.currentTheme, let updatedTheme = updatedThemes[theme.id] {
return ThemeSettings(currentTheme: updatedTheme)
}
return current
})
- }
+ }.start()
}
addedIncomingMessageIds.append(contentsOf: addedSecretMessageIds)
diff --git a/submodules/TelegramCore/TelegramCore/Themes.swift b/submodules/TelegramCore/TelegramCore/Themes.swift
index f7ee62972b..b4d8de7ff7 100644
--- a/submodules/TelegramCore/TelegramCore/Themes.swift
+++ b/submodules/TelegramCore/TelegramCore/Themes.swift
@@ -33,7 +33,7 @@ private let themeFormat = "ios"
private let themeFileExtension = "tgios-theme"
#endif
-public func telegramThemes(postbox: Postbox, network: Network, forceUpdate: Bool = false) -> Signal<[TelegramTheme], NoError> {
+public func telegramThemes(postbox: Postbox, network: Network, accountManager: AccountManager, forceUpdate: Bool = false) -> Signal<[TelegramTheme], NoError> {
let fetch: ([TelegramTheme]?, Int32?) -> Signal<[TelegramTheme], NoError> = { current, hash in
network.request(Api.functions.account.getThemes(format: themeFormat, hash: hash ?? 0))
|> retryRequest
@@ -51,6 +51,19 @@ public func telegramThemes(postbox: Postbox, network: Network, forceUpdate: Bool
}
}
|> mapToSignal { items, hash -> Signal<[TelegramTheme], NoError> in
+ let _ = accountManager.transaction { transaction in
+ transaction.updateSharedData(SharedDataKeys.themeSettings, { current in
+ var updated = current as? ThemeSettings ?? ThemeSettings(currentTheme: nil)
+ for theme in items {
+ if theme.id == updated.currentTheme?.id {
+ updated = ThemeSettings(currentTheme: theme)
+ break
+ }
+ }
+ return updated
+ })
+ }.start()
+
return postbox.transaction { transaction -> [TelegramTheme] in
var entries: [OrderedItemListEntry] = []
for item in items {
@@ -135,7 +148,7 @@ private func checkThemeUpdated(network: Network, theme: TelegramTheme) -> Signal
}
}
-private func saveUnsaveTheme(account: Account, theme: TelegramTheme, unsave: Bool) -> Signal {
+private func saveUnsaveTheme(account: Account, accountManager: AccountManager, theme: TelegramTheme, unsave: Bool) -> Signal {
return account.postbox.transaction { transaction -> Signal in
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
var items = entries.map { $0.contents as! TelegramTheme }
@@ -156,7 +169,7 @@ private func saveUnsaveTheme(account: Account, theme: TelegramTheme, unsave: Boo
return .complete()
}
|> mapToSignal { _ -> Signal in
- return telegramThemes(postbox: account.postbox, network: account.network, forceUpdate: true)
+ return telegramThemes(postbox: account.postbox, network: account.network, accountManager: accountManager, forceUpdate: true)
|> take(1)
|> mapToSignal { _ -> Signal in
return .complete()
@@ -426,12 +439,12 @@ public final class ThemeSettings: PreferencesEntry, Equatable {
}
}
-public func saveThemeInteractively(account: Account, theme: TelegramTheme) -> Signal {
- return saveUnsaveTheme(account: account, theme: theme, unsave: false)
+public func saveThemeInteractively(account: Account, accountManager: AccountManager, theme: TelegramTheme) -> Signal {
+ return saveUnsaveTheme(account: account, accountManager: accountManager, theme: theme, unsave: false)
}
-public func deleteThemeInteractively(account: Account, theme: TelegramTheme) -> Signal {
- return saveUnsaveTheme(account: account, theme: theme, unsave: true)
+public func deleteThemeInteractively(account: Account, accountManager: AccountManager, theme: TelegramTheme) -> Signal {
+ return saveUnsaveTheme(account: account, accountManager: accountManager, theme: theme, unsave: true)
}
public func applyTheme(accountManager: AccountManager, account: Account, theme: TelegramTheme?) -> Signal {
@@ -450,46 +463,104 @@ public func applyTheme(accountManager: AccountManager, account: Account, theme:
}
func managedThemesUpdates(accountManager: AccountManager, postbox: Postbox, network: Network) -> Signal {
+ let currentTheme = Atomic(value: nil)
return accountManager.sharedData(keys: [SharedDataKeys.themeSettings])
- |> mapToSignal { sharedData -> Signal in
+ |> map { sharedData -> TelegramTheme? in
let themeSettings = (sharedData.entries[SharedDataKeys.themeSettings] as? ThemeSettings) ?? ThemeSettings(currentTheme: nil)
- if let currentTheme = themeSettings.currentTheme {
+ return themeSettings.currentTheme
+ }
+ |> filter { theme in
+ return theme?.id != currentTheme.with({ $0 })?.id
+ }
+ |> mapToSignal { theme -> Signal in
+ let _ = currentTheme.swap(theme)
+ if let theme = theme {
let poll = Signal { subscriber in
- return checkThemeUpdated(network: network, theme: currentTheme).start(next: { result in
+ let actualTheme = currentTheme.with { $0 } ?? theme
+ return checkThemeUpdated(network: network, theme: actualTheme).start(next: { result in
if case let .updated(updatedTheme) = result {
+ let _ = currentTheme.swap(theme)
let _ = accountManager.transaction { transaction in
transaction.updateSharedData(SharedDataKeys.themeSettings, { _ in
return ThemeSettings(currentTheme: updatedTheme)
})
}.start()
let _ = postbox.transaction { transaction in
-
- }
+ let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
+ let items = entries.map { entry -> TelegramTheme in
+ let theme = entry.contents as! TelegramTheme
+ if theme.id == updatedTheme.id {
+ return updatedTheme
+ } else {
+ return theme
+ }
+ }
+ var updatedEntries: [OrderedItemListEntry] = []
+ for item in items {
+ var intValue = Int32(updatedEntries.count)
+ let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
+ updatedEntries.append(OrderedItemListEntry(id: id, contents: item))
+ }
+ transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
+ }.start()
}
subscriber.putCompletion()
})
}
- return ((.complete() |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue())) |> then(poll)) |> restart
+ return (poll |> then(.complete() |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
} else {
return .complete()
}
}
}
-public func actualizedTheme(accountManager: AccountManager, theme: TelegramTheme) -> Signal {
+private func areThemesEqual(_ lhs: TelegramTheme, _ rhs: TelegramTheme) -> Bool {
+ if lhs.title != rhs.title {
+ return false
+ }
+ if lhs.slug != rhs.slug {
+ return false
+ }
+ if lhs.file?.id != rhs.file?.id {
+ return false
+ }
+ return true
+}
+
+public func actualizedTheme(account: Account, accountManager: AccountManager, theme: TelegramTheme) -> Signal {
var currentTheme = theme
return accountManager.sharedData(keys: [SharedDataKeys.themeSettings])
- |> map { sharedData -> TelegramTheme in
+ |> mapToSignal { sharedData -> Signal in
let themeSettings = (sharedData.entries[SharedDataKeys.themeSettings] as? ThemeSettings) ?? ThemeSettings(currentTheme: nil)
- if let updatedTheme = themeSettings.currentTheme, updatedTheme.id == currentTheme.id {
- if updatedTheme != currentTheme {
+ if let updatedTheme = themeSettings.currentTheme, updatedTheme.id == theme.id {
+ if !areThemesEqual(updatedTheme, currentTheme) {
currentTheme = updatedTheme
- return updatedTheme
+ return .single(updatedTheme)
} else {
- return currentTheme
+ return .single(currentTheme)
}
} else {
- return theme
+ return account.postbox.combinedView(keys: [PostboxViewKey.orderedItemList(id: Namespaces.OrderedItemList.CloudThemes)])
+ |> map { view -> [TelegramTheme] in
+ if let view = view.views[.orderedItemList(id: Namespaces.OrderedItemList.CloudThemes)] as? OrderedItemListView {
+ return view.items.compactMap { $0.contents as? TelegramTheme }
+ } else {
+ return []
+ }
+ }
+ |> map { themes -> TelegramTheme in
+ let updatedTheme = themes.filter { $0.id == theme.id }.first
+ if let updatedTheme = updatedTheme {
+ if !areThemesEqual(updatedTheme, currentTheme) {
+ currentTheme = updatedTheme
+ return updatedTheme
+ } else {
+ return currentTheme
+ }
+ } else {
+ return currentTheme
+ }
+ }
}
}
}
diff --git a/submodules/TelegramUI/TelegramUI/AccountContext.swift b/submodules/TelegramUI/TelegramUI/AccountContext.swift
index d219e4832f..01a1625fe4 100644
--- a/submodules/TelegramUI/TelegramUI/AccountContext.swift
+++ b/submodules/TelegramUI/TelegramUI/AccountContext.swift
@@ -111,6 +111,7 @@ public final class AccountContextImpl: AccountContext {
public let liveLocationManager: LiveLocationManager?
public let wallpaperUploadManager: WallpaperUploadManager?
+ private let themeUpdateManager: ThemeUpdateManager?
public let peerChannelMemberCategoriesContextsManager = PeerChannelMemberCategoriesContextsManager()
@@ -143,9 +144,11 @@ public final class AccountContextImpl: AccountContext {
if sharedContext.applicationBindings.isMainApp {
self.prefetchManager = PrefetchManager(sharedContext: sharedContext, account: account, fetchManager: self.fetchManager)
self.wallpaperUploadManager = WallpaperUploadManagerImpl(sharedContext: sharedContext, account: account, presentationData: sharedContext.presentationData)
+ self.themeUpdateManager = ThemeUpdateManagerImpl(sharedContext: sharedContext, account: account)
} else {
self.prefetchManager = nil
self.wallpaperUploadManager = nil
+ self.themeUpdateManager = nil
}
let updatedLimitsConfiguration = account.postbox.preferencesView(keys: [PreferencesKeys.limitsConfiguration])
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift
index 8422618ad7..4beb03ffe5 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift
@@ -402,7 +402,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
if let (media, flags) = mediaAndFlags {
if let file = media as? TelegramMediaFile {
if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 {
- let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
+ let automaticDownload = true
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload ? .full : .none, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
refineContentImageLayout = refineLayout
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift
index 223f86a833..a9d4ceb999 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift
@@ -246,7 +246,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
mainMedia = webpage.file ?? webpage.image
}
- if let file = mainMedia as? TelegramMediaFile {
+ if let file = mainMedia as? TelegramMediaFile, webpage.type != "telegram_theme" {
if let embedUrl = webpage.embedUrl, !embedUrl.isEmpty {
if automaticPlayback {
mediaAndFlags = (file, [.preferMediaBeforeText])
@@ -293,9 +293,18 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags())
}
}
- } else if type == "telegram_theme", let files = webpage.files, let file = files.first {
- let media = WallpaperPreviewMedia(content: .file(file, nil, true))
- mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags())
+ } else if type == "telegram_theme" {
+ var file: TelegramMediaFile?
+ let mimeType = "application/x-tgtheme-ios"
+ if let contentFiles = webpage.files, let filteredFile = contentFiles.filter({ $0.mimeType == mimeType }).first {
+ file = filteredFile
+ } else if let contentFile = webpage.file, contentFile.mimeType == mimeType {
+ file = contentFile
+ }
+ if let file = file {
+ let media = WallpaperPreviewMedia(content: .file(file, nil, true))
+ mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags())
+ }
}
}
@@ -323,7 +332,9 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
case "telegram_theme":
title = item.presentationData.strings.Conversation_Theme
text = nil
- actionTitle = item.presentationData.strings.Conversation_ViewTheme
+ if mediaAndFlags != nil {
+ actionTitle = item.presentationData.strings.Conversation_ViewTheme
+ }
default:
break
}
diff --git a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift
index 8856221b1d..285f72a1b5 100644
--- a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift
+++ b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift
@@ -463,8 +463,15 @@ func openChatTheme(context: AccountContext, message: Message, present: @escaping
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
let _ = (context.sharedContext.resolveUrl(account: context.account, url: content.url)
|> deliverOnMainQueue).start(next: { resolvedUrl in
- if case let .theme(slug) = resolvedUrl, let files = content.files {
- if let file = files.filter({ $0.mimeType == "application/x-tgtheme-ios" }).first, let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let theme = makePresentationTheme(data: data) {
+ var file: TelegramMediaFile?
+ let mimeType = "application/x-tgtheme-ios"
+ if let contentFiles = content.files, let filteredFile = contentFiles.filter({ $0.mimeType == mimeType }).first {
+ file = filteredFile
+ } else if let contentFile = content.file, contentFile.mimeType == mimeType {
+ file = contentFile
+ }
+ if case let .theme(slug) = resolvedUrl, let file = file {
+ if let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let theme = makePresentationTheme(data: data) {
let controller = ThemePreviewController(context: context, previewTheme: theme, source: .slug(slug, file))
present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
diff --git a/submodules/TelegramUI/TelegramUI/TelegramRootController.swift b/submodules/TelegramUI/TelegramUI/TelegramRootController.swift
index 264e1570c3..2c5e678bdb 100644
--- a/submodules/TelegramUI/TelegramUI/TelegramRootController.swift
+++ b/submodules/TelegramUI/TelegramUI/TelegramRootController.swift
@@ -45,7 +45,6 @@ public final class TelegramRootController: NavigationController {
self.presentationDataDisposable = (context.sharedContext.presentationData
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self {
-
if presentationData.chatWallpaper != strongSelf.presentationData.chatWallpaper {
let navigationDetailsBackgroundMode: NavigationEmptyDetailsBackgoundMode?
switch presentationData.chatWallpaper {
diff --git a/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift b/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift
new file mode 100644
index 0000000000..2e2c6d9512
--- /dev/null
+++ b/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift
@@ -0,0 +1,158 @@
+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] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings
+ }
+ |> deliverOn(queue)).start(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()
+ if let previousThemeSettings = previousThemeSettings {
+ previousIds.insert(previousThemeSettings.theme.index)
+ }
+
+ var validIds = Set()
+ var themes: [Int64: (PresentationThemeReference, Bool)] = [:]
+ if case .cloud = themeSettings.theme {
+ validIds.insert(themeSettings.theme.index)
+ themes[themeSettings.theme.index] = (themeSettings.theme, false)
+ }
+
+ 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, 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
+ if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
+ resolvedWallpaper = cachedWallpaper(account: account, slug: file.slug)
+ |> 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: CGSize(width: 100.0, height: 100.0), resource: file.file.resource), reference: .media(media: .standalone(media: file.file), 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)
+ return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper)), presentationTheme))
+ }
+ } else {
+ return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: 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 as? PresentationThemeSettings {
+ current = entry
+ } else {
+ current = PresentationThemeSettings.defaultSettings
+ }
+
+ let chatWallpaper: TelegramWallpaper
+ if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[updatedTheme.index] {
+ chatWallpaper = themeSpecificWallpaper
+ } else if let presentationTheme = presentationTheme {
+ if case let .cloud(info) = updatedTheme, let resolvedWallpaper = info.resolvedWallpaper {
+ chatWallpaper = resolvedWallpaper
+ } else {
+ chatWallpaper = presentationTheme.chat.defaultWallpaper
+ }
+ } else {
+ chatWallpaper = current.chatWallpaper
+ }
+
+ return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: updatedTheme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
+ })
+ }).start()
+ }
+ })
+ self.contexts[id] = ThemeUpdateManagerContext(themeReference: theme, disposable: disposable, isAutoNight: isAutoNight)
+ }
+ }
+
+ for id in previousIds {
+ if !validIds.contains(id) {
+ self.contexts[id] = nil
+ }
+ }
+ }
+ }
+}
diff --git a/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj b/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj
index ad63274493..909c30af3e 100644
--- a/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj
+++ b/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj
@@ -31,6 +31,7 @@
094735192277483C00EA2312 /* anim_infotip.json in Resources */ = {isa = PBXBuildFile; fileRef = 094735182277483B00EA2312 /* anim_infotip.json */; };
09510B1322F96E5B0078CAB7 /* ChatScheduleTimeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09510B1222F96E5B0078CAB7 /* ChatScheduleTimeController.swift */; };
09510B1522F96E6C0078CAB7 /* ChatScheduleTimeControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09510B1422F96E6C0078CAB7 /* ChatScheduleTimeControllerNode.swift */; };
+ 095214EF2318D4D3008CDD87 /* ThemeUpdateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095214EE2318D4D3008CDD87 /* ThemeUpdateManager.swift */; };
0962E67921B67A9800245FD9 /* ChatMessageAnimatedStickerItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E67821B67A9800245FD9 /* ChatMessageAnimatedStickerItemNode.swift */; };
09749BC521F0E024008FDDE9 /* StickersChatInputContextPanelItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09749BC421F0E024008FDDE9 /* StickersChatInputContextPanelItem.swift */; };
09874E4F21078FA100E190B8 /* Generic.html in Resources */ = {isa = PBXBuildFile; fileRef = 0979788321065F8C0077D77F /* Generic.html */; };
@@ -596,6 +597,7 @@
094735182277483B00EA2312 /* anim_infotip.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_infotip.json; sourceTree = ""; };
09510B1222F96E5B0078CAB7 /* ChatScheduleTimeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatScheduleTimeController.swift; sourceTree = ""; };
09510B1422F96E6C0078CAB7 /* ChatScheduleTimeControllerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatScheduleTimeControllerNode.swift; sourceTree = ""; };
+ 095214EE2318D4D3008CDD87 /* ThemeUpdateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeUpdateManager.swift; sourceTree = ""; };
0962E67821B67A9800245FD9 /* ChatMessageAnimatedStickerItemNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageAnimatedStickerItemNode.swift; sourceTree = ""; };
09749BC421F0E024008FDDE9 /* StickersChatInputContextPanelItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickersChatInputContextPanelItem.swift; sourceTree = ""; };
0979788021065F8B0077D77F /* VimeoUserScript.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = VimeoUserScript.js; sourceTree = ""; };
@@ -2540,6 +2542,7 @@
D0F3A8AA1E82D83E00B4C64C /* TelegramAccountAuxiliaryMethods.swift */,
D01DBA9A209CC6AD00C64E64 /* ChatLinkPreview.swift */,
090B48C72200BCA8005083FA /* WallpaperUploadManager.swift */,
+ 095214EE2318D4D3008CDD87 /* ThemeUpdateManager.swift */,
09D96898221DE92600B1458A /* ID3ArtworkReader.swift */,
D099E21F229405BB00561B75 /* Weak.swift */,
);
@@ -3053,6 +3056,7 @@
D0EC6DEC1EB9F58900EBF1C3 /* ChatToastAlertPanelNode.swift in Sources */,
D0EC6DED1EB9F58900EBF1C3 /* ChatHistoryNavigationButtonNode.swift in Sources */,
D0EC6DF51EB9F58900EBF1C3 /* PeerMediaCollectionController.swift in Sources */,
+ 095214EF2318D4D3008CDD87 /* ThemeUpdateManager.swift in Sources */,
D0EC6DF61EB9F58900EBF1C3 /* PeerMediaCollectionControllerNode.swift in Sources */,
D0EC6DF81EB9F58900EBF1C3 /* PeerMediaCollectionInterfaceState.swift in Sources */,
D0EC6DF91EB9F58900EBF1C3 /* PeerMediaCollectionInterfaceStateButtons.swift in Sources */,
diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift
index a2488a3c28..29075a0ec9 100644
--- a/submodules/WallpaperResources/Sources/WallpaperResources.swift
+++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift
@@ -10,8 +10,9 @@ import TinyThumbnail
import PhotoResources
import LocalMediaResources
import TelegramPresentationData
+import TelegramUIPreferences
-private func wallpaperDatas(account: Account, accountManager: AccountManager, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, thumbnail: Bool = false, onlyFullSize: Bool = false, autoFetchFullSize: Bool = false, synchronousLoad: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> {
+public func wallpaperDatas(account: Account, accountManager: AccountManager, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, thumbnail: Bool = false, onlyFullSize: Bool = false, autoFetchFullSize: Bool = false, synchronousLoad: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> {
if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.firstIndex(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.firstIndex(where: { $0.representation == largestRepresentation }) {
let maybeFullSize: Signal
@@ -51,7 +52,13 @@ private func wallpaperDatas(account: Account, accountManager: AccountManager, fi
} else {
maybeFullSize = combineLatest(accountManager.mediaBox.resourceData(largestRepresentation.resource), account.postbox.mediaBox.resourceData(largestRepresentation.resource))
|> map { sharedData, data -> MediaResourceData in
- if sharedData.complete {
+ if sharedData.complete && data.complete {
+ if sharedData.size > data.size {
+ return sharedData
+ } else {
+ return data
+ }
+ } else if sharedData.complete {
return sharedData
} else {
return data
@@ -168,9 +175,6 @@ public func wallpaperImage(account: Account, accountManager: AccountManager, fil
return signal
|> map { (thumbnailData, fullSizeData, fullSizeComplete) in
- if let fullSizeData = fullSizeData, let fileReference = fileReference, fullSizeComplete {
- accountManager.mediaBox.storeResourceData(fileReference.media.resource.id, data: fullSizeData)
- }
return { arguments in
let drawingRect = arguments.drawingRect
var fittedSize = arguments.imageSize
@@ -259,7 +263,6 @@ public func wallpaperImage(account: Account, accountManager: AccountManager, fil
}
let context = DrawingContext(size: arguments.drawingSize, clear: true)
-
context.withFlippedContext { c in
c.setBlendMode(.copy)
if arguments.imageSize.width < arguments.boundingSize.width || arguments.imageSize.height < arguments.boundingSize.height {
@@ -278,9 +281,7 @@ public func wallpaperImage(account: Account, accountManager: AccountManager, fil
drawImage(context: c, image: fullSizeImage, orientation: imageOrientation, in: fittedRect)
}
}
-
addCorners(context, arguments: arguments)
-
return context
}
}
@@ -596,7 +597,7 @@ public func photoWallpaper(postbox: Postbox, photoLibraryResource: PhotoLibraryM
}
}
-public func telegramThemeData(account: Account, accountManager: AccountManager, resource: MediaResource, synchronousLoad: Bool) -> Signal {
+public func telegramThemeData(account: Account, accountManager: AccountManager, resource: MediaResource, synchronousLoad: Bool = false) -> Signal {
let maybeFetched = accountManager.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
return maybeFetched
|> take(1)
@@ -653,7 +654,7 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp
case let .color(value):
c.setFillColor(UIColor(rgb: UInt32(bitPattern: value)).cgColor)
c.fill(drawingRect)
- case let .file(file):
+ case .file:
c.setFillColor(theme.chatList.backgroundColor.cgColor)
c.fill(drawingRect)
@@ -804,12 +805,17 @@ public func themeImage(account: Account, accountManager: AccountManager, fileRef
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
var convertedRepresentations: [ImageRepresentationWithReference] = []
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: CGSize(width: 100.0, height: 100.0), resource: file.file.resource), reference: .media(media: .standalone(media: file.file), resource: file.file.resource)))
- return wallpaperImage(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
- |> map { _ -> (PresentationTheme?, UIImage?, Data?) in
- if let path = accountManager.mediaBox.completedResourcePath(file.file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let image = UIImage(data: data) {
- return (theme, image, thumbnailData)
+ 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<(PresentationTheme?, UIImage?, Data?), NoError> in
+ guard complete, let fullSizeData = fullSizeData else {
+ return .complete()
+ }
+ accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData)
+
+ if let image = UIImage(data: fullSizeData) {
+ return .single((theme, image, thumbnailData))
} else {
- return (theme, nil, thumbnailData)
+ return .single((theme, nil, thumbnailData))
}
}
} else {
@@ -890,3 +896,124 @@ public func themeImage(account: Account, accountManager: AccountManager, fileRef
}
}
}
+
+public func themeIconImage(account: Account, accountManager: AccountManager, theme: PresentationThemeReference, accentColor: UIColor?) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
+ let signal: Signal<(UIColor, UIColor, UIColor, UIImage?), NoError>
+ if case let .builtin(theme) = theme {
+ switch theme {
+ case .dayClassic:
+ signal = .single((UIColor(rgb: 0xd6e2ee), UIColor(rgb: 0xffffff), UIColor(rgb: 0xe1ffc7), nil))
+ case .day:
+ signal = .single((.white, UIColor(rgb: 0xd5dde6), accentColor ?? UIColor(rgb: 0x007aff), nil))
+ case .night:
+ signal = .single((.black, UIColor(rgb: 0x1f1f1f), accentColor ?? UIColor(rgb: 0x313131), nil))
+ case .nightAccent:
+ let accentColor = accentColor ?? UIColor(rgb: 0x007aff)
+ signal = .single((accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18), accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25), accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59), nil))
+ }
+ } else {
+ var resource: MediaResource?
+ if case let .local(theme) = theme {
+ resource = theme.resource
+ } else if case let .cloud(theme) = theme {
+ resource = theme.theme.file?.resource
+ }
+ if let resource = resource {
+ signal = telegramThemeData(account: account, accountManager: accountManager, resource: resource, synchronousLoad: false)
+ |> mapToSignal { data -> Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> in
+ if let data = data, let theme = makePresentationTheme(data: data) {
+ var wallpaperSignal: Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> = .complete()
+ let backgroundColor: UIColor
+ let incomingColor = theme.chat.message.incoming.bubble.withoutWallpaper.fill
+ let outgoingColor = theme.chat.message.outgoing.bubble.withoutWallpaper.fill
+ switch theme.chat.defaultWallpaper {
+ case .builtin:
+ backgroundColor = UIColor(rgb: 0xd6e2ee)
+ case let .color(color):
+ backgroundColor = UIColor(rgb: UInt32(bitPattern: color))
+ case .image:
+ backgroundColor = .black
+ case let .file(file):
+ backgroundColor = theme.chatList.backgroundColor
+ wallpaperSignal = cachedWallpaper(account: account, slug: file.slug)
+ |> mapToSignal { wallpaper in
+ if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
+ var convertedRepresentations: [ImageRepresentationWithReference] = []
+ convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: CGSize(width: 100.0, height: 100.0), resource: file.file.resource), reference: .media(media: .standalone(media: file.file), 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<(UIColor, UIColor, UIColor, UIImage?), NoError> in
+ guard complete, let fullSizeData = fullSizeData else {
+ return .complete()
+ }
+ accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData)
+
+ if let image = UIImage(data: fullSizeData) {
+ return .single((backgroundColor, incomingColor, outgoingColor, image))
+ } else {
+ return .complete()
+ }
+ }
+ } else {
+ return .complete()
+ }
+ }
+ }
+ return .single((backgroundColor, incomingColor, outgoingColor, nil))
+ |> then(wallpaperSignal)
+ } else {
+ return .complete()
+ }
+ }
+ } else {
+ signal = .never()
+ }
+ }
+ return signal
+ |> map { colors in
+ return { arguments in
+ let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil)
+ let drawingRect = arguments.drawingRect
+
+ context.withContext { c in
+ c.setFillColor(colors.0.cgColor)
+ c.fill(drawingRect)
+
+ if let image = colors.3 {
+ let initialThumbnailContextFittingSize = arguments.imageSize.fitted(CGSize(width: 90.0, height: 90.0))
+ let thumbnailContextSize = image.size.aspectFilled(initialThumbnailContextFittingSize)
+ let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0)
+ thumbnailContext.withFlippedContext { c in
+ c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: thumbnailContextSize))
+ }
+ telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
+
+ if let blurredThumbnailImage = thumbnailContext.generateImage(), let cgImage = blurredThumbnailImage.cgImage {
+ let fittedSize = thumbnailContext.size.aspectFilled(CGSize(width: drawingRect.size.width + 1.0, height: drawingRect.size.height + 1.0))
+ c.saveGState()
+ c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
+ c.scaleBy(x: 1.0, y: -1.0)
+ c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
+ c.draw(cgImage, in: CGRect(origin: CGPoint(x: (drawingRect.size.width - fittedSize.width) / 2.0, y: (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize))
+ c.restoreGState()
+ }
+ }
+
+ let incoming = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: colors.1)
+ let outgoing = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), color: colors.2)
+
+ c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
+ c.scaleBy(x: 1.0, y: -1.0)
+ c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
+
+ c.draw(incoming!.cgImage!, in: CGRect(x: 9.0, y: 34.0, width: 57.0, height: 16.0))
+
+ c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
+ c.scaleBy(x: -1.0, y: 1.0)
+ c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
+ c.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0))
+ }
+
+ return context
+ }
+ }
+}