Reaction improvements

This commit is contained in:
Ali
2021-12-21 03:46:43 +04:00
parent b4382b6fc0
commit 7021252dc6
51 changed files with 1602 additions and 351 deletions

View File

@@ -24,6 +24,67 @@ private struct FetchControls {
}
final class ChatMessageInteractiveFileNode: ASDisplayNode {
final class Arguments {
let context: AccountContext
let presentationData: ChatPresentationData
let message: Message
let topMessage: Message
let associatedData: ChatMessageItemAssociatedData
let chatLocation: ChatLocation
let attributes: ChatMessageEntryAttributes
let isPinned: Bool
let forcedIsEdited: Bool
let file: TelegramMediaFile
let automaticDownload: Bool
let incoming: Bool
let isRecentActions: Bool
let forcedResourceStatus: FileMediaResourceStatus?
let dateAndStatusType: ChatMessageDateAndStatusType?
let displayReactions: Bool
let messageSelection: Bool?
let constrainedSize: CGSize
init(
context: AccountContext,
presentationData: ChatPresentationData,
message: Message,
topMessage: Message,
associatedData: ChatMessageItemAssociatedData,
chatLocation: ChatLocation,
attributes: ChatMessageEntryAttributes,
isPinned: Bool,
forcedIsEdited: Bool,
file: TelegramMediaFile,
automaticDownload: Bool,
incoming: Bool,
isRecentActions: Bool,
forcedResourceStatus: FileMediaResourceStatus?,
dateAndStatusType: ChatMessageDateAndStatusType?,
displayReactions: Bool,
messageSelection: Bool?,
constrainedSize: CGSize
) {
self.context = context
self.presentationData = presentationData
self.message = message
self.topMessage = topMessage
self.associatedData = associatedData
self.chatLocation = chatLocation
self.attributes = attributes
self.isPinned = isPinned
self.forcedIsEdited = forcedIsEdited
self.file = file
self.automaticDownload = automaticDownload
self.incoming = incoming
self.isRecentActions = isRecentActions
self.forcedResourceStatus = forcedResourceStatus
self.dateAndStatusType = dateAndStatusType
self.displayReactions = displayReactions
self.messageSelection = messageSelection
self.constrainedSize = constrainedSize
}
}
private var selectionNode: FileMessageSelectionNode?
private let titleNode: TextNode
@@ -213,7 +274,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
}
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ topMessage: Message, _ associatedData: ChatMessageItemAssociatedData, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ isPinned: Bool, _ forcedIsEdited: Bool, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool, ListViewItemUpdateAnimation) -> Void))) {
func asyncLayout() -> (Arguments) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool, ListViewItemUpdateAnimation) -> Void))) {
let currentFile = self.file
let titleAsyncLayout = TextNode.asyncLayout(self.titleNode)
@@ -223,11 +284,11 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
let currentMessage = self.message
return { context, presentationData, message, topMessage, associatedData, chatLocation, attributes, isPinned, forcedIsEdited, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, messageSelection, constrainedSize in
return { arguments in
return (CGFloat.greatestFiniteMagnitude, { constrainedSize in
let titleFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 16.0 / 17.0))
let descriptionFont = Font.with(size: floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0), design: .regular, weight: .regular, traits: [.monospacedNumbers])
let durationFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 11.0 / 17.0))
let titleFont = Font.regular(floor(arguments.presentationData.fontSize.baseDisplaySize * 16.0 / 17.0))
let descriptionFont = Font.with(size: floor(arguments.presentationData.fontSize.baseDisplaySize * 13.0 / 17.0), design: .regular, weight: .regular, traits: [.monospacedNumbers])
let durationFont = Font.regular(floor(arguments.presentationData.fontSize.baseDisplaySize * 11.0 / 17.0))
var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
var updatedStatusSignal: Signal<(FileMediaResourceStatus, MediaResourceStatus?), NoError>?
@@ -237,58 +298,58 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
var mediaUpdated = false
if let currentFile = currentFile {
mediaUpdated = file != currentFile
mediaUpdated = arguments.file != currentFile
} else {
mediaUpdated = true
}
var statusUpdated = mediaUpdated
if currentMessage?.id != message.id || currentMessage?.flags != message.flags {
if currentMessage?.id != arguments.message.id || currentMessage?.flags != arguments.message.flags {
statusUpdated = true
}
let hasThumbnail = (!file.previewRepresentations.isEmpty || file.immediateThumbnailData != nil) && !file.isMusic && !file.isVoice
let hasThumbnail = (!arguments.file.previewRepresentations.isEmpty || arguments.file.immediateThumbnailData != nil) && !arguments.file.isMusic && !arguments.file.isVoice
if mediaUpdated {
if largestImageRepresentation(file.previewRepresentations) != nil || file.immediateThumbnailData != nil {
updateImageSignal = chatMessageImageFile(account: context.account, fileReference: .message(message: MessageReference(message), media: file), thumbnail: true)
if largestImageRepresentation(arguments.file.previewRepresentations) != nil || arguments.file.immediateThumbnailData != nil {
updateImageSignal = chatMessageImageFile(account: arguments.context.account, fileReference: .message(message: MessageReference(arguments.message), media: arguments.file), thumbnail: true)
}
updatedFetchControls = FetchControls(fetch: { [weak self] in
if let strongSelf = self {
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: context, message: message, file: file, userInitiated: true).start())
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: arguments.context, message: arguments.message, file: arguments.file, userInitiated: true).start())
}
}, cancel: {
messageMediaFileCancelInteractiveFetch(context: context, messageId: message.id, file: file)
messageMediaFileCancelInteractiveFetch(context: arguments.context, messageId: arguments.message.id, file: arguments.file)
})
}
if statusUpdated {
if message.flags.isSending {
updatedStatusSignal = combineLatest(messageFileMediaResourceStatus(context: context, file: file, message: message, isRecentActions: isRecentActions), messageMediaFileStatus(context: context, messageId: message.id, file: file))
if arguments.message.flags.isSending {
updatedStatusSignal = combineLatest(messageFileMediaResourceStatus(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions), messageMediaFileStatus(context: arguments.context, messageId: arguments.message.id, file: arguments.file))
|> map { resourceStatus, actualFetchStatus -> (FileMediaResourceStatus, MediaResourceStatus?) in
return (resourceStatus, actualFetchStatus)
}
updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: false)
updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false)
} else {
updatedStatusSignal = messageFileMediaResourceStatus(context: context, file: file, message: message, isRecentActions: isRecentActions)
updatedStatusSignal = messageFileMediaResourceStatus(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions)
|> map { resourceStatus -> (FileMediaResourceStatus, MediaResourceStatus?) in
return (resourceStatus, nil)
}
updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: false)
updatedAudioLevelEventsSignal = messageFileMediaPlaybackAudioLevelEvents(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false)
}
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: context, file: file, message: message, isRecentActions: isRecentActions, isGlobalSearch: false)
updatedPlaybackStatusSignal = messageFileMediaPlaybackStatus(context: arguments.context, file: arguments.file, message: arguments.message, isRecentActions: arguments.isRecentActions, isGlobalSearch: false)
}
var consumableContentIcon: UIImage?
for attribute in message.attributes {
for attribute in arguments.message.attributes {
if let attribute = attribute as? ConsumableContentMessageAttribute {
let isConsumed = attribute.consumed
if !isConsumed {
if incoming {
consumableContentIcon = PresentationResourcesChat.chatBubbleConsumableContentIncomingIcon(presentationData.theme.theme)
if arguments.incoming {
consumableContentIcon = PresentationResourcesChat.chatBubbleConsumableContentIncomingIcon(arguments.presentationData.theme.theme)
} else {
consumableContentIcon = PresentationResourcesChat.chatBubbleConsumableContentOutgoingIcon(presentationData.theme.theme)
consumableContentIcon = PresentationResourcesChat.chatBubbleConsumableContentOutgoingIcon(arguments.presentationData.theme.theme)
}
}
break
@@ -303,20 +364,20 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
var isVoice = false
var audioDuration: Int32 = 0
let messageTheme = incoming ? presentationData.theme.theme.chat.message.incoming : presentationData.theme.theme.chat.message.outgoing
let messageTheme = arguments.incoming ? arguments.presentationData.theme.theme.chat.message.incoming : arguments.presentationData.theme.theme.chat.message.outgoing
for attribute in file.attributes {
for attribute in arguments.file.attributes {
if case let .Audio(voice, duration, title, performer, waveform) = attribute {
isAudio = true
if let forcedResourceStatus = forcedResourceStatus, statusUpdated {
if let forcedResourceStatus = arguments.forcedResourceStatus, statusUpdated {
updatedStatusSignal = .single((forcedResourceStatus, nil))
} else if let currentUpdatedStatusSignal = updatedStatusSignal {
updatedStatusSignal = currentUpdatedStatusSignal
|> map { status, _ in
switch status.mediaStatus {
case let .fetchStatus(fetchStatus):
if !voice && !message.flags.isSending {
if !voice && !arguments.message.flags.isSending {
return (FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: status.fetchStatus), nil)
} else {
return (FileMediaResourceStatus(mediaStatus: .fetchStatus(fetchStatus), fetchStatus: status.fetchStatus), nil)
@@ -336,12 +397,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
audioWaveform = AudioWaveform(bitstream: waveform, bitsPerSample: 5)
}
} else {
candidateTitleString = NSAttributedString(string: title ?? (file.fileName ?? "Unknown Track"), font: titleFont, textColor: messageTheme.fileTitleColor)
candidateTitleString = NSAttributedString(string: title ?? (arguments.file.fileName ?? "Unknown Track"), font: titleFont, textColor: messageTheme.fileTitleColor)
let descriptionText: String
if let performer = performer {
descriptionText = performer
} else if let size = file.size {
descriptionText = dataSizeString(size, formatting: DataSizeStringFormatting(chatPresentationData: presentationData))
} else if let size = arguments.file.size {
descriptionText = dataSizeString(size, formatting: DataSizeStringFormatting(chatPresentationData: arguments.presentationData))
} else {
descriptionText = ""
}
@@ -357,15 +418,15 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
if let candidateTitleString = candidateTitleString {
titleString = candidateTitleString
} else if !isVoice {
titleString = NSAttributedString(string: file.fileName ?? "File", font: titleFont, textColor: messageTheme.fileTitleColor)
titleString = NSAttributedString(string: arguments.file.fileName ?? "File", font: titleFont, textColor: messageTheme.fileTitleColor)
}
if let candidateDescriptionString = candidateDescriptionString {
descriptionString = candidateDescriptionString
} else if !isVoice {
let descriptionText: String
if let size = file.size {
descriptionText = dataSizeString(size, formatting: DataSizeStringFormatting(chatPresentationData: presentationData))
if let size = arguments.file.size {
descriptionText = dataSizeString(size, formatting: DataSizeStringFormatting(chatPresentationData: arguments.presentationData))
} else {
descriptionText = ""
}
@@ -383,7 +444,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
let (descriptionLayout, descriptionApply) = descriptionAsyncLayout(TextNodeLayoutArguments(attributedString: descriptionString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let fileSizeString: String
if let _ = file.size {
if let _ = arguments.file.size {
fileSizeString = "000.0 MB"
} else {
fileSizeString = ""
@@ -423,47 +484,54 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
var statusSuggestedWidthAndContinue: (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> Void))?
if let statusType = dateAndStatusType {
if let statusType = arguments.dateAndStatusType {
var edited = false
if attributes.updatingMedia != nil {
if arguments.attributes.updatingMedia != nil {
edited = true
}
var viewCount: Int?
var dateReplies = 0
let dateReactionsAndPeers = mergedMessageReactionsAndPeers(message: topMessage)
for attribute in message.attributes {
let dateReactionsAndPeers = mergedMessageReactionsAndPeers(message: arguments.topMessage)
for attribute in arguments.message.attributes {
if let attribute = attribute as? EditedMessageAttribute {
edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = chatLocation {
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info {
} else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = arguments.chatLocation {
if let channel = arguments.message.peers[arguments.message.id.peerId] as? TelegramChannel, case .group = channel.info {
dateReplies = Int(attribute.count)
}
}
}
if forcedIsEdited {
if arguments.forcedIsEdited {
edited = true
}
let dateText = stringForMessageTimestampStatus(accountPeerId: context.account.peerId, message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings)
let dateText = stringForMessageTimestampStatus(accountPeerId: arguments.context.account.peerId, message: arguments.message, dateTimeFormat: arguments.presentationData.dateTimeFormat, nameDisplayOrder: arguments.presentationData.nameDisplayOrder, strings: arguments.presentationData.strings)
let displayReactionsInline = shouldDisplayInlineDateReactions(message: arguments.message)
var reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings?
if displayReactionsInline || arguments.displayReactions {
reactionSettings = ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: displayReactionsInline, preferAdditionalInset: !displayReactionsInline)
}
statusSuggestedWidthAndContinue = statusLayout(ChatMessageDateAndStatusNode.Arguments(
context: context,
presentationData: presentationData,
context: arguments.context,
presentationData: arguments.presentationData,
edited: edited,
impressionCount: viewCount,
dateText: dateText,
type: statusType,
layoutInput: .trailingContent(contentWidth: iconFrame == nil ? 1000.0 : controlAreaWidth, reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: shouldDisplayInlineDateReactions(message: message), preferAdditionalInset: !shouldDisplayInlineDateReactions(message: message))),
layoutInput: .trailingContent(contentWidth: iconFrame == nil ? 1000.0 : controlAreaWidth, reactionSettings: reactionSettings),
constrainedSize: constrainedSize,
availableReactions: associatedData.availableReactions,
availableReactions: arguments.associatedData.availableReactions,
reactions: dateReactionsAndPeers.reactions,
reactionPeers: dateReactionsAndPeers.peers,
replyCount: dateReplies,
isPinned: isPinned && !associatedData.isInPinnedListMode,
hasAutoremove: message.isSelfExpiring,
canViewReactionList: canViewMessageReactionList(message: message)
isPinned: arguments.isPinned && !arguments.associatedData.isInPinnedListMode,
hasAutoremove: arguments.message.isSelfExpiring,
canViewReactionList: canViewMessageReactionList(message: arguments.message)
))
}
@@ -488,9 +556,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
if hasThumbnail {
fileIconImage = nil
} else {
let principalGraphics = PresentationResourcesChat.principalGraphics(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
let principalGraphics = PresentationResourcesChat.principalGraphics(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper, bubbleCorners: arguments.presentationData.chatBubbleCorners)
fileIconImage = incoming ? principalGraphics.radialIndicatorFileIconIncoming : principalGraphics.radialIndicatorFileIconOutgoing
fileIconImage = arguments.incoming ? principalGraphics.radialIndicatorFileIconIncoming : principalGraphics.radialIndicatorFileIconOutgoing
}
return (minLayoutWidth, { boundingWidth in
@@ -537,7 +605,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
let streamingCacheStatusFrame: CGRect
if (isAudio && !isVoice) || file.previewRepresentations.isEmpty {
if (isAudio && !isVoice) || arguments.file.previewRepresentations.isEmpty {
streamingCacheStatusFrame = CGRect(origin: CGPoint(x: progressFrame.maxX - streamingProgressDiameter + 2.0, y: progressFrame.maxY - streamingProgressDiameter + 2.0), size: CGSize(width: streamingProgressDiameter, height: streamingProgressDiameter))
} else {
streamingCacheStatusFrame = CGRect()
@@ -545,10 +613,10 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
return (fittedLayoutSize, { [weak self] synchronousLoads, animation in
if let strongSelf = self {
strongSelf.context = context
strongSelf.presentationData = presentationData
strongSelf.message = message
strongSelf.file = file
strongSelf.context = arguments.context
strongSelf.presentationData = arguments.presentationData
strongSelf.message = arguments.message
strongSelf.file = arguments.file
let _ = titleApply()
let _ = descriptionApply()
@@ -604,7 +672,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
strongSelf.waveformScrubbingNode?.frame = CGRect(origin: CGPoint(x: 57.0, y: 1.0), size: CGSize(width: boundingWidth - 60.0, height: 15.0))
let waveformColor: UIColor
if incoming {
if arguments.incoming {
if consumableContentIcon != nil {
waveformColor = messageTheme.mediaActiveControlColor
} else {
@@ -680,8 +748,8 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}))
}
strongSelf.waveformNode.displaysAsynchronously = !presentationData.isPreview
strongSelf.statusNode?.displaysAsynchronously = !presentationData.isPreview
strongSelf.waveformNode.displaysAsynchronously = !arguments.presentationData.isPreview
strongSelf.statusNode?.displaysAsynchronously = !arguments.presentationData.isPreview
strongSelf.statusNode?.frame = CGRect(origin: CGPoint(), size: progressFrame.size)
strongSelf.statusContainerNode.frame = progressFrame
@@ -695,14 +763,14 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
if let updatedFetchControls = updatedFetchControls {
let _ = strongSelf.fetchControls.swap(updatedFetchControls)
if automaticDownload {
if arguments.automaticDownload {
updatedFetchControls.fetch()
}
}
let isAnimated = !synchronousLoads
let transition: ContainedViewLayoutTransition = isAnimated ? .animated(duration: 0.2, curve: .spring) : .immediate
if let selection = messageSelection {
if let selection = arguments.messageSelection {
if let streamingStatusNode = strongSelf.streamingStatusNode {
transition.updateAlpha(node: streamingStatusNode, alpha: 0.0)
transition.updateTransformScale(node: streamingStatusNode, scale: 0.2)
@@ -713,14 +781,14 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
selectionNode.updateSelected(selection, animated: isAnimated)
} else {
let type: FileMessageSelectionNode.NodeType
if file.isVoice {
if arguments.file.isVoice {
type = .voice
} else if file.isMusic || file.previewRepresentations.isEmpty {
} else if arguments.file.isMusic || arguments.file.previewRepresentations.isEmpty {
type = .file
} else {
type = .media
}
let selectionNode = FileMessageSelectionNode(theme: presentationData.theme.theme, incoming: incoming, type: type, toggle: { [weak self] value in
let selectionNode = FileMessageSelectionNode(theme: arguments.presentationData.theme.theme, incoming: arguments.incoming, type: type, toggle: { [weak self] value in
self?.toggleSelection(value)
})
strongSelf.selectionNode = selectionNode
@@ -750,7 +818,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
strongSelf.updateStatus(animated: isAnimated)
if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) {
if let forwardInfo = arguments.message.forwardInfo, forwardInfo.flags.contains(.isImported) {
strongSelf.dateAndStatusNode.pressed = {
guard let strongSelf = self else {
return
@@ -1062,12 +1130,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
self.fetchingCompactTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingCompactSize)
}
static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ topMessage: Message, _ associatedData: ChatMessageItemAssociatedData, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ isPinned: Bool, _ forcedIsEdited: Bool, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool, ListViewItemUpdateAnimation) -> ChatMessageInteractiveFileNode))) {
static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (Arguments) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool, ListViewItemUpdateAnimation) -> ChatMessageInteractiveFileNode))) {
let currentAsyncLayout = node?.asyncLayout()
return { context, presentationData, message, topMessage, associatedData, chatLocation, attributes, isPinned, forcedIsEdited, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, messageSelection, constrainedSize in
return { arguments in
var fileNode: ChatMessageInteractiveFileNode
var fileLayout: (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ topMessage: Message, _ associatedData: ChatMessageItemAssociatedData, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ isPinned: Bool, _ forcedIsEdited: Bool, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool, ListViewItemUpdateAnimation) -> Void)))
var fileLayout: (Arguments) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool, ListViewItemUpdateAnimation) -> Void)))
if let node = node, let currentAsyncLayout = currentAsyncLayout {
fileNode = node
@@ -1077,7 +1145,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
fileLayout = fileNode.asyncLayout()
}
let (initialWidth, continueLayout) = fileLayout(context, presentationData, message, topMessage, associatedData, chatLocation, attributes, isPinned, forcedIsEdited, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, messageSelection, constrainedSize)
let (initialWidth, continueLayout) = fileLayout(arguments)
return (initialWidth, { constrainedSize in
let (finalWidth, finalLayout) = continueLayout(constrainedSize)