Improve animations

This commit is contained in:
isaac 2026-04-30 12:56:55 +04:00
parent d6899098a6
commit 7ef7f16727
20 changed files with 42 additions and 42 deletions

View file

@ -176,7 +176,7 @@ public final class BrowserBookmarksScreen: ViewController {
}, sendGift: { _ in
}, openUniqueGift: { _ in
}, openMessageFeeException: {
}, requestMessageUpdate: { _, _ in
}, requestMessageUpdate: { _, _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {
}, scrollToMessageId: { _ in

View file

@ -1856,7 +1856,7 @@ open class ListViewImpl: ASDisplayNode, ListView, ASScrollViewDelegate, ASGestur
})
}
public func transaction(deleteIndices: [ListViewDeleteItem], insertIndicesAndItems: [ListViewInsertItem], updateIndicesAndItems: [ListViewUpdateItem], options: ListViewDeleteAndInsertOptions, scrollToItem: ListViewScrollToItem? = nil, additionalScrollDistance: CGFloat = 0.0, updateSizeAndInsets: ListViewUpdateSizeAndInsets? = nil, stationaryItemRange: (Int, Int)? = nil, updateOpaqueState: Any?, completion: @escaping (ListViewDisplayedItemRange) -> Void = { _ in }) {
public func transaction(deleteIndices: [ListViewDeleteItem], insertIndicesAndItems: [ListViewInsertItem], updateIndicesAndItems: [ListViewUpdateItem], options: ListViewDeleteAndInsertOptions, scrollToItem: ListViewScrollToItem? = nil, additionalScrollDistance: CGFloat = 0.0, updateSizeAndInsets: ListViewUpdateSizeAndInsets? = nil, stationaryItemRange: (Int, Int)? = nil, customAnimationTransition: ControlledTransition? = nil, updateOpaqueState: Any?, completion: @escaping (ListViewDisplayedItemRange) -> Void = { _ in }) {
if deleteIndices.isEmpty && insertIndicesAndItems.isEmpty && updateIndicesAndItems.isEmpty && scrollToItem == nil && updateSizeAndInsets == nil && additionalScrollDistance.isZero {
if let updateOpaqueState = updateOpaqueState {
self.opaqueTransactionState = updateOpaqueState
@ -1868,7 +1868,7 @@ open class ListViewImpl: ASDisplayNode, ListView, ASScrollViewDelegate, ASGestur
self.transactionQueue.addTransaction({ [weak self] transactionCompletion in
if let strongSelf = self {
strongSelf.transactionOffset = 0.0
strongSelf.deleteAndInsertItemsTransaction(deleteIndices: deleteIndices, insertIndicesAndItems: insertIndicesAndItems, updateIndicesAndItems: updateIndicesAndItems, options: options, scrollToItem: scrollToItem, additionalScrollDistance: additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: stationaryItemRange, updateOpaqueState: updateOpaqueState, customAnimationTransition: updateSizeAndInsets?.customAnimationTransition, completion: { [weak strongSelf] in
strongSelf.deleteAndInsertItemsTransaction(deleteIndices: deleteIndices, insertIndicesAndItems: insertIndicesAndItems, updateIndicesAndItems: updateIndicesAndItems, options: options, scrollToItem: scrollToItem, additionalScrollDistance: additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: stationaryItemRange, updateOpaqueState: updateOpaqueState, customAnimationTransition: customAnimationTransition ?? updateSizeAndInsets?.customAnimationTransition, completion: { [weak strongSelf] in
completion(strongSelf?.immediateDisplayedItemRange() ?? ListViewDisplayedItemRange(loadedRange: nil, visibleRange: nil))
transactionCompletion()

View file

@ -225,7 +225,7 @@ open class ChatMessageBubbleContentNode: ASDisplayNode {
public var updateIsTextSelectionActive: ((Bool) -> Void)?
public var requestInlineUpdate: (() -> Void)?
public var requestFullUpdate: (() -> Void)?
public var requestFullUpdate: ((ControlledTransition?) -> Void)?
open var disablesClipping: Bool {
return false

View file

@ -4816,12 +4816,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
strongSelf.internalUpdateLayout()
}
contentNode.requestFullUpdate = { [weak strongSelf] in
contentNode.requestFullUpdate = { [weak strongSelf] customTransition in
guard let strongSelf, let item = strongSelf.item else {
return
}
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
item.controllerInteraction.requestMessageUpdate(item.message.id, false, customTransition)
}
}
}
@ -5636,7 +5636,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
if attribute.isQuote, !replyInfoNode.isQuoteExpanded {
replyInfoNode.isQuoteExpanded = true
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
return
}
var progress: Promise<Bool>?
@ -5668,7 +5668,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
if attribute.isQuote, !replyInfoNode.isQuoteExpanded {
replyInfoNode.isQuoteExpanded = true
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
return
}
@ -7309,10 +7309,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
if item.controllerInteraction.summarizedMessageIds.contains(item.message.id) {
item.controllerInteraction.summarizedMessageIds.remove(item.message.id)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
} else {
item.controllerInteraction.summarizedMessageIds.insert(item.message.id)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
let translateToLanguage = item.associatedData.translateToLanguage
var requestSummary = true
@ -7327,7 +7327,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|> deliverOnMainQueue).start(error: { error in
if case .limitExceededPremium = error, let parentController = item.controllerInteraction.navigationController()?.topViewController as? ViewController {
item.controllerInteraction.summarizedMessageIds.remove(item.message.id)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
let controller = premiumAlertController(
context: item.context,
parentController: parentController,

View file

@ -134,7 +134,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
guard let item = self.item else{
return
}
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
public override func willUpdateIsExtractedToContextPreview(_ value: Bool) {

View file

@ -54,7 +54,7 @@ public class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
self.interactiveFileNode.requestUpdateLayout = { [weak self] _ in
if let strongSelf = self, let item = strongSelf.item {
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
}

View file

@ -308,7 +308,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
guard let item = self.item else{
return
}
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
private func makeProgress() -> Promise<Bool> {

View file

@ -94,7 +94,7 @@ public class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentN
self.interactiveVideoNode.requestUpdateLayout = { [weak self] _ in
if let strongSelf = self, let item = strongSelf.item {
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
}
self.interactiveVideoNode.updateTranscriptionExpanded = { [weak self] state in
@ -102,13 +102,13 @@ public class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentN
let previous = strongSelf.audioTranscriptionState
strongSelf.audioTranscriptionState = state
strongSelf.interactiveFileNode.audioTranscriptionState = state
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, state != .inProgress && previous != state)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, state != .inProgress && previous != state, nil)
}
}
self.interactiveVideoNode.updateTranscriptionText = { [weak self] text in
if let strongSelf = self, let item = strongSelf.item {
strongSelf.interactiveFileNode.forcedAudioTranscriptionText = text
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
}
self.interactiveFileNode.updateTranscriptionExpanded = { [weak self] state in
@ -116,7 +116,7 @@ public class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentN
let previous = strongSelf.audioTranscriptionState
strongSelf.audioTranscriptionState = state
strongSelf.interactiveVideoNode.audioTranscriptionState = state
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, previous != state)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, previous != state, nil)
}
}
@ -134,7 +134,7 @@ public class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentN
self.interactiveFileNode.requestUpdateLayout = { [weak self] _ in
if let strongSelf = self, let item = strongSelf.item {
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
}

View file

@ -439,7 +439,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
if let strongSelf = self {
strongSelf.timeoutTimer?.0.invalidate()
strongSelf.timeoutTimer = nil
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
}, queue: Queue.mainQueue())
strongSelf.timeoutTimer = (timer, timeoutDeadline)

View file

@ -2165,7 +2165,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
guard let item = self.item else {
return
}
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
private func updatePollAddOptionFocused(_ focus: Bool) {
@ -2372,7 +2372,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
item.controllerInteraction.requestOpenMessagePollResults(item.message.id, pollId)
case .anonymous:
self.isPreviewingResults = !self.isPreviewingResults
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
}
} else if !selectedOpaqueIdentifiers.isEmpty {

View file

@ -197,7 +197,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
} else {
self.expandedBlockIds.insert(blockId)
}
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
self.textNode.textNode.requestDisplayContentsUnderSpoilers = { [weak self] location in
guard let self else {
@ -841,7 +841,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.relativeDateTimer = nil
}
strongSelf.relativeDateTimer = (SwiftSignalKit.Timer(timeout: Double(formattedDateUpdatePeriod), repeat: true, completion: { [weak self] in
self?.requestFullUpdate?()
self?.requestFullUpdate?(ControlledTransition(duration: 0.15, curve: .easeInOut, interactive: false))
}, queue: Queue.mainQueue()), formattedDateUpdatePeriod)
strongSelf.relativeDateTimer?.timer.start()
} else if let (timer, _) = strongSelf.relativeDateTimer {
@ -1117,7 +1117,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateAlpha(node: statusNode, alpha: 1.0)
}
self.requestFullUpdate?()
self.requestFullUpdate?(ControlledTransition(duration: 0.15, curve: .easeInOut, interactive: false))
} else {
var requestUpdate = false
let glyphCount = textRevealAnimationState.glyphCount(timestamp: timestamp)
@ -1137,7 +1137,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
}
if requestUpdate {
self.requestFullUpdate?()
self.requestFullUpdate?(ControlledTransition(duration: 0.15, curve: .easeInOut, interactive: false))
}
}
}
@ -1737,7 +1737,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
}
self.displayContentsUnderSpoilers = (value, location)
if let item = self.item {
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
}

View file

@ -152,7 +152,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
}
self.contentNode.requestUpdateLayout = { [weak self] in
if let strongSelf = self, let item = strongSelf.item {
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false)
let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false, nil)
}
}
self.contentNode.defaultContentAction = { [weak self] in

View file

@ -653,7 +653,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, sendGift: { _ in
}, openUniqueGift: { _ in
}, openMessageFeeException: {
}, requestMessageUpdate: { _, _ in
}, requestMessageUpdate: { _, _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {
}, scrollToMessageId: { _ in

View file

@ -500,7 +500,7 @@ public final class ChatSendGroupMediaMessageContextPreview: UIView, ChatSendMess
}, sendGift: { _ in
}, openUniqueGift: { _ in
}, openMessageFeeException: {
}, requestMessageUpdate: { _, _ in
}, requestMessageUpdate: { _, _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {
}, scrollToMessageId: { _ in

View file

@ -288,7 +288,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
public let sendGift: (EnginePeer.Id) -> Void
public let openUniqueGift: (String) -> Void
public let openMessageFeeException: () -> Void
public let requestMessageUpdate: (MessageId, Bool) -> Void
public let requestMessageUpdate: (MessageId, Bool, ControlledTransition?) -> Void
public let cancelInteractiveKeyboardGestures: () -> Void
public let dismissTextInput: () -> Void
public let scrollToMessageId: (MessageIndex) -> Void
@ -465,7 +465,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
sendGift: @escaping (EnginePeer.Id) -> Void,
openUniqueGift: @escaping (String) -> Void,
openMessageFeeException: @escaping () -> Void,
requestMessageUpdate: @escaping (MessageId, Bool) -> Void,
requestMessageUpdate: @escaping (MessageId, Bool, ControlledTransition?) -> Void,
cancelInteractiveKeyboardGestures: @escaping () -> Void,
dismissTextInput: @escaping () -> Void,
scrollToMessageId: @escaping (MessageIndex) -> Void,

View file

@ -1271,7 +1271,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.openPremiumGift()
}, openUniqueGift: { _ in
}, openMessageFeeException: {
}, requestMessageUpdate: { _, _ in
}, requestMessageUpdate: { _, _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {
}, scrollToMessageId: { _ in

View file

@ -4377,7 +4377,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.displayPollSolution(solution: solution, sourceNode: sourceNode, isAutomatic: false)
} else if let messageId = self.controllerInteraction?.currentPollMessageWithTooltip {
self.controllerInteraction?.currentPollMessageWithTooltip = nil
self.controllerInteraction?.requestMessageUpdate(messageId, false)
self.controllerInteraction?.requestMessageUpdate(messageId, false, nil)
}
}, displayPsa: { [weak self] type, sourceNode in
self?.displayPsa(type: type, sourceNode: sourceNode, isAutomatic: false)
@ -5317,9 +5317,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.present(controller, in: .window(.root))
})
}
}, requestMessageUpdate: { [weak self] id, scroll in
}, requestMessageUpdate: { [weak self] id, scroll, customTransition in
if let self {
self.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll)
self.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll, customTransition: customTransition)
}
}, cancelInteractiveKeyboardGestures: { [weak self] in
if let self {
@ -8212,7 +8212,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let messageId = item.message.id
self.controllerInteraction?.currentPollMessageWithTooltip = messageId
self.controllerInteraction?.requestMessageUpdate(messageId, false)
self.controllerInteraction?.requestMessageUpdate(messageId, false, nil)
}
public func displayPromoAnnouncement(text: String) {

View file

@ -4749,7 +4749,7 @@ public final class ChatHistoryListNodeImpl: ListViewImpl, ChatHistoryNode, ChatH
}
}
func requestMessageUpdate(_ id: MessageId, andScrollToItem scroll: Bool = false) {
func requestMessageUpdate(_ id: MessageId, andScrollToItem scroll: Bool = false, customTransition: ControlledTransition? = nil) {
if let historyView = self.historyView {
var messageItem: ChatMessageItem?
self.forEachItemNode({ itemNode in
@ -4795,7 +4795,7 @@ public final class ChatHistoryListNodeImpl: ListViewImpl, ChatHistoryNode, ChatH
scrollToItem = ListViewScrollToItem(index: index, position: .center(.top), animated: true, curve: .Spring(duration: 0.4), directionHint: .Down, displayLink: true)
}
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [updateItem], options: [.AnimateInsertion, .Synchronous], scrollToItem: scrollToItem, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [updateItem], options: [.AnimateInsertion, .Synchronous], scrollToItem: scrollToItem, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, customAnimationTransition: customTransition, updateOpaqueState: nil, completion: { _ in })
break loop
}
case let .MessageGroupEntry(_, messages, presentationData):
@ -4816,7 +4816,7 @@ public final class ChatHistoryListNodeImpl: ListViewImpl, ChatHistoryNode, ChatH
scrollToItem = ListViewScrollToItem(index: index, position: .center(.top), animated: true, curve: .Spring(duration: 0.4), directionHint: .Down, displayLink: true)
}
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [updateItem], options: [.AnimateInsertion, .Synchronous], scrollToItem: scrollToItem, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [updateItem], options: [.AnimateInsertion, .Synchronous], scrollToItem: scrollToItem, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, customAnimationTransition: customTransition, updateOpaqueState: nil, completion: { _ in })
break loop
}
default:

View file

@ -245,7 +245,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
}, sendGift: { _ in
}, openUniqueGift: { _ in
}, openMessageFeeException: {
}, requestMessageUpdate: { _, _ in
}, requestMessageUpdate: { _, _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {
}, scrollToMessageId: { _ in

View file

@ -2554,7 +2554,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
},
openMessageFeeException: {
},
requestMessageUpdate: { _, _ in
requestMessageUpdate: { _, _, _ in
},
cancelInteractiveKeyboardGestures: {
},