Gradient updates

This commit is contained in:
Ali 2021-05-27 04:14:04 +04:00
parent 9e99110a55
commit d09fd03c1f
35 changed files with 594 additions and 217 deletions

View File

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

View File

@ -12,6 +12,7 @@ swift_library(
deps = [
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
],
visibility = [
"//visibility:public",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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