Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions submodules/Display/Source/ImageNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,39 @@ public struct ImageCorners: Equatable {
public func withRemovedTails() -> ImageCorners {
return ImageCorners(topLeft: self.topLeft.withoutTail, topRight: self.topRight.withoutTail, bottomLeft: self.bottomLeft.withoutTail, bottomRight: self.bottomRight.withoutTail)
}

public var layerCorners: (radius: CGFloat, cornerMask: CACornerMask)? {
guard case .Corner(let topLeftRadius) = topLeft,
case .Corner(let topRightRadius) = topRight,
case .Corner(let bottomLeftRadius) = bottomLeft,
case .Corner(let bottomRightRadius) = bottomRight
else { return nil }

let maxRadius = max(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius)
if maxRadius.isZero { return (.zero, []) }

guard topLeftRadius.isZero || topLeftRadius == maxRadius,
topRightRadius.isZero || topRightRadius == maxRadius,
bottomLeftRadius.isZero || bottomLeftRadius == maxRadius,
bottomRightRadius.isZero || bottomRightRadius == maxRadius
else { return nil }

var maskedCorners: CACornerMask = []
if !topLeftRadius.isZero {
maskedCorners.insert(.layerMinXMinYCorner)
}
if !topRightRadius.isZero {
maskedCorners.insert(.layerMaxXMinYCorner)
}
if !bottomLeftRadius.isZero {
maskedCorners.insert(.layerMaxXMinYCorner)
}
if !bottomRightRadius.isZero {
maskedCorners.insert(.layerMaxXMaxYCorner)
}

return (maxRadius, maskedCorners)
}
}

public func ==(lhs: ImageCorners, rhs: ImageCorners) -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ public class MediaDustNode: ASDisplayNode {
}

public func update(size: CGSize, color: UIColor, transition: ContainedViewLayoutTransition) {
guard size != self.currentParams?.size || color != self.currentParams?.color else { return }
self.currentParams = (size, color)

let bounds = CGRect(origin: .zero, size: size)
Expand Down
12 changes: 9 additions & 3 deletions submodules/PhotoResources/Sources/PhotoResources.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1162,7 +1162,9 @@ public func chatSecretPhoto(account: Account, userLocation: MediaResourceUserLoc

adjustSaturationInContext(context: context, saturation: 1.7)

addCorners(context, arguments: arguments)
if !arguments.corners.isEmpty {
addCorners(context, arguments: arguments)
}

return context
}
Expand Down Expand Up @@ -2154,7 +2156,9 @@ public func chatSecretMessageVideo(account: Account, userLocation: MediaResource
}
}

addCorners(context, arguments: arguments)
if !arguments.corners.isEmpty {
addCorners(context, arguments: arguments)
}

return context
}
Expand Down Expand Up @@ -2969,7 +2973,9 @@ public func playerAlbumArt(postbox: Postbox, engine: TelegramEngine, fileReferen
}
}

addCorners(context, arguments: arguments)
if !arguments.corners.isEmpty {
addCorners(context, arguments: arguments)
}

return context
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ public final class SemanticStatusNode: ASControlNode {
guard let strongSelf = self, ready else {
return
}
let context = transform(TransformImageArguments(corners: ImageCorners(radius: strongSelf.bounds.width / 2.0), imageSize: strongSelf.bounds.size, boundingSize: strongSelf.bounds.size, intrinsicInsets: UIEdgeInsets()))
let context = transform(TransformImageArguments(corners: ImageCorners(), imageSize: strongSelf.bounds.size, boundingSize: strongSelf.bounds.size, intrinsicInsets: UIEdgeInsets()))

let previousAppearanceContext = strongSelf.appearanceContext
strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage())
Expand Down
148 changes: 98 additions & 50 deletions submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,52 @@ private final class ChatMessageBubbleClippingNode: ASDisplayNode {
}
}

private final class DisplaySynchronizer {

private var uiTasks: [() -> Void] = []

private lazy var displayLink: CADisplayLink = {
let displayLink = CADisplayLink(target: self, selector: #selector(performUITasks))
if #available(iOS 15.0, *), UIScreen.main.maximumFramesPerSecond > 60 {
displayLink.preferredFrameRateRange = CAFrameRateRange(minimum: 30.0, maximum: 120.0, preferred: 120.0)
}
displayLink.isPaused = true
displayLink.add(to: .main, forMode: .common)
return displayLink
}()

deinit {
self.displayLink.invalidate()
}

@objc private func performUITasks() {
guard !self.uiTasks.isEmpty else {
self.displayLink.isPaused = true
return
}

let allowedDuration = self.displayLink.duration / 2.0
let deadline = self.displayLink.targetTimestamp
var hasExecuted = false

while !self.uiTasks.isEmpty, !hasExecuted || CACurrentMediaTime() + allowedDuration < deadline {
let task = self.uiTasks.removeFirst()
Queue.mainQueue().async(task)

hasExecuted = true
}
}

fileprivate func scheduleUITask(synchronously: Bool, task: @escaping () -> Void) {
if synchronously {
task()
} else {
self.uiTasks.append(task)
self.displayLink.isPaused = false
}
}
}

func hasCommentButton(item: ChatMessageItem) -> Bool {
let firstMessage = item.content.firstMessage

Expand Down Expand Up @@ -539,6 +585,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode

private var currentSwipeAction: ChatControllerInteractionSwipeAction?

private static let synchronizer = DisplaySynchronizer()

//private let debugNode: ASDisplayNode

override var visibility: ListViewItemNodeVisibility {
Expand Down Expand Up @@ -1071,8 +1119,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
for contentNode in self.contentNodes {
if let message = contentNode.item?.message {
currentContentClassesPropertiesAndLayouts.append((message, type(of: contentNode) as AnyClass, contentNode.supportsMosaic, contentNode.asyncLayoutContent()))
} else {
assertionFailure()
}
}

Expand Down Expand Up @@ -2452,51 +2498,53 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
let disablesComments = !hasInstantVideo

return (layout, { animation, applyInfo, synchronousLoads in
return ChatMessageBubbleItemNode.applyLayout(selfReference: selfReference, animation, synchronousLoads,
params: params,
applyInfo: applyInfo,
layout: layout,
item: item,
forwardSource: forwardSource,
forwardAuthorSignature: forwardAuthorSignature,
accessibilityData: accessibilityData,
actionButtonsSizeAndApply: actionButtonsSizeAndApply,
reactionButtonsSizeAndApply: reactionButtonsSizeAndApply,
updatedMergedTop: updatedMergedTop,
updatedMergedBottom: updatedMergedBottom,
hideBackground: hideBackground,
incoming: incoming,
graphics: graphics,
presentationContext: item.controllerInteraction.presentationContext,
bubbleContentWidth: bubbleContentWidth,
backgroundFrame: backgroundFrame,
deliveryFailedInset: deliveryFailedInset,
nameNodeSizeApply: nameNodeSizeApply,
contentOrigin: contentOrigin,
nameNodeOriginY: nameNodeOriginY,
layoutConstants: layoutConstants,
currentCredibilityIcon: currentCredibilityIcon,
adminNodeSizeApply: adminNodeSizeApply,
contentUpperRightCorner: contentUpperRightCorner,
threadInfoSizeApply: threadInfoSizeApply,
threadInfoOriginY: threadInfoOriginY,
forwardInfoSizeApply: forwardInfoSizeApply,
forwardInfoOriginY: forwardInfoOriginY,
replyInfoSizeApply: replyInfoSizeApply,
replyInfoOriginY: replyInfoOriginY,
removedContentNodeIndices: removedContentNodeIndices,
addedContentNodes: addedContentNodes,
contentNodeMessagesAndClasses: contentNodeMessagesAndClasses,
contentNodeFramesPropertiesAndApply: contentNodeFramesPropertiesAndApply,
contentContainerNodeFrames: contentContainerNodeFrames,
mosaicStatusOrigin: mosaicStatusOrigin,
mosaicStatusSizeAndApply: mosaicStatusSizeAndApply,
needsShareButton: needsShareButton,
shareButtonOffset: shareButtonOffset,
avatarOffset: avatarOffset,
hidesHeaders: hidesHeaders,
disablesComments: disablesComments
)
Self.synchronizer.scheduleUITask(synchronously: synchronousLoads || animation.isAnimated) {
ChatMessageBubbleItemNode.applyLayout(selfReference: selfReference, animation, synchronousLoads,
params: params,
applyInfo: applyInfo,
layout: layout,
item: item,
forwardSource: forwardSource,
forwardAuthorSignature: forwardAuthorSignature,
accessibilityData: accessibilityData,
actionButtonsSizeAndApply: actionButtonsSizeAndApply,
reactionButtonsSizeAndApply: reactionButtonsSizeAndApply,
updatedMergedTop: updatedMergedTop,
updatedMergedBottom: updatedMergedBottom,
hideBackground: hideBackground,
incoming: incoming,
graphics: graphics,
presentationContext: item.controllerInteraction.presentationContext,
bubbleContentWidth: bubbleContentWidth,
backgroundFrame: backgroundFrame,
deliveryFailedInset: deliveryFailedInset,
nameNodeSizeApply: nameNodeSizeApply,
contentOrigin: contentOrigin,
nameNodeOriginY: nameNodeOriginY,
layoutConstants: layoutConstants,
currentCredibilityIcon: currentCredibilityIcon,
adminNodeSizeApply: adminNodeSizeApply,
contentUpperRightCorner: contentUpperRightCorner,
threadInfoSizeApply: threadInfoSizeApply,
threadInfoOriginY: threadInfoOriginY,
forwardInfoSizeApply: forwardInfoSizeApply,
forwardInfoOriginY: forwardInfoOriginY,
replyInfoSizeApply: replyInfoSizeApply,
replyInfoOriginY: replyInfoOriginY,
removedContentNodeIndices: removedContentNodeIndices,
addedContentNodes: addedContentNodes,
contentNodeMessagesAndClasses: contentNodeMessagesAndClasses,
contentNodeFramesPropertiesAndApply: contentNodeFramesPropertiesAndApply,
contentContainerNodeFrames: contentContainerNodeFrames,
mosaicStatusOrigin: mosaicStatusOrigin,
mosaicStatusSizeAndApply: mosaicStatusSizeAndApply,
needsShareButton: needsShareButton,
shareButtonOffset: shareButtonOffset,
avatarOffset: avatarOffset,
hidesHeaders: hidesHeaders,
disablesComments: disablesComments
)
}
})
}

Expand Down Expand Up @@ -3096,15 +3144,15 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
}

assert(sortedContentNodes.count == updatedContentNodes.count)

strongSelf.contentNodes = sortedContentNodes
}

var shouldClipOnTransitions = true
var contentNodeIndex = 0
for (relativeFrame, _, useContentOrigin, apply) in contentNodeFramesPropertiesAndApply {
apply(animation, synchronousLoads, applyInfo)
Self.synchronizer.scheduleUITask(synchronously: synchronousLoads || animation.isAnimated) {
apply(animation, synchronousLoads, applyInfo)
}

if contentNodeIndex >= strongSelf.contentNodes.count {
break
Expand Down
Loading