diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 0650e11e07..56aa5b7adf 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -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 diff --git a/submodules/GradientBackground/BUILD b/submodules/GradientBackground/BUILD index 400475ef4f..f216710644 100644 --- a/submodules/GradientBackground/BUILD +++ b/submodules/GradientBackground/BUILD @@ -12,6 +12,7 @@ swift_library( deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", ], visibility = [ "//visibility:public", diff --git a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift index f199180434..35c44b3d41 100644 --- a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift +++ b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift @@ -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>.Index? + + public init(parentNode: GradientBackgroundNode) { + self.parentNode = parentNode + + super.init() + + self.index = parentNode.cloneNodes.add(Weak(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>() 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 } diff --git a/submodules/SSignalKit/SwiftSignalKit/Source/Bag.swift b/submodules/SSignalKit/SwiftSignalKit/Source/Bag.swift index 08d5aef3a4..59d287b874 100644 --- a/submodules/SSignalKit/SwiftSignalKit/Source/Bag.swift +++ b/submodules/SSignalKit/SwiftSignalKit/Source/Bag.swift @@ -1,5 +1,17 @@ import Foundation +public final class Weak { + private weak var _value: T? + + public var value: T? { + return self._value + } + + public init(_ value: T) { + self._value = value + } +} + public final class Bag { public typealias Index = Int private var nextIndex: Index = 0 @@ -73,6 +85,46 @@ public final class Bag { } } +public final class SparseBag: 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 { + 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() diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 0ba964acf0..1b32ab836f 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -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 { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index 79e551b85f..000b2d8d36 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -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 { diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 065a2222ba..edb4bfd164 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -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 { diff --git a/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift b/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift index 3ea7e29693..2168936c9b 100644 --- a/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift +++ b/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift @@ -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]) } } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 239a149ae3..6db4ff4ec9 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -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 } diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 7a1a876a2a..77ab7b72df 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -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 diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 70b4a09ce0..5a07d05a5e 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -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) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index f21f1b68b8..681011ffcf 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -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 { diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 2d1cc1eaac..73d537fed4 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -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 diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift index bbd74bbbcb..78a554dfaf 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift @@ -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 diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift index 94e0b1520d..f0affb12bb 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 474399ba46..d6c4b39a39 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift index 4a1e522476..ea1418dc23 100644 --- a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift @@ -138,6 +138,7 @@ public final class ChatControllerInteraction { var searchTextHighightState: (String, [MessageIndex])? var seenOneTimeAnimatedMedia = Set() 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) + ) } } diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index f1bde10726..5062c8e5e0 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -230,7 +230,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { private var onLayoutCompletions: [(ContainedViewLayoutTransition) -> Void] = [] - init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: MediaAutoDownloadSettings, navigationBar: NavigationBar?, controller: ChatControllerImpl?) { + init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, 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 diff --git a/submodules/TelegramUI/Sources/ChatImportStatusPanel.swift b/submodules/TelegramUI/Sources/ChatImportStatusPanel.swift index f3f3653ec6..0af27ac9e3 100644 --- a/submodules/TelegramUI/Sources/ChatImportStatusPanel.swift +++ b/submodules/TelegramUI/Sources/ChatImportStatusPanel.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/ChatMessageBackground.swift b/submodules/TelegramUI/Sources/ChatMessageBackground.swift index 7163759182..5f1ffcb608 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBackground.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBackground.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift index 37d4933fa0..4b75a924eb 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift @@ -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() }) diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index a820cf5c14..b5437fbd3a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -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) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift index a550cf4a3d..0c19b74988 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift index 61eb90e62c..966cf27ff6 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index 4fec8deb0e..625ab8e1a8 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 08932027df..180e91b098 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift index c826a4aff4..1355f4e8b0 100644 --- a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift +++ b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift @@ -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)) diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index dce0a10637..f6d13ed6be 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -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) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 86e2611cbc..97e2314ed6 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 1fedd17eca..b4408f5f1f 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/Weak.swift b/submodules/TelegramUI/Sources/Weak.swift index 435c4bdb7e..fbf287572c 100644 --- a/submodules/TelegramUI/Sources/Weak.swift +++ b/submodules/TelegramUI/Sources/Weak.swift @@ -1,12 +1,2 @@ import Foundation -final class Weak { - private weak var _value: T? - var value: T? { - return self._value - } - - init(_ value: T) { - self._value = value - } -} diff --git a/submodules/TelegramVoip/Sources/GroupCallContext.swift b/submodules/TelegramVoip/Sources/GroupCallContext.swift index 7202b4a990..0aa3a31d3e 100644 --- a/submodules/TelegramVoip/Sources/GroupCallContext.swift +++ b/submodules/TelegramVoip/Sources/GroupCallContext.swift @@ -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) diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index d50eaee876..fe14cff19f 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -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? diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 65a64fded9..c531b0c0ea 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -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.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() 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 + } } diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index 11598515ef..4768846afe 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -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)