Cloud themes improvements

This commit is contained in:
Ilya Laktyushin
2019-08-29 15:50:28 +03:00
parent a706852dfc
commit 028299631e
10 changed files with 264 additions and 137 deletions

View File

@@ -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?()
})

View File

@@ -222,13 +222,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let peers = SimpleDictionary<PeerId, Peer>()
let messages = SimpleDictionary<MessageId, Message>()
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

View File

@@ -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()

View File

@@ -167,18 +167,14 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let peers = SimpleDictionary<PeerId, Peer>()
let messages = SimpleDictionary<MessageId, Message>()
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

View File

@@ -135,9 +135,8 @@ private func saveUnsaveTheme(account: Account, theme: TelegramTheme, unsave: Boo
return account.postbox.transaction { transaction -> Signal<Void, NoError> 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<UploadedThemeData, NoError> {
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<UploadedThemeData, NoError> in
return .single(UploadedThemeData(resource: resource, content: .error))
return .single(UploadedThemeData(content: .error))
}
}
private func uploadTheme(account: Account, resource: MediaResource) -> Signal<UploadThemeResult, UploadThemeError> {
private func uploadedThemeThumbnail(postbox: Postbox, network: Network, data: Data) -> Signal<UploadedThemeData, NoError> {
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<UploadedThemeData, NoError> in
return .single(UploadedThemeData(content: .error))
}
}
private func uploadTheme(account: Account, resource: MediaResource, thumbnailData: Data? = nil) -> Signal<UploadThemeResult, UploadThemeError> {
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<UploadedThemeData?, UploadThemeError>
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<UploadThemeResult, UploadThemeError> in
return uploadedTheme(postbox: account.postbox, network: account.network, resource: resource)
|> mapError { _ -> UploadThemeError in return .generic }
|> mapToSignal { result -> Signal<UploadThemeResult, UploadThemeError> 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<UploadThemeResult, UploadThemeError> 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<CreateThemeResult, CreateThemeError> {
return uploadTheme(account: account, resource: resource)
public func createTheme(account: Account, title: String, resource: MediaResource, thumbnailData: Data? = nil) -> Signal<CreateThemeResult, CreateThemeError> {
return uploadTheme(account: account, resource: resource, thumbnailData: thumbnailData)
|> mapError { _ in return CreateThemeError.generic }
|> mapToSignal { result -> Signal<CreateThemeResult, CreateThemeError> 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<CreateThemeResult, CreateThemeError> {
public func updateTheme(account: Account, theme: TelegramTheme, title: String?, slug: String?, resource: MediaResource?, thumbnailData: Data? = nil) -> Signal<CreateThemeResult, CreateThemeError> {
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<UploadThemeResult?, UploadThemeError>
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)

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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<CachedWallpaper?, NoError> {
return account.postbox.transaction { transaction -> Signal<CachedWallpaper?, NoError> 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<TelegramWallpaper?, NoError> in
return .single(nil)
}
|> mapToSignal { wallpaper -> Signal<CachedWallpaper?, NoError> 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()
}

View File

@@ -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)

View File

@@ -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 = "<group>"; };
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 = "<group>"; };
D03E484923076B4A0049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -88,6 +90,7 @@
D03E485423076BB70049C28B /* WallpaperResources.swift */,
D03E487123076CF10049C28B /* FrameworkBundle.swift */,
D03E484823076B4A0049C28B /* WallpaperResources.h */,
095214EC2317EF76008CDD87 /* WallpaperCache.swift */,
);
path = Sources;
sourceTree = "<group>";
@@ -191,6 +194,7 @@
buildActionMask = 2147483647;
files = (
D03E487223076CF10049C28B /* FrameworkBundle.swift in Sources */,
095214ED2317EF76008CDD87 /* WallpaperCache.swift in Sources */,
D03E485523076BB70049C28B /* WallpaperResources.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;