mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +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 {
|
switch self {
|
||||||
case .immediate:
|
case .immediate:
|
||||||
completion(true)
|
completion(true)
|
||||||
case let .animated(duration, curve):
|
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 {
|
switch self {
|
||||||
case .immediate:
|
case .immediate:
|
||||||
completion?()
|
completion?()
|
||||||
case let .animated(duration, curve):
|
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?()
|
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 {
|
switch self {
|
||||||
case .immediate:
|
case .immediate:
|
||||||
if let completion = completion {
|
if let completion = completion {
|
||||||
@ -961,12 +961,12 @@ public extension ContainedViewLayoutTransition {
|
|||||||
calculatedFrom = fromScale
|
calculatedFrom = fromScale
|
||||||
calculatedTo = CGPoint(x: 1.0, y: 1.0)
|
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 {
|
if let completion = completion {
|
||||||
completion(result)
|
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 topTaggedMessages: [Message]
|
||||||
public let additionalData: [AdditionalMessageHistoryViewDataEntry]
|
public let additionalData: [AdditionalMessageHistoryViewDataEntry]
|
||||||
public let isLoading: Bool
|
public let isLoading: Bool
|
||||||
|
public let isLoadingEarlier: Bool
|
||||||
public let isAddedToChatList: Bool
|
public let isAddedToChatList: Bool
|
||||||
|
|
||||||
public init(tagMask: MessageTags?, namespaces: MessageIdNamespaces, entries: [MessageHistoryEntry], holeEarlier: Bool, holeLater: Bool, isLoading: 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.topTaggedMessages = []
|
||||||
self.additionalData = []
|
self.additionalData = []
|
||||||
self.isLoading = isLoading
|
self.isLoading = isLoading
|
||||||
|
self.isLoadingEarlier = true
|
||||||
self.isAddedToChatList = false
|
self.isAddedToChatList = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -981,8 +983,10 @@ public final class MessageHistoryView {
|
|||||||
self.holeLater = true
|
self.holeLater = true
|
||||||
self.earlierId = nil
|
self.earlierId = nil
|
||||||
self.laterId = nil
|
self.laterId = nil
|
||||||
|
self.isLoadingEarlier = true
|
||||||
case let .loaded(state):
|
case let .loaded(state):
|
||||||
var isLoading = false
|
var isLoading = false
|
||||||
|
var isLoadingEarlier = false
|
||||||
switch state.anchor {
|
switch state.anchor {
|
||||||
case .lowerBound:
|
case .lowerBound:
|
||||||
self.anchorIndex = .lowerBound
|
self.anchorIndex = .lowerBound
|
||||||
@ -996,6 +1000,10 @@ public final class MessageHistoryView {
|
|||||||
if state.entries.isEmpty && state.hole != nil {
|
if state.entries.isEmpty && state.hole != nil {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
}
|
}
|
||||||
|
if state.entries.count <= 1 && state.hole != nil {
|
||||||
|
isLoadingEarlier = true
|
||||||
|
}
|
||||||
|
self.isLoadingEarlier = isLoadingEarlier
|
||||||
entries = []
|
entries = []
|
||||||
if let transientReadStates = mutableView.transientReadStates, case let .peer(states) = transientReadStates {
|
if let transientReadStates = mutableView.transientReadStates, case let .peer(states) = transientReadStates {
|
||||||
for entry in state.entries {
|
for entry in state.entries {
|
||||||
@ -1199,6 +1207,7 @@ public final class MessageHistoryView {
|
|||||||
self.topTaggedMessages = base.topTaggedMessages
|
self.topTaggedMessages = base.topTaggedMessages
|
||||||
self.additionalData = base.additionalData
|
self.additionalData = base.additionalData
|
||||||
self.isLoading = base.isLoading
|
self.isLoading = base.isLoading
|
||||||
|
self.isLoadingEarlier = base.isLoadingEarlier
|
||||||
self.isAddedToChatList = base.isAddedToChatList
|
self.isAddedToChatList = base.isAddedToChatList
|
||||||
|
|
||||||
if let combinedReadStates = combinedReadStates {
|
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)
|
strongSelf.chatDisplayNode.messageTransitionNode.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
var blurredHistoryNode: ASImageNode?
|
var blurredHistoryNode: ASImageNode?
|
||||||
let historyNodeContainer: ASDisplayNode
|
let historyNodeContainer: ASDisplayNode
|
||||||
let loadingNode: ChatLoadingNode
|
let loadingNode: ChatLoadingNode
|
||||||
private var loadingPlaceholderNode: ChatLoadingPlaceholderNode
|
private(set) var loadingPlaceholderNode: ChatLoadingPlaceholderNode?
|
||||||
|
|
||||||
private var emptyNode: ChatEmptyNode?
|
private var emptyNode: ChatEmptyNode?
|
||||||
private(set) var emptyType: ChatHistoryNodeLoadState.EmptyType?
|
private(set) var emptyType: ChatHistoryNodeLoadState.EmptyType?
|
||||||
@ -222,36 +222,52 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
private var isLoadingValue: Bool = false
|
private var isLoadingValue: Bool = false
|
||||||
private func updateIsLoading(isLoading: Bool, animated: Bool) {
|
private func updateIsLoading(isLoading: Bool, animated: Bool) {
|
||||||
loadingPlaceholderNode.isHidden = true
|
let useLoadingPlaceholder = self.chatLocation.peerId?.namespace != Namespaces.Peer.CloudUser
|
||||||
|
|
||||||
if isLoading != self.isLoadingValue {
|
if isLoading != self.isLoadingValue {
|
||||||
self.isLoadingValue = isLoading
|
self.isLoadingValue = isLoading
|
||||||
if isLoading {
|
if isLoading {
|
||||||
// self.historyNodeContainer.supernode?.insertSubnode(self.loadingNode, belowSubnode: self.historyNodeContainer)
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadingPlaceholderNode.alpha = 1.0
|
||||||
|
loadingPlaceholderNode.isHidden = false
|
||||||
|
} else {
|
||||||
|
self.historyNodeContainer.supernode?.insertSubnode(self.loadingNode, belowSubnode: self.historyNodeContainer)
|
||||||
self.loadingNode.isHidden = false
|
self.loadingNode.isHidden = false
|
||||||
self.loadingNode.layer.removeAllAnimations()
|
self.loadingNode.layer.removeAllAnimations()
|
||||||
self.loadingNode.alpha = 1.0
|
self.loadingNode.alpha = 1.0
|
||||||
if animated {
|
if animated {
|
||||||
self.loadingNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
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
|
|
||||||
}
|
}
|
||||||
|
} 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
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.loadingPlaceholderNode.animateOut(self.historyNode)
|
}
|
||||||
|
} else {
|
||||||
self.historyNode.alpha = 1.0
|
|
||||||
self.historyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
|
||||||
|
|
||||||
self.loadingNode.alpha = 0.0
|
self.loadingNode.alpha = 0.0
|
||||||
if animated {
|
if animated {
|
||||||
self.loadingNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false)
|
self.loadingNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false)
|
||||||
@ -269,6 +285,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var lastSendTimestamp = 0.0
|
private var lastSendTimestamp = 0.0
|
||||||
|
|
||||||
@ -415,8 +432,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
self.loadingNode = ChatLoadingNode(theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper, bubbleCorners: self.chatPresentationInterfaceState.bubbleCorners)
|
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.inputPanelContainerNode = ChatInputPanelContainer()
|
||||||
self.inputPanelOverlayNode = SparseNode()
|
self.inputPanelOverlayNode = SparseNode()
|
||||||
self.inputPanelClippingNode = SparseNode()
|
self.inputPanelClippingNode = SparseNode()
|
||||||
@ -491,12 +506,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
assert(Queue.mainQueue().isCurrent())
|
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
|
self.historyNode.setLoadStateUpdated { [weak self] loadState, animated in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let wasLoading = strongSelf.isLoadingValue
|
let wasLoading = strongSelf.isLoadingValue
|
||||||
@ -561,13 +570,20 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
self.addSubnode(self.contentContainerNode)
|
self.addSubnode(self.contentContainerNode)
|
||||||
self.contentContainerNode.addSubnode(self.backgroundNode)
|
self.contentContainerNode.addSubnode(self.backgroundNode)
|
||||||
self.contentContainerNode.addSubnode(self.loadingPlaceholderNode)
|
|
||||||
self.contentContainerNode.addSubnode(self.historyNodeContainer)
|
self.contentContainerNode.addSubnode(self.historyNodeContainer)
|
||||||
|
|
||||||
if let navigationBar = self.navigationBar {
|
if let navigationBar = self.navigationBar {
|
||||||
self.contentContainerNode.addSubnode(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
|
self.inputPanelContainerNode.expansionUpdated = { [weak self] transition in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1417,7 +1433,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
//transition.updateFrame(node: self.historyScrollingArea, frame: contentBounds)
|
//transition.updateFrame(node: self.historyScrollingArea, frame: contentBounds)
|
||||||
|
|
||||||
transition.updateFrame(node: self.loadingNode, 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 {
|
if let restrictedNode = self.restrictedNode {
|
||||||
transition.updateFrame(node: restrictedNode, frame: contentBounds)
|
transition.updateFrame(node: restrictedNode, frame: contentBounds)
|
||||||
@ -1575,8 +1593,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.visibleAreaInset = visibleAreaInset
|
self.visibleAreaInset = visibleAreaInset
|
||||||
self.loadingNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition)
|
self.loadingNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition)
|
||||||
|
|
||||||
self.loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition)
|
if let loadingPlaceholderNode = self.loadingPlaceholderNode {
|
||||||
self.loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition)
|
||||||
|
loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
if let containerNode = self.containerNode {
|
if let containerNode = self.containerNode {
|
||||||
contentBottomInset += 8.0
|
contentBottomInset += 8.0
|
||||||
@ -2210,6 +2230,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper)
|
self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper)
|
||||||
|
|
||||||
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
|
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
|
||||||
|
self.loadingPlaceholderNode?.updatePresentationInterfaceState(chatPresentationInterfaceState)
|
||||||
|
|
||||||
var updatedInputFocus = self.chatPresentationInterfaceStateRequiresInputFocus(self.chatPresentationInterfaceState) != self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState)
|
var updatedInputFocus = self.chatPresentationInterfaceStateRequiresInputFocus(self.chatPresentationInterfaceState) != self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState)
|
||||||
if self.chatPresentationInterfaceStateInputView(self.chatPresentationInterfaceState) !== self.chatPresentationInterfaceStateInputView(chatPresentationInterfaceState) {
|
if self.chatPresentationInterfaceStateInputView(self.chatPresentationInterfaceState) !== self.chatPresentationInterfaceStateInputView(chatPresentationInterfaceState) {
|
||||||
|
@ -2558,9 +2558,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
}
|
}
|
||||||
loadState = .empty(emptyType)
|
loadState = .empty(emptyType)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if historyView.originalView.isLoadingEarlier && strongSelf.chatLocation.peerId?.namespace != Namespaces.Peer.CloudUser {
|
||||||
|
loadState = .loading
|
||||||
} else {
|
} else {
|
||||||
loadState = .messages
|
loadState = .messages
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
loadState = .loading
|
loadState = .loading
|
||||||
}
|
}
|
||||||
@ -2639,7 +2643,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
strongSelf._buttonKeyboardMessage.set(.single(transition.keyboardButtonsMessage))
|
strongSelf._buttonKeyboardMessage.set(.single(transition.keyboardButtonsMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
if transition.animateIn || animateIn {
|
if (transition.animateIn || animateIn) && !"".isEmpty {
|
||||||
let heightNorm = strongSelf.bounds.height - strongSelf.insets.top
|
let heightNorm = strongSelf.bounds.height - strongSelf.insets.top
|
||||||
strongSelf.forEachVisibleItemNode { itemNode in
|
strongSelf.forEachVisibleItemNode { itemNode in
|
||||||
let delayFactor = itemNode.frame.minY / heightNorm
|
let delayFactor = itemNode.frame.minY / heightNorm
|
||||||
|
@ -7,6 +7,7 @@ import TelegramPresentationData
|
|||||||
import ActivityIndicator
|
import ActivityIndicator
|
||||||
import WallpaperBackgroundNode
|
import WallpaperBackgroundNode
|
||||||
import ShimmerEffect
|
import ShimmerEffect
|
||||||
|
import ChatPresentationInterfaceState
|
||||||
|
|
||||||
final class ChatLoadingNode: ASDisplayNode {
|
final class ChatLoadingNode: ASDisplayNode {
|
||||||
private let backgroundNode: NavigationBackgroundNode
|
private let backgroundNode: NavigationBackgroundNode
|
||||||
@ -47,10 +48,12 @@ final class ChatLoadingNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let avatarSize = CGSize(width: 38.0, height: 38.0)
|
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 {
|
final class ChatLoadingPlaceholderMessageContainer {
|
||||||
let avatarNode: ASImageNode?
|
var avatarNode: ASImageNode?
|
||||||
let avatarBorderNode: ASImageNode?
|
var avatarBorderNode: ASImageNode?
|
||||||
|
|
||||||
let bubbleNode: ASImageNode
|
let bubbleNode: ASImageNode
|
||||||
let bubbleBorderNode: ASImageNode
|
let bubbleBorderNode: ASImageNode
|
||||||
@ -63,20 +66,7 @@ final class ChatLoadingPlaceholderMessageContainer {
|
|||||||
return self.bubbleNode.frame
|
return self.bubbleNode.frame
|
||||||
}
|
}
|
||||||
|
|
||||||
init(hasAvatar: Bool, bubbleImage: UIImage?, bubbleBorderImage: UIImage?) {
|
init(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
|
|
||||||
}
|
|
||||||
|
|
||||||
self.bubbleNode = ASImageNode()
|
self.bubbleNode = ASImageNode()
|
||||||
self.bubbleNode.displaysAsynchronously = false
|
self.bubbleNode.displaysAsynchronously = false
|
||||||
self.bubbleNode.image = bubbleImage
|
self.bubbleNode.image = bubbleImage
|
||||||
@ -87,38 +77,59 @@ final class ChatLoadingPlaceholderMessageContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setup(maskNode: ASDisplayNode, borderMaskNode: ASDisplayNode) {
|
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)
|
maskNode.addSubnode(self.bubbleNode)
|
||||||
borderMaskNode.addSubnode(self.bubbleBorderNode)
|
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 {
|
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 {
|
} 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) {
|
func animateBackgroundFrame(to frame: CGRect, transition: ContainedViewLayoutTransition) {
|
||||||
let targetFrame = CGRect(origin: CGPoint(x: self.bubbleNode.frame.minX, y: frame.minY), size: frame.size)
|
// 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.bubbleNode, frame: targetFrame)
|
||||||
transition.updateFrame(node: self.bubbleBorderNode, frame: targetFrame)
|
// transition.updateFrame(node: self.bubbleBorderNode, frame: targetFrame)
|
||||||
|
//
|
||||||
if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode {
|
// 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)
|
// 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: avatarNode, frame: avatarFrame)
|
||||||
transition.updateFrame(node: avatarBorderNode, 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
|
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 {
|
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)
|
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 maskNode: ASDisplayNode
|
||||||
private let borderMaskNode: ASDisplayNode
|
private let borderMaskNode: ASDisplayNode
|
||||||
|
|
||||||
|
private let scrollingContainer: ASDisplayNode
|
||||||
private let containerNode: ASDisplayNode
|
private let containerNode: ASDisplayNode
|
||||||
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||||
private let backgroundColorNode: ASDisplayNode
|
private let backgroundColorNode: ASDisplayNode
|
||||||
@ -154,8 +166,10 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var validLayout: (CGSize, UIEdgeInsets)?
|
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.backgroundNode = backgroundNode
|
||||||
|
|
||||||
|
self.scrollingContainer = ASDisplayNode()
|
||||||
self.maskNode = ASDisplayNode()
|
self.maskNode = ASDisplayNode()
|
||||||
self.borderMaskNode = ASDisplayNode()
|
self.borderMaskNode = ASDisplayNode()
|
||||||
|
|
||||||
@ -164,7 +178,7 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||||||
|
|
||||||
var messageContainers: [ChatLoadingPlaceholderMessageContainer] = []
|
var messageContainers: [ChatLoadingPlaceholderMessageContainer] = []
|
||||||
for _ in 0 ..< 8 {
|
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)
|
container.setup(maskNode: self.maskNode, borderMaskNode: self.borderMaskNode)
|
||||||
messageContainers.append(container)
|
messageContainers.append(container)
|
||||||
}
|
}
|
||||||
@ -184,14 +198,14 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.containerNode)
|
self.addSubnode(self.scrollingContainer)
|
||||||
|
|
||||||
|
self.scrollingContainer.addSubnode(self.containerNode)
|
||||||
self.containerNode.addSubnode(self.backgroundColorNode)
|
self.containerNode.addSubnode(self.backgroundColorNode)
|
||||||
self.containerNode.addSubnode(self.effectNode)
|
self.containerNode.addSubnode(self.effectNode)
|
||||||
|
|
||||||
self.addSubnode(self.borderNode)
|
self.scrollingContainer.addSubnode(self.borderNode)
|
||||||
self.borderNode.addSubnode(self.borderEffectNode)
|
self.borderNode.addSubnode(self.borderEffectNode)
|
||||||
// self.addSubnode(self.maskNode)
|
|
||||||
// self.addSubnode(self.borderMaskNode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
@ -200,73 +214,174 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||||||
self.containerNode.view.mask = self.maskNode.view
|
self.containerNode.view.mask = self.maskNode.view
|
||||||
self.borderNode.view.mask = self.borderMaskNode.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 {
|
guard let listNode = historyNode as? ListView, let (size, _) = self.validLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backgroundNode?.updateIsLooping(false)
|
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 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
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let bubbleItemNode = itemNode as? ChatMessageBubbleItemNode,
|
let delayFactor = listItemNode.frame.minY / heightNorm
|
||||||
bubbleItemNode.contentNodes.contains(where: { $0 is ChatMessageActionBubbleContentNode }) {
|
let delay = Double(delayFactor * 0.1)
|
||||||
|
|
||||||
|
if skipCount > 0 {
|
||||||
|
skipCount -= 1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let messageContainer = self.messageContainers[i]
|
if let itemNode = itemNode as? ChatUnreadItemNode {
|
||||||
messageContainer.animateWith(listItemNode, transition: transition)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
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
|
var offset = lastFrame.minY
|
||||||
for k in i ..< self.messageContainers.count {
|
for k in index ..< self.messageContainers.count {
|
||||||
let messageContainer = self.messageContainers[k]
|
let messageContainer = self.messageContainers[k]
|
||||||
let messageSize = messageContainer.frame.size
|
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
|
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)
|
self.absolutePosition = (rect, containerSize)
|
||||||
if let backgroundContent = self.backgroundContent {
|
if let backgroundContent = self.backgroundContent {
|
||||||
var backgroundFrame = backgroundContent.frame
|
var backgroundFrame = backgroundContent.frame
|
||||||
backgroundFrame.origin.x += rect.minX
|
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)
|
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) {
|
func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayout = (size, insets)
|
self.validLayout = (size, insets)
|
||||||
|
|
||||||
let bounds = CGRect(origin: .zero, size: size)
|
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.maskNode, frame: bounds)
|
||||||
transition.updateFrame(node: self.borderMaskNode, frame: bounds)
|
transition.updateFrame(node: self.borderMaskNode, frame: bounds)
|
||||||
transition.updateFrame(node: self.containerNode, frame: bounds)
|
transition.updateFrame(node: self.containerNode, frame: bounds)
|
||||||
transition.updateFrame(node: self.borderNode, frame: bounds)
|
transition.updateFrame(node: self.borderNode, frame: bounds)
|
||||||
|
|
||||||
transition.updateFrame(node: self.backgroundColorNode, frame: bounds)
|
transition.updateFrame(node: self.backgroundColorNode, frame: bounds)
|
||||||
|
|
||||||
transition.updateFrame(node: self.effectNode, frame: bounds)
|
transition.updateFrame(node: self.effectNode, frame: bounds)
|
||||||
transition.updateFrame(node: self.borderEffectNode, frame: bounds)
|
transition.updateFrame(node: self.borderEffectNode, frame: bounds)
|
||||||
|
|
||||||
@ -291,15 +406,19 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||||||
]
|
]
|
||||||
|
|
||||||
var offset: CGFloat = 5.0
|
var offset: CGFloat = 5.0
|
||||||
var i = 0
|
var index = 0
|
||||||
for messageContainer in self.messageContainers {
|
if let (insetCount, _) = self.bottomInset {
|
||||||
let messageSize = dimensions[i % 12]
|
index += insetCount
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
self.backgroundColorNode.isHidden = true
|
||||||
} else {
|
} else {
|
||||||
self.backgroundColorNode.isHidden = false
|
self.backgroundColorNode.isHidden = false
|
||||||
@ -318,10 +437,7 @@ final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||||||
if let backgroundContent = self.backgroundContent {
|
if let backgroundContent = self.backgroundContent {
|
||||||
transition.updateFrame(node: backgroundContent, frame: bounds)
|
transition.updateFrame(node: backgroundContent, frame: bounds)
|
||||||
if let (rect, containerSize) = self.absolutePosition {
|
if let (rect, containerSize) = self.absolutePosition {
|
||||||
var backgroundFrame = backgroundContent.frame
|
self.update(rect: rect, within: containerSize)
|
||||||
backgroundFrame.origin.x += rect.minX
|
|
||||||
backgroundFrame.origin.y += rect.minY
|
|
||||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2673,40 +2673,14 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, transition: ContainedViewLayoutTransition) {
|
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, delay: Double, transition: ContainedViewLayoutTransition) {
|
||||||
guard let _ = self.item, let animationNode = self.animationNode else {
|
guard let item = self.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetFrame = self.view.convert(animationNode.frame, to: messageContainer.parentView)
|
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||||
messageContainer.animateBackgroundFrame(to: targetFrame, transition: transition)
|
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)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func openMessageContextMenu() {
|
override func openMessageContextMenu() {
|
||||||
|
@ -797,46 +797,14 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, transition: ContainedViewLayoutTransition) {
|
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, delay: Double, transition: ContainedViewLayoutTransition) {
|
||||||
guard let _ = self.item else {
|
guard let item = self.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetFrame = self.view.convert(self.backgroundNode.frame, to: messageContainer.parentView)
|
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||||
messageContainer.animateBackgroundFrame(to: targetFrame, transition: transition)
|
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)
|
||||||
// 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))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: CombinedTransition) {
|
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)
|
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) {
|
func animateFromSnapshot(snapshotView: UIView, transition: CombinedTransition) {
|
||||||
snapshotView.frame = self.interactiveVideoNode.view.convert(snapshotView.frame, from: self.contextSourceNode.contentNode.view)
|
snapshotView.frame = self.interactiveVideoNode.view.convert(snapshotView.frame, from: self.contextSourceNode.contentNode.view)
|
||||||
self.interactiveVideoNode.animateFromSnapshot(snapshotView: snapshotView, transition: transition)
|
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() {
|
override func openMessageContextMenu() {
|
||||||
guard let item = self.item else {
|
guard let item = self.item else {
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user