Message effects improvements

This commit is contained in:
Isaac
2024-05-13 20:14:15 +04:00
parent 3beaf8d580
commit 8736981248
43 changed files with 1045 additions and 505 deletions

View File

@@ -13,11 +13,15 @@ import MosaicLayout
import WallpaperBackgroundNode
import AccountContext
import ChatMessageBackground
import ChatSendMessageActionUI
import ComponentFlow
import ComponentDisplayAdapters
private class MediaPickerSelectedItemNode: ASDisplayNode {
let asset: TGMediaEditableItem
private let interaction: MediaPickerInteraction?
private let enableAnimations: Bool
private let isExternalPreview: Bool
private let imageNode: ImageNode
private var checkNode: InteractiveCheckNode?
@@ -57,10 +61,11 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
private var videoDuration: Double?
init(asset: TGMediaEditableItem, interaction: MediaPickerInteraction?, enableAnimations: Bool) {
init(asset: TGMediaEditableItem, interaction: MediaPickerInteraction?, enableAnimations: Bool, isExternalPreview: Bool) {
self.asset = asset
self.interaction = interaction
self.enableAnimations = enableAnimations
self.isExternalPreview = isExternalPreview
self.imageNode = ImageNode()
self.imageNode.contentMode = .scaleAspectFill
@@ -279,7 +284,7 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
if let checkNode = self.checkNode {
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
transition.updateAlpha(node: checkNode, alpha: selectionState.count() < 2 ? 0.0 : 1.0)
transition.updateAlpha(node: checkNode, alpha: (self.isExternalPreview || selectionState.count() < 2) ? 0.0 : 1.0)
}
}
}
@@ -382,7 +387,7 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
return view
}
func animateFrom(_ view: UIView) {
func animateFrom(_ view: UIView, transition: ContainedViewLayoutTransition) {
view.alpha = 0.0
let frame = view.convert(view.bounds, to: self.supernode?.view)
@@ -392,13 +397,19 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
self.durationBackgroundNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.updateLayout(size: frame.size, transition: .immediate)
self.updateLayout(size: targetFrame.size, transition: .animated(duration: 0.25, curve: .spring))
self.layer.animateFrame(from: frame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak view] _ in
view?.alpha = 1.0
self.updateLayout(size: targetFrame.size, transition: transition)
transition.animateFrame(layer: self.layer, from: frame, to: targetFrame, completion: { [weak self, weak view] _ in
guard let self else {
view?.alpha = 1.0
return
}
if !self.isExternalPreview {
view?.alpha = 1.0
}
})
}
func animateTo(_ view: UIView, dustNode: ASDisplayNode?, completion: @escaping (Bool) -> Void) {
func animateTo(_ view: UIView, dustNode: ASDisplayNode?, transition: ContainedViewLayoutTransition, completion: @escaping (Bool) -> Void) {
view.alpha = 0.0
let frame = self.frame
@@ -418,14 +429,13 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
self.addSubnode(dustNode)
dustNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
dustNode.layer.animatePosition(from: CGPoint(x: frame.width / 2.0, y: frame.height / 2.0), to: dustNode.position, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
transition.animatePositionAdditive(layer: dustNode.layer, offset: CGPoint(x: frame.width / 2.0, y: frame.height / 2.0), to: dustNode.position)
self.spoilerNode?.dustNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
}
self.corners = []
self.updateLayout(size: targetFrame.size, transition: .animated(duration: 0.25, curve: .spring))
self.layer.animateFrame(from: frame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak view, weak self] _ in
self.updateLayout(size: targetFrame.size, transition: transition)
transition.animateFrame(layer: self.layer, from: frame, to: targetFrame, removeOnCompletion: false, completion: { [weak view, weak self] _ in
view?.alpha = 1.0
self?.durationTextNode?.layer.removeAllAnimations()
@@ -471,7 +481,7 @@ private class MessageBackgroundNode: ASDisplayNode {
private var absoluteRect: (CGRect, CGSize)?
func update(size: CGSize, theme: PresentationTheme, wallpaper: TelegramWallpaper, graphics: PrincipalThemeEssentialGraphics, wallpaperBackgroundNode: WallpaperBackgroundNode, transition: ContainedViewLayoutTransition) {
func update(size: CGSize, theme: PresentationTheme, wallpaper: TelegramWallpaper, graphics: PrincipalThemeEssentialGraphics, wallpaperBackgroundNode: WallpaperBackgroundNode?, transition: ContainedViewLayoutTransition) {
self.backgroundNode.setType(type: .outgoing(.Extracted), highlighted: false, graphics: graphics, maskMode: false, hasWallpaper: wallpaper.hasWallpaper, transition: transition, backgroundNode: wallpaperBackgroundNode)
self.backgroundWallpaperNode.setType(type: .outgoing(.Extracted), theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), essentialGraphics: graphics, maskMode: true, backgroundNode: wallpaperBackgroundNode)
self.shadowNode.setType(type: .outgoing(.Extracted), hasWallpaper: wallpaper.hasWallpaper, graphics: graphics)
@@ -496,11 +506,13 @@ private class MessageBackgroundNode: ASDisplayNode {
}
}
final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDelegate {
final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDelegate, ChatSendMessageContextScreenMediaPreview {
private let context: AccountContext
private let persistentItems: Bool
private let isExternalPreview: Bool
var globalClippingRect: CGRect?
fileprivate let wallpaperBackgroundNode: WallpaperBackgroundNode
fileprivate var wallpaperBackgroundNode: WallpaperBackgroundNode?
private let scrollNode: ASScrollNode
private var backgroundNodes: [Int: MessageBackgroundNode] = [:]
private var itemNodes: [String: MediaPickerSelectedItemNode] = [:]
@@ -518,17 +530,28 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
private var didSetReady = false
private var ready = Promise<Bool>()
init(context: AccountContext, persistentItems: Bool) {
private var contentSize: CGSize = CGSize()
var isReady: Signal<Bool, NoError> {
return self.ready.get()
}
init(context: AccountContext, persistentItems: Bool, isExternalPreview: Bool) {
self.context = context
self.persistentItems = persistentItems
self.isExternalPreview = isExternalPreview
self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false)
self.wallpaperBackgroundNode.backgroundColor = .black
self.scrollNode = ASScrollNode()
self.scrollNode.clipsToBounds = false
super.init()
self.addSubnode(self.wallpaperBackgroundNode)
if !self.isExternalPreview {
let wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false)
wallpaperBackgroundNode.backgroundColor = .black
self.wallpaperBackgroundNode = wallpaperBackgroundNode
self.addSubnode(wallpaperBackgroundNode)
}
self.addSubnode(self.scrollNode)
}
@@ -578,7 +601,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
var getTransitionView: (String) -> (UIView, ASDisplayNode?, (Bool) -> Void)? = { _ in return nil }
func animateIn(initiated: @escaping () -> Void, completion: @escaping () -> Void = {}) {
func animateIn(transition: ContainedViewLayoutTransition, initiated: @escaping () -> Void, completion: @escaping () -> Void = {}) {
let _ = (self.ready.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] _ in
@@ -589,79 +612,105 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
strongSelf.alpha = 1.0
initiated()
strongSelf.wallpaperBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { _ in
if let wallpaperBackgroundNode = strongSelf.wallpaperBackgroundNode {
wallpaperBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { _ in
completion()
})
wallpaperBackgroundNode.layer.animateScale(from: 1.2, to: 1.0, duration: 0.33, timingFunction: kCAMediaTimingFunctionSpring)
} else {
completion()
})
strongSelf.wallpaperBackgroundNode.layer.animateScale(from: 1.2, to: 1.0, duration: 0.33, timingFunction: kCAMediaTimingFunctionSpring)
}
for (_, backgroundNode) in strongSelf.backgroundNodes {
backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
if strongSelf.isExternalPreview {
Transition.immediate.setScale(layer: backgroundNode.layer, scale: 0.001)
transition.updateTransformScale(layer: backgroundNode.layer, scale: 1.0)
}
}
for (identifier, itemNode) in strongSelf.itemNodes {
if let (transitionView, _, _) = strongSelf.getTransitionView(identifier) {
itemNode.animateFrom(transitionView)
itemNode.animateFrom(transitionView, transition: transition)
} else {
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
if strongSelf.isExternalPreview {
itemNode.alpha = 0.0
transition.animateTransformScale(node: itemNode, from: 0.001)
transition.updateAlpha(node: itemNode, alpha: 1.0)
} else {
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
}
}
}
if let topNode = strongSelf.messageNodes?.first, !topNode.alpha.isZero {
topNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
topNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -30.0), to: CGPoint(), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
transition.animatePositionAdditive(layer: topNode.layer, offset: CGPoint(x: 0.0, y: -30.0))
}
if let bottomNode = strongSelf.messageNodes?.last, !bottomNode.alpha.isZero {
bottomNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
bottomNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 30.0), to: CGPoint(), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
transition.animatePositionAdditive(layer: bottomNode.layer, offset: CGPoint(x: 0.0, y: 30.0))
}
})
}
func animateOut(completion: @escaping () -> Void = {}) {
self.wallpaperBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak self] _ in
completion()
if let strongSelf = self {
Queue.mainQueue().after(0.01) {
for (_, backgroundNode) in strongSelf.backgroundNodes {
backgroundNode.layer.removeAllAnimations()
func animateOut(transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) {
if let wallpaperBackgroundNode = self.wallpaperBackgroundNode{
wallpaperBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak self] _ in
completion()
if let strongSelf = self {
Queue.mainQueue().after(0.01) {
for (_, backgroundNode) in strongSelf.backgroundNodes {
backgroundNode.layer.removeAllAnimations()
}
for (_, itemNode) in strongSelf.itemNodes {
itemNode.layer.removeAllAnimations()
}
strongSelf.messageNodes?.first?.layer.removeAllAnimations()
strongSelf.messageNodes?.last?.layer.removeAllAnimations()
strongSelf.wallpaperBackgroundNode?.layer.removeAllAnimations()
}
for (_, itemNode) in strongSelf.itemNodes {
itemNode.layer.removeAllAnimations()
}
strongSelf.messageNodes?.first?.layer.removeAllAnimations()
strongSelf.messageNodes?.last?.layer.removeAllAnimations()
strongSelf.wallpaperBackgroundNode.layer.removeAllAnimations()
}
}
})
self.wallpaperBackgroundNode.layer.animateScale(from: 1.0, to: 1.2, duration: 0.33, timingFunction: kCAMediaTimingFunctionSpring)
})
wallpaperBackgroundNode.layer.animateScale(from: 1.0, to: 1.2, duration: 0.33, timingFunction: kCAMediaTimingFunctionSpring)
} else {
completion()
}
for (_, backgroundNode) in self.backgroundNodes {
backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false)
if self.isExternalPreview {
transition.updateTransformScale(layer: backgroundNode.layer, scale: 0.001)
}
}
for (identifier, itemNode) in self.itemNodes {
if let (transitionView, maybeDustNode, completion) = self.getTransitionView(identifier) {
itemNode.animateTo(transitionView, dustNode: maybeDustNode, completion: completion)
itemNode.animateTo(transitionView, dustNode: maybeDustNode, transition: transition, completion: completion)
} else {
itemNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false)
if self.isExternalPreview {
transition.updateTransformScale(node: itemNode, scale: 0.001)
transition.updateAlpha(node: itemNode, alpha: 0.0)
} else {
itemNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false)
}
}
}
if let topNode = self.messageNodes?.first {
topNode.layer.animateAlpha(from: topNode.alpha, to: 0.0, duration: 0.15, removeOnCompletion: false)
topNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -30.0), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
transition.animatePositionAdditive(layer: topNode.layer, offset: CGPoint(), to: CGPoint(x: 0.0, y: -30.0), removeOnCompletion: false)
}
if let bottomNode = self.messageNodes?.last {
bottomNode.layer.animateAlpha(from: bottomNode.alpha, to: 0.0, duration: 0.15, removeOnCompletion: false)
bottomNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 30.0), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
transition.animatePositionAdditive(layer: bottomNode.layer, offset: CGPoint(), to: CGPoint(x: 0.0, y: 30.0), removeOnCompletion: false)
}
}
@@ -782,7 +831,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
if let current = self.itemNodes[identifier] {
itemNode = current
} else {
itemNode = MediaPickerSelectedItemNode(asset: asset, interaction: self.interaction, enableAnimations: self.context.sharedContext.energyUsageSettings.fullTranslucency)
itemNode = MediaPickerSelectedItemNode(asset: asset, interaction: self.interaction, enableAnimations: self.context.sharedContext.energyUsageSettings.fullTranslucency, isExternalPreview: self.isExternalPreview)
self.itemNodes[identifier] = itemNode
self.scrollNode.addSubnode(itemNode)
@@ -852,55 +901,61 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
}
}
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1))
var peers = SimpleDictionary<PeerId, Peer>()
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)
let previewText = groupLayouts.count > 1 ? presentationData.strings.Attachment_MessagesPreview : presentationData.strings.Attachment_MessagePreview
let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true, isStandalone: false)
let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true, isStandalone: false)
let headerItems: [ListViewItem] = [previewItem, dragItem]
let params = ListViewItemLayoutParams(width: size.width, leftInset: insets.left, rightInset: insets.right, availableHeight: size.height)
if let messageNodes = self.messageNodes {
for i in 0 ..< headerItems.count {
let itemNode = messageNodes[i]
headerItems[i].updateNode(async: { $0() }, node: {
return itemNode
}, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in
let nodeFrame = CGRect(origin: itemNode.frame.origin, size: CGSize(width: size.width, height: layout.size.height))
itemNode.contentSize = layout.contentSize
itemNode.insets = layout.insets
itemNode.frame = nodeFrame
itemNode.isUserInteractionEnabled = false
apply(ListViewItemApply(isOnScreen: true))
})
if !self.isExternalPreview {
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1))
var peers = SimpleDictionary<PeerId, Peer>()
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)
let previewText = groupLayouts.count > 1 ? presentationData.strings.Attachment_MessagesPreview : presentationData.strings.Attachment_MessagePreview
let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true, isStandalone: false)
let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true, isStandalone: false)
let headerItems: [ListViewItem] = [previewItem, dragItem]
let params = ListViewItemLayoutParams(width: size.width, leftInset: insets.left, rightInset: insets.right, availableHeight: size.height)
if let messageNodes = self.messageNodes {
for i in 0 ..< headerItems.count {
let itemNode = messageNodes[i]
headerItems[i].updateNode(async: { $0() }, node: {
return itemNode
}, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in
let nodeFrame = CGRect(origin: itemNode.frame.origin, size: CGSize(width: size.width, height: layout.size.height))
itemNode.contentSize = layout.contentSize
itemNode.insets = layout.insets
itemNode.frame = nodeFrame
itemNode.isUserInteractionEnabled = false
apply(ListViewItemApply(isOnScreen: true))
})
}
} else {
var messageNodes: [ListViewItemNode] = []
for i in 0 ..< headerItems.count {
var itemNode: ListViewItemNode?
headerItems[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: nil, nextItem: nil, completion: { node, apply in
itemNode = node
apply().1(ListViewItemApply(isOnScreen: true))
})
itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
itemNode!.isUserInteractionEnabled = false
messageNodes.append(itemNode!)
self.scrollNode.addSubnode(itemNode!)
}
self.messageNodes = messageNodes
}
} else {
var messageNodes: [ListViewItemNode] = []
for i in 0 ..< headerItems.count {
var itemNode: ListViewItemNode?
headerItems[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: nil, nextItem: nil, completion: { node, apply in
itemNode = node
apply().1(ListViewItemApply(isOnScreen: true))
})
itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
itemNode!.isUserInteractionEnabled = false
messageNodes.append(itemNode!)
self.scrollNode.addSubnode(itemNode!)
}
self.messageNodes = messageNodes
}
let spacing: CGFloat = 8.0
var contentHeight: CGFloat = 60.0
var contentHeight: CGFloat = 0.0
var contentWidth: CGFloat = 0.0
if !self.isExternalPreview {
contentHeight += 60.0
}
if let previewNode = self.messageNodes?.first {
transition.updateFrame(node: previewNode, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top + 28.0), size: previewNode.frame.size))
@@ -915,26 +970,35 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
var groupIndex = 0
for (items, groupSize) in groupLayouts {
let groupRect = CGRect(origin: CGPoint(x: insets.left + floorToScreenPixels((size.width - insets.left - insets.right - groupSize.width) / 2.0), y: insets.top + contentHeight), size: groupSize)
let groupBackgroundNode: MessageBackgroundNode
if let current = self.backgroundNodes[groupIndex] {
groupBackgroundNode = current
} else {
groupBackgroundNode = MessageBackgroundNode()
groupBackgroundNode.displaysAsynchronously = false
self.backgroundNodes[groupIndex] = groupBackgroundNode
self.scrollNode.insertSubnode(groupBackgroundNode, at: 0)
var groupRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top + contentHeight), size: groupSize)
if !self.isExternalPreview {
groupRect.origin.x = insets.left + floorToScreenPixels((size.width - insets.left - insets.right - groupSize.width) / 2.0)
}
var itemTransition = transition
if groupBackgroundNode.frame.width.isZero {
itemTransition = .immediate
}
itemTransition.updateFrame(node: groupBackgroundNode, frame: groupRect.insetBy(dx: -5.0, dy: -2.0).offsetBy(dx: 3.0, dy: 0.0))
groupBackgroundNode.update(size: groupBackgroundNode.frame.size, theme: theme, wallpaper: wallpaper, graphics: graphics, wallpaperBackgroundNode: self.wallpaperBackgroundNode, transition: itemTransition)
if !self.isExternalPreview {
let groupBackgroundNode: MessageBackgroundNode
if let current = self.backgroundNodes[groupIndex] {
groupBackgroundNode = current
} else {
groupBackgroundNode = MessageBackgroundNode()
groupBackgroundNode.displaysAsynchronously = false
self.backgroundNodes[groupIndex] = groupBackgroundNode
self.scrollNode.insertSubnode(groupBackgroundNode, at: 0)
}
if groupBackgroundNode.frame.width.isZero {
itemTransition = .immediate
}
let groupBackgroundFrame = groupRect.insetBy(dx: -5.0, dy: -2.0).offsetBy(dx: 3.0, dy: 0.0)
itemTransition.updatePosition(node: groupBackgroundNode, position: groupBackgroundFrame.center)
itemTransition.updateBounds(node: groupBackgroundNode, bounds: CGRect(origin: CGPoint(), size: groupBackgroundFrame.size))
groupBackgroundNode.update(size: groupBackgroundNode.frame.size, theme: theme, wallpaper: wallpaper, graphics: graphics, wallpaperBackgroundNode: self.wallpaperBackgroundNode, transition: itemTransition)
}
var isFirstGroup = true
for (item, itemRect, itemPosition) in items {
if let identifier = item.uniqueIdentifier, let itemNode = self.itemNodes[identifier] {
var corners: CACornerMask = []
@@ -968,7 +1032,13 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
}
}
contentHeight += groupSize.height + spacing
if isFirstGroup {
isFirstGroup = false
} else {
contentHeight += spacing
}
contentHeight += groupSize.height
contentWidth = max(contentWidth, groupSize.width)
groupIndex += 1
}
@@ -1032,7 +1102,13 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
self.updateAbsoluteRects()
self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight)
if self.isExternalPreview {
self.scrollNode.view.contentSize = CGSize(width: contentWidth, height: contentHeight)
} else {
self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight)
}
self.contentSize = CGSize(width: contentWidth, height: contentHeight)
}
func updateSelectionState() {
@@ -1047,6 +1123,25 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
}
}
func animateIn(transition: Transition) {
self.animateIn(transition: transition.containedViewLayoutTransition, initiated: {}, completion: {})
}
func animateOut(transition: Transition) {
self.animateOut(transition: transition.containedViewLayoutTransition, completion: {})
}
func update(containerSize: CGSize, transition: Transition) -> CGSize {
if var validLayout = self.validLayout {
validLayout.size = containerSize
self.validLayout = validLayout
}
self.updateItems(transition: transition.containedViewLayoutTransition)
return self.contentSize
}
func updateLayout(size: CGSize, insets: UIEdgeInsets, items: [TGMediaSelectableItem], grouped: Bool, theme: PresentationTheme, wallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners, transition: ContainedViewLayoutTransition) {
let previous = self.validLayout
self.validLayout = (size, insets, items, grouped, theme, wallpaper, bubbleCorners)
@@ -1068,10 +1163,12 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
}
let inset: CGFloat = insets.left == 70 ? insets.left : 0.0
self.wallpaperBackgroundNode.update(wallpaper: wallpaper, animated: false)
self.wallpaperBackgroundNode.updateBubbleTheme(bubbleTheme: theme, bubbleCorners: bubbleCorners)
transition.updateFrame(node: self.wallpaperBackgroundNode, frame: CGRect(origin: CGPoint(x: inset, y: 0.0), size: CGSize(width: size.width - inset * 2.0, height: size.height)))
self.wallpaperBackgroundNode.updateLayout(size: CGSize(width: size.width - inset * 2.0, height: size.height), displayMode: .aspectFill, transition: transition)
if let wallpaperBackgroundNode = self.wallpaperBackgroundNode {
wallpaperBackgroundNode.update(wallpaper: wallpaper, animated: false)
wallpaperBackgroundNode.updateBubbleTheme(bubbleTheme: theme, bubbleCorners: bubbleCorners)
transition.updateFrame(node: wallpaperBackgroundNode, frame: CGRect(origin: CGPoint(x: inset, y: 0.0), size: CGSize(width: size.width - inset * 2.0, height: size.height)))
wallpaperBackgroundNode.updateLayout(size: CGSize(width: size.width - inset * 2.0, height: size.height), displayMode: .aspectFill, transition: transition)
}
self.updateItems(transition: itemsTransition)