diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift index c64e0483a4..bdc257b773 100644 --- a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift +++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift @@ -9,6 +9,7 @@ import TelegramUIPreferences import ItemListUI import AlertUI import LegacyMediaPickerUI +import WallpaperResources import AccountContext private final class EditThemeControllerArguments { @@ -301,7 +302,6 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll if let navigateToChat = navigateToChat { presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_ThemeTemplateAlert, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_SavedMessages, action: { completion() - navigateToChat(context.account.peerId) }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { completion() @@ -312,37 +312,56 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll } let theme: PresentationTheme? - let custom: Bool + let hasCustomFile: Bool if let updatedTheme = state.updatedTheme { theme = updatedTheme.withUpdated(name: state.title, author: "", defaultWallpaper: nil) - custom = true + hasCustomFile = true } else { if case let .edit(info) = mode, let _ = info.theme.file { theme = nil - custom = true + hasCustomFile = true } else { theme = state.previewTheme.withUpdated(name: state.title, author: "", defaultWallpaper: nil) - custom = false + hasCustomFile = false } } let themeResource: LocalFileMediaResource? let themeData: Data? + let themeThumbnailData: Data? if let theme = theme, let themeString = encodePresentationTheme(theme), let data = themeString.data(using: .utf8) { let resource = LocalFileMediaResource(fileId: arc4random64()) context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data) themeResource = resource themeData = data + + 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: theme, size: size) + })?.cgImage { + context.draw(image, in: CGRect(origin: CGPoint(), size: size)) + } + }, scale: 1.0) + themeThumbnailData = themeThumbnail?.jpegData(compressionQuality: 0.6) } else { themeResource = nil themeData = nil + themeThumbnailData = nil + } + + let resolvedWallpaper: TelegramWallpaper? + if let theme = theme, case let .file(file) = theme.chat.defaultWallpaper, file.id != 0 { + resolvedWallpaper = theme.chat.defaultWallpaper + updateCachedWallpaper(account: context.account, wallpaper: theme.chat.defaultWallpaper) + } else { + resolvedWallpaper = nil } switch mode { case .create: if let themeResource = themeResource { - let _ = (createTheme(account: context.account, resource: themeResource, title: state.title) + let _ = (createTheme(account: context.account, title: state.title, resource: themeResource, thumbnailData: themeThumbnailData) |> deliverOnMainQueue).start(next: { next in if case let .result(resultTheme) = next { let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start() @@ -359,7 +378,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data) } - let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: nil)) + let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper)) var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers if let theme = theme { themeSpecificChatWallpapers[themeReference.index] = theme.chat.defaultWallpaper @@ -368,7 +387,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll 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) }) } |> deliverOnMainQueue).start(completed: { - if !custom { + if !hasCustomFile { saveThemeTemplateFile(state.title, themeResource, { dismissImpl?() }) @@ -403,7 +422,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data) } - let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: nil)) + let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper)) var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers if let theme = theme { themeSpecificChatWallpapers[themeReference.index] = theme.chat.defaultWallpaper @@ -412,7 +431,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll 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) }) } |> deliverOnMainQueue).start(completed: { - if let themeResource = themeResource, !custom { + if let themeResource = themeResource, !hasCustomFile { saveThemeTemplateFile(state.title, themeResource, { dismissImpl?() }) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 5696ebf78f..fee231be66 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -222,13 +222,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let peers = SimpleDictionary() let messages = SimpleDictionary() let selfPeer = TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 1), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name.components(separatedBy: " ").first, lastName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name.components(separatedBy: " ").last, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 2), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name.components(separatedBy: " ").first, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - + let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 1), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 2), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let peer3 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: 3), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil) - let peer3Author = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName.components(separatedBy: " ").first, lastName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName.components(separatedBy: " ").last, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - - let peer4 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name.components(separatedBy: " ").first, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer3Author = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer4 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let timestamp = self.referenceTimestamp diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift index a7305e0d58..d1343ca9d6 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift @@ -150,6 +150,12 @@ public final class ThemePreviewController: ViewController { } } +// let resolvedWallpaper: TelegramWallpaper? +// if let theme = theme, case let .file(file) = theme.chat.defaultWallpaper, file.id != 0 { +// resolvedWallpaper = theme.chat.defaultWallpaper +// updateCachedWallpaper(account: context.account, wallpaper: theme.chat.defaultWallpaper) +// } + let _ = (signal |> deliverOnMainQueue).start(completed: { [weak self] in if let strongSelf = self { strongSelf.dismiss() diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index aa17e9fadb..419713d492 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -167,18 +167,14 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let peers = SimpleDictionary() let messages = SimpleDictionary() let selfPeer = TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 1), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name.components(separatedBy: " ").first, lastName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name.components(separatedBy: " ").last, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 2), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name.components(separatedBy: " ").first, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - + let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 1), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 2), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let peer3 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: 3), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil) - let peer3Author = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName.components(separatedBy: " ").first, lastName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName.components(separatedBy: " ").last, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - - let peer4 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name.components(separatedBy: " ").first, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - + let peer3Author = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer4 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 4), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let peer5 = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: 5), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil) - - let peer6 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: 5), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name.components(separatedBy: " ").first, lastName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name.components(separatedBy: " ").last, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - let peer7 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 6), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name.components(separatedBy: " ").first, lastName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name.components(separatedBy: " ").last, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer6 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: 5), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer7 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 6), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let timestamp = self.referenceTimestamp diff --git a/submodules/TelegramCore/TelegramCore/Themes.swift b/submodules/TelegramCore/TelegramCore/Themes.swift index 87139785d5..bada345a5b 100644 --- a/submodules/TelegramCore/TelegramCore/Themes.swift +++ b/submodules/TelegramCore/TelegramCore/Themes.swift @@ -135,9 +135,8 @@ private func saveUnsaveTheme(account: Account, theme: TelegramTheme, unsave: Boo return account.postbox.transaction { transaction -> Signal in let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes) var items = entries.map { $0.contents as! TelegramTheme } - if unsave { - items = items.filter { $0.id != theme.id } - } else { + items = items.filter { $0.id != theme.id } + if !unsave { items.insert(theme, at: 0) } var updatedEntries: [OrderedItemListEntry] = [] @@ -182,7 +181,6 @@ public enum UploadThemeError { } private struct UploadedThemeData { - fileprivate let resource: MediaResource fileprivate let content: UploadedThemeDataContent } @@ -194,45 +192,70 @@ private enum UploadedThemeDataContent { private func uploadedTheme(postbox: Postbox, network: Network, resource: MediaResource) -> Signal { return multipartUpload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .file), hintFileSize: nil, hintFileIsLarge: false) |> map { result -> UploadedThemeData in - return UploadedThemeData(resource: resource, content: .result(result)) + return UploadedThemeData(content: .result(result)) } |> `catch` { _ -> Signal in - return .single(UploadedThemeData(resource: resource, content: .error)) + return .single(UploadedThemeData(content: .error)) } } -private func uploadTheme(account: Account, resource: MediaResource) -> Signal { +private func uploadedThemeThumbnail(postbox: Postbox, network: Network, data: Data) -> Signal { + return multipartUpload(network: network, postbox: postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false) + |> map { result -> UploadedThemeData in + return UploadedThemeData(content: .result(result)) + } + |> `catch` { _ -> Signal in + return .single(UploadedThemeData(content: .error)) + } +} + +private func uploadTheme(account: Account, resource: MediaResource, thumbnailData: Data? = nil) -> Signal { let fileName = "theme.\(themeFileExtension)" let mimeType = "application/x-tgtheme-\(themeFormat)" - return uploadedTheme(postbox: account.postbox, network: account.network, resource: resource) - |> mapError { _ -> UploadThemeError in return .generic } - |> mapToSignal { result -> Signal<(UploadThemeResult, MediaResource?), UploadThemeError> in - switch result.content { - case .error: - return .fail(.generic) - case let .result(resultData): - switch resultData { - case let .progress(progress): - return .single((.progress(progress), result.resource)) - case let .inputFile(file): - return account.network.request(Api.functions.account.uploadTheme(flags: 0, file: file, thumb: nil, fileName: fileName, mimeType: mimeType)) - |> mapError { _ in return UploadThemeError.generic } - |> mapToSignal { document -> Signal<(UploadThemeResult, MediaResource?), UploadThemeError> in - if let file = telegramMediaFileFromApiDocument(document) { - return .single((.complete(file), result.resource)) - } else { - return .fail(.generic) + let uploadedThumbnail: Signal + if let thumbnailData = thumbnailData { + uploadedThumbnail = uploadedThemeThumbnail(postbox: account.postbox, network: account.network, data: thumbnailData) + |> mapError { _ -> UploadThemeError in return .generic } + |> map(Optional.init) + } else { + uploadedThumbnail = .single(nil) + } + + return uploadedThumbnail + |> mapToSignal { thumbnailResult -> Signal in + return uploadedTheme(postbox: account.postbox, network: account.network, resource: resource) + |> mapError { _ -> UploadThemeError in return .generic } + |> mapToSignal { result -> Signal in + switch result.content { + case .error: + return .fail(.generic) + case let .result(resultData): + switch resultData { + case let .progress(progress): + return .single(.progress(progress)) + case let .inputFile(file): + var flags: Int32 = 0 + var thumbnailFile: Api.InputFile? + if let thumbnailResult = thumbnailResult?.content, case let .result(result) = thumbnailResult, case let .inputFile(file) = result { + thumbnailFile = file + flags |= 1 << 0 } - } - default: - return .fail(.generic) + return account.network.request(Api.functions.account.uploadTheme(flags: flags, file: file, thumb: thumbnailFile, fileName: fileName, mimeType: mimeType)) + |> mapError { _ in return UploadThemeError.generic } + |> mapToSignal { document -> Signal in + if let file = telegramMediaFileFromApiDocument(document) { + return .single(.complete(file)) + } else { + return .fail(.generic) + } + } + default: + return .fail(.generic) + } } } } - |> map { result, _ -> UploadThemeResult in - return result - } } public enum CreateThemeError { @@ -244,8 +267,8 @@ public enum CreateThemeResult { case progress(Float) } -public func createTheme(account: Account, resource: MediaResource, title: String) -> Signal { - return uploadTheme(account: account, resource: resource) +public func createTheme(account: Account, title: String, resource: MediaResource, thumbnailData: Data? = nil) -> Signal { + return uploadTheme(account: account, resource: resource, thumbnailData: thumbnailData) |> mapError { _ in return CreateThemeError.generic } |> mapToSignal { result -> Signal in switch result { @@ -283,7 +306,7 @@ public func createTheme(account: Account, resource: MediaResource, title: String } } -public func updateTheme(account: Account, theme: TelegramTheme, title: String?, slug: String?, resource: MediaResource?) -> Signal { +public func updateTheme(account: Account, theme: TelegramTheme, title: String?, slug: String?, resource: MediaResource?, thumbnailData: Data? = nil) -> Signal { guard title != nil || slug != nil || resource != nil else { return .complete() } @@ -294,10 +317,9 @@ public func updateTheme(account: Account, theme: TelegramTheme, title: String?, if let _ = slug { flags |= 1 << 0 } - let uploadSignal: Signal if let resource = resource { - uploadSignal = uploadTheme(account: account, resource: resource) + uploadSignal = uploadTheme(account: account, resource: resource, thumbnailData: thumbnailData) |> map(Optional.init) } else { uploadSignal = .single(nil) diff --git a/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift index ca358f5386..bd68554e6a 100644 --- a/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift @@ -114,10 +114,10 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { panelHeight = 45.0 } - transition.updateFrame(node: self.downButton, frame: CGRect(origin: CGPoint(x: width - rightInset - 48.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight))) - transition.updateFrame(node: self.upButton, frame: CGRect(origin: CGPoint(x: width - rightInset - 48.0 - 43.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight))) - transition.updateFrame(node: self.calendarButton, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 60.0, height: panelHeight))) - transition.updateFrame(node: self.membersButton, frame: CGRect(origin: CGPoint(x: leftInset + 43.0, y: 0.0), size: CGSize(width: 60.0, height: panelHeight))) + self.downButton.frame = CGRect(origin: CGPoint(x: width - rightInset - 48.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight)) + self.upButton.frame = CGRect(origin: CGPoint(x: width - rightInset - 48.0 - 43.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight)) + self.calendarButton.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 60.0, height: panelHeight)) + self.membersButton.frame = CGRect(origin: CGPoint(x: leftInset + 43.0, y: 0.0), size: CGSize(width: 60.0, height: panelHeight)) var resultIndex: Int? var resultCount: Int? @@ -155,7 +155,7 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { let makeLabelLayout = TextNode.asyncLayout(self.resultsLabel) let (labelSize, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: resultsText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - leftInset - rightInset - 50.0, height: 100.0), alignment: .left, cutout: nil, insets: UIEdgeInsets())) let _ = labelApply() - self.resultsLabel.frame = CGRect(origin: CGPoint(x: leftInset + 105.0, y: floor((panelHeight - labelSize.size.height) / 2.0)), size: labelSize.size) + self.resultsLabel.frame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: floor((panelHeight - labelSize.size.height) / 2.0)), size: labelSize.size) let indicatorSize = self.activityIndicator.measure(CGSize(width: 22.0, height: 22.0)) self.activityIndicator.frame = CGRect(origin: CGPoint(x: width - rightInset - 41.0, y: floor((panelHeight - indicatorSize.height) / 2.0)), size: indicatorSize) diff --git a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift index 1049722f83..11e37ba283 100644 --- a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift +++ b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift @@ -53,13 +53,13 @@ public struct ApplicationSpecificSharedDataKeys { private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { case instantPageStoredState = 0 case cachedInstantPages = 1 - case resolvedWallpapers = 2 + case cachedWallpapers = 2 } public struct ApplicationSpecificItemCacheCollectionId { public static let instantPageStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.instantPageStoredState.rawValue) public static let cachedInstantPages = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedInstantPages.rawValue) - public static let resolvedWallpapers = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.resolvedWallpapers.rawValue) + public static let cachedWallpapers = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedWallpapers.rawValue) } private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 { diff --git a/submodules/WallpaperResources/Sources/WallpaperCache.swift b/submodules/WallpaperResources/Sources/WallpaperCache.swift new file mode 100644 index 0000000000..4797136cf7 --- /dev/null +++ b/submodules/WallpaperResources/Sources/WallpaperCache.swift @@ -0,0 +1,69 @@ +import Foundation +import UIKit +import SwiftSignalKit +import Postbox +import TelegramApi +import TelegramCore +import TelegramUIPreferences +import PersistentStringHash + +public final class CachedWallpaper: PostboxCoding { + public let wallpaper: TelegramWallpaper + + public init(wallpaper: TelegramWallpaper) { + self.wallpaper = wallpaper + } + + public init(decoder: PostboxDecoder) { + self.wallpaper = decoder.decodeObjectForKey("wallpaper", decoder: { TelegramWallpaper(decoder: $0) }) as! TelegramWallpaper + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeObject(self.wallpaper, forKey: "wallpaper") + } +} + +private let collectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 10000, highWaterItemCount: 20000) + +public func cachedWallpaper(account: Account, slug: String) -> Signal { + return account.postbox.transaction { transaction -> Signal in + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: Int64(bitPattern: slug.persistentHashValue)) + if let entry = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedWallpapers, key: key)) as? CachedWallpaper { + return .single(entry) + } else { + return getWallpaper(account: account, slug: slug) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { wallpaper -> Signal in + return account.postbox.transaction { transaction -> CachedWallpaper? in + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: Int64(bitPattern: slug.persistentHashValue)) + let id = ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedWallpapers, key: key) + if let wallpaper = wallpaper { + let entry = CachedWallpaper(wallpaper: wallpaper) + transaction.putItemCacheEntry(id: id, entry: entry, collectionSpec: collectionSpec) + return entry + } else { + transaction.removeItemCacheEntry(id: id) + return nil + } + } + } + } + } |> switchToLatest +} + +public func updateCachedWallpaper(account: Account, wallpaper: TelegramWallpaper) { + guard case let .file(file) = wallpaper, file.id != 0 else { + return + } + let _ = (account.postbox.transaction { transaction in + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: Int64(bitPattern: file.slug.persistentHashValue)) + let id = ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedWallpapers, key: key) + transaction.putItemCacheEntry(id: id, entry: CachedWallpaper(wallpaper: wallpaper), collectionSpec: collectionSpec) + }).start() +} diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index 3e2d4b639e..205b3fb165 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -633,6 +633,86 @@ private func generateBackArrowImage(color: UIColor) -> UIImage? { }) } +public func drawThemeImage(context c: CGContext, theme: PresentationTheme, size: CGSize) { + let drawingRect = CGRect(origin: CGPoint(), size: size) + + switch theme.chat.defaultWallpaper { + case .builtin: + if let filePath = frameworkBundle.path(forResource: "ChatWallpaperBuiltin0", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath), let cgImage = image.cgImage { + let size = image.size.aspectFilled(drawingRect.size) + c.draw(cgImage, in: CGRect(origin: CGPoint(x: (drawingRect.size.width - size.width) / 2.0, y: (drawingRect.size.height - size.height) / 2.0), size: size)) + } + case let .color(value): + c.setFillColor(UIColor(rgb: UInt32(bitPattern: value)).cgColor) + c.fill(drawingRect) + case let .file(file): + c.setFillColor(theme.chatList.backgroundColor.cgColor) + c.fill(drawingRect) + default: + break + } + + c.setFillColor(theme.rootController.navigationBar.backgroundColor.cgColor) + c.fill(CGRect(origin: CGPoint(x: 0.0, y: drawingRect.height - 42.0), size: CGSize(width: drawingRect.width, height: 42.0))) + + c.setFillColor(theme.rootController.navigationBar.separatorColor.cgColor) + c.fill(CGRect(origin: CGPoint(x: 1.0, y: drawingRect.height - 43.0), size: CGSize(width: drawingRect.width - 2.0, height: 1.0))) + + c.setFillColor(theme.rootController.navigationBar.secondaryTextColor.cgColor) + c.fillEllipse(in: CGRect(origin: CGPoint(x: drawingRect.width - 28.0 - 7.0, y: drawingRect.height - 7.0 - 28.0 - UIScreenPixel), size: CGSize(width: 28.0, height: 28.0))) + + if let arrow = generateBackArrowImage(color: theme.rootController.navigationBar.buttonColor), let image = arrow.cgImage { + c.draw(image, in: CGRect(x: 9.0, y: drawingRect.height - 11.0 - 22.0 + UIScreenPixel, width: 13.0, height: 22.0)) + } + c.setFillColor(theme.chat.inputPanel.panelBackgroundColor.cgColor) + c.fill(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: drawingRect.width, height: 42.0))) + + c.setFillColor(theme.chat.inputPanel.panelSeparatorColor.cgColor) + c.fill(CGRect(origin: CGPoint(x: 1.0, y: 42.0), size: CGSize(width: drawingRect.width - 2.0, height: 1.0))) + + c.setFillColor(theme.chat.inputPanel.inputBackgroundColor.cgColor) + c.setStrokeColor(theme.chat.inputPanel.inputStrokeColor.cgColor) + + c.setLineWidth(1.0) + let path = UIBezierPath(roundedRect: CGRect(x: 34.0, y: 6.0, width: drawingRect.width - 34.0 * 2.0, height: 31.0), cornerRadius: 15.5) + c.addPath(path.cgPath) + c.drawPath(using: .fillStroke) + + if let attachment = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconAttachment"), color: theme.chat.inputPanel.panelControlColor), let image = attachment.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(x: 3.0, y: 6.0 + UIScreenPixel), size: attachment.size.fitted(CGSize(width: 30.0, height: 30.0)))) + } + + if let microphone = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconMicrophone"), color: theme.chat.inputPanel.panelControlColor), let image = microphone.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(x: drawingRect.width - 3.0 - 29.0, y: 7.0 + UIScreenPixel), size: microphone.size.fitted(CGSize(width: 30.0, height: 30.0)))) + } + + c.saveGState() + c.setFillColor(theme.chat.message.incoming.bubble.withoutWallpaper.fill.cgColor) + c.setStrokeColor(theme.chat.message.incoming.bubble.withoutWallpaper.stroke.cgColor) + c.translateBy(x: 5.0, y: 65.0) + c.translateBy(x: 114.0, y: 32.0) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -114.0, y: -32.0) + let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") + c.strokePath() + let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") + c.fillPath() + c.restoreGState() + + c.saveGState() + c.setFillColor(theme.chat.message.outgoing.bubble.withoutWallpaper.fill.cgColor) + c.setStrokeColor(theme.chat.message.outgoing.bubble.withoutWallpaper.stroke.cgColor) + c.translateBy(x: drawingRect.width - 114.0 - 5.0, y: 25.0) + c.translateBy(x: 114.0, y: 32.0) + c.scaleBy(x: -1.0, y: -1.0) + c.translateBy(x: 0, y: -32.0) + let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") + c.strokePath() + let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") + c.fillPath() + c.restoreGState() +} + public func themeImage(account: Account, accountManager: AccountManager, fileReference: FileMediaReference, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { return telegramThemeData(account: account, accountManager: accountManager, resource: fileReference.media.resource, synchronousLoad: synchronousLoad) |> map { data in @@ -648,74 +728,7 @@ public func themeImage(account: Account, accountManager: AccountManager, fileRef context.withFlippedContext { c in c.setBlendMode(.normal) if let theme = theme { - switch theme.chat.defaultWallpaper { - case .builtin: - if let filePath = frameworkBundle.path(forResource: "ChatWallpaperBuiltin0", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath), let cgImage = image.cgImage { - c.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: drawingRect.width, height: drawingRect.height)) - } - case let .color(value): - c.setFillColor(UIColor(rgb: UInt32(bitPattern: value)).cgColor) - c.fill(drawingRect) - case let .file(file): - c.setFillColor(theme.chatList.backgroundColor.cgColor) - c.fill(drawingRect) - default: - break - } - - c.setFillColor(theme.rootController.navigationBar.backgroundColor.cgColor) - c.fill(CGRect(origin: CGPoint(x: 0.0, y: drawingRect.height - 42.0), size: CGSize(width: drawingRect.width, height: 42.0))) - - c.setFillColor(theme.rootController.navigationBar.separatorColor.cgColor) - c.fill(CGRect(origin: CGPoint(x: 1.0, y: drawingRect.height - 43.0), size: CGSize(width: drawingRect.width - 2.0, height: 1.0))) - - c.setFillColor(theme.rootController.navigationBar.secondaryTextColor.cgColor) - c.fillEllipse(in: CGRect(origin: CGPoint(x: drawingRect.width - 28.0 - 7.0, y: drawingRect.height - 7.0 - 28.0 - UIScreenPixel), size: CGSize(width: 28.0, height: 28.0))) - - if let arrow = generateBackArrowImage(color: theme.rootController.navigationBar.buttonColor), let image = arrow.cgImage { - c.draw(image, in: CGRect(x: 9.0, y: drawingRect.height - 11.0 - 22.0 + UIScreenPixel, width: 13.0, height: 22.0)) - } - c.setFillColor(theme.chat.inputPanel.panelBackgroundColor.cgColor) - c.fill(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: drawingRect.width, height: 42.0))) - - c.setFillColor(theme.chat.inputPanel.panelSeparatorColor.cgColor) - c.fill(CGRect(origin: CGPoint(x: 1.0, y: 42.0), size: CGSize(width: drawingRect.width - 2.0, height: 1.0))) - - c.setFillColor(theme.chat.inputPanel.inputBackgroundColor.cgColor) - c.setStrokeColor(theme.chat.inputPanel.inputStrokeColor.cgColor) - - c.setLineWidth(1.0) - let path = UIBezierPath(roundedRect: CGRect(x: 34.0, y: 6.0, width: drawingRect.width - 34.0 * 2.0, height: 31.0), cornerRadius: 15.5) - c.addPath(path.cgPath) - c.drawPath(using: .fillStroke) - - if let attachment = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconAttachment"), color: theme.chat.inputPanel.panelControlColor), let image = attachment.cgImage { - c.draw(image, in: CGRect(origin: CGPoint(x: 3.0, y: 6.0 + UIScreenPixel), size: attachment.size.fitted(CGSize(width: 30.0, height: 30.0)))) - } - - if let microphone = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/IconMicrophone"), color: theme.chat.inputPanel.panelControlColor), let image = microphone.cgImage { - c.draw(image, in: CGRect(origin: CGPoint(x: drawingRect.width - 3.0 - 29.0, y: 7.0 + UIScreenPixel), size: microphone.size.fitted(CGSize(width: 30.0, height: 30.0)))) - } - - c.saveGState() - c.setFillColor(theme.chat.message.incoming.bubble.withoutWallpaper.fill.cgColor) - c.setStrokeColor(theme.chat.message.incoming.bubble.withoutWallpaper.stroke.cgColor) - c.translateBy(x: 5.0, y: 65.0) - c.translateBy(x: 114.0, y: 32.0) - c.scaleBy(x: 1.0, y: -1.0) - c.translateBy(x: -114.0, y: -32.0) - let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 Z") - c.restoreGState() - - c.saveGState() - c.setFillColor(theme.chat.message.outgoing.bubble.withoutWallpaper.fill.cgColor) - c.setStrokeColor(theme.chat.message.outgoing.bubble.withoutWallpaper.stroke.cgColor) - c.translateBy(x: drawingRect.width - 114.0 - 5.0, y: 25.0) - c.translateBy(x: 114.0, y: 32.0) - c.scaleBy(x: -1.0, y: -1.0) - c.translateBy(x: 0, y: -32.0) - let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 Z") - c.restoreGState() + drawThemeImage(context: c, theme: theme, size: arguments.drawingSize) c.setStrokeColor(theme.rootController.navigationBar.separatorColor.cgColor) c.setLineWidth(2.0) diff --git a/submodules/WallpaperResources/WallpaperResources_Xcode.xcodeproj/project.pbxproj b/submodules/WallpaperResources/WallpaperResources_Xcode.xcodeproj/project.pbxproj index 464d07d274..b9bf77669f 100644 --- a/submodules/WallpaperResources/WallpaperResources_Xcode.xcodeproj/project.pbxproj +++ b/submodules/WallpaperResources/WallpaperResources_Xcode.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 095214ED2317EF76008CDD87 /* WallpaperCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095214EC2317EF76008CDD87 /* WallpaperCache.swift */; }; D03E484A23076B4A0049C28B /* WallpaperResources.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E484823076B4A0049C28B /* WallpaperResources.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03E485523076BB70049C28B /* WallpaperResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E485423076BB70049C28B /* WallpaperResources.swift */; }; D03E485823076BCB0049C28B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D03E485723076BCB0049C28B /* Foundation.framework */; }; @@ -24,6 +25,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 095214EC2317EF76008CDD87 /* WallpaperCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallpaperCache.swift; sourceTree = ""; }; D03E484523076B4A0049C28B /* WallpaperResources.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WallpaperResources.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03E484823076B4A0049C28B /* WallpaperResources.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WallpaperResources.h; sourceTree = ""; }; D03E484923076B4A0049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -88,6 +90,7 @@ D03E485423076BB70049C28B /* WallpaperResources.swift */, D03E487123076CF10049C28B /* FrameworkBundle.swift */, D03E484823076B4A0049C28B /* WallpaperResources.h */, + 095214EC2317EF76008CDD87 /* WallpaperCache.swift */, ); path = Sources; sourceTree = ""; @@ -191,6 +194,7 @@ buildActionMask = 2147483647; files = ( D03E487223076CF10049C28B /* FrameworkBundle.swift in Sources */, + 095214ED2317EF76008CDD87 /* WallpaperCache.swift in Sources */, D03E485523076BB70049C28B /* WallpaperResources.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0;