mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-03 21:16:35 +00:00
Cloud Themes wallpapers improvements
This commit is contained in:
parent
c05c59a1b7
commit
2c624975c5
@ -226,20 +226,24 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
initialState = EditThemeControllerState(mode: mode, title: "", slug: "", updatedTheme: nil, updating: false)
|
||||
previewThemePromise.set(.single(presentationData.theme.withUpdated(name: "", author: nil, defaultWallpaper: presentationData.chatWallpaper)))
|
||||
case let .edit(theme):
|
||||
if let file = theme.theme.file, let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let theme = makePresentationTheme(data: data) {
|
||||
case let .edit(info):
|
||||
if let file = info.theme.file, let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let theme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) {
|
||||
if case let .file(file) = theme.chat.defaultWallpaper, file.id == 0 {
|
||||
previewThemePromise.set(cachedWallpaper(account: context.account, slug: file.slug)
|
||||
|> map ({ wallpaper -> PresentationTheme in
|
||||
return theme.withUpdated(name: nil, author: nil, defaultWallpaper: wallpaper?.wallpaper)
|
||||
if let wallpaper = wallpaper {
|
||||
return theme.withUpdated(name: nil, author: nil, defaultWallpaper: wallpaper.wallpaper)
|
||||
} else {
|
||||
return theme.withUpdated(name: nil, author: nil, defaultWallpaper: .color(Int32(bitPattern: theme.chatList.backgroundColor.rgb)))
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
previewThemePromise.set(.single(theme))
|
||||
previewThemePromise.set(.single(theme.withUpdated(name: nil, author: nil, defaultWallpaper: info.resolvedWallpaper)))
|
||||
}
|
||||
} else {
|
||||
previewThemePromise.set(.single(context.sharedContext.currentPresentationData.with { $0 }.theme))
|
||||
}
|
||||
initialState = EditThemeControllerState(mode: mode, title: theme.theme.title, slug: theme.theme.slug, updatedTheme: nil, updating: false)
|
||||
initialState = EditThemeControllerState(mode: mode, title: info.theme.title, slug: info.theme.slug, updatedTheme: nil, updating: false)
|
||||
}
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
@ -373,9 +377,13 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
||||
themeResource = resource
|
||||
themeData = data
|
||||
|
||||
var wallpaperImage: UIImage?
|
||||
if case .file = theme.chat.defaultWallpaper {
|
||||
wallpaperImage = chatControllerBackgroundImage(theme: theme, wallpaper: theme.chat.defaultWallpaper, 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: theme, size: size)
|
||||
drawThemeImage(context: c, theme: theme, wallpaperImage: wallpaperImage, size: size)
|
||||
})?.cgImage {
|
||||
context.draw(image, in: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
@ -410,17 +418,14 @@ 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
|
||||
if let theme = theme {
|
||||
themeSpecificChatWallpapers[themeReference.index] = 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)
|
||||
})
|
||||
} |> deliverOnMainQueue).start(completed: {
|
||||
|
||||
@ -75,7 +75,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
self.scrollNode = ASScrollNode()
|
||||
self.pageControlBackgroundNode = ASDisplayNode()
|
||||
self.pageControlBackgroundNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.3)
|
||||
self.pageControlBackgroundNode.cornerRadius = 6.0
|
||||
self.pageControlBackgroundNode.cornerRadius = 8.0
|
||||
|
||||
self.pageControlNode = PageControlNode(dotColor: self.theme.chatList.unreadBadgeActiveBackgroundColor, inactiveDotColor: self.presentationData.theme.list.pageIndicatorInactiveColor)
|
||||
|
||||
@ -168,10 +168,10 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
}
|
||||
})
|
||||
|
||||
self.serviceColorDisposable = (chatServiceBackgroundColor(wallpaper: self.presentationData.theme.chat.defaultWallpaper, mediaBox: context.account.postbox.mediaBox)
|
||||
self.serviceColorDisposable = (chatServiceBackgroundColor(wallpaper: self.presentationData.chatWallpaper, mediaBox: context.account.postbox.mediaBox)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] color in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.presentationData.theme.chat.defaultWallpaper.hasWallpaper {
|
||||
if strongSelf.presentationData.chatWallpaper.hasWallpaper {
|
||||
strongSelf.pageControlBackgroundNode.backgroundColor = color
|
||||
} else {
|
||||
strongSelf.pageControlBackgroundNode.backgroundColor = .clear
|
||||
|
||||
@ -10,6 +10,7 @@ import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import ShareController
|
||||
import CounterContollerTitleView
|
||||
import WallpaperResources
|
||||
|
||||
public enum ThemePreviewSource {
|
||||
case theme(TelegramTheme)
|
||||
@ -22,6 +23,7 @@ public final class ThemePreviewController: ViewController {
|
||||
private let previewTheme: PresentationTheme
|
||||
private let source: ThemePreviewSource
|
||||
private let theme = Promise<TelegramTheme?>()
|
||||
private let wallpaper = Promise<TelegramWallpaper>()
|
||||
|
||||
private var controllerNode: ThemePreviewControllerNode {
|
||||
return self.displayNode as! ThemePreviewControllerNode
|
||||
@ -57,6 +59,15 @@ public final class ThemePreviewController: ViewController {
|
||||
themeName = previewTheme.name.string
|
||||
}
|
||||
|
||||
if case let .file(file) = previewTheme.chat.defaultWallpaper, file.id == 0 {
|
||||
self.wallpaper.set(cachedWallpaper(account: context.account, slug: file.slug)
|
||||
|> mapToSignal { wallpaper in
|
||||
return .single(wallpaper?.wallpaper ?? .color(Int32(bitPattern: previewTheme.chatList.backgroundColor.rgb)))
|
||||
})
|
||||
} else {
|
||||
self.wallpaper.set(.single(previewTheme.chat.defaultWallpaper))
|
||||
}
|
||||
|
||||
if let author = previewTheme.author {
|
||||
let titleView = CounterContollerTitleView(theme: self.previewTheme)
|
||||
titleView.title = CounterContollerTitle(title: themeName, counter: author)
|
||||
|
||||
@ -30,6 +30,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private let previewTheme: PresentationTheme
|
||||
private var presentationData: PresentationData
|
||||
|
||||
public let wallpaperPromise = Promise<TelegramWallpaper>()
|
||||
|
||||
private let referenceTimestamp: Int32
|
||||
|
||||
private let scrollNode: ASScrollNode
|
||||
@ -40,13 +42,15 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private var chatNodes: [ListViewItemNode]?
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
private let chatBackgroundNode: WallpaperBackgroundNode
|
||||
private let instantChatBackgroundNode: WallpaperBackgroundNode
|
||||
private let remoteChatBackgroundNode: TransformImageNode
|
||||
private var messageNodes: [ListViewItemNode]?
|
||||
|
||||
private let toolbarNode: WallpaperGalleryToolbarNode
|
||||
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
|
||||
private var wallpaperDisposable: Disposable?
|
||||
private var colorDisposable: Disposable?
|
||||
|
||||
init(context: AccountContext, previewTheme: PresentationTheme, dismiss: @escaping () -> Void, apply: @escaping () -> Void) {
|
||||
@ -63,17 +67,20 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.referenceTimestamp = Int32(calendar.date(from: components)?.timeIntervalSince1970 ?? 0.0)
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
||||
self.pageControlBackgroundNode = ASDisplayNode()
|
||||
self.pageControlBackgroundNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.3)
|
||||
self.pageControlBackgroundNode.cornerRadius = 6.0
|
||||
self.pageControlBackgroundNode.cornerRadius = 8.0
|
||||
|
||||
self.pageControlNode = PageControlNode(dotColor: previewTheme.chatList.unreadBadgeActiveBackgroundColor, inactiveDotColor: previewTheme.list.pageIndicatorInactiveColor)
|
||||
|
||||
self.chatListBackgroundNode = ASDisplayNode()
|
||||
self.chatBackgroundNode = WallpaperBackgroundNode()
|
||||
self.chatBackgroundNode.displaysAsynchronously = false
|
||||
self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: previewTheme, wallpaper: previewTheme.chat.defaultWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
||||
self.chatBackgroundNode.motionEnabled = previewTheme.chat.defaultWallpaper.settings?.motion ?? false
|
||||
self.instantChatBackgroundNode = WallpaperBackgroundNode()
|
||||
self.instantChatBackgroundNode.displaysAsynchronously = false
|
||||
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: previewTheme, wallpaper: previewTheme.chat.defaultWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
||||
self.instantChatBackgroundNode.motionEnabled = previewTheme.chat.defaultWallpaper.settings?.motion ?? false
|
||||
|
||||
self.remoteChatBackgroundNode = TransformImageNode()
|
||||
|
||||
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings)
|
||||
|
||||
@ -94,7 +101,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.maskNode.image = generateMaskImage(color: self.previewTheme.chatList.backgroundColor)
|
||||
|
||||
if case let .color(value) = self.previewTheme.chat.defaultWallpaper {
|
||||
self.chatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
self.instantChatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
|
||||
}
|
||||
|
||||
self.pageControlNode.isUserInteractionEnabled = false
|
||||
@ -107,7 +114,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.addSubnode(self.toolbarNode)
|
||||
|
||||
self.scrollNode.addSubnode(self.chatListBackgroundNode)
|
||||
self.scrollNode.addSubnode(self.chatBackgroundNode)
|
||||
self.scrollNode.addSubnode(self.instantChatBackgroundNode)
|
||||
self.scrollNode.addSubnode(self.remoteChatBackgroundNode)
|
||||
|
||||
self.toolbarNode.cancel = {
|
||||
dismiss()
|
||||
@ -116,7 +124,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
apply()
|
||||
}
|
||||
|
||||
self.colorDisposable = (chatServiceBackgroundColor(wallpaper: self.previewTheme.chat.defaultWallpaper, mediaBox: context.account.postbox.mediaBox)
|
||||
self.colorDisposable = (self.wallpaperPromise.get()
|
||||
|> mapToSignal { wallpaper in
|
||||
return chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: context.account.postbox.mediaBox)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] color in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.previewTheme.chat.defaultWallpaper.hasWallpaper {
|
||||
@ -294,7 +305,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
|
||||
itemNode!.isUserInteractionEnabled = false
|
||||
messageNodes.append(itemNode!)
|
||||
self.chatBackgroundNode.addSubnode(itemNode!)
|
||||
self.instantChatBackgroundNode.addSubnode(itemNode!)
|
||||
}
|
||||
self.messageNodes = messageNodes
|
||||
}
|
||||
@ -315,7 +326,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
let toolbarHeight = 49.0 + layout.intrinsicInsets.bottom
|
||||
self.chatListBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height)
|
||||
self.chatBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
|
||||
self.instantChatBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
|
||||
self.remoteChatBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
|
||||
|
||||
self.scrollNode.view.contentSize = CGSize(width: bounds.width * 2.0, height: bounds.height)
|
||||
|
||||
|
||||
@ -138,6 +138,8 @@ final class HistoryViewStateValidationContexts {
|
||||
|
||||
private var contexts: [Int32: HistoryStateValidationContext] = [:]
|
||||
|
||||
private var previousPeerValidationTimestamps: [PeerId: Double] = [:]
|
||||
|
||||
init(queue: Queue, postbox: Postbox, network: Network, accountPeerId: PeerId) {
|
||||
self.queue = queue
|
||||
self.postbox = postbox
|
||||
@ -264,21 +266,27 @@ final class HistoryViewStateValidationContexts {
|
||||
} else if view.namespaces.contains(Namespaces.Message.ScheduledCloud) {
|
||||
if let _ = self.contexts[id] {
|
||||
} else if let location = location, case let .peer(peerId) = location {
|
||||
let context = HistoryStateValidationContext()
|
||||
self.contexts[id] = context
|
||||
|
||||
let disposable = MetaDisposable()
|
||||
let batch = HistoryStateValidationBatch(disposable: disposable)
|
||||
context.batch = batch
|
||||
|
||||
let messages: [Message] = view.entries.map { $0.message }.filter { $0.id.namespace == Namespaces.Message.ScheduledCloud }
|
||||
let timestamp = self.network.context.globalTime()
|
||||
if let previousTimestamp = self.previousPeerValidationTimestamps[peerId], timestamp < previousTimestamp + 60 {
|
||||
} else {
|
||||
self.previousPeerValidationTimestamps[peerId] = timestamp
|
||||
|
||||
let context = HistoryStateValidationContext()
|
||||
self.contexts[id] = context
|
||||
|
||||
disposable.set((validateScheduledMessagesBatch(postbox: self.postbox, network: self.network, accountPeerId: peerId, tag: nil, messages: messages, historyState: .scheduledMessages(peerId))
|
||||
|> deliverOn(self.queue)).start(completed: { [weak self] in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[id] {
|
||||
context.batch = nil
|
||||
}
|
||||
}))
|
||||
let disposable = MetaDisposable()
|
||||
let batch = HistoryStateValidationBatch(disposable: disposable)
|
||||
context.batch = batch
|
||||
|
||||
let messages: [Message] = view.entries.map { $0.message }.filter { $0.id.namespace == Namespaces.Message.ScheduledCloud }
|
||||
|
||||
disposable.set((validateScheduledMessagesBatch(postbox: self.postbox, network: self.network, accountPeerId: peerId, tag: nil, messages: messages, historyState: .scheduledMessages(peerId))
|
||||
|> deliverOn(self.queue)).start(completed: { [weak self] in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[id] {
|
||||
context.batch = nil
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,7 +194,7 @@ public extension Message {
|
||||
if self.flags.contains(.Failed) {
|
||||
return true
|
||||
} else if self.id.namespace == Namespaces.Message.ScheduledCloud {
|
||||
return timestamp > self.timestamp + 30
|
||||
return timestamp > self.timestamp + 60
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ extension TelegramWallpaper: Codable {
|
||||
case "builtin":
|
||||
self = .builtin(WallpaperSettings())
|
||||
default:
|
||||
if let color = UIColor(hexString: value) {
|
||||
if value.count == 6, let color = UIColor(hexString: value) {
|
||||
self = .color(Int32(bitPattern: color.rgb))
|
||||
} else {
|
||||
self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: false, isDark: false, slug: value, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: false, motion: false, color: nil, intensity: nil))
|
||||
|
||||
@ -5561,7 +5561,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let _ = (enqueueMessages(account: self.context.account, peerId: peerId, messages: self.transformEnqueueMessages(messages))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
||||
if let strongSelf = self, !strongSelf.presentationInterfaceState.isScheduledMessages {
|
||||
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
||||
}
|
||||
})
|
||||
|
||||
self.donateIntent()
|
||||
|
||||
@ -1075,27 +1075,43 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
|
||||
public func scrollScreenToTop() {
|
||||
var currentMessage: Message?
|
||||
if let historyView = self.historyView {
|
||||
if let visibleRange = self.displayedItemRange.loadedRange {
|
||||
var index = historyView.filteredEntries.count - 1
|
||||
loop: for entry in historyView.filteredEntries {
|
||||
if index >= visibleRange.firstIndex && index <= visibleRange.lastIndex {
|
||||
if case let .MessageEntry(message, _, _, _, _, _) = entry {
|
||||
currentMessage = message
|
||||
break loop
|
||||
} else if case let .MessageGroupEntry(_, messages, _) = entry {
|
||||
currentMessage = messages.first?.0
|
||||
break loop
|
||||
}
|
||||
if let subject = self.subject, case .scheduledMessages = subject {
|
||||
if let historyView = self.historyView {
|
||||
if let entry = historyView.filteredEntries.first {
|
||||
var currentMessage: Message?
|
||||
if case let .MessageEntry(message, _, _, _, _, _) = entry {
|
||||
currentMessage = message
|
||||
} else if case let .MessageGroupEntry(_, messages, _) = entry {
|
||||
currentMessage = messages.first?.0
|
||||
}
|
||||
if let message = currentMessage, let anchorMessage = self.anchorMessageInCurrentHistoryView() {
|
||||
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(index: .message(message.index), anchorIndex: .message(message.index), sourceIndex: .upperBound, scrollPosition: .bottom(0.0), animated: true), id: self.takeNextHistoryLocationId())
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let currentMessage = currentMessage {
|
||||
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(index: .message(currentMessage.index), anchorIndex: .message(currentMessage.index), sourceIndex: .upperBound, scrollPosition: .top(0.0), animated: true), id: self.takeNextHistoryLocationId())
|
||||
} else {
|
||||
var currentMessage: Message?
|
||||
if let historyView = self.historyView {
|
||||
if let visibleRange = self.displayedItemRange.loadedRange {
|
||||
var index = historyView.filteredEntries.count - 1
|
||||
loop: for entry in historyView.filteredEntries {
|
||||
if index >= visibleRange.firstIndex && index <= visibleRange.lastIndex {
|
||||
if case let .MessageEntry(message, _, _, _, _, _) = entry {
|
||||
currentMessage = message
|
||||
break loop
|
||||
} else if case let .MessageGroupEntry(_, messages, _) = entry {
|
||||
currentMessage = messages.first?.0
|
||||
break loop
|
||||
}
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let currentMessage = currentMessage {
|
||||
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Scroll(index: .message(currentMessage.index), anchorIndex: .message(currentMessage.index), sourceIndex: .upperBound, scrollPosition: .top(0.0), animated: true), id: self.takeNextHistoryLocationId())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, account: A
|
||||
type = .Generic(type: .UpdateVisible)
|
||||
}
|
||||
} else {
|
||||
type = .Generic(type: updateType)
|
||||
type = .Generic(type: .Generic)
|
||||
}
|
||||
return .HistoryView(view: view, type: type, scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: chatScrollPosition, initialData: ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData), id: location.id)
|
||||
}
|
||||
|
||||
@ -367,6 +367,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||
return nil
|
||||
}))
|
||||
case let .theme(media):
|
||||
params.dismissInput()
|
||||
let path = params.context.account.postbox.mediaBox.completedResourcePath(media.resource)
|
||||
var previewTheme: PresentationTheme?
|
||||
if let path = path, let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) {
|
||||
|
||||
@ -98,9 +98,17 @@ public enum PresentationThemeReference: PostboxCoding, Equatable {
|
||||
case 0:
|
||||
self = .builtin(PresentationBuiltinThemeReference(rawValue: decoder.decodeInt32ForKey("t", orElse: 0))!)
|
||||
case 1:
|
||||
self = .local(decoder.decodeObjectForKey("localTheme", decoder: { PresentationLocalTheme(decoder: $0) }) as! PresentationLocalTheme)
|
||||
if let localTheme = decoder.decodeObjectForKey("localTheme", decoder: { PresentationLocalTheme(decoder: $0) }) as? PresentationLocalTheme {
|
||||
self = .local(localTheme)
|
||||
} else {
|
||||
self = .builtin(.dayClassic)
|
||||
}
|
||||
case 2:
|
||||
self = .cloud(decoder.decodeObjectForKey("cloudTheme", decoder: { PresentationCloudTheme(decoder: $0) }) as! PresentationCloudTheme)
|
||||
if let cloudTheme = decoder.decodeObjectForKey("cloudTheme", decoder: { PresentationCloudTheme(decoder: $0) }) as? PresentationCloudTheme {
|
||||
self = .cloud(cloudTheme)
|
||||
} else {
|
||||
self = .builtin(.dayClassic)
|
||||
}
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .builtin(.dayClassic)
|
||||
@ -438,7 +446,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.chatWallpaper = (decoder.decodeObjectForKey("w", decoder: { TelegramWallpaper(decoder: $0) }) as? TelegramWallpaper) ?? .builtin(WallpaperSettings())
|
||||
self.theme = decoder.decodeObjectForKey("t", decoder: { PresentationThemeReference(decoder: $0) }) as! PresentationThemeReference
|
||||
self.theme = decoder.decodeObjectForKey("t", decoder: { PresentationThemeReference(decoder: $0) }) as? PresentationThemeReference ?? .builtin(.dayClassic)
|
||||
|
||||
self.themeSpecificChatWallpapers = decoder.decodeObjectDictionaryForKey("themeSpecificChatWallpapers", keyDecoder: { decoder in
|
||||
return decoder.decodeInt64ForKey("k", orElse: 0)
|
||||
|
||||
@ -641,7 +641,7 @@ private func generateBackArrowImage(color: UIColor) -> UIImage? {
|
||||
})
|
||||
}
|
||||
|
||||
public func drawThemeImage(context c: CGContext, theme: PresentationTheme, size: CGSize) {
|
||||
public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallpaperImage: UIImage? = nil, size: CGSize) {
|
||||
let drawingRect = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
switch theme.chat.defaultWallpaper {
|
||||
@ -656,6 +656,11 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, size:
|
||||
case let .file(file):
|
||||
c.setFillColor(theme.chatList.backgroundColor.cgColor)
|
||||
c.fill(drawingRect)
|
||||
|
||||
if let image = wallpaperImage, 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))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -722,30 +727,162 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, size:
|
||||
}
|
||||
|
||||
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
|
||||
let theme: PresentationTheme?
|
||||
if let data = data {
|
||||
theme = makePresentationTheme(data: data)
|
||||
let maybeFetched = accountManager.mediaBox.resourceData(fileReference.media.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
|
||||
let data = maybeFetched
|
||||
|> take(1)
|
||||
|> mapToSignal { maybeData -> Signal<(Data?, Data?), NoError> in
|
||||
if maybeData.complete {
|
||||
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
|
||||
return .single((loadedData, nil))
|
||||
} else {
|
||||
theme = nil
|
||||
let decodedThumbnailData = fileReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail)
|
||||
|
||||
let previewRepresentation = fileReference.media.previewRepresentations.first
|
||||
let fetchedThumbnail: Signal<FetchResourceSourceType, FetchResourceError>
|
||||
if let previewRepresentation = previewRepresentation {
|
||||
fetchedThumbnail = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: fileReference.resourceReference(previewRepresentation.resource))
|
||||
} else {
|
||||
fetchedThumbnail = .complete()
|
||||
}
|
||||
|
||||
let thumbnailData: Signal<Data?, NoError>
|
||||
if let previewRepresentation = previewRepresentation {
|
||||
thumbnailData = Signal<Data?, NoError> { subscriber in
|
||||
let fetchedDisposable = fetchedThumbnail.start()
|
||||
let thumbnailDisposable = account.postbox.mediaBox.resourceData(previewRepresentation.resource).start(next: { next in
|
||||
let data = next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])
|
||||
if let data = data, data.count > 0 {
|
||||
subscriber.putNext(data)
|
||||
} else {
|
||||
subscriber.putNext(decodedThumbnailData)
|
||||
}
|
||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||
|
||||
return ActionDisposable {
|
||||
fetchedDisposable.dispose()
|
||||
thumbnailDisposable.dispose()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
thumbnailData = .single(decodedThumbnailData)
|
||||
}
|
||||
|
||||
let reference = fileReference.resourceReference(fileReference.media.resource)
|
||||
let fullSizeData = Signal<Data?, NoError> { subscriber in
|
||||
let fetch = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: reference).start()
|
||||
let disposable = (account.postbox.mediaBox.resourceData(reference.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false)
|
||||
|> map { data -> Data? in
|
||||
return data.complete ? try? Data(contentsOf: URL(fileURLWithPath: data.path)) : nil
|
||||
}).start(next: { next in
|
||||
if let data = next {
|
||||
accountManager.mediaBox.storeResourceData(reference.resource.id, data: data)
|
||||
}
|
||||
subscriber.putNext(next)
|
||||
}, error: { error in
|
||||
subscriber.putError(error)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
return ActionDisposable {
|
||||
fetch.dispose()
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
return thumbnailData |> mapToSignal { thumbnailData in
|
||||
return fullSizeData |> map { fullSizeData in
|
||||
return (fullSizeData, thumbnailData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|> mapToSignal { (fullSizeData, thumbnailData) -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in
|
||||
if let fullSizeData = fullSizeData, let theme = makePresentationTheme(data: fullSizeData) {
|
||||
if case let .file(file) = theme.chat.defaultWallpaper, file.id == 0 {
|
||||
return cachedWallpaper(account: account, slug: file.slug)
|
||||
|> mapToSignal { wallpaper -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> 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: .wallpaper(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)
|
||||
} else {
|
||||
return (theme, nil, thumbnailData)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single((theme, nil, thumbnailData))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single((theme, nil, thumbnailData))
|
||||
}
|
||||
} else {
|
||||
return .single((nil, nil, thumbnailData))
|
||||
}
|
||||
}
|
||||
return data
|
||||
|> map { theme, wallpaperImage, thumbnailData in
|
||||
return { arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: 0.0, clear: true)
|
||||
var thumbnailImage: CGImage?
|
||||
if let thumbnailData = thumbnailData, let imageSource = CGImageSourceCreateWithData(thumbnailData as CFData, nil), let image = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
|
||||
thumbnailImage = image
|
||||
}
|
||||
|
||||
var blurredThumbnailImage: UIImage?
|
||||
if let thumbnailImage = thumbnailImage {
|
||||
let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height)
|
||||
if thumbnailSize.width > 200.0 {
|
||||
blurredThumbnailImage = UIImage(cgImage: thumbnailImage)
|
||||
} else {
|
||||
let initialThumbnailContextFittingSize = arguments.imageSize.fitted(CGSize(width: 90.0, height: 90.0))
|
||||
|
||||
let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize)
|
||||
let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0)
|
||||
thumbnailContext.withFlippedContext { c in
|
||||
c.draw(thumbnailImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize))
|
||||
}
|
||||
telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
|
||||
|
||||
var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5))
|
||||
if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 {
|
||||
thumbnailContextFittingSize = thumbnailContextFittingSize.aspectFilled(CGSize(width: 150.0, height: 150.0))
|
||||
}
|
||||
|
||||
blurredThumbnailImage = thumbnailContext.generateImage()
|
||||
}
|
||||
}
|
||||
|
||||
let drawingRect = arguments.drawingRect
|
||||
context.withFlippedContext { c in
|
||||
c.setBlendMode(.normal)
|
||||
if let theme = theme {
|
||||
drawThemeImage(context: c, theme: theme, size: arguments.drawingSize)
|
||||
if let blurredThumbnailImage = blurredThumbnailImage, theme == nil {
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: 0.0, clear: true)
|
||||
context.withFlippedContext { c in
|
||||
c.setBlendMode(.copy)
|
||||
if let cgImage = blurredThumbnailImage.cgImage {
|
||||
c.interpolationQuality = .none
|
||||
let fittedSize = blurredThumbnailImage.size.aspectFilled(arguments.drawingSize)
|
||||
drawImage(context: c, image: cgImage, orientation: .up, in: CGRect(origin: CGPoint(x: (drawingRect.width - fittedSize.width) / 2.0, y: (drawingRect.height - fittedSize.height) / 2.0), size: fittedSize))
|
||||
c.setBlendMode(.normal)
|
||||
}
|
||||
}
|
||||
addCorners(context, arguments: arguments)
|
||||
return context
|
||||
}
|
||||
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: 0.0, clear: true)
|
||||
if let theme = theme {
|
||||
context.withFlippedContext { c in
|
||||
c.setBlendMode(.normal)
|
||||
|
||||
drawThemeImage(context: c, theme: theme, wallpaperImage: wallpaperImage, size: arguments.drawingSize)
|
||||
|
||||
c.setStrokeColor(theme.rootController.navigationBar.separatorColor.cgColor)
|
||||
c.setLineWidth(2.0)
|
||||
let borderPath = UIBezierPath(roundedRect: drawingRect, cornerRadius: 4.0)
|
||||
c.addPath(borderPath.cgPath)
|
||||
c.drawPath(using: .stroke)
|
||||
} else if let emptyColor = arguments.emptyColor {
|
||||
c.setFillColor(emptyColor.cgColor)
|
||||
c.fill(drawingRect)
|
||||
}
|
||||
}
|
||||
addCorners(context, arguments: arguments)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user