mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-31 15:37:01 +00:00
Merge commit 'eaf511b67fc3cab7261e5107b8b3ac812d173dce'
This commit is contained in:
commit
79799804f1
@ -598,21 +598,21 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func animatePositionAdditive(layer: CALayer, offset: CGFloat, removeOnCompletion: Bool = true, completion: @escaping (Bool) -> Void) {
|
||||
func animatePositionAdditive(layer: CALayer, offset: CGFloat, delay: Double = 0.0, removeOnCompletion: Bool = true, completion: @escaping (Bool) -> Void) {
|
||||
switch self {
|
||||
case .immediate:
|
||||
completion(true)
|
||||
case let .animated(duration, curve):
|
||||
layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: completion)
|
||||
layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, delay: delay, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
func animatePositionAdditive(node: ASDisplayNode, offset: CGPoint, removeOnCompletion: Bool = true, completion: (() -> Void)? = nil) {
|
||||
func animatePositionAdditive(node: ASDisplayNode, offset: CGPoint, delay: Double = 0.0, removeOnCompletion: Bool = true, completion: (() -> Void)? = nil) {
|
||||
switch self {
|
||||
case .immediate:
|
||||
completion?()
|
||||
case let .animated(duration, curve):
|
||||
node.layer.animatePosition(from: offset, to: CGPoint(), duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: { _ in
|
||||
node.layer.animatePosition(from: offset, to: CGPoint(), duration: duration, delay: delay, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: { _ in
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
@ -948,7 +948,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func animateTransformScale(node: ASDisplayNode, from fromScale: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
||||
func animateTransformScale(node: ASDisplayNode, from fromScale: CGPoint, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
||||
switch self {
|
||||
case .immediate:
|
||||
if let completion = completion {
|
||||
@ -961,12 +961,12 @@ public extension ContainedViewLayoutTransition {
|
||||
calculatedFrom = fromScale
|
||||
calculatedTo = CGPoint(x: 1.0, y: 1.0)
|
||||
|
||||
node.layer.animateScaleX(from: calculatedFrom.x, to: calculatedTo.x, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
||||
node.layer.animateScaleX(from: calculatedFrom.x, to: calculatedTo.x, duration: duration, delay: delay, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
||||
if let completion = completion {
|
||||
completion(result)
|
||||
}
|
||||
})
|
||||
node.layer.animateScaleY(from: calculatedFrom.y, to: calculatedTo.y, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction)
|
||||
node.layer.animateScaleY(from: calculatedFrom.y, to: calculatedTo.y, duration: duration, delay: delay, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -947,6 +947,7 @@ public final class MessageHistoryView {
|
||||
public let topTaggedMessages: [Message]
|
||||
public let additionalData: [AdditionalMessageHistoryViewDataEntry]
|
||||
public let isLoading: Bool
|
||||
public let isLoadingEarlier: Bool
|
||||
public let isAddedToChatList: Bool
|
||||
|
||||
public init(tagMask: MessageTags?, namespaces: MessageIdNamespaces, entries: [MessageHistoryEntry], holeEarlier: Bool, holeLater: Bool, isLoading: Bool) {
|
||||
@ -964,6 +965,7 @@ public final class MessageHistoryView {
|
||||
self.topTaggedMessages = []
|
||||
self.additionalData = []
|
||||
self.isLoading = isLoading
|
||||
self.isLoadingEarlier = true
|
||||
self.isAddedToChatList = false
|
||||
}
|
||||
|
||||
@ -981,8 +983,10 @@ public final class MessageHistoryView {
|
||||
self.holeLater = true
|
||||
self.earlierId = nil
|
||||
self.laterId = nil
|
||||
self.isLoadingEarlier = true
|
||||
case let .loaded(state):
|
||||
var isLoading = false
|
||||
var isLoadingEarlier = false
|
||||
switch state.anchor {
|
||||
case .lowerBound:
|
||||
self.anchorIndex = .lowerBound
|
||||
@ -996,6 +1000,10 @@ public final class MessageHistoryView {
|
||||
if state.entries.isEmpty && state.hole != nil {
|
||||
isLoading = true
|
||||
}
|
||||
if state.entries.count <= 1 && state.hole != nil {
|
||||
isLoadingEarlier = true
|
||||
}
|
||||
self.isLoadingEarlier = isLoadingEarlier
|
||||
entries = []
|
||||
if let transientReadStates = mutableView.transientReadStates, case let .peer(states) = transientReadStates {
|
||||
for entry in state.entries {
|
||||
@ -1199,6 +1207,7 @@ public final class MessageHistoryView {
|
||||
self.topTaggedMessages = base.topTaggedMessages
|
||||
self.additionalData = base.additionalData
|
||||
self.isLoading = base.isLoading
|
||||
self.isLoadingEarlier = base.isLoadingEarlier
|
||||
self.isAddedToChatList = base.isAddedToChatList
|
||||
|
||||
if let combinedReadStates = combinedReadStates {
|
||||
|
@ -5866,6 +5866,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.loadingPlaceholderNode?.addContentOffset(offset: offset, transition: transition)
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode)
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
var blurredHistoryNode: ASImageNode?
|
||||
let historyNodeContainer: ASDisplayNode
|
||||
let loadingNode: ChatLoadingNode
|
||||
private var loadingPlaceholderNode: ChatLoadingPlaceholderNode
|
||||
private(set) var loadingPlaceholderNode: ChatLoadingPlaceholderNode?
|
||||
|
||||
private var emptyNode: ChatEmptyNode?
|
||||
private(set) var emptyType: ChatHistoryNodeLoadState.EmptyType?
|
||||
@ -222,49 +222,66 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
private var isLoadingValue: Bool = false
|
||||
private func updateIsLoading(isLoading: Bool, animated: Bool) {
|
||||
loadingPlaceholderNode.isHidden = true
|
||||
let useLoadingPlaceholder = self.chatLocation.peerId?.namespace != Namespaces.Peer.CloudUser
|
||||
|
||||
if isLoading != self.isLoadingValue {
|
||||
self.isLoadingValue = isLoading
|
||||
if isLoading {
|
||||
// self.historyNodeContainer.supernode?.insertSubnode(self.loadingNode, belowSubnode: self.historyNodeContainer)
|
||||
self.loadingNode.isHidden = false
|
||||
self.loadingNode.layer.removeAllAnimations()
|
||||
self.loadingNode.alpha = 1.0
|
||||
if animated {
|
||||
self.loadingNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
}
|
||||
|
||||
self.loadingPlaceholderNode.alpha = 1.0
|
||||
self.loadingPlaceholderNode.isHidden = false
|
||||
self.historyNode.alpha = 0.0
|
||||
} else {
|
||||
self.loadingPlaceholderNode.alpha = 0.0
|
||||
self.loadingPlaceholderNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, completion: { [weak self] completed in
|
||||
if let strongSelf = self {
|
||||
strongSelf.loadingPlaceholderNode.layer.removeAllAnimations()
|
||||
if completed {
|
||||
strongSelf.loadingPlaceholderNode.isHidden = true
|
||||
if useLoadingPlaceholder {
|
||||
let loadingPlaceholderNode: ChatLoadingPlaceholderNode
|
||||
if let current = self.loadingPlaceholderNode {
|
||||
loadingPlaceholderNode = current
|
||||
} else {
|
||||
loadingPlaceholderNode = ChatLoadingPlaceholderNode(theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper, bubbleCorners: self.chatPresentationInterfaceState.bubbleCorners, backgroundNode: self.backgroundNode)
|
||||
loadingPlaceholderNode.updatePresentationInterfaceState(self.chatPresentationInterfaceState)
|
||||
self.contentContainerNode.insertSubnode(loadingPlaceholderNode, aboveSubnode: self.backgroundNode)
|
||||
|
||||
self.loadingPlaceholderNode = loadingPlaceholderNode
|
||||
|
||||
loadingPlaceholderNode.setup(self.historyNode)
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate, listViewTransaction: { _, _, _, _ in
|
||||
}, updateExtraNavigationBarBackgroundHeight: { _, _ in
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
self.loadingPlaceholderNode.animateOut(self.historyNode)
|
||||
|
||||
self.historyNode.alpha = 1.0
|
||||
self.historyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
|
||||
self.loadingNode.alpha = 0.0
|
||||
if animated {
|
||||
self.loadingNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false)
|
||||
self.loadingNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, completion: { [weak self] completed in
|
||||
if let strongSelf = self {
|
||||
strongSelf.loadingNode.layer.removeAllAnimations()
|
||||
if completed {
|
||||
strongSelf.loadingNode.isHidden = true
|
||||
}
|
||||
}
|
||||
})
|
||||
loadingPlaceholderNode.alpha = 1.0
|
||||
loadingPlaceholderNode.isHidden = false
|
||||
} else {
|
||||
self.loadingNode.isHidden = true
|
||||
self.historyNodeContainer.supernode?.insertSubnode(self.loadingNode, belowSubnode: self.historyNodeContainer)
|
||||
self.loadingNode.isHidden = false
|
||||
self.loadingNode.layer.removeAllAnimations()
|
||||
self.loadingNode.alpha = 1.0
|
||||
if animated {
|
||||
self.loadingNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if useLoadingPlaceholder {
|
||||
if let loadingPlaceholderNode = self.loadingPlaceholderNode {
|
||||
loadingPlaceholderNode.animateOut(self.historyNode, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.loadingPlaceholderNode?.removeFromSupernode()
|
||||
strongSelf.loadingPlaceholderNode = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
self.loadingNode.alpha = 0.0
|
||||
if animated {
|
||||
self.loadingNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false)
|
||||
self.loadingNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, completion: { [weak self] completed in
|
||||
if let strongSelf = self {
|
||||
strongSelf.loadingNode.layer.removeAllAnimations()
|
||||
if completed {
|
||||
strongSelf.loadingNode.isHidden = true
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
self.loadingNode.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -414,9 +431,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
})
|
||||
|
||||
self.loadingNode = ChatLoadingNode(theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper, bubbleCorners: self.chatPresentationInterfaceState.bubbleCorners)
|
||||
|
||||
self.loadingPlaceholderNode = ChatLoadingPlaceholderNode(theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper, bubbleCorners: self.chatPresentationInterfaceState.bubbleCorners, backgroundNode: self.backgroundNode, hasAvatar: chatLocation.peerId?.namespace != Namespaces.Peer.CloudUser)
|
||||
|
||||
|
||||
self.inputPanelContainerNode = ChatInputPanelContainer()
|
||||
self.inputPanelOverlayNode = SparseNode()
|
||||
self.inputPanelClippingNode = SparseNode()
|
||||
@ -490,13 +505,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
|
||||
// self.updateIsLoading(isLoading: true, animated: false)
|
||||
//
|
||||
// Queue.mainQueue().after(1.0) {
|
||||
// self.updateIsLoading(isLoading: false, animated: true)
|
||||
// }
|
||||
|
||||
|
||||
self.historyNode.setLoadStateUpdated { [weak self] loadState, animated in
|
||||
if let strongSelf = self {
|
||||
let wasLoading = strongSelf.isLoadingValue
|
||||
@ -561,12 +570,19 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
self.addSubnode(self.contentContainerNode)
|
||||
self.contentContainerNode.addSubnode(self.backgroundNode)
|
||||
self.contentContainerNode.addSubnode(self.loadingPlaceholderNode)
|
||||
self.contentContainerNode.addSubnode(self.historyNodeContainer)
|
||||
|
||||
if let navigationBar = self.navigationBar {
|
||||
self.contentContainerNode.addSubnode(navigationBar)
|
||||
}
|
||||
|
||||
// Queue.mainQueue().after(0.2) {
|
||||
// self.updateIsLoading(isLoading: true, animated: false)
|
||||
// }
|
||||
//
|
||||
// Queue.mainQueue().after(3.0) {
|
||||
// self.updateIsLoading(isLoading: false, animated: true)
|
||||
// }
|
||||
|
||||
self.inputPanelContainerNode.expansionUpdated = { [weak self] transition in
|
||||
guard let strongSelf = self else {
|
||||
@ -1417,7 +1433,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
//transition.updateFrame(node: self.historyScrollingArea, frame: contentBounds)
|
||||
|
||||
transition.updateFrame(node: self.loadingNode, frame: contentBounds)
|
||||
transition.updateFrame(node: self.loadingPlaceholderNode, frame: contentBounds)
|
||||
if let loadingPlaceholderNode = self.loadingPlaceholderNode {
|
||||
transition.updateFrame(node: loadingPlaceholderNode, frame: contentBounds)
|
||||
}
|
||||
|
||||
if let restrictedNode = self.restrictedNode {
|
||||
transition.updateFrame(node: restrictedNode, frame: contentBounds)
|
||||
@ -1575,8 +1593,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.visibleAreaInset = visibleAreaInset
|
||||
self.loadingNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition)
|
||||
|
||||
self.loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition)
|
||||
self.loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
||||
if let loadingPlaceholderNode = self.loadingPlaceholderNode {
|
||||
loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition)
|
||||
loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
||||
}
|
||||
|
||||
if let containerNode = self.containerNode {
|
||||
contentBottomInset += 8.0
|
||||
@ -2210,6 +2230,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper)
|
||||
|
||||
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
|
||||
self.loadingPlaceholderNode?.updatePresentationInterfaceState(chatPresentationInterfaceState)
|
||||
|
||||
var updatedInputFocus = self.chatPresentationInterfaceStateRequiresInputFocus(self.chatPresentationInterfaceState) != self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState)
|
||||
if self.chatPresentationInterfaceStateInputView(self.chatPresentationInterfaceState) !== self.chatPresentationInterfaceStateInputView(chatPresentationInterfaceState) {
|
||||
|
@ -2559,7 +2559,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
loadState = .empty(emptyType)
|
||||
}
|
||||
} else {
|
||||
loadState = .messages
|
||||
if historyView.originalView.isLoadingEarlier && strongSelf.chatLocation.peerId?.namespace != Namespaces.Peer.CloudUser {
|
||||
loadState = .loading
|
||||
} else {
|
||||
loadState = .messages
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loadState = .loading
|
||||
@ -2639,12 +2643,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
strongSelf._buttonKeyboardMessage.set(.single(transition.keyboardButtonsMessage))
|
||||
}
|
||||
|
||||
if transition.animateIn || animateIn {
|
||||
if (transition.animateIn || animateIn) && !"".isEmpty {
|
||||
let heightNorm = strongSelf.bounds.height - strongSelf.insets.top
|
||||
strongSelf.forEachVisibleItemNode { itemNode in
|
||||
let delayFactor = itemNode.frame.minY / heightNorm
|
||||
let delay = Double(delayFactor * 0.1)
|
||||
|
||||
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
|
||||
itemNode.layer.animateScale(from: 0.94, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
@ -2657,7 +2661,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
strongSelf.forEachItemHeaderNode { itemNode in
|
||||
let delayFactor = itemNode.frame.minY / heightNorm
|
||||
let delay = Double(delayFactor * 0.2)
|
||||
|
||||
|
||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
|
||||
itemNode.layer.animateScale(from: 0.94, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import TelegramPresentationData
|
||||
import ActivityIndicator
|
||||
import WallpaperBackgroundNode
|
||||
import ShimmerEffect
|
||||
import ChatPresentationInterfaceState
|
||||
|
||||
final class ChatLoadingNode: ASDisplayNode {
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
@ -47,10 +48,12 @@ final class ChatLoadingNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
private let avatarSize = CGSize(width: 38.0, height: 38.0)
|
||||
private let avatarImage = generateFilledCircleImage(diameter: avatarSize.width, color: .white)
|
||||
private let avatarBorderImage = generateCircleImage(diameter: avatarSize.width, lineWidth: 1.0 - UIScreenPixel, color: .white)
|
||||
|
||||
final class ChatLoadingPlaceholderMessageContainer {
|
||||
let avatarNode: ASImageNode?
|
||||
let avatarBorderNode: ASImageNode?
|
||||
var avatarNode: ASImageNode?
|
||||
var avatarBorderNode: ASImageNode?
|
||||
|
||||
let bubbleNode: ASImageNode
|
||||
let bubbleBorderNode: ASImageNode
|
||||
@ -63,20 +66,7 @@ final class ChatLoadingPlaceholderMessageContainer {
|
||||
return self.bubbleNode.frame
|
||||
}
|
||||
|
||||
init(hasAvatar: Bool, bubbleImage: UIImage?, bubbleBorderImage: UIImage?) {
|
||||
if hasAvatar {
|
||||
self.avatarNode = ASImageNode()
|
||||
self.avatarNode?.displaysAsynchronously = false
|
||||
self.avatarNode?.image = generateFilledCircleImage(diameter: avatarSize.width, color: .white)
|
||||
|
||||
self.avatarBorderNode = ASImageNode()
|
||||
self.avatarBorderNode?.displaysAsynchronously = false
|
||||
self.avatarBorderNode?.image = generateCircleImage(diameter: avatarSize.width, lineWidth: 1.0 - UIScreenPixel, color: .red)
|
||||
} else {
|
||||
self.avatarNode = nil
|
||||
self.avatarBorderNode = nil
|
||||
}
|
||||
|
||||
init(bubbleImage: UIImage?, bubbleBorderImage: UIImage?) {
|
||||
self.bubbleNode = ASImageNode()
|
||||
self.bubbleNode.displaysAsynchronously = false
|
||||
self.bubbleNode.image = bubbleImage
|
||||
@ -87,38 +77,59 @@ final class ChatLoadingPlaceholderMessageContainer {
|
||||
}
|
||||
|
||||
func setup(maskNode: ASDisplayNode, borderMaskNode: ASDisplayNode) {
|
||||
if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode {
|
||||
maskNode.addSubnode(avatarNode)
|
||||
borderMaskNode.addSubnode(avatarBorderNode)
|
||||
}
|
||||
maskNode.addSubnode(self.bubbleNode)
|
||||
borderMaskNode.addSubnode(self.bubbleBorderNode)
|
||||
}
|
||||
|
||||
func animateWith(_ listItemNode: ListViewItemNode, transition: ContainedViewLayoutTransition) {
|
||||
func animateWith(_ listItemNode: ListViewItemNode, delay: Double, transition: ContainedViewLayoutTransition) {
|
||||
listItemNode.allowsGroupOpacity = true
|
||||
listItemNode.alpha = 1.0
|
||||
listItemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: delay, completion: { _ in
|
||||
listItemNode.allowsGroupOpacity = false
|
||||
})
|
||||
|
||||
if let bubbleItemNode = listItemNode as? ChatMessageBubbleItemNode {
|
||||
bubbleItemNode.animateFromLoadingPlaceholder(messageContainer: self, transition: transition)
|
||||
bubbleItemNode.animateFromLoadingPlaceholder(messageContainer: self, delay: delay, transition: transition)
|
||||
} else if let stickerItemNode = listItemNode as? ChatMessageStickerItemNode {
|
||||
stickerItemNode.animateFromLoadingPlaceholder(messageContainer: self, delay: delay, transition: transition)
|
||||
} else if let stickerItemNode = listItemNode as? ChatMessageAnimatedStickerItemNode {
|
||||
stickerItemNode.animateFromLoadingPlaceholder(messageContainer: self, transition: transition)
|
||||
stickerItemNode.animateFromLoadingPlaceholder(messageContainer: self, delay: delay, transition: transition)
|
||||
} else if let videoItemNode = listItemNode as? ChatMessageInstantVideoItemNode {
|
||||
videoItemNode.animateFromLoadingPlaceholder(messageContainer: self, delay: delay, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func animateBackgroundFrame(to frame: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
let targetFrame = CGRect(origin: CGPoint(x: self.bubbleNode.frame.minX, y: frame.minY), size: frame.size)
|
||||
|
||||
transition.updateFrame(node: self.bubbleNode, frame: targetFrame)
|
||||
transition.updateFrame(node: self.bubbleBorderNode, frame: targetFrame)
|
||||
|
||||
if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode {
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: 3.0, y: frame.maxY + 1.0 - avatarSize.height), size: avatarSize)
|
||||
|
||||
transition.updateFrame(node: avatarNode, frame: avatarFrame)
|
||||
transition.updateFrame(node: avatarBorderNode, frame: avatarFrame)
|
||||
}
|
||||
// let targetFrame = CGRect(origin: CGPoint(x: self.bubbleNode.frame.minX, y: frame.minY), size: frame.size)
|
||||
//
|
||||
// transition.updateFrame(node: self.bubbleNode, frame: targetFrame)
|
||||
// transition.updateFrame(node: self.bubbleBorderNode, frame: targetFrame)
|
||||
//
|
||||
// if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode {
|
||||
// let avatarFrame = CGRect(origin: CGPoint(x: 3.0, y: frame.maxY + 1.0 - avatarSize.height), size: avatarSize)
|
||||
//
|
||||
// transition.updateFrame(node: avatarNode, frame: avatarFrame)
|
||||
// transition.updateFrame(node: avatarBorderNode, frame: avatarFrame)
|
||||
// }
|
||||
}
|
||||
|
||||
func update(size: CGSize, rect: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
func update(size: CGSize, hasAvatar: Bool, rect: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
var avatarOffset: CGFloat = 0.0
|
||||
|
||||
if hasAvatar && self.avatarNode == nil {
|
||||
let avatarNode = ASImageNode()
|
||||
avatarNode.displaysAsynchronously = false
|
||||
avatarNode.image = avatarImage
|
||||
self.bubbleNode.supernode?.addSubnode(avatarNode)
|
||||
self.avatarNode = avatarNode
|
||||
|
||||
let avatarBorderNode = ASImageNode()
|
||||
avatarBorderNode.displaysAsynchronously = false
|
||||
avatarBorderNode.image = avatarBorderImage
|
||||
self.bubbleBorderNode.supernode?.addSubnode(avatarBorderNode)
|
||||
self.avatarBorderNode = avatarBorderNode
|
||||
}
|
||||
|
||||
if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode {
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: 3.0, y: rect.maxY + 1.0 - avatarSize.height), size: avatarSize)
|
||||
|
||||
@ -140,6 +151,7 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
private let maskNode: ASDisplayNode
|
||||
private let borderMaskNode: ASDisplayNode
|
||||
|
||||
private let scrollingContainer: ASDisplayNode
|
||||
private let containerNode: ASDisplayNode
|
||||
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||
private let backgroundColorNode: ASDisplayNode
|
||||
@ -154,8 +166,10 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
|
||||
private var validLayout: (CGSize, UIEdgeInsets)?
|
||||
|
||||
init(theme: PresentationTheme, chatWallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners, backgroundNode: WallpaperBackgroundNode, hasAvatar: Bool) {
|
||||
init(theme: PresentationTheme, chatWallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners, backgroundNode: WallpaperBackgroundNode) {
|
||||
self.backgroundNode = backgroundNode
|
||||
|
||||
self.scrollingContainer = ASDisplayNode()
|
||||
self.maskNode = ASDisplayNode()
|
||||
self.borderMaskNode = ASDisplayNode()
|
||||
|
||||
@ -164,7 +178,7 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
|
||||
var messageContainers: [ChatLoadingPlaceholderMessageContainer] = []
|
||||
for _ in 0 ..< 8 {
|
||||
let container = ChatLoadingPlaceholderMessageContainer(hasAvatar: hasAvatar, bubbleImage: bubbleImage, bubbleBorderImage: bubbleBorderImage)
|
||||
let container = ChatLoadingPlaceholderMessageContainer(bubbleImage: bubbleImage, bubbleBorderImage: bubbleBorderImage)
|
||||
container.setup(maskNode: self.maskNode, borderMaskNode: self.borderMaskNode)
|
||||
messageContainers.append(container)
|
||||
}
|
||||
@ -184,14 +198,14 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
self.addSubnode(self.scrollingContainer)
|
||||
|
||||
self.scrollingContainer.addSubnode(self.containerNode)
|
||||
self.containerNode.addSubnode(self.backgroundColorNode)
|
||||
self.containerNode.addSubnode(self.effectNode)
|
||||
|
||||
self.addSubnode(self.borderNode)
|
||||
self.scrollingContainer.addSubnode(self.borderNode)
|
||||
self.borderNode.addSubnode(self.borderEffectNode)
|
||||
// self.addSubnode(self.maskNode)
|
||||
// self.addSubnode(self.borderMaskNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -200,73 +214,174 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
self.containerNode.view.mask = self.maskNode.view
|
||||
self.borderNode.view.mask = self.borderMaskNode.view
|
||||
|
||||
// self.backgroundNode?.updateIsLooping(true)
|
||||
self.backgroundNode?.updateIsLooping(true)
|
||||
}
|
||||
|
||||
func animateOut(_ historyNode: ChatHistoryNode) {
|
||||
private var bottomInset: (Int, CGFloat)?
|
||||
func setup(_ historyNode: ChatHistoryNode) {
|
||||
guard let listNode = historyNode as? ListView else {
|
||||
return
|
||||
}
|
||||
|
||||
var count = 0
|
||||
var inset: CGFloat = 0.0
|
||||
listNode.forEachVisibleItemNode { itemNode in
|
||||
inset += itemNode.frame.height
|
||||
count += 1
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
self.bottomInset = (count, inset)
|
||||
}
|
||||
|
||||
self.scrollingContainer.bounds = self.scrollingContainer.bounds.offsetBy(dx: 0.0, dy: inset)
|
||||
}
|
||||
|
||||
func animateOut(_ historyNode: ChatHistoryNode, completion: @escaping () -> Void = {}) {
|
||||
guard let listNode = historyNode as? ListView, let (size, _) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
self.backgroundNode?.updateIsLooping(false)
|
||||
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut)
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring)
|
||||
|
||||
var lastFrame: CGRect?
|
||||
|
||||
var i = 0
|
||||
let heightNorm = listNode.bounds.height - listNode.insets.top
|
||||
|
||||
var index = 0
|
||||
var skipCount = self.bottomInset?.0 ?? 0
|
||||
listNode.forEachVisibleItemNode { itemNode in
|
||||
guard i < self.messageContainers.count, let listItemNode = itemNode as? ListViewItemNode else {
|
||||
guard index < self.messageContainers.count, let listItemNode = itemNode as? ListViewItemNode else {
|
||||
return
|
||||
}
|
||||
|
||||
let delayFactor = listItemNode.frame.minY / heightNorm
|
||||
let delay = Double(delayFactor * 0.1)
|
||||
|
||||
if skipCount > 0 {
|
||||
skipCount -= 1
|
||||
return
|
||||
}
|
||||
|
||||
if let bubbleItemNode = itemNode as? ChatMessageBubbleItemNode,
|
||||
bubbleItemNode.contentNodes.contains(where: { $0 is ChatMessageActionBubbleContentNode }) {
|
||||
if let itemNode = itemNode as? ChatUnreadItemNode {
|
||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: 0.0)
|
||||
return
|
||||
}
|
||||
if let itemNode = itemNode as? ChatReplyCountItemNode {
|
||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: 0.0)
|
||||
return
|
||||
}
|
||||
|
||||
let messageContainer = self.messageContainers[i]
|
||||
messageContainer.animateWith(listItemNode, transition: transition)
|
||||
if let bubbleItemNode = listItemNode as? ChatMessageBubbleItemNode, bubbleItemNode.contentNodes.contains(where: { $0 is ChatMessageActionBubbleContentNode }) {
|
||||
return
|
||||
}
|
||||
|
||||
let messageContainer = self.messageContainers[index]
|
||||
messageContainer.animateWith(listItemNode, delay: delay, transition: transition)
|
||||
|
||||
lastFrame = messageContainer.frame
|
||||
|
||||
i += 1
|
||||
index += 1
|
||||
}
|
||||
|
||||
if let lastFrame = lastFrame, i < self.messageContainers.count {
|
||||
skipCount = self.bottomInset?.0 ?? 0
|
||||
listNode.forEachItemHeaderNode { itemNode in
|
||||
var animateScale = true
|
||||
if itemNode is ChatMessageAvatarHeaderNode {
|
||||
animateScale = false
|
||||
if skipCount > 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
if itemNode is ChatMessageDateHeaderNode {
|
||||
if skipCount > 0 {
|
||||
skipCount -= 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let delayFactor = itemNode.frame.minY / heightNorm
|
||||
let delay = Double(delayFactor * 0.2)
|
||||
|
||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
|
||||
if animateScale {
|
||||
itemNode.layer.animateScale(from: 0.94, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
}
|
||||
|
||||
self.alpha = 0.0
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
|
||||
if let lastFrame = lastFrame, index < self.messageContainers.count {
|
||||
var offset = lastFrame.minY
|
||||
for k in i ..< self.messageContainers.count {
|
||||
for k in index ..< self.messageContainers.count {
|
||||
let messageContainer = self.messageContainers[k]
|
||||
let messageSize = messageContainer.frame.size
|
||||
|
||||
messageContainer.update(size: size, rect: CGRect(origin: CGPoint(x: 0.0, y: offset - messageSize.height), size: messageSize), transition: transition)
|
||||
messageContainer.update(size: size, hasAvatar: self.isGroup, rect: CGRect(origin: CGPoint(x: 0.0, y: offset - messageSize.height), size: messageSize), transition: transition)
|
||||
offset -= messageSize.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
|
||||
private var ignoreFirstContentOffset = true
|
||||
func addContentOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
guard !self.ignoreFirstContentOffset else {
|
||||
self.ignoreFirstContentOffset = false
|
||||
return
|
||||
}
|
||||
self.scrollingContainer.bounds = self.scrollingContainer.bounds.offsetBy(dx: 0.0, dy: -offset)
|
||||
transition.animateOffsetAdditive(node: self.scrollingContainer, offset: offset)
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
self.update(rect: rect, within: containerSize)
|
||||
}
|
||||
}
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
|
||||
self.absolutePosition = (rect, containerSize)
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundFrame.origin.y += rect.minY - self.scrollingContainer.bounds.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private var isGroup = false
|
||||
func updatePresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState) {
|
||||
var isGroup = false
|
||||
if let peer = chatPresentationInterfaceState.renderedPeer?.peer {
|
||||
if peer is TelegramGroup {
|
||||
isGroup = true
|
||||
} else if let channel = peer as? TelegramChannel, case .group = channel.info {
|
||||
isGroup = true
|
||||
}
|
||||
}
|
||||
|
||||
if self.isGroup != isGroup {
|
||||
self.isGroup = isGroup
|
||||
if let (size, insets) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets)
|
||||
|
||||
let bounds = CGRect(origin: .zero, size: size)
|
||||
|
||||
transition.updateFrame(node: self.scrollingContainer, frame: bounds)
|
||||
|
||||
transition.updateFrame(node: self.maskNode, frame: bounds)
|
||||
transition.updateFrame(node: self.borderMaskNode, frame: bounds)
|
||||
transition.updateFrame(node: self.containerNode, frame: bounds)
|
||||
transition.updateFrame(node: self.borderNode, frame: bounds)
|
||||
|
||||
transition.updateFrame(node: self.backgroundColorNode, frame: bounds)
|
||||
|
||||
transition.updateFrame(node: self.effectNode, frame: bounds)
|
||||
transition.updateFrame(node: self.borderEffectNode, frame: bounds)
|
||||
|
||||
@ -291,15 +406,19 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
]
|
||||
|
||||
var offset: CGFloat = 5.0
|
||||
var i = 0
|
||||
for messageContainer in self.messageContainers {
|
||||
let messageSize = dimensions[i % 12]
|
||||
messageContainer.update(size: size, rect: CGRect(origin: CGPoint(x: 0.0, y: size.height - insets.bottom - offset - messageSize.height), size: messageSize), transition: transition)
|
||||
offset += messageSize.height
|
||||
i += 1
|
||||
var index = 0
|
||||
if let (insetCount, _) = self.bottomInset {
|
||||
index += insetCount
|
||||
}
|
||||
|
||||
if backgroundNode?.hasExtraBubbleBackground() == true {
|
||||
for messageContainer in self.messageContainers {
|
||||
let messageSize = dimensions[index % 8]
|
||||
messageContainer.update(size: size, hasAvatar: self.isGroup, rect: CGRect(origin: CGPoint(x: 0.0, y: size.height - insets.bottom - offset - messageSize.height), size: messageSize), transition: transition)
|
||||
offset += messageSize.height
|
||||
index += 1
|
||||
}
|
||||
|
||||
if self.backgroundNode?.hasExtraBubbleBackground() == true {
|
||||
self.backgroundColorNode.isHidden = true
|
||||
} else {
|
||||
self.backgroundColorNode.isHidden = false
|
||||
@ -318,10 +437,7 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
if let backgroundContent = self.backgroundContent {
|
||||
transition.updateFrame(node: backgroundContent, frame: bounds)
|
||||
if let (rect, containerSize) = self.absolutePosition {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
self.update(rect: rect, within: containerSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2673,40 +2673,14 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, transition: ContainedViewLayoutTransition) {
|
||||
guard let _ = self.item, let animationNode = self.animationNode else {
|
||||
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, delay: Double, transition: ContainedViewLayoutTransition) {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
|
||||
let targetFrame = self.view.convert(animationNode.frame, to: messageContainer.parentView)
|
||||
messageContainer.animateBackgroundFrame(to: targetFrame, transition: transition)
|
||||
|
||||
var backgroundFrame = messageContainer.frame
|
||||
backgroundFrame.size.width = backgroundFrame.size.height
|
||||
|
||||
// let localSourceContentFrame = self.contextSourceNode.contentNode.view.convert(backgroundFrame.offsetBy(dx: self.contextSourceNode.contentRect.minX, dy: self.contextSourceNode.contentRect.minY), to: self.contextSourceNode.contentNode.view)
|
||||
|
||||
// let sourceCenter = CGPoint(
|
||||
// x: localSourceContentFrame.minX + 11.2,
|
||||
// y: localSourceContentFrame.midY - 1.8
|
||||
// )
|
||||
|
||||
let sourceScale: CGFloat = 36.0 / self.imageNode.frame.height
|
||||
|
||||
let offset = CGPoint(x: -90.0, y: 90.0)
|
||||
// let offset = CGPoint(
|
||||
// x: sourceCenter.x - self.imageNode.frame.midX,
|
||||
// y: sourceCenter.y - self.imageNode.frame.midY
|
||||
// )
|
||||
|
||||
transition.animatePositionAdditive(layer: self.imageNode.layer, offset: offset)
|
||||
transition.animateTransformScale(node: self.imageNode, from: sourceScale)
|
||||
if let animationNode = self.animationNode {
|
||||
transition.animatePositionAdditive(layer: animationNode.layer, offset: offset)
|
||||
transition.animateTransformScale(node: animationNode, from: sourceScale)
|
||||
}
|
||||
transition.animatePositionAdditive(layer: self.placeholderNode.layer, offset: offset)
|
||||
transition.animateTransformScale(node: self.placeholderNode, from: sourceScale)
|
||||
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||
transition.animatePositionAdditive(node: self, offset: CGPoint(x: incoming ? 30.0 : -30.0, y: -30.0), delay: delay)
|
||||
transition.animateTransformScale(node: self, from: CGPoint(x: 0.85, y: 0.85), delay: delay)
|
||||
}
|
||||
|
||||
override func openMessageContextMenu() {
|
||||
|
@ -797,46 +797,14 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, transition: ContainedViewLayoutTransition) {
|
||||
guard let _ = self.item else {
|
||||
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, delay: Double, transition: ContainedViewLayoutTransition) {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
|
||||
let targetFrame = self.view.convert(self.backgroundNode.frame, to: messageContainer.parentView)
|
||||
messageContainer.animateBackgroundFrame(to: targetFrame, transition: transition)
|
||||
|
||||
// let backgroundFrame = messageContainer.frame
|
||||
//// let widthDifference = self.backgroundNode.frame.width - backgroundFrame.width
|
||||
//// let heightDifference = self.backgroundNode.frame.height - backgroundFrame.height
|
||||
//
|
||||
// if let type = self.backgroundNode.type {
|
||||
// if case .none = type {
|
||||
// } else {
|
||||
// self.clippingNode.clipsToBounds = true
|
||||
// }
|
||||
// }
|
||||
// transition.animateFrame(layer: self.clippingNode.layer, from: CGRect(origin: CGPoint(x: self.clippingNode.frame.minX, y: backgroundFrame.minY), size: backgroundFrame.size), completion: { [weak self] _ in
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// strongSelf.clippingNode.clipsToBounds = false
|
||||
// })
|
||||
// transition.animateOffsetAdditive(layer: self.clippingNode.layer, offset: backgroundFrame.minY - self.clippingNode.frame.minY)
|
||||
//
|
||||
// let combinedTransition = CombinedTransition(horizontal: transition, vertical: transition)
|
||||
//
|
||||
// self.backgroundWallpaperNode.animateFrom(sourceView: messageContainer.bubbleNode.view, mediaBox: item.context.account.postbox.mediaBox, transition: combinedTransition)
|
||||
// self.backgroundNode.animateFrom(sourceView: messageContainer.bubbleNode.view, transition: combinedTransition)
|
||||
|
||||
// for contentNode in self.contentNodes {
|
||||
// if let contentNode = contentNode as? ChatMessageTextBubbleContentNode {
|
||||
// let localSourceContentFrame = self.mainContextSourceNode.contentNode.view.convert(textInput.contentView.frame.offsetBy(dx: self.mainContextSourceNode.contentRect.minX, dy: self.mainContextSourceNode.contentRect.minY), to: contentNode.view)
|
||||
// textInput.contentView.frame = localSourceContentFrame
|
||||
// contentNode.animateFrom(sourceView: textInput.contentView, scrollOffset: textInput.scrollOffset, widthDifference: widthDifference, transition: transition)
|
||||
// } else if let contentNode = contentNode as? ChatMessageWebpageBubbleContentNode {
|
||||
// transition.animatePositionAdditive(node: contentNode, offset: CGPoint(x: 0.0, y: heightDifference))
|
||||
// }
|
||||
// }
|
||||
|
||||
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||
transition.animatePositionAdditive(node: self, offset: CGPoint(x: incoming ? 30.0 : -30.0, y: -30.0), delay: delay)
|
||||
transition.animateTransformScale(node: self, from: CGPoint(x: 0.85, y: 0.85), delay: delay)
|
||||
}
|
||||
|
||||
func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: CombinedTransition) {
|
||||
|
@ -1165,6 +1165,16 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, delay: Double, transition: ContainedViewLayoutTransition) {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
|
||||
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||
transition.animatePositionAdditive(node: self, offset: CGPoint(x: incoming ? 30.0 : -30.0, y: -30.0), delay: delay)
|
||||
transition.animateTransformScale(node: self, from: CGPoint(x: 0.85, y: 0.85), delay: delay)
|
||||
}
|
||||
|
||||
func animateFromSnapshot(snapshotView: UIView, transition: CombinedTransition) {
|
||||
snapshotView.frame = self.interactiveVideoNode.view.convert(snapshotView.frame, from: self.contextSourceNode.contentNode.view)
|
||||
self.interactiveVideoNode.animateFromSnapshot(snapshotView: snapshotView, transition: transition)
|
||||
|
@ -1641,6 +1641,16 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, delay: Double, transition: ContainedViewLayoutTransition) {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
|
||||
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||
transition.animatePositionAdditive(node: self, offset: CGPoint(x: incoming ? 30.0 : -30.0, y: -30.0), delay: delay)
|
||||
transition.animateTransformScale(node: self, from: CGPoint(x: 0.85, y: 0.85), delay: delay)
|
||||
}
|
||||
|
||||
override func openMessageContextMenu() {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user