mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Gradient updates
This commit is contained in:
parent
9e99110a55
commit
d09fd03c1f
@ -591,7 +591,7 @@ public protocol SharedAccountContext: class {
|
||||
func makeComposeController(context: AccountContext) -> ViewController
|
||||
func makeChatListController(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController
|
||||
func makeChatController(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, botStart: ChatControllerInitialBotStart?, mode: ChatControllerPresentationMode) -> ChatController
|
||||
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?) -> ListViewItem
|
||||
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?, backgroundNode: ASDisplayNode?) -> ListViewItem
|
||||
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
|
||||
func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController?
|
||||
func makeContactSelectionController(_ params: ContactSelectionControllerParams) -> ContactSelectionController
|
||||
|
@ -12,6 +12,7 @@ swift_library(
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -2,6 +2,7 @@ import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
private func shiftArray(array: [CGPoint], offset: Int) -> [CGPoint] {
|
||||
var newArray = array
|
||||
@ -117,6 +118,26 @@ private func generateGradient(size: CGSize, colors: [UIColor], positions: [CGPoi
|
||||
}
|
||||
|
||||
public final class GradientBackgroundNode: ASDisplayNode {
|
||||
public final class CloneNode: ASImageNode {
|
||||
private weak var parentNode: GradientBackgroundNode?
|
||||
private var index: SparseBag<Weak<CloneNode>>.Index?
|
||||
|
||||
public init(parentNode: GradientBackgroundNode) {
|
||||
self.parentNode = parentNode
|
||||
|
||||
super.init()
|
||||
|
||||
self.index = parentNode.cloneNodes.add(Weak<CloneNode>(self))
|
||||
self.image = parentNode.contentView.image
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let parentNode = self.parentNode, let index = self.index {
|
||||
parentNode.cloneNodes.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static let basePositions: [CGPoint] = [
|
||||
CGPoint(x: 0.80, y: 0.10),
|
||||
CGPoint(x: 0.60, y: 0.20),
|
||||
@ -156,7 +177,8 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
var numberOfFrames: Int
|
||||
var curve: ContainedViewLayoutTransitionCurve
|
||||
}
|
||||
private var cachedPhaseTransition: [PhaseTransitionKey: [UIImage]] = [:]
|
||||
|
||||
private let cloneNodes = SparseBag<Weak<CloneNode>>()
|
||||
|
||||
override public init() {
|
||||
self.contentView = UIImageView()
|
||||
@ -208,12 +230,32 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
animation.isRemovedOnCompletion = true
|
||||
self.contentView.layer.removeAnimation(forKey: "contents")
|
||||
self.contentView.layer.add(animation, forKey: "contents")
|
||||
|
||||
for cloneNode in self.cloneNodes {
|
||||
if let value = cloneNode.value {
|
||||
value.image = images.last
|
||||
value.layer.removeAnimation(forKey: "contents")
|
||||
value.layer.add(animation.copy() as! CAAnimation, forKey: "contents")
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
self.contentView.image = generateGradient(size: imageSize, colors: colors, positions: positions)
|
||||
let image = generateGradient(size: imageSize, colors: colors, positions: positions)
|
||||
self.contentView.image = image
|
||||
|
||||
for cloneNode in self.cloneNodes {
|
||||
cloneNode.value?.image = image
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if sizeUpdated {
|
||||
self.contentView.image = generateGradient(size: imageSize, colors: colors, positions: positions)
|
||||
let image = generateGradient(size: imageSize, colors: colors, positions: positions)
|
||||
self.contentView.image = image
|
||||
|
||||
for cloneNode in self.cloneNodes {
|
||||
cloneNode.value?.image = image
|
||||
}
|
||||
|
||||
self.validPhase = self.phase
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,17 @@
|
||||
import Foundation
|
||||
|
||||
public final class Weak<T: AnyObject> {
|
||||
private weak var _value: T?
|
||||
|
||||
public var value: T? {
|
||||
return self._value
|
||||
}
|
||||
|
||||
public init(_ value: T) {
|
||||
self._value = value
|
||||
}
|
||||
}
|
||||
|
||||
public final class Bag<T> {
|
||||
public typealias Index = Int
|
||||
private var nextIndex: Index = 0
|
||||
@ -73,6 +85,46 @@ public final class Bag<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public final class SparseBag<T>: Sequence {
|
||||
public typealias Index = Int
|
||||
private var nextIndex: Index = 0
|
||||
private var items: [Index: T] = [:]
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
public func add(_ item: T) -> Index {
|
||||
let key = self.nextIndex
|
||||
self.nextIndex += 1
|
||||
self.items[key] = item
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
public func get(_ index: Index) -> T? {
|
||||
return self.items[index]
|
||||
}
|
||||
|
||||
public func remove(_ index: Index) {
|
||||
self.items.removeValue(forKey: index)
|
||||
}
|
||||
|
||||
public func removeAll() {
|
||||
self.items.removeAll()
|
||||
}
|
||||
|
||||
public var isEmpty: Bool {
|
||||
return self.items.isEmpty
|
||||
}
|
||||
|
||||
public func makeIterator() -> AnyIterator<T> {
|
||||
var iterator = self.items.makeIterator()
|
||||
return AnyIterator { () -> T? in
|
||||
return iterator.next()?.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class CounterBag {
|
||||
private var nextIndex: Int = 1
|
||||
private var items = Set<Int>()
|
||||
|
@ -71,6 +71,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||
|
||||
self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper)
|
||||
self.chatBackgroundNode.updateBubbleTheme(bubbleTheme: self.presentationData.theme, bubbleCorners: self.presentationData.chatBubbleCorners)
|
||||
|
||||
self.toolbarNode = BubbleSettingsToolbarNode(presentationThemeSettings: self.presentationThemeSettings, presentationData: self.presentationData)
|
||||
|
||||
@ -170,20 +171,20 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
|
||||
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
|
||||
|
||||
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
|
||||
|
||||
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
|
||||
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
|
||||
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
|
||||
|
||||
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
|
||||
|
||||
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
|
||||
|
||||
let width: CGFloat
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
|
@ -159,7 +159,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName, psaType: nil, flags: [])
|
||||
|
||||
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil)
|
||||
|
||||
var node: ListViewItemNode?
|
||||
if let current = currentNode {
|
||||
|
@ -83,6 +83,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||
|
||||
self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper)
|
||||
self.chatBackgroundNode.updateBubbleTheme(bubbleTheme: self.presentationData.theme, bubbleCorners: self.presentationData.chatBubbleCorners)
|
||||
|
||||
self.toolbarNode = TextSelectionToolbarNode(presentationThemeSettings: self.presentationThemeSettings, presentationData: self.presentationData)
|
||||
|
||||
@ -316,20 +317,20 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
|
||||
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
|
||||
|
||||
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
|
||||
|
||||
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
|
||||
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
|
||||
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
|
||||
|
||||
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
|
||||
|
||||
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode))
|
||||
|
||||
let width: CGFloat
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
|
@ -102,10 +102,12 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if colors.count >= 2 {
|
||||
self.imageNode.layer.compositingFilter = "softLightBlendMode"
|
||||
self.backgroundNode.image = generateGradientImage(size: CGSize(width: 80.0, height: 80.0), colors: colors.map(UIColor.init(rgb:)), locations: [0.0, 1.0], direction: .vertical)
|
||||
self.backgroundNode.backgroundColor = nil
|
||||
} else if colors.count >= 1 {
|
||||
self.backgroundNode.image = nil
|
||||
self.imageNode.layer.compositingFilter = "softLightBlendMode"
|
||||
self.backgroundNode.backgroundColor = UIColor(rgb: colors[0])
|
||||
}
|
||||
}
|
||||
|
@ -464,7 +464,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
updatedTheme = theme
|
||||
}
|
||||
|
||||
let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: updatedTheme!, wallpaper: wallpaper, bubbleCorners: bubbleCorners)
|
||||
let _ = PresentationResourcesChat.principalGraphics(theme: updatedTheme!, wallpaper: wallpaper, bubbleCorners: bubbleCorners)
|
||||
} else {
|
||||
updatedTheme = nil
|
||||
}
|
||||
@ -486,12 +486,15 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
strongSelf.toolbarNode.updateThemeAndStrings(theme: theme, strings: strongSelf.presentationData.strings)
|
||||
strongSelf.chatListBackgroundNode.backgroundColor = theme.chatList.backgroundColor
|
||||
strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor)
|
||||
|
||||
strongSelf.backgroundNode.updateBubbleTheme(bubbleTheme: theme, bubbleCorners: strongSelf.presentationData.chatBubbleCorners)
|
||||
}
|
||||
|
||||
strongSelf.serviceBackgroundColor = serviceBackgroundColor
|
||||
strongSelf.serviceBackgroundColorPromise.set(.single(serviceBackgroundColor))
|
||||
|
||||
strongSelf.backgroundNode.update(wallpaper: wallpaper)
|
||||
|
||||
strongSelf.ready.set(.single(true))
|
||||
|
||||
strongSelf.wallpaper = wallpaper
|
||||
@ -840,7 +843,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
}, clickThroughMessage: { [weak self] in
|
||||
self?.updateSection(.background)
|
||||
self?.requestSectionUpdate?(.background)
|
||||
})
|
||||
}, backgroundNode: self.backgroundNode)
|
||||
return item
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
self.ready.set(.single(true))
|
||||
self.instantChatBackgroundNode.update(wallpaper: wallpaper)
|
||||
|
||||
self.instantChatBackgroundNode.view.contentMode = .scaleAspectFill
|
||||
|
||||
self.remoteChatBackgroundNode = TransformImageNode()
|
||||
@ -197,8 +198,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
if gradientColors.count >= 3 {
|
||||
self.chatContainerNode.insertSubnode(self.wallpaperNode, belowSubnode: self.messagesContainerNode)
|
||||
self.wallpaperNode.update(wallpaper: self.wallpaper)
|
||||
}
|
||||
|
||||
self.wallpaperNode.update(wallpaper: self.wallpaper)
|
||||
self.wallpaperNode.updateBubbleTheme(bubbleTheme: self.previewTheme, bubbleCorners: self.presentationData.chatBubbleCorners)
|
||||
|
||||
self.remoteChatBackgroundNode.imageUpdated = { [weak self] image in
|
||||
if let strongSelf = self, strongSelf.blurredNode.supernode != nil {
|
||||
@ -498,7 +501,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
sampleMessages.append(message8)
|
||||
|
||||
items = sampleMessages.reversed().map { message in
|
||||
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperNode)
|
||||
}
|
||||
|
||||
let width: CGFloat
|
||||
|
@ -133,15 +133,16 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ThemeSettingsChatPreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let currentItem = self.item
|
||||
|
||||
let currentNodes = self.messageNodes
|
||||
|
||||
var currentBackgroundNode = self.backgroundNode
|
||||
|
||||
return { item, params, neighbors in
|
||||
var updatedBackgroundSignal: Signal<(UIImage?, Bool)?, NoError>?
|
||||
if currentItem?.wallpaper != item.wallpaper {
|
||||
updatedBackgroundSignal = chatControllerBackgroundImageSignal(wallpaper: item.wallpaper, mediaBox: item.context.sharedContext.accountManager.mediaBox, accountMediaBox: item.context.account.postbox.mediaBox)
|
||||
if currentBackgroundNode == nil {
|
||||
currentBackgroundNode = WallpaperBackgroundNode(context: item.context)
|
||||
}
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
@ -160,7 +161,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode))
|
||||
}
|
||||
|
||||
var nodes: [ListViewItemNode] = []
|
||||
@ -220,10 +221,9 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
topOffset += node.frame.size.height
|
||||
}
|
||||
|
||||
if strongSelf.backgroundNode == nil {
|
||||
let backgroundNode = WallpaperBackgroundNode(context: item.context)
|
||||
strongSelf.backgroundNode = backgroundNode
|
||||
strongSelf.insertSubnode(backgroundNode, at: 0)
|
||||
if let currentBackgroundNode = currentBackgroundNode, strongSelf.backgroundNode !== currentBackgroundNode {
|
||||
strongSelf.backgroundNode = currentBackgroundNode
|
||||
strongSelf.insertSubnode(currentBackgroundNode, at: 0)
|
||||
}
|
||||
|
||||
/*if let updatedBackgroundSignal = updatedBackgroundSignal {
|
||||
@ -287,6 +287,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
if let backgroundNode = strongSelf.backgroundNode {
|
||||
backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0)
|
||||
backgroundNode.update(wallpaper: item.wallpaper)
|
||||
backgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
backgroundNode.updateLayout(size: backgroundNode.bounds.size, transition: .immediate)
|
||||
}
|
||||
strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
|
@ -280,6 +280,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
var isBlurrable = true
|
||||
|
||||
self.nativeNode.updateBubbleTheme(bubbleTheme: presentationData.theme, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
|
||||
switch entry {
|
||||
case let .wallpaper(wallpaper, _):
|
||||
if case let .file(_, _, _, _, isPattern, _, _, _, settings) = wallpaper, isPattern {
|
||||
@ -1062,10 +1064,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
let theme = self.presentationData.theme.withUpdated(preview: true)
|
||||
|
||||
let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode))
|
||||
|
||||
let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode))
|
||||
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
|
||||
if let _ = self.messageNodes {
|
||||
|
@ -1029,7 +1029,7 @@ final class MessageStoryRenderer {
|
||||
let theme = self.presentationData.theme.withUpdated(preview: true)
|
||||
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
|
||||
|
||||
let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)]
|
||||
let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil)]
|
||||
|
||||
let inset: CGFloat = 16.0
|
||||
let width = layout.size.width - inset * 2.0
|
||||
|
@ -192,55 +192,48 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
|
||||
public let hasWallpaper: Bool
|
||||
|
||||
init(mediaBox: MediaBox, presentationTheme: PresentationTheme, wallpaper initialWallpaper: TelegramWallpaper, preview: Bool = false, knockoutMode: Bool, bubbleCorners: PresentationChatBubbleCorners) {
|
||||
init(presentationTheme: PresentationTheme, wallpaper initialWallpaper: TelegramWallpaper, preview: Bool = false, bubbleCorners: PresentationChatBubbleCorners) {
|
||||
let theme = presentationTheme.chat
|
||||
var wallpaper = initialWallpaper
|
||||
let wallpaper = initialWallpaper
|
||||
self.hasWallpaper = !wallpaper.isEmpty
|
||||
|
||||
let incoming: PresentationThemeBubbleColorComponents = wallpaper.isEmpty ? theme.message.incoming.bubble.withoutWallpaper : theme.message.incoming.bubble.withWallpaper
|
||||
let outgoing: PresentationThemeBubbleColorComponents = wallpaper.isEmpty ? theme.message.outgoing.bubble.withoutWallpaper : theme.message.outgoing.bubble.withWallpaper
|
||||
|
||||
if knockoutMode {
|
||||
let wallpaperImage = chatControllerBackgroundImage(theme: presentationTheme, wallpaper: wallpaper, mediaBox: mediaBox, knockoutMode: false)
|
||||
self.incomingBubbleGradientImage = wallpaperImage
|
||||
self.outgoingBubbleGradientImage = wallpaperImage
|
||||
wallpaper = presentationTheme.chat.defaultWallpaper
|
||||
|
||||
var incomingGradientColors: (UIColor, UIColor)?
|
||||
if incoming.fill.rgb != incoming.gradientFill.rgb {
|
||||
incomingGradientColors = (incoming.fill, incoming.gradientFill)
|
||||
}
|
||||
if let incomingGradientColors = incomingGradientColors {
|
||||
self.incomingBubbleGradientImage = generateImage(CGSize(width: 1.0, height: 512.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
|
||||
var locations: [CGFloat] = [0.0, 1.0]
|
||||
let colors = [incomingGradientColors.0.cgColor, incomingGradientColors.1.cgColor] as NSArray
|
||||
|
||||
let colorSpace = deviceColorSpace
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
|
||||
|
||||
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||
})
|
||||
} else {
|
||||
var incomingGradientColors: (UIColor, UIColor)?
|
||||
if incoming.fill.rgb != incoming.gradientFill.rgb {
|
||||
incomingGradientColors = (incoming.fill, incoming.gradientFill)
|
||||
}
|
||||
if let incomingGradientColors = incomingGradientColors {
|
||||
self.incomingBubbleGradientImage = generateImage(CGSize(width: 1.0, height: 512.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
|
||||
var locations: [CGFloat] = [0.0, 1.0]
|
||||
let colors = [incomingGradientColors.0.cgColor, incomingGradientColors.1.cgColor] as NSArray
|
||||
|
||||
let colorSpace = deviceColorSpace
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
|
||||
|
||||
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||
})
|
||||
} else {
|
||||
self.incomingBubbleGradientImage = nil
|
||||
}
|
||||
|
||||
var outgoingGradientColors: (UIColor, UIColor)?
|
||||
if outgoing.fill.rgb != outgoing.gradientFill.rgb {
|
||||
outgoingGradientColors = (outgoing.fill, outgoing.gradientFill)
|
||||
}
|
||||
if let outgoingGradientColors = outgoingGradientColors {
|
||||
self.outgoingBubbleGradientImage = generateImage(CGSize(width: 1.0, height: 512.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
|
||||
var locations: [CGFloat] = [0.0, 1.0]
|
||||
let colors = [outgoingGradientColors.0.cgColor, outgoingGradientColors.1.cgColor] as NSArray
|
||||
|
||||
let colorSpace = deviceColorSpace
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
|
||||
|
||||
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||
})
|
||||
} else {
|
||||
self.outgoingBubbleGradientImage = nil
|
||||
}
|
||||
self.incomingBubbleGradientImage = nil
|
||||
}
|
||||
|
||||
var outgoingGradientColors: (UIColor, UIColor)?
|
||||
if outgoing.fill.rgb != outgoing.gradientFill.rgb {
|
||||
outgoingGradientColors = (outgoing.fill, outgoing.gradientFill)
|
||||
}
|
||||
if let outgoingGradientColors = outgoingGradientColors {
|
||||
self.outgoingBubbleGradientImage = generateImage(CGSize(width: 1.0, height: 512.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
|
||||
var locations: [CGFloat] = [0.0, 1.0]
|
||||
let colors = [outgoingGradientColors.0.cgColor, outgoingGradientColors.1.cgColor] as NSArray
|
||||
|
||||
let colorSpace = deviceColorSpace
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
|
||||
|
||||
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||
})
|
||||
} else {
|
||||
self.outgoingBubbleGradientImage = nil
|
||||
}
|
||||
|
||||
let incomingKnockout = self.incomingBubbleGradientImage != nil
|
||||
|
@ -86,10 +86,10 @@ public struct PresentationResourcesChat {
|
||||
})
|
||||
}
|
||||
|
||||
public static func principalGraphics(mediaBox: MediaBox, knockoutWallpaper: Bool, theme: PresentationTheme, wallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners) -> PrincipalThemeEssentialGraphics {
|
||||
public static func principalGraphics(theme: PresentationTheme, wallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners) -> PrincipalThemeEssentialGraphics {
|
||||
let hasWallpaper = !wallpaper.isEmpty
|
||||
return theme.object(PresentationResourceParameterKey.chatPrincipalThemeEssentialGraphics(hasWallpaper: hasWallpaper, bubbleCorners: bubbleCorners), { theme in
|
||||
return PrincipalThemeEssentialGraphics(mediaBox: mediaBox, presentationTheme: theme, wallpaper: wallpaper, preview: theme.preview, knockoutMode: knockoutWallpaper, bubbleCorners: bubbleCorners)
|
||||
return PrincipalThemeEssentialGraphics(presentationTheme: theme, wallpaper: wallpaper, preview: theme.preview, bubbleCorners: bubbleCorners)
|
||||
}) as! PrincipalThemeEssentialGraphics
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,7 @@ import Markdown
|
||||
import TelegramPermissionsUI
|
||||
import Speak
|
||||
import UniversalMediaPlayer
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
extension ChatLocation {
|
||||
var peerId: PeerId {
|
||||
@ -215,7 +216,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
private let galleryHiddenMesageAndMediaDisposable = MetaDisposable()
|
||||
private let temporaryHiddenGalleryMediaDisposable = MetaDisposable()
|
||||
|
||||
|
||||
private let chatBackgroundNode: WallpaperBackgroundNode
|
||||
private var controllerInteraction: ChatControllerInteraction?
|
||||
private var interfaceInteraction: ChatPanelInterfaceInteraction?
|
||||
|
||||
@ -429,6 +431,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.subject = subject
|
||||
self.botStart = botStart
|
||||
self.peekData = peekData
|
||||
|
||||
self.chatBackgroundNode = WallpaperBackgroundNode(context: context)
|
||||
|
||||
var locationBroadcastPanelSource: LocationBroadcastPanelSource
|
||||
var groupCallPanelSource: GroupCallPanelSource
|
||||
@ -2690,7 +2694,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}, cancelInteractiveKeyboardGestures: { [weak self] in
|
||||
(self?.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
|
||||
self?.chatDisplayNode.cancelInteractiveKeyboardGestures()
|
||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings)
|
||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings, presentationContext: ChatPresentationContext(backgroundNode: self.chatBackgroundNode))
|
||||
|
||||
self.controllerInteraction = controllerInteraction
|
||||
|
||||
@ -4083,7 +4087,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = ChatControllerNode(context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, subject: self.subject, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar, controller: self)
|
||||
self.displayNode = ChatControllerNode(context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, subject: self.subject, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar, backgroundNode: self.chatBackgroundNode, controller: self)
|
||||
|
||||
self.chatDisplayNode.historyNode.didScrollWithOffset = { [weak self] offset, transition, itemNode in
|
||||
guard let strongSelf = self else {
|
||||
|
@ -138,6 +138,7 @@ public final class ChatControllerInteraction {
|
||||
var searchTextHighightState: (String, [MessageIndex])?
|
||||
var seenOneTimeAnimatedMedia = Set<MessageId>()
|
||||
var currentMessageWithLoadingReplyThread: MessageId?
|
||||
let presentationContext: ChatPresentationContext
|
||||
|
||||
init(
|
||||
openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool,
|
||||
@ -216,7 +217,8 @@ public final class ChatControllerInteraction {
|
||||
cancelInteractiveKeyboardGestures: @escaping () -> Void,
|
||||
automaticMediaDownloadSettings: MediaAutoDownloadSettings,
|
||||
pollActionState: ChatInterfacePollActionState,
|
||||
stickerSettings: ChatInterfaceStickerSettings
|
||||
stickerSettings: ChatInterfaceStickerSettings,
|
||||
presentationContext: ChatPresentationContext
|
||||
) {
|
||||
self.openMessage = openMessage
|
||||
self.openPeer = openPeer
|
||||
@ -297,6 +299,8 @@ public final class ChatControllerInteraction {
|
||||
|
||||
self.pollActionState = pollActionState
|
||||
self.stickerSettings = stickerSettings
|
||||
|
||||
self.presentationContext = presentationContext
|
||||
}
|
||||
|
||||
static var `default`: ChatControllerInteraction {
|
||||
@ -345,6 +349,9 @@ public final class ChatControllerInteraction {
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||
pollActionState: ChatInterfacePollActionState(),
|
||||
stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false),
|
||||
presentationContext: ChatPresentationContext(backgroundNode: nil)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
private var onLayoutCompletions: [(ContainedViewLayoutTransition) -> Void] = []
|
||||
|
||||
init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: MediaAutoDownloadSettings, navigationBar: NavigationBar?, controller: ChatControllerImpl?) {
|
||||
init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: MediaAutoDownloadSettings, navigationBar: NavigationBar?, backgroundNode: WallpaperBackgroundNode, controller: ChatControllerImpl?) {
|
||||
self.context = context
|
||||
self.chatLocation = chatLocation
|
||||
self.controllerInteraction = controllerInteraction
|
||||
@ -239,8 +239,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.navigationBar = navigationBar
|
||||
self.controller = controller
|
||||
|
||||
self.backgroundNode = WallpaperBackgroundNode(context: context)
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
self.backgroundNode = backgroundNode
|
||||
|
||||
self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer()
|
||||
self.titleAccessoryPanelContainer.clipsToBounds = true
|
||||
@ -363,6 +362,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
})
|
||||
|
||||
self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper)
|
||||
self.backgroundNode.updateBubbleTheme(bubbleTheme: chatPresentationInterfaceState.theme, bubbleCorners: chatPresentationInterfaceState.bubbleCorners)
|
||||
|
||||
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
|
||||
self.historyNode.enableExtractedBackgrounds = true
|
||||
|
@ -28,7 +28,7 @@ final class ChatImportStatusPanel: ASDisplayNode {
|
||||
if self.theme !== presentationData.theme.theme {
|
||||
self.theme = presentationData.theme.theme
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
self.backgroundNode.image = graphics.dateFloatingBackground
|
||||
self.secondaryBackgroundNode.image = graphics.dateFloatingBackground
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
enum ChatMessageBackgroundMergeType: Equatable {
|
||||
case None, Side, Top(side: Bool), Bottom, Both, Extracted
|
||||
@ -65,6 +66,7 @@ class ChatMessageBackground: ASDisplayNode {
|
||||
private var maskMode: Bool?
|
||||
private let imageNode: ASImageNode
|
||||
private let outlineImageNode: ASImageNode
|
||||
private weak var backgroundNode: WallpaperBackgroundNode?
|
||||
|
||||
var hasImage: Bool {
|
||||
self.imageNode.image != nil
|
||||
@ -92,19 +94,20 @@ class ChatMessageBackground: ASDisplayNode {
|
||||
}
|
||||
|
||||
func setMaskMode(_ maskMode: Bool) {
|
||||
if let type = self.type, let hasWallpaper = self.hasWallpaper, let highlighted = self.currentHighlighted, let graphics = self.graphics {
|
||||
self.setType(type: type, highlighted: highlighted, graphics: graphics, maskMode: maskMode, hasWallpaper: hasWallpaper, transition: .immediate)
|
||||
if let type = self.type, let hasWallpaper = self.hasWallpaper, let highlighted = self.currentHighlighted, let graphics = self.graphics, let backgroundNode = self.backgroundNode {
|
||||
self.setType(type: type, highlighted: highlighted, graphics: graphics, maskMode: maskMode, hasWallpaper: hasWallpaper, transition: .immediate, backgroundNode: backgroundNode)
|
||||
}
|
||||
}
|
||||
|
||||
func setType(type: ChatMessageBackgroundType, highlighted: Bool, graphics: PrincipalThemeEssentialGraphics, maskMode: Bool, hasWallpaper: Bool, transition: ContainedViewLayoutTransition) {
|
||||
func setType(type: ChatMessageBackgroundType, highlighted: Bool, graphics: PrincipalThemeEssentialGraphics, maskMode: Bool, hasWallpaper: Bool, transition: ContainedViewLayoutTransition, backgroundNode: WallpaperBackgroundNode?) {
|
||||
let previousType = self.type
|
||||
if let currentType = previousType, currentType == type, self.currentHighlighted == highlighted, self.graphics === graphics, self.maskMode == maskMode, self.hasWallpaper == hasWallpaper {
|
||||
if let currentType = previousType, currentType == type, self.currentHighlighted == highlighted, self.graphics === graphics, backgroundNode === self.backgroundNode, self.maskMode == maskMode, self.hasWallpaper == hasWallpaper {
|
||||
return
|
||||
}
|
||||
self.type = type
|
||||
self.currentHighlighted = highlighted
|
||||
self.graphics = graphics
|
||||
self.backgroundNode = backgroundNode
|
||||
self.hasWallpaper = hasWallpaper
|
||||
|
||||
let image: UIImage?
|
||||
@ -113,7 +116,7 @@ class ChatMessageBackground: ASDisplayNode {
|
||||
case .none:
|
||||
image = nil
|
||||
case let .incoming(mergeType):
|
||||
if maskMode && graphics.incomingBubbleGradientImage != nil {
|
||||
if maskMode, let backgroundNode = backgroundNode, backgroundNode.hasBubbleBackground(for: .incoming) {
|
||||
image = nil
|
||||
} else {
|
||||
switch mergeType {
|
||||
@ -136,7 +139,7 @@ class ChatMessageBackground: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
case let .outgoing(mergeType):
|
||||
if maskMode && graphics.outgoingBubbleGradientImage != nil {
|
||||
if maskMode, let backgroundNode = backgroundNode, backgroundNode.hasBubbleBackground(for: .outgoing) {
|
||||
image = nil
|
||||
} else {
|
||||
switch mergeType {
|
||||
|
@ -3,6 +3,7 @@ import AsyncDisplayKit
|
||||
import Display
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
private let maskInset: CGFloat = 1.0
|
||||
|
||||
@ -54,19 +55,20 @@ func bubbleMaskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThe
|
||||
}
|
||||
|
||||
final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
private let backgroundContent: ASDisplayNode
|
||||
private var backgroundContent: WallpaperBackgroundNode.BubbleBackgroundNode?
|
||||
|
||||
private var currentType: ChatMessageBackgroundType?
|
||||
private var currentMaskMode: Bool?
|
||||
private var theme: ChatPresentationThemeData?
|
||||
private var essentialGraphics: PrincipalThemeEssentialGraphics?
|
||||
private weak var backgroundNode: WallpaperBackgroundNode?
|
||||
|
||||
private var maskView: UIImageView?
|
||||
|
||||
private var fixedMaskMode: Bool?
|
||||
|
||||
var hasImage: Bool {
|
||||
return self.backgroundContent.contents != nil
|
||||
return self.backgroundContent != nil
|
||||
}
|
||||
|
||||
override var frame: CGRect {
|
||||
@ -77,32 +79,34 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
maskView.frame = maskFrame
|
||||
}
|
||||
}
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
backgroundContent.frame = self.bounds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init() {
|
||||
self.backgroundContent = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.addSubnode(self.backgroundContent)
|
||||
}
|
||||
|
||||
func setMaskMode(_ maskMode: Bool, mediaBox: MediaBox) {
|
||||
if let currentType = self.currentType, let theme = self.theme, let essentialGraphics = self.essentialGraphics {
|
||||
self.setType(type: currentType, theme: theme, mediaBox: mediaBox, essentialGraphics: essentialGraphics, maskMode: maskMode)
|
||||
if let currentType = self.currentType, let theme = self.theme, let essentialGraphics = self.essentialGraphics, let backgroundNode = self.backgroundNode {
|
||||
self.setType(type: currentType, theme: theme, essentialGraphics: essentialGraphics, maskMode: maskMode, backgroundNode: backgroundNode)
|
||||
}
|
||||
}
|
||||
|
||||
func setType(type: ChatMessageBackgroundType, theme: ChatPresentationThemeData, mediaBox: MediaBox, essentialGraphics: PrincipalThemeEssentialGraphics, maskMode inputMaskMode: Bool) {
|
||||
func setType(type: ChatMessageBackgroundType, theme: ChatPresentationThemeData, essentialGraphics: PrincipalThemeEssentialGraphics, maskMode inputMaskMode: Bool, backgroundNode: WallpaperBackgroundNode?) {
|
||||
let maskMode = self.fixedMaskMode ?? inputMaskMode
|
||||
|
||||
if self.currentType != type || self.theme != theme || self.currentMaskMode != maskMode || self.essentialGraphics !== essentialGraphics {
|
||||
if self.currentType != type || self.theme != theme || self.currentMaskMode != maskMode || self.essentialGraphics !== essentialGraphics || self.backgroundNode !== backgroundNode {
|
||||
let typeUpdated = self.currentType != type
|
||||
|
||||
self.currentType = type
|
||||
self.theme = theme
|
||||
self.essentialGraphics = essentialGraphics
|
||||
self.backgroundNode = backgroundNode
|
||||
|
||||
if maskMode != self.currentMaskMode {
|
||||
self.currentMaskMode = maskMode
|
||||
@ -124,14 +128,33 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .none:
|
||||
self.backgroundContent.contents = nil
|
||||
case .incoming:
|
||||
self.backgroundContent.contents = essentialGraphics.incomingBubbleGradientImage?.cgImage
|
||||
case .outgoing:
|
||||
self.backgroundContent.contents = essentialGraphics.outgoingBubbleGradientImage?.cgImage
|
||||
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
backgroundContent.frame = self.bounds
|
||||
}
|
||||
|
||||
if typeUpdated {
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
self.backgroundContent = nil
|
||||
backgroundContent.removeFromSupernode()
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .none:
|
||||
break
|
||||
case .incoming:
|
||||
if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .incoming) {
|
||||
backgroundContent.frame = self.bounds
|
||||
self.backgroundContent = backgroundContent
|
||||
self.insertSubnode(backgroundContent, at: 0)
|
||||
}
|
||||
case .outgoing:
|
||||
if let backgroundContent = backgroundNode?.makeBubbleBackground(for: .outgoing) {
|
||||
backgroundContent.frame = self.bounds
|
||||
self.backgroundContent = backgroundContent
|
||||
self.insertSubnode(backgroundContent, at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let maskView = self.maskView {
|
||||
@ -141,22 +164,28 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
}
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize) {
|
||||
self.backgroundContent.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize)
|
||||
//self.backgroundContent.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize)
|
||||
self.backgroundContent?.update(rect: rect, within: containerSize)
|
||||
}
|
||||
|
||||
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: animationCurve)
|
||||
transition.animatePositionAdditive(node: self.backgroundContent, offset: CGPoint(x: -value.x, y: -value.y))
|
||||
//let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: animationCurve)
|
||||
//transition.animatePositionAdditive(node: self.backgroundContent, offset: CGPoint(x: -value.x, y: -value.y))
|
||||
self.backgroundContent?.offset(value: value, animationCurve: animationCurve, duration: duration)
|
||||
}
|
||||
|
||||
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
|
||||
self.backgroundContent.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: value)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
|
||||
//self.backgroundContent.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: value)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
|
||||
self.backgroundContent?.offsetSpring(value: value, duration: duration, damping: damping)
|
||||
}
|
||||
|
||||
func updateFrame(_ value: CGRect, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) {
|
||||
if let maskView = self.maskView {
|
||||
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)).insetBy(dx: -maskInset, dy: -maskInset))
|
||||
}
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
transition.updateFrame(node: backgroundContent, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)))
|
||||
}
|
||||
transition.updateFrame(node: self, frame: value, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
@ -166,6 +195,9 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
if let maskView = self.maskView {
|
||||
transition.updateFrame(layer: maskView.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)).insetBy(dx: -maskInset, dy: -maskInset))
|
||||
}
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
transition.updateFrame(layer: backgroundContent.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)))
|
||||
}
|
||||
transition.updateFrame(layer: self.layer, frame: value, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
|
@ -22,6 +22,8 @@ import PersistentStringHash
|
||||
import GridMessageSelectionNode
|
||||
import AppBundle
|
||||
import Markdown
|
||||
import WallpaperBackgroundNode
|
||||
import SwiftSignalKit
|
||||
|
||||
enum InternalBubbleTapAction {
|
||||
case action(() -> Void)
|
||||
@ -228,6 +230,14 @@ private enum ContentNodeOperation {
|
||||
case insert(index: Int, node: ChatMessageBubbleContentNode)
|
||||
}
|
||||
|
||||
class ChatPresentationContext {
|
||||
weak var backgroundNode: WallpaperBackgroundNode?
|
||||
|
||||
init(backgroundNode: WallpaperBackgroundNode?) {
|
||||
self.backgroundNode = backgroundNode
|
||||
}
|
||||
}
|
||||
|
||||
class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode {
|
||||
class ContentContainer {
|
||||
let contentMessageStableId: UInt32
|
||||
@ -237,7 +247,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
var backgroundNode: ChatMessageBackground?
|
||||
var selectionBackgroundNode: ASDisplayNode?
|
||||
|
||||
private var currentParams: (size: CGSize, contentOrigin: CGPoint, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, mediaBox: MediaBox, messageSelection: Bool?, selectionInsets: UIEdgeInsets)?
|
||||
private var currentParams: (size: CGSize, contentOrigin: CGPoint, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, presentationContext: ChatPresentationContext, mediaBox: MediaBox, messageSelection: Bool?, selectionInsets: UIEdgeInsets)?
|
||||
|
||||
init(contentMessageStableId: UInt32) {
|
||||
self.contentMessageStableId = contentMessageStableId
|
||||
@ -301,8 +311,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
transition.updateAlpha(node: backgroundNode, alpha: 1.0)
|
||||
transition.updateAlpha(node: backgroundWallpaperNode, alpha: 1.0)
|
||||
|
||||
backgroundNode.setType(type: type, highlighted: false, graphics: currentParams.graphics, maskMode: true, hasWallpaper: currentParams.presentationData.theme.wallpaper.hasWallpaper, transition: .immediate)
|
||||
backgroundWallpaperNode.setType(type: type, theme: currentParams.presentationData.theme, mediaBox: currentParams.mediaBox, essentialGraphics: currentParams.graphics, maskMode: true)
|
||||
backgroundNode.setType(type: type, highlighted: false, graphics: currentParams.graphics, maskMode: true, hasWallpaper: currentParams.presentationData.theme.wallpaper.hasWallpaper, transition: .immediate, backgroundNode: currentParams.presentationContext.backgroundNode)
|
||||
backgroundWallpaperNode.setType(type: type, theme: currentParams.presentationData.theme, essentialGraphics: currentParams.graphics, maskMode: true, backgroundNode: currentParams.presentationContext.backgroundNode)
|
||||
}
|
||||
|
||||
if let currentParams = self.currentParams {
|
||||
@ -335,8 +345,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
fileprivate func isExtractedToContextPreviewUpdated(_ isExtractedToContextPreview: Bool) {
|
||||
}
|
||||
|
||||
fileprivate func update(size: CGSize, contentOrigin: CGPoint, selectionInsets: UIEdgeInsets, index: Int, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, mediaBox: MediaBox, messageSelection: Bool?) {
|
||||
self.currentParams = (size, contentOrigin, presentationData, graphics, backgroundType, mediaBox, messageSelection, selectionInsets)
|
||||
fileprivate func update(size: CGSize, contentOrigin: CGPoint, selectionInsets: UIEdgeInsets, index: Int, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, presentationContext: ChatPresentationContext, mediaBox: MediaBox, messageSelection: Bool?) {
|
||||
self.currentParams = (size, contentOrigin, presentationData, graphics, backgroundType, presentationContext, mediaBox, messageSelection, selectionInsets)
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
var incoming: Bool = false
|
||||
@ -2177,7 +2187,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets)
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
|
||||
|
||||
var updatedMergedTop = mergedBottom
|
||||
var updatedMergedBottom = mergedTop
|
||||
@ -2207,6 +2217,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
hideBackground: hideBackground,
|
||||
incoming: incoming,
|
||||
graphics: graphics,
|
||||
presentationContext: item.controllerInteraction.presentationContext,
|
||||
bubbleContentWidth: bubbleContentWidth,
|
||||
backgroundFrame: backgroundFrame,
|
||||
deliveryFailedInset: deliveryFailedInset,
|
||||
@ -2247,6 +2258,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
hideBackground: Bool,
|
||||
incoming: Bool,
|
||||
graphics: PrincipalThemeEssentialGraphics,
|
||||
presentationContext: ChatPresentationContext,
|
||||
bubbleContentWidth: CGFloat,
|
||||
backgroundFrame: CGRect,
|
||||
deliveryFailedInset: CGFloat,
|
||||
@ -2305,8 +2317,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
backgroundType = .incoming(mergeType)
|
||||
}
|
||||
let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper
|
||||
strongSelf.backgroundNode.setType(type: backgroundType, highlighted: strongSelf.highlightedState, graphics: graphics, maskMode: strongSelf.backgroundMaskMode, hasWallpaper: hasWallpaper, transition: transition)
|
||||
strongSelf.backgroundWallpaperNode.setType(type: backgroundType, theme: item.presentationData.theme, mediaBox: item.context.account.postbox.mediaBox, essentialGraphics: graphics, maskMode: strongSelf.backgroundMaskMode)
|
||||
strongSelf.backgroundNode.setType(type: backgroundType, highlighted: strongSelf.highlightedState, graphics: graphics, maskMode: strongSelf.backgroundMaskMode, hasWallpaper: hasWallpaper, transition: transition, backgroundNode: presentationContext.backgroundNode)
|
||||
strongSelf.backgroundWallpaperNode.setType(type: backgroundType, theme: item.presentationData.theme, essentialGraphics: graphics, maskMode: strongSelf.backgroundMaskMode, backgroundNode: presentationContext.backgroundNode)
|
||||
strongSelf.shadowNode.setType(type: backgroundType, hasWallpaper: hasWallpaper, graphics: graphics)
|
||||
if case .none = backgroundType {
|
||||
strongSelf.clippingNode.clipsToBounds = false
|
||||
@ -2587,7 +2599,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
selectionInsets.bottom = groupOverlap / 2.0
|
||||
}
|
||||
|
||||
contentContainer?.update(size: relativeFrame.size, contentOrigin: contentOrigin, selectionInsets: selectionInsets, index: index, presentationData: item.presentationData, graphics: graphics, backgroundType: backgroundType, mediaBox: item.context.account.postbox.mediaBox, messageSelection: itemSelection)
|
||||
contentContainer?.update(size: relativeFrame.size, contentOrigin: contentOrigin, selectionInsets: selectionInsets, index: index, presentationData: item.presentationData, graphics: graphics, backgroundType: backgroundType, presentationContext: item.controllerInteraction.presentationContext, mediaBox: item.context.account.postbox.mediaBox, messageSelection: itemSelection)
|
||||
|
||||
index += 1
|
||||
}
|
||||
@ -3630,10 +3642,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
if self.highlightedState != highlighted {
|
||||
self.highlightedState = highlighted
|
||||
if let backgroundType = self.backgroundType {
|
||||
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
|
||||
|
||||
let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper
|
||||
self.backgroundNode.setType(type: backgroundType, highlighted: highlighted, graphics: graphics, maskMode: self.mainContextSourceNode.isExtractedToContextPreview, hasWallpaper: hasWallpaper, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
|
||||
self.backgroundNode.setType(type: backgroundType, highlighted: highlighted, graphics: graphics, maskMode: self.mainContextSourceNode.isExtractedToContextPreview, hasWallpaper: hasWallpaper, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate, backgroundNode: item.controllerInteraction.presentationContext.backgroundNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
|
||||
let themeUpdated = presentationData.theme != currentTheme || type != currentType
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
let isDefaultWallpaper = serviceMessageColorHasDefaultWallpaper(presentationData.theme.wallpaper)
|
||||
let offset: CGFloat = -UIScreenPixel
|
||||
|
||||
|
@ -165,7 +165,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
|
||||
|
||||
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
|
||||
self.backgroundNode.image = graphics.dateStaticBackground
|
||||
self.stickBackgroundNode.image = graphics.dateFloatingBackground
|
||||
@ -198,7 +198,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
|
||||
let previousPresentationData = self.presentationData
|
||||
self.presentationData = presentationData
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
|
||||
self.backgroundNode.image = graphics.dateStaticBackground
|
||||
self.stickBackgroundNode.image = graphics.dateFloatingBackground
|
||||
|
@ -470,7 +470,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
if hasThumbnail {
|
||||
fileIconImage = nil
|
||||
} else {
|
||||
let principalGraphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
let principalGraphics = PresentationResourcesChat.principalGraphics(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
|
||||
|
||||
fileIconImage = incoming ? principalGraphics.radialIndicatorFileIconIncoming : principalGraphics.radialIndicatorFileIconOutgoing
|
||||
}
|
||||
|
@ -538,7 +538,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false), presentationContext: ChatPresentationContext(backgroundNode: self.backgroundNode))
|
||||
self.controllerInteraction = controllerInteraction
|
||||
|
||||
self.listNode.displayedItemRangeChanged = { [weak self] displayedRange, opaqueTransactionState in
|
||||
|
@ -155,7 +155,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: true))
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: true), presentationContext: ChatPresentationContext(backgroundNode: nil))
|
||||
|
||||
self.blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||
|
||||
|
@ -146,7 +146,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
}, displayUndo: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false), presentationContext: ChatPresentationContext(backgroundNode: nil))
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
||||
|
@ -2162,7 +2162,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false), presentationContext: ChatPresentationContext(backgroundNode: nil))
|
||||
self.hiddenMediaDisposable = context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -24,6 +24,7 @@ import AlertUI
|
||||
import PresentationDataUtils
|
||||
import LocationUI
|
||||
import AppLock
|
||||
import WallpaperBackgroundNode
|
||||
|
||||
private final class AccountUserInterfaceInUseContext {
|
||||
let subscribers = Bag<(Bool) -> Void>()
|
||||
@ -1218,66 +1219,63 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return PeerSelectionControllerImpl(params)
|
||||
}
|
||||
|
||||
public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)? = nil, clickThroughMessage: (() -> Void)? = nil) -> ListViewItem {
|
||||
public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)? = nil, clickThroughMessage: (() -> Void)? = nil, backgroundNode: ASDisplayNode?) -> ListViewItem {
|
||||
let controllerInteraction: ChatControllerInteraction
|
||||
if tapMessage != nil || clickThroughMessage != nil {
|
||||
controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, activateMessagePinch: { _ in
|
||||
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||
}, tapMessage: { message in
|
||||
tapMessage?(message)
|
||||
}, clickThroughMessage: {
|
||||
clickThroughMessage?()
|
||||
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in
|
||||
return false
|
||||
}, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, presentController: { _, _ in }, navigationController: {
|
||||
return nil
|
||||
}, chatControllerNode: {
|
||||
return nil
|
||||
}, reactionContainerNode: {
|
||||
return nil
|
||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
|
||||
}, canSetupReply: { _ in
|
||||
return .none
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
}, displayMessageTooltip: { _, _, _, _ in
|
||||
}, seekToTimecode: { _, _, _ in
|
||||
}, scheduleCurrentMessage: {
|
||||
}, sendScheduledMessagesNow: { _ in
|
||||
}, editScheduledMessagesTime: { _ in
|
||||
}, performTextSelectionAction: { _, _, _ in
|
||||
}, updateMessageLike: { _, _ in
|
||||
}, openMessageReactions: { _ in
|
||||
}, displayImportedMessageTooltip: { _ in
|
||||
}, displaySwipeToReplyHint: {
|
||||
}, dismissReplyMarkupMessage: { _ in
|
||||
}, openMessagePollResults: { _, _ in
|
||||
}, openPollCreation: { _ in
|
||||
}, displayPollSolution: { _, _ in
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: { _ in
|
||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageReplies: { _, _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
}, openMessageStats: { _ in
|
||||
}, editMessageMedia: { _, _ in
|
||||
}, copyText: { _ in
|
||||
}, displayUndo: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||
} else {
|
||||
controllerInteraction = defaultChatControllerInteraction
|
||||
}
|
||||
|
||||
controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, activateMessagePinch: { _ in
|
||||
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||
}, tapMessage: { message in
|
||||
tapMessage?(message)
|
||||
}, clickThroughMessage: {
|
||||
clickThroughMessage?()
|
||||
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in
|
||||
return false
|
||||
}, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, presentController: { _, _ in }, navigationController: {
|
||||
return nil
|
||||
}, chatControllerNode: {
|
||||
return nil
|
||||
}, reactionContainerNode: {
|
||||
return nil
|
||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
|
||||
}, canSetupReply: { _ in
|
||||
return .none
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
}, displayMessageTooltip: { _, _, _, _ in
|
||||
}, seekToTimecode: { _, _, _ in
|
||||
}, scheduleCurrentMessage: {
|
||||
}, sendScheduledMessagesNow: { _ in
|
||||
}, editScheduledMessagesTime: { _ in
|
||||
}, performTextSelectionAction: { _, _, _ in
|
||||
}, updateMessageLike: { _, _ in
|
||||
}, openMessageReactions: { _ in
|
||||
}, displayImportedMessageTooltip: { _ in
|
||||
}, displaySwipeToReplyHint: {
|
||||
}, dismissReplyMarkupMessage: { _ in
|
||||
}, openMessagePollResults: { _, _ in
|
||||
}, openPollCreation: { _ in
|
||||
}, displayPollSolution: { _, _ in
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: { _ in
|
||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageReplies: { _, _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
}, openMessageStats: { _ in
|
||||
}, editMessageMedia: { _, _ in
|
||||
}, copyText: { _ in
|
||||
}, displayUndo: { _ in
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false), presentationContext: ChatPresentationContext(backgroundNode: backgroundNode as? WallpaperBackgroundNode))
|
||||
|
||||
let content: ChatMessageItemContent
|
||||
let chatLocation: ChatLocation
|
||||
|
@ -1,12 +1,2 @@
|
||||
import Foundation
|
||||
|
||||
final class Weak<T: AnyObject> {
|
||||
private weak var _value: T?
|
||||
var value: T? {
|
||||
return self._value
|
||||
}
|
||||
|
||||
init(_ value: T) {
|
||||
self._value = value
|
||||
}
|
||||
}
|
||||
|
@ -504,8 +504,9 @@ public final class OngoingGroupCallContext {
|
||||
mainView?.updateIsEnabled(value)
|
||||
}
|
||||
)
|
||||
let cloneVideoView = cloneView.flatMap { cloneView in
|
||||
return OngoingCallContextPresentationCallVideoView(
|
||||
var cloneVideoView: OngoingCallContextPresentationCallVideoView?
|
||||
if let cloneView = cloneView {
|
||||
cloneVideoView = OngoingCallContextPresentationCallVideoView(
|
||||
view: cloneView,
|
||||
setOnFirstFrameReceived: { [weak cloneView] f in
|
||||
cloneView?.setOnFirstFrameReceived(f)
|
||||
|
@ -220,6 +220,16 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else if component.contains("~") {
|
||||
let components = component.components(separatedBy: "~")
|
||||
if components.count >= 1 && components.count <= 4 {
|
||||
let colors = components.compactMap { component in
|
||||
return UIColor(hexString: component)?.rgb
|
||||
}
|
||||
parameter = .gradient(colors, nil)
|
||||
} else {
|
||||
parameter = .color(UIColor(rgb: 0xffffff))
|
||||
}
|
||||
} else {
|
||||
var options: WallpaperPresentationOptions = []
|
||||
var intensity: Int32?
|
||||
|
@ -14,6 +14,159 @@ import Postbox
|
||||
private let motionAmount: CGFloat = 32.0
|
||||
|
||||
public final class WallpaperBackgroundNode: ASDisplayNode {
|
||||
public final class BubbleBackgroundNode: ASDisplayNode {
|
||||
public enum BubbleType {
|
||||
case incoming
|
||||
case outgoing
|
||||
}
|
||||
|
||||
private let bubbleType: BubbleType
|
||||
private let contentNode: ASImageNode
|
||||
|
||||
private var cleanWallpaperNode: ASDisplayNode?
|
||||
private var gradientWallpaperNode: GradientBackgroundNode.CloneNode?
|
||||
private weak var backgroundNode: WallpaperBackgroundNode?
|
||||
private var index: SparseBag<BubbleBackgroundNode>.Index?
|
||||
|
||||
init(backgroundNode: WallpaperBackgroundNode, bubbleType: BubbleType) {
|
||||
self.backgroundNode = backgroundNode
|
||||
self.bubbleType = bubbleType
|
||||
|
||||
self.contentNode = ASImageNode()
|
||||
self.contentNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
|
||||
self.index = backgroundNode.bubbleBackgroundNodeReferences.add(BubbleBackgroundNodeReference(node: self))
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let index = self.index, let backgroundNode = self.backgroundNode {
|
||||
backgroundNode.bubbleBackgroundNodeReferences.remove(index)
|
||||
}
|
||||
}
|
||||
|
||||
func updateContents() {
|
||||
guard let backgroundNode = self.backgroundNode else {
|
||||
return
|
||||
}
|
||||
|
||||
if let bubbleTheme = backgroundNode.bubbleTheme, let wallpaper = backgroundNode.wallpaper, let bubbleCorners = backgroundNode.bubbleCorners {
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: bubbleTheme, wallpaper: wallpaper, bubbleCorners: bubbleCorners)
|
||||
var needsCleanBackground = false
|
||||
self.contentNode.backgroundColor = backgroundNode.contentNode.backgroundColor
|
||||
switch self.bubbleType {
|
||||
case .incoming:
|
||||
self.contentNode.image = graphics.incomingBubbleGradientImage
|
||||
needsCleanBackground = bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill.alpha <= 0.99 || bubbleTheme.chat.message.incoming.bubble.withWallpaper.gradientFill.alpha <= 0.99
|
||||
case .outgoing:
|
||||
self.contentNode.image = graphics.outgoingBubbleGradientImage
|
||||
needsCleanBackground = bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill.alpha <= 0.99 || bubbleTheme.chat.message.outgoing.bubble.withWallpaper.gradientFill.alpha <= 0.99
|
||||
}
|
||||
|
||||
var hasComplexGradient = false
|
||||
switch wallpaper {
|
||||
case let .file(_, _, _, _, isPattern, _, _, _, settings):
|
||||
hasComplexGradient = settings.colors.count >= 3
|
||||
if !isPattern {
|
||||
needsCleanBackground = false
|
||||
}
|
||||
case let .gradient(colors, _):
|
||||
hasComplexGradient = colors.count >= 3
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
var needsGradientBackground = false
|
||||
var needsWallpaperBackground = false
|
||||
|
||||
if needsCleanBackground {
|
||||
if hasComplexGradient {
|
||||
needsGradientBackground = backgroundNode.gradientBackgroundNode != nil
|
||||
} else {
|
||||
needsWallpaperBackground = true
|
||||
}
|
||||
}
|
||||
|
||||
if needsWallpaperBackground {
|
||||
if self.cleanWallpaperNode == nil {
|
||||
let cleanWallpaperNode = ASImageNode()
|
||||
self.cleanWallpaperNode = cleanWallpaperNode
|
||||
cleanWallpaperNode.frame = self.contentNode.frame
|
||||
self.insertSubnode(cleanWallpaperNode, at: 0)
|
||||
}
|
||||
self.cleanWallpaperNode?.contents = backgroundNode.contentNode.contents
|
||||
} else {
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
self.cleanWallpaperNode = nil
|
||||
cleanWallpaperNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
if needsGradientBackground, let gradientBackgroundNode = backgroundNode.gradientBackgroundNode {
|
||||
if self.gradientWallpaperNode == nil {
|
||||
let gradientWallpaperNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode)
|
||||
gradientWallpaperNode.frame = self.contentNode.frame
|
||||
self.gradientWallpaperNode = gradientWallpaperNode
|
||||
self.insertSubnode(gradientWallpaperNode, at: 0)
|
||||
}
|
||||
} else {
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
self.gradientWallpaperNode = nil
|
||||
gradientWallpaperNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.contentNode.image = nil
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
self.cleanWallpaperNode = nil
|
||||
cleanWallpaperNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func update(rect: CGRect, within containerSize: CGSize) {
|
||||
self.contentNode.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize)
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
cleanWallpaperNode.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize)
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
gradientWallpaperNode.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize)
|
||||
}
|
||||
}
|
||||
|
||||
public func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: animationCurve)
|
||||
transition.animatePositionAdditive(node: self.contentNode, offset: CGPoint(x: -value.x, y: -value.y))
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
transition.animatePositionAdditive(node: cleanWallpaperNode, offset: CGPoint(x: -value.x, y: -value.y))
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
transition.animatePositionAdditive(node: gradientWallpaperNode, offset: CGPoint(x: -value.x, y: -value.y))
|
||||
}
|
||||
}
|
||||
|
||||
public func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
|
||||
self.contentNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: value)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
cleanWallpaperNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: value)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
gradientWallpaperNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: value)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class BubbleBackgroundNodeReference {
|
||||
weak var node: BubbleBackgroundNode?
|
||||
|
||||
init(node: BubbleBackgroundNode) {
|
||||
self.node = node
|
||||
}
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
|
||||
private let contentNode: ASDisplayNode
|
||||
@ -24,6 +177,10 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
|
||||
private var wallpaper: TelegramWallpaper?
|
||||
|
||||
private let patternImageDisposable = MetaDisposable()
|
||||
|
||||
private var bubbleTheme: PresentationTheme?
|
||||
private var bubbleCorners: PresentationChatBubbleCorners?
|
||||
private var bubbleBackgroundNodeReferences = SparseBag<BubbleBackgroundNodeReference>()
|
||||
|
||||
private var motionEnabled: Bool = false {
|
||||
didSet {
|
||||
@ -206,6 +363,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
|
||||
self.patternImageNode.isHidden = true
|
||||
}
|
||||
|
||||
self.updateBubbles()
|
||||
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
@ -236,4 +395,51 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition) {
|
||||
self.gradientBackgroundNode?.animateEvent(transition: transition)
|
||||
}
|
||||
|
||||
public func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
|
||||
if self.bubbleTheme !== bubbleTheme || self.bubbleCorners != bubbleCorners {
|
||||
self.bubbleTheme = bubbleTheme
|
||||
self.bubbleCorners = bubbleCorners
|
||||
|
||||
self.updateBubbles()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateBubbles() {
|
||||
for reference in self.bubbleBackgroundNodeReferences {
|
||||
reference.node?.updateContents()
|
||||
}
|
||||
}
|
||||
|
||||
public func hasBubbleBackground(for type: WallpaperBackgroundNode.BubbleBackgroundNode.BubbleType) -> Bool {
|
||||
guard let bubbleTheme = self.bubbleTheme, let wallpaper = self.wallpaper, let bubbleCorners = self.bubbleCorners else {
|
||||
return false
|
||||
}
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: bubbleTheme, wallpaper: wallpaper, bubbleCorners: bubbleCorners)
|
||||
switch type {
|
||||
case .incoming:
|
||||
if graphics.incomingBubbleGradientImage != nil {
|
||||
return true
|
||||
}
|
||||
if bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill.alpha <= 0.99 {
|
||||
return true
|
||||
}
|
||||
case .outgoing:
|
||||
if graphics.outgoingBubbleGradientImage != nil {
|
||||
return true
|
||||
}
|
||||
if bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill.alpha <= 0.99 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public func makeBubbleBackground(for type: WallpaperBackgroundNode.BubbleBackgroundNode.BubbleType) -> WallpaperBackgroundNode.BubbleBackgroundNode? {
|
||||
let node = WallpaperBackgroundNode.BubbleBackgroundNode(backgroundNode: self, bubbleType: type)
|
||||
node.updateContents()
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
@ -584,25 +584,38 @@ public func gradientImage(_ colors: [UIColor], rotation: Int32? = nil) -> Signal
|
||||
}
|
||||
return .single({ arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, clear: !arguments.corners.isEmpty)
|
||||
|
||||
let drawingRect = arguments.drawingRect
|
||||
|
||||
context.withContext { c in
|
||||
let gradientColors = colors.map { $0.withAlphaComponent(1.0).cgColor } as CFArray
|
||||
let delta: CGFloat = 1.0 / (CGFloat(colors.count) - 1.0)
|
||||
|
||||
var locations: [CGFloat] = []
|
||||
for i in 0 ..< colors.count {
|
||||
locations.append(delta * CGFloat(i))
|
||||
}
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||
if colors.count >= 3 {
|
||||
let image = GradientBackgroundNode.generatePreview(size: CGSize(width: 60.0, height: 60.0), colors: colors)
|
||||
c.translateBy(x: drawingRect.midX, y: drawingRect.midY)
|
||||
c.scaleBy(x: 1.0, y: -1.0)
|
||||
c.translateBy(x: -drawingRect.midX, y: -drawingRect.midY)
|
||||
c.draw(image.cgImage!, in: drawingRect)
|
||||
c.translateBy(x: drawingRect.midX, y: drawingRect.midY)
|
||||
c.scaleBy(x: 1.0, y: -1.0)
|
||||
c.translateBy(x: -drawingRect.midX, y: -drawingRect.midY)
|
||||
} else {
|
||||
let gradientColors = colors.map { $0.withAlphaComponent(1.0).cgColor } as CFArray
|
||||
let delta: CGFloat = 1.0 / (CGFloat(colors.count) - 1.0)
|
||||
|
||||
if let rotation = rotation {
|
||||
c.translateBy(x: arguments.drawingSize.width / 2.0, y: arguments.drawingSize.height / 2.0)
|
||||
c.rotate(by: CGFloat(rotation) * CGFloat.pi / 180.0)
|
||||
c.translateBy(x: -arguments.drawingSize.width / 2.0, y: -arguments.drawingSize.height / 2.0)
|
||||
var locations: [CGFloat] = []
|
||||
for i in 0 ..< colors.count {
|
||||
locations.append(delta * CGFloat(i))
|
||||
}
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||
|
||||
if let rotation = rotation {
|
||||
c.translateBy(x: arguments.drawingSize.width / 2.0, y: arguments.drawingSize.height / 2.0)
|
||||
c.rotate(by: CGFloat(rotation) * CGFloat.pi / 180.0)
|
||||
c.translateBy(x: -arguments.drawingSize.width / 2.0, y: -arguments.drawingSize.height / 2.0)
|
||||
}
|
||||
|
||||
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: arguments.drawingSize.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
|
||||
}
|
||||
|
||||
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: arguments.drawingSize.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
|
||||
}
|
||||
|
||||
addCorners(context, arguments: arguments)
|
||||
|
Loading…
x
Reference in New Issue
Block a user