Reaction improvements

This commit is contained in:
Ali 2022-01-07 21:03:44 +04:00
parent d7a5983255
commit 8f23d13f1b
25 changed files with 314 additions and 215 deletions

View File

@ -602,7 +602,7 @@ public protocol SharedAccountContext: AnyObject {
func makeComposeController(context: AccountContext) -> ViewController
func makeChatListController(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController
func makeChatController(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, botStart: ChatControllerInitialBotStart?, mode: ChatControllerPresentationMode) -> ChatController
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?) -> ListViewItem
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, isCentered: Bool) -> ListViewItem
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController?
func makeContactSelectionController(_ params: ContactSelectionControllerParams) -> ContactSelectionController

View File

@ -13,7 +13,7 @@ import AnimatedAvatarSetNode
import ReactionImageComponent
public final class ReactionIconView: PortalSourceView {
fileprivate let imageView: UIImageView
public let imageView: UIImageView
override public init(frame: CGRect) {
self.imageView = UIImageView()
@ -23,11 +23,11 @@ public final class ReactionIconView: PortalSourceView {
self.addSubview(self.imageView)
}
required init?(coder: NSCoder) {
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(size: CGSize, transition: ContainedViewLayoutTransition) {
public func update(size: CGSize, transition: ContainedViewLayoutTransition) {
transition.updateFrame(view: self.imageView, frame: CGRect(origin: CGPoint(), size: size))
}
}
@ -42,7 +42,6 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
}
struct Counter: Equatable {
var frame: CGRect
var components: [CounterLayout.Component]
}
@ -122,7 +121,19 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
return
}
let image = generateImage(layout.baseSize, rotatedContext: { size, context in
var totalComponentWidth: CGFloat = 0.0
if let counter = layout.counter {
for component in counter.components {
totalComponentWidth += component.bounds.width
}
}
var imageWidth = layout.baseSize.width
while imageWidth < layout.baseSize.height / 2.0 + 1.0 + totalComponentWidth + 8.0 {
imageWidth += 2.0
}
let image = generateImage(CGSize(width: imageWidth, height: layout.baseSize.height), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
@ -143,15 +154,18 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - size.height, y: 0.0), size: CGSize(width: size.height, height: size.height)))
context.fill(CGRect(origin: CGPoint(x: size.height / 2.0, y: 0.0), size: CGSize(width: size.width - size.height, height: size.height)))
#if DEBUG && false
context.setFillColor(UIColor.blue.withAlphaComponent(0.5).cgColor)
context.fill(CGRect(origin: CGPoint(x: layout.baseSize.height / 2.0 + 1.0, y: 0.0), size: CGSize(width: size.width - (layout.baseSize.height / 2.0 + 1.0 + 8.0), height: size.height)))
#endif
if let counter = layout.counter {
context.setBlendMode(foregroundColor.alpha < 1.0 ? .copy : .normal)
let isForegroundTransparent = foregroundColor.alpha < 1.0
context.setBlendMode(isForegroundTransparent ? .copy : .normal)
var totalComponentWidth: CGFloat = 0.0
for component in counter.components {
totalComponentWidth += component.bounds.width
}
//let textAreaWidth = size.width - (layout.baseSize.height / 2.0 + 1.0 + 8.0)
var textOrigin: CGFloat = size.width - counter.frame.width - 8.0 + floorToScreenPixels((counter.frame.width - totalComponentWidth) / 2.0)
var textOrigin: CGFloat = layout.baseSize.height / 2.0 + 1.0
textOrigin = max(textOrigin, layout.baseSize.height / 2.0 + UIScreenPixel)
var rightTextOrigin = textOrigin + totalComponentWidth
@ -186,15 +200,28 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
previousComponentVerticalOffset = -previousComponentVerticalOffset
}
let componentOrigin = rightTextOrigin - previousComponent.bounds.width
let string = NSAttributedString(string: previousComponent.string, font: Font.medium(11.0), textColor: foregroundColor.mixedWith(backgroundColor, alpha: 1.0 - previousComponentAlpha))
var componentOrigin = rightTextOrigin - previousComponent.bounds.width
componentOrigin = max(componentOrigin, layout.baseSize.height / 2.0 + UIScreenPixel)
let previousColor: UIColor
if isForegroundTransparent {
previousColor = foregroundColor.mixedWith(backgroundColor, alpha: 1.0 - previousComponentAlpha)
} else {
previousColor = foregroundColor.withMultipliedAlpha(previousComponentAlpha)
}
let string = NSAttributedString(string: previousComponent.string, font: Font.medium(11.0), textColor: previousColor)
string.draw(at: previousComponent.bounds.origin.offsetBy(dx: componentOrigin, dy: floorToScreenPixels(size.height - previousComponent.bounds.height) / 2.0 + previousComponentVerticalOffset))
}
}
}
let componentOrigin = rightTextOrigin - component.bounds.width
let string = NSAttributedString(string: component.string, font: Font.medium(11.0), textColor: foregroundColor.mixedWith(backgroundColor, alpha: 1.0 - componentAlpha))
let currentColor: UIColor
if isForegroundTransparent {
currentColor = foregroundColor.mixedWith(backgroundColor, alpha: 1.0 - componentAlpha)
} else {
currentColor = foregroundColor.withMultipliedAlpha(componentAlpha)
}
let string = NSAttributedString(string: component.string, font: Font.medium(11.0), textColor: currentColor)
string.draw(at: component.bounds.origin.offsetBy(dx: componentOrigin, dy: floorToScreenPixels(size.height - component.bounds.height) / 2.0 + componentVerticalOffset))
rightTextOrigin -= component.bounds.width
@ -266,7 +293,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
resultComponents.append(Component(string: component, bounds: boundingRect))
if spec.stringComponents.count <= 2 {
if i == spec.stringComponents.count - 1 && component.count == 1 && component[component.startIndex].isNumber {
resultSize.width += CounterLayout.maxDigitWidth
} else {
resultSize.width += boundingRect.width
@ -296,9 +323,9 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
let sideInsets: CGFloat
let imageFrame: CGRect
let imageSize: CGSize
let counterLayout: CounterLayout?
let counterFrame: CGRect?
let backgroundLayout: ContainerButtonNode.Layout
@ -309,8 +336,8 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
backgroundColor: UInt32,
sideInsets: CGFloat,
imageFrame: CGRect,
imageSize: CGSize,
counterLayout: CounterLayout?,
counterFrame: CGRect?,
backgroundLayout: ContainerButtonNode.Layout,
size: CGSize
) {
@ -318,18 +345,19 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
self.backgroundColor = backgroundColor
self.sideInsets = sideInsets
self.imageFrame = imageFrame
self.imageSize = imageSize
self.counterLayout = counterLayout
self.counterFrame = counterFrame
self.backgroundLayout = backgroundLayout
self.size = size
}
static func calculate(spec: Spec, currentLayout: Layout?) -> Layout {
let sideInsets: CGFloat = 8.0
let sideInsets: CGFloat = 11.0
let height: CGFloat = 30.0
let spacing: CGFloat = 4.0
let spacing: CGFloat = 2.0
let defaultImageSize = CGSize(width: 26.0, height: 26.0)
let boundingImageSize = CGSize(width: 20.0, height: 20.0)
let defaultImageSize = CGSize(width: boundingImageSize.width + floor(boundingImageSize.width * 0.5 * 2.0), height: boundingImageSize.height + floor(boundingImageSize.height * 0.5 * 2.0))
let imageSize: CGSize
if let file = spec.component.reaction.centerAnimation {
imageSize = file.dimensions?.cgSize.aspectFitted(defaultImageSize) ?? defaultImageSize
@ -341,15 +369,20 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
for character in countString(Int64(spec.component.count)) {
counterComponents.append(String(character))
}
#if DEBUG && false
counterComponents.removeAll()
for character in "42" {
counterComponents.append(String(character))
}
#endif
let backgroundColor = spec.component.isSelected ? spec.component.colors.selectedBackground : spec.component.colors.deselectedBackground
let imageFrame = CGRect(origin: CGPoint(x: sideInsets, y: floorToScreenPixels((height - imageSize.height) / 2.0)), size: imageSize)
let imageFrame = CGRect(origin: CGPoint(x: sideInsets + floorToScreenPixels((boundingImageSize.width - imageSize.width) / 2.0), y: floorToScreenPixels((height - imageSize.height) / 2.0)), size: imageSize)
var counterLayout: CounterLayout?
var counterFrame: CGRect?
var size = CGSize(width: imageSize.width + sideInsets * 2.0, height: height)
var size = CGSize(width: boundingImageSize.width + sideInsets * 2.0, height: height)
if !spec.component.avatarPeers.isEmpty {
size.width += 4.0 + 24.0
if spec.component.avatarPeers.count > 1 {
@ -372,7 +405,6 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
}
counterLayout = counterValue
size.width += spacing + counterValue.size.width
counterFrame = CGRect(origin: CGPoint(x: size.width - sideInsets - counterValue.size.width, y: floorToScreenPixels((height - counterValue.size.height) / 2.0)), size: counterValue.size)
}
let backgroundColors = ReactionButtonAsyncNode.ContainerButtonNode.Colors(
@ -382,15 +414,14 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
extractedForeground: spec.component.colors.extractedForeground
)
var backgroundCounter: ReactionButtonAsyncNode.ContainerButtonNode.Counter?
if let counterLayout = counterLayout, let counterFrame = counterFrame {
if let counterLayout = counterLayout {
backgroundCounter = ReactionButtonAsyncNode.ContainerButtonNode.Counter(
frame: counterFrame,
components: counterLayout.components
)
}
let backgroundLayout = ContainerButtonNode.Layout(
colors: backgroundColors,
baseSize: CGSize(width: height + 18.0, height: height),
baseSize: CGSize(width: height + 2.0, height: height),
counter: backgroundCounter
)
@ -399,8 +430,8 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
backgroundColor: backgroundColor,
sideInsets: sideInsets,
imageFrame: imageFrame,
imageSize: boundingImageSize,
counterLayout: counterLayout,
counterFrame: counterFrame,
backgroundLayout: backgroundLayout,
size: size
)
@ -492,7 +523,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
if self.layout?.spec.component.reaction != layout.spec.component.reaction {
if let file = layout.spec.component.reaction.centerAnimation {
self.iconImageDisposable.set((reactionStaticImage(context: layout.spec.component.context, animation: file, pixelSize: CGSize(width: 72.0, height: 72.0))
self.iconImageDisposable.set((reactionStaticImage(context: layout.spec.component.context, animation: file, pixelSize: CGSize(width: 64.0 * UIScreenScale, height: 64.0 * UIScreenScale))
|> deliverOnMainQueue).start(next: { [weak self] data in
guard let strongSelf = self else {
return
@ -526,7 +557,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
animation: animation,
synchronousLoad: false
)
animation.animator.updateFrame(layer: avatarsView.layer, frame: CGRect(origin: CGPoint(x: layout.imageFrame.maxX + 4.0, y: floorToScreenPixels((layout.size.height - avatarsSize.height) / 2.0)), size: CGSize(width: avatarsSize.width, height: avatarsSize.height)), completion: nil)
animation.animator.updateFrame(layer: avatarsView.layer, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(layout.imageFrame.midX + layout.imageSize.width / 2.0) + 4.0, y: floorToScreenPixels((layout.size.height - avatarsSize.height) / 2.0)), size: CGSize(width: avatarsSize.width, height: avatarsSize.height)), completion: nil)
} else if let avatarsView = self.avatarsView {
self.avatarsView = nil
if animation.isAnimated {

View File

@ -13,7 +13,7 @@ import RLottieBinding
import GZip
public func reactionStaticImage(context: AccountContext, animation: TelegramMediaFile, pixelSize: CGSize) -> Signal<EngineMediaResource.ResourceData, NoError> {
return context.engine.resources.custom(id: "\(animation.resource.id.stringRepresentation):reaction-static-v7", fetch: EngineMediaResource.Fetch {
return context.engine.resources.custom(id: "\(animation.resource.id.stringRepresentation):reaction-static-\(pixelSize.width)x\(pixelSize.height)-v10", fetch: EngineMediaResource.Fetch {
return Signal { subscriber in
let fetchDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: MediaResourceReference.standalone(resource: animation.resource)).start()
let dataDisposable = context.account.postbox.mediaBox.resourceData(animation.resource).start(next: { data in
@ -30,25 +30,14 @@ public func reactionStaticImage(context: AccountContext, animation: TelegramMedi
return
}
let innerInsets = UIEdgeInsets(top: 2.4, left: 2.4, bottom: 2.4, right: 2.4)
let renderContext = DrawingContext(size: CGSize(width: floor(pixelSize.width * (1.0 + innerInsets.left + innerInsets.right)), height: floor(pixelSize.height * (1.0 + innerInsets.bottom + innerInsets.top))), scale: 1.0, clear: true)
let renderContext = DrawingContext(size: pixelSize, scale: 1.0, clear: true)
instance.renderFrame(with: Int32(instance.frameCount - 1), into: renderContext.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(renderContext.size.width * renderContext.scale), height: Int32(renderContext.size.height * renderContext.scale), bytesPerRow: Int32(renderContext.bytesPerRow))
guard let image = renderContext.generateImage() else {
return
}
let clippingContext = DrawingContext(size: pixelSize, scale: 1.0, clear: true)
clippingContext.withContext { context in
UIGraphicsPushContext(context)
image.draw(at: CGPoint(x: -innerInsets.left * pixelSize.width, y: -innerInsets.top * pixelSize.height))
UIGraphicsPopContext()
}
guard let pngData = clippingContext.generateImage()?.pngData() else {
guard let pngData = image.pngData() else {
return
}

View File

@ -840,6 +840,10 @@ public class TextNode: ASDisplayNode {
self.clipsToBounds = false
}
override public func didLoad() {
super.didLoad()
}
public func attributesAtPoint(_ point: CGPoint, orNearest: Bool = false) -> (Int, [NSAttributedString.Key: Any])? {
if let cachedLayout = self.cachedLayout {
return cachedLayout.attributesAtPoint(point, orNearest: orNearest)

View File

@ -209,6 +209,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
let rowHeight: CGFloat = 30.0
let visibleBounds = self.scrollNode.view.bounds
let appearBounds = self.scrollNode.view.bounds.insetBy(dx: 16.0, dy: 0.0)
self.previewingItemContainer.bounds = visibleBounds
let highlightedReactionIndex = self.items.firstIndex(where: { $0.reaction == self.highlightedReaction })
@ -223,9 +224,9 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
var baseItemFrame = CGRect(origin: CGPoint(x: sideInset + column * (itemSize + itemSpacing), y: verticalInset + floor((rowHeight - itemSize) / 2.0) + itemOffsetY), size: CGSize(width: itemSize, height: itemSize))
if let highlightedReactionIndex = highlightedReactionIndex {
if i > highlightedReactionIndex {
baseItemFrame.origin.x += 4.0
baseItemFrame.origin.x += 8.0
} else if i == highlightedReactionIndex {
baseItemFrame.origin.x += 2.0
baseItemFrame.origin.x += 4.0
}
}
@ -238,6 +239,9 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
let updatedSize = CGSize(width: floor(itemFrame.width * 1.66), height: floor(itemFrame.height * 1.66))
itemFrame = CGRect(origin: CGPoint(x: itemFrame.midX - updatedSize.width / 2.0, y: itemFrame.maxY + 4.0 - updatedSize.height), size: updatedSize)
isPreviewing = true
} else if self.highlightedReaction != nil {
let updatedSize = CGSize(width: floor(itemFrame.width * 0.9), height: floor(itemFrame.height * 0.9))
itemFrame = CGRect(origin: CGPoint(x: itemFrame.midX - updatedSize.width / 2.0, y: itemFrame.midY - updatedSize.height / 2.0), size: updatedSize)
}
var animateIn = false
@ -276,7 +280,13 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
itemNode.updateLayout(size: itemFrame.size, isExpanded: false, isPreviewing: isPreviewing, transition: transition)
if animateIn {
itemNode.appear(animated: !self.context.sharedContext.currentPresentationData.with({ $0 }).reduceMotion)
if !self.context.sharedContext.currentPresentationData.with({ $0 }).reduceMotion {
if appearBounds.intersects(baseItemFrame) {
itemNode.appear(animated: true)
}
} else {
itemNode.appear(animated: false)
}
}
}
}
@ -498,15 +508,9 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
let selfSourceRect = itemNode.view.convert(itemNode.view.bounds, to: self.view)
let selfTargetRect = self.view.convert(targetView.bounds, from: targetView)
let expandedSize: CGSize
if targetView.bounds.width < 20.0 {
expandedSize = CGSize(width: 21.0, height: 21.0)
} else {
expandedSize = CGSize(width: 32.0, height: 32.0)
}
let expandedSize: CGSize = selfTargetRect.size
var expandedFrame = CGRect(origin: CGPoint(x: floor(selfTargetRect.midX - expandedSize.width / 2.0), y: floor(selfTargetRect.midY - expandedSize.height / 2.0)), size: expandedSize)
expandedFrame.origin.y += UIScreenPixel
let expandedFrame = CGRect(origin: CGPoint(x: selfTargetRect.midX - expandedSize.width / 2.0, y: selfTargetRect.midY - expandedSize.height / 2.0), size: expandedSize)
let effectFrame = expandedFrame.insetBy(dx: -60.0, dy: -60.0)
@ -727,6 +731,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
public final class StandaloneReactionAnimation: ASDisplayNode {
private var itemNode: ReactionNode? = nil
private var itemNodeIsEmbedded: Bool = false
private let hapticFeedback = HapticFeedback()
private var isCancelled: Bool = false
@ -760,24 +765,25 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
}
self.itemNode = itemNode
self.addSubnode(itemNode)
if hideNode {
targetView.isHidden = true
if let targetView = targetView as? ReactionIconView {
self.itemNodeIsEmbedded = true
targetView.addSubnode(itemNode)
targetView.imageView.isHidden = true
} else {
self.addSubnode(itemNode)
if hideNode {
targetView.isHidden = true
}
}
itemNode.isExtracted = true
let selfTargetRect = self.view.convert(targetView.bounds, from: targetView)
let expandedSize: CGSize
if targetView.bounds.width < 20.0 {
expandedSize = CGSize(width: 21.0, height: 21.0)
} else {
expandedSize = CGSize(width: 32.0, height: 32.0)
}
let expandedSize: CGSize = selfTargetRect.size
var expandedFrame = CGRect(origin: CGPoint(x: floor(selfTargetRect.midX - expandedSize.width / 2.0), y: floor(selfTargetRect.midY - expandedSize.height / 2.0)), size: expandedSize)
expandedFrame.origin.y += UIScreenPixel
let expandedFrame = CGRect(origin: CGPoint(x: selfTargetRect.midX - expandedSize.width / 2.0, y: selfTargetRect.midY - expandedSize.height / 2.0), size: expandedSize)
let effectFrame = expandedFrame.insetBy(dx: -60.0, dy: -60.0)
@ -789,17 +795,21 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
sourceSnapshotView?.removeFromSuperview()
})
self.addSubnode(itemNode)
itemNode.frame = expandedFrame
itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, isPreviewing: false, transition: .immediate)
itemNode.layer.animateSpring(from: (selfTargetRect.width / expandedFrame.width) as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
if targetView.bounds.width < 20.0 {
itemNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.15)
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
if self.itemNodeIsEmbedded {
itemNode.frame = targetView.bounds
} else {
itemNode.frame = expandedFrame
itemNode.layer.animateSpring(from: (selfTargetRect.width / expandedFrame.width) as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4)
if targetView.bounds.width < 25.0 {
itemNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.15)
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
}
}
itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, isPreviewing: false, transition: .immediate)
let additionalAnimationNode = AnimatedStickerNode()
additionalAnimationNode.setup(source: AnimatedStickerResourceSource(account: itemNode.context.account, resource: itemNode.item.applicationAnimation.resource), width: Int(effectFrame.width * 2.0), height: Int(effectFrame.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: itemNode.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(itemNode.item.applicationAnimation.resource.id)))
additionalAnimationNode.frame = effectFrame
@ -824,10 +834,24 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
intermediateCompletion()
return
}
strongSelf.animateFromItemNodeToReaction(itemNode: itemNode, targetView: targetView, hideNode: hideNode, completion: {
mainAnimationCompleted = true
intermediateCompletion()
})
if let targetView = strongSelf.targetView {
if let targetView = targetView as? ReactionIconView {
targetView.imageView.isHidden = false
} else {
if strongSelf.hideNode {
targetView.alpha = 1.0
targetView.isHidden = false
}
}
}
if strongSelf.itemNodeIsEmbedded {
strongSelf.itemNode?.removeFromSupernode()
}
mainAnimationCompleted = true
intermediateCompletion()
}
}
@ -838,17 +862,14 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
beginDismissAnimation()
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1 * UIView.animationDurationFactor(), execute: {
additionalAnimationNode.visibility = true
})
additionalAnimationNode.visibility = true
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0, execute: {
beginDismissAnimation()
})
}
private func animateFromItemNodeToReaction(itemNode: ReactionNode, targetView: UIView, hideNode: Bool, completion: @escaping () -> Void) {
/*private func animateFromItemNodeToReaction(itemNode: ReactionNode, targetView: UIView, hideNode: Bool, completion: @escaping () -> Void) {
if "".isEmpty {
if hideNode {
targetView.alpha = 1.0
@ -903,7 +924,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
})
itemNode.layer.animateScale(from: 1.0, to: (targetSnapshotView.bounds.width * 1.0) / itemNode.bounds.width, duration: duration, removeOnCompletion: false)
}
}*/
public func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition) {
self.bounds = self.bounds.offsetBy(dx: 0.0, dy: offset.y)
@ -914,8 +935,16 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
self.isCancelled = true
if let targetView = self.targetView, self.hideNode {
targetView.alpha = 1.0
targetView.isHidden = false
if let targetView = targetView as? ReactionIconView {
targetView.imageView.isHidden = false
} else {
targetView.alpha = 1.0
targetView.isHidden = false
}
}
if self.itemNodeIsEmbedded {
self.itemNode?.removeFromSupernode()
}
}
}

View File

@ -120,8 +120,7 @@ final class ReactionNode: ASDisplayNode {
var animationFrame = CGRect(origin: CGPoint(x: floor((intrinsicSize.width - animationDisplaySize.width) / 2.0), y: floor((intrinsicSize.height - animationDisplaySize.height) / 2.0)), size: animationDisplaySize)
animationFrame.origin.y = floor(animationFrame.origin.y + animationFrame.height * offsetFactor)
let expandedInset: CGFloat = floor(size.width / 32.0 * 60.0)
let expandedAnimationFrame = animationFrame.insetBy(dx: -expandedInset, dy: -expandedInset)
let expandedAnimationFrame = animationFrame
if isExpanded, self.animationNode == nil {
let animationNode = AnimatedStickerNode()
@ -176,16 +175,19 @@ final class ReactionNode: ASDisplayNode {
}
animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.17, execute: {
animationNode.visibility = true
})
} else {
if let stillAnimationNode = self.stillAnimationNode {
self.stillAnimationNode = nil
stillAnimationNode.removeFromSupernode()
}
self.staticAnimationNode.isHidden = true
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.17, execute: {
animationNode.visibility = true
})
}
}
if self.validSize != size {

View File

@ -170,20 +170,20 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
let width: CGFloat
if case .regular = layout.metrics.widthClass {

View File

@ -149,7 +149,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName, psaType: nil, flags: [])
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil)
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, isCentered: false)
var node: ListViewItemNode?
if let current = currentNode {

View File

@ -16,13 +16,16 @@ import ReactionImageComponent
private final class QuickReactionSetupControllerArguments {
let context: AccountContext
let selectItem: (String) -> Void
let toggleReaction: () -> Void
init(
context: AccountContext,
selectItem: @escaping (String) -> Void
selectItem: @escaping (String) -> Void,
toggleReaction: @escaping () -> Void
) {
self.context = context
self.selectItem = selectItem
self.toggleReaction = toggleReaction
}
}
@ -141,7 +144,10 @@ private enum QuickReactionSetupControllerEntry: ItemListNodeEntry {
dateTimeFormat: dateTimeFormat,
nameDisplayOrder: nameDisplayOrder,
availableReactions: availableReactions,
reaction: reaction
reaction: reaction,
toggleReaction: {
arguments.toggleReaction()
}
)
case let .demoDescription(text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
@ -167,13 +173,15 @@ private enum QuickReactionSetupControllerEntry: ItemListNodeEntry {
}
private struct QuickReactionSetupControllerState: Equatable {
var hasReaction: Bool = false
}
private func quickReactionSetupControllerEntries(
presentationData: PresentationData,
availableReactions: AvailableReactions?,
images: [String: UIImage],
reactionSettings: ReactionSettings
reactionSettings: ReactionSettings,
state: QuickReactionSetupControllerState
) -> [QuickReactionSetupControllerEntry] {
var entries: [QuickReactionSetupControllerEntry] = []
@ -186,7 +194,7 @@ private func quickReactionSetupControllerEntries(
dateTimeFormat: presentationData.dateTimeFormat,
nameDisplayOrder: presentationData.nameDisplayOrder,
availableReactions: availableReactions,
reaction: reactionSettings.quickReaction
reaction: state.hasReaction ? reactionSettings.quickReaction : nil
))
entries.append(.demoDescription(presentationData.strings.Settings_QuickReactionSetup_DemoInfo))
@ -224,14 +232,24 @@ public func quickReactionSetupController(
var dismissImpl: (() -> Void)?
let _ = dismissImpl
let _ = updateState
let actionsDisposable = DisposableSet()
let arguments = QuickReactionSetupControllerArguments(
context: context,
selectItem: { reaction in
selectItem: { reaction in
updateState { state in
var state = state
state.hasReaction = false
return state
}
let _ = context.engine.stickers.updateQuickReaction(reaction: reaction).start()
},
toggleReaction: {
updateState { state in
var state = state
state.hasReaction = !state.hasReaction
return state
}
}
)
@ -297,14 +315,15 @@ public func quickReactionSetupController(
images
)
|> deliverOnMainQueue
|> map { presentationData, _, availableReactions, settings, images -> (ItemListControllerState, (ItemListNodeState, Any)) in
|> map { presentationData, state, availableReactions, settings, images -> (ItemListControllerState, (ItemListNodeState, Any)) in
let title: String = presentationData.strings.Settings_QuickReactionSetup_Title
let entries = quickReactionSetupControllerEntries(
presentationData: presentationData,
availableReactions: availableReactions,
images: images,
reactionSettings: settings
reactionSettings: settings,
state: state
)
let controllerState = ItemListControllerState(

View File

@ -11,7 +11,6 @@ import ItemListUI
import PresentationDataUtils
import AccountContext
import WallpaperBackgroundNode
import AvatarNode
import ReactionSelectionNode
class ReactionChatPreviewItem: ListViewItem, ItemListItem {
@ -26,8 +25,9 @@ class ReactionChatPreviewItem: ListViewItem, ItemListItem {
let nameDisplayOrder: PresentationPersonNameOrder
let availableReactions: AvailableReactions?
let reaction: String?
let toggleReaction: () -> Void
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, availableReactions: AvailableReactions?, reaction: String?) {
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, availableReactions: AvailableReactions?, reaction: String?, toggleReaction: @escaping () -> Void) {
self.context = context
self.theme = theme
self.strings = strings
@ -39,6 +39,7 @@ class ReactionChatPreviewItem: ListViewItem, ItemListItem {
self.nameDisplayOrder = nameDisplayOrder
self.availableReactions = availableReactions
self.reaction = reaction
self.toggleReaction = toggleReaction
}
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
@ -80,7 +81,6 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
private let topStripeNode: ASDisplayNode
private let bottomStripeNode: ASDisplayNode
private let maskNode: ASImageNode
private let avatarNode: ASImageNode
private let containerNode: ASDisplayNode
@ -98,8 +98,6 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
self.maskNode = ASImageNode()
self.avatarNode = ASImageNode()
self.containerNode = ASDisplayNode()
self.containerNode.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
@ -107,7 +105,6 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
self.clipsToBounds = true
self.addSubnode(self.avatarNode)
self.addSubnode(self.containerNode)
}
@ -127,45 +124,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
switch gesture {
case .doubleTap:
if let item = self.item, let updatedReaction = item.reaction, let availableReactions = item.availableReactions, let messageNode = self.messageNode as? ChatMessageItemNodeProtocol {
if let targetView = messageNode.targetReactionView(value: updatedReaction) {
for reaction in availableReactions.reactions {
if reaction.value == updatedReaction {
if let standaloneReactionAnimation = self.standaloneReactionAnimation {
standaloneReactionAnimation.cancel()
standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in
standaloneReactionAnimation?.removeFromSupernode()
})
self.standaloneReactionAnimation = nil
}
if let supernode = self.supernode {
let standaloneReactionAnimation = StandaloneReactionAnimation()
self.standaloneReactionAnimation = standaloneReactionAnimation
supernode.addSubnode(standaloneReactionAnimation)
standaloneReactionAnimation.frame = supernode.bounds
standaloneReactionAnimation.animateReactionSelection(
context: item.context, theme: item.theme, reaction: ReactionContextItem(
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
appearAnimation: reaction.appearAnimation,
stillAnimation: reaction.selectAnimation,
listAnimation: reaction.activateAnimation,
applicationAnimation: reaction.effectAnimation
),
targetView: targetView,
hideNode: true,
completion: { [weak standaloneReactionAnimation] in
standaloneReactionAnimation?.removeFromSupernode()
}
)
}
break
}
}
}
}
self.item?.toggleReaction()
default:
break
}
@ -175,9 +134,59 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
}
}
private func beginReactionAnimation() {
if let item = self.item, let updatedReaction = item.reaction, let availableReactions = item.availableReactions, let messageNode = self.messageNode as? ChatMessageItemNodeProtocol {
if let targetView = messageNode.targetReactionView(value: updatedReaction) {
for reaction in availableReactions.reactions {
guard let centerAnimation = reaction.centerAnimation else {
continue
}
guard let aroundAnimation = reaction.aroundAnimation else {
continue
}
if reaction.value == updatedReaction {
if let standaloneReactionAnimation = self.standaloneReactionAnimation {
standaloneReactionAnimation.cancel()
standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in
standaloneReactionAnimation?.removeFromSupernode()
})
self.standaloneReactionAnimation = nil
}
if let supernode = self.supernode {
let standaloneReactionAnimation = StandaloneReactionAnimation()
self.standaloneReactionAnimation = standaloneReactionAnimation
supernode.addSubnode(standaloneReactionAnimation)
standaloneReactionAnimation.frame = supernode.bounds
standaloneReactionAnimation.animateReactionSelection(
context: item.context, theme: item.theme, reaction: ReactionContextItem(
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
appearAnimation: reaction.appearAnimation,
stillAnimation: reaction.selectAnimation,
listAnimation: centerAnimation,
applicationAnimation: aroundAnimation
),
targetView: targetView,
hideNode: true,
completion: { [weak standaloneReactionAnimation] in
standaloneReactionAnimation?.removeFromSupernode()
}
)
}
break
}
}
}
}
}
func asyncLayout() -> (_ item: ReactionChatPreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let currentNode = self.messageNode
let previousItem = self.item
var currentBackgroundNode = self.backgroundNode
return { item, params, neighbors in
@ -190,13 +199,12 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
let insets: UIEdgeInsets
let separatorHeight = UIScreenPixel
let chatPeerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(1))
let userPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2))
let chatPeerId = userPeerId
var peers = SimpleDictionary<PeerId, Peer>()
let messages = SimpleDictionary<MessageId, Message>()
peers[chatPeerId] = TelegramGroup(id: chatPeerId, title: "Chat", photo: [], participantCount: 1, role: .member, membership: .Member, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 1, version: 1)
peers[userPeerId] = TelegramUser(id: userPeerId, accessHash: nil, firstName: item.strings.Settings_QuickReactionSetup_DemoMessageAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let messageText = item.strings.Settings_QuickReactionSetup_DemoMessageText
@ -206,12 +214,12 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
attributes.append(ReactionsMessageAttribute(canViewList: false, reactions: [MessageReaction(value: reaction, count: 1, isSelected: true)], recentPeers: []))
}
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions)
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, isCentered: true)
var node: ListViewItemNode?
if let current = currentNode {
node = current
messageItem.updateNode(async: { $0() }, node: { return current }, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in
messageItem.updateNode(async: { $0() }, node: { return current }, params: params, previousItem: nil, nextItem: nil, animation: .System(duration: 0.3, transition: ControlledTransition(duration: 0.3, curve: .easeInOut, interactive: false)), completion: { (layout, apply) in
let nodeFrame = CGRect(origin: current.frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height))
current.contentSize = layout.contentSize
@ -228,7 +236,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
node?.isUserInteractionEnabled = false
}
var contentSize = CGSize(width: params.width, height: 8.0 + 8.0)
var contentSize = CGSize(width: params.width, height: 16.0 + 16.0)
if let node = node {
contentSize.height += node.frame.size.height
}
@ -252,7 +260,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
var topOffset: CGFloat = 8.0
var topOffset: CGFloat = 16.0
if let node = node {
strongSelf.messageNode = node
if node.supernode == nil {
@ -260,23 +268,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
}
node.updateFrame(CGRect(origin: CGPoint(x: 0.0, y: topOffset), size: node.frame.size), within: layout.contentSize)
let avatarSize: CGFloat = 34.0
if strongSelf.avatarNode.image == nil {
strongSelf.avatarNode.image = generateImage(CGSize(width: avatarSize, height: avatarSize), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.addEllipse(in: CGRect(origin: CGPoint(), size: size))
context.clip()
UIGraphicsPushContext(context)
if let image = UIImage(bundleImageName: "Avatar/SampleAvatar1") {
image.draw(in: CGRect(origin: CGPoint(), size: size))
}
UIGraphicsPopContext()
})
}
topOffset += node.frame.size.height
strongSelf.avatarNode.frame = CGRect(origin: CGPoint(x: params.leftInset + 7.0, y: topOffset - avatarSize), size: CGSize(width: avatarSize, height: avatarSize))
}
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
@ -333,6 +325,10 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
if let previousItem = previousItem, previousItem.reaction != item.reaction {
strongSelf.beginReactionAnimation()
}
}
})
}

View File

@ -413,20 +413,20 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
let width: CGFloat
if case .regular = layout.metrics.widthClass {

View File

@ -1043,7 +1043,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
return state
}, animated: true)
}, clickThroughMessage: {
}, backgroundNode: self.backgroundNode, availableReactions: nil)
}, backgroundNode: self.backgroundNode, availableReactions: nil, isCentered: false)
return item
}

View File

@ -593,7 +593,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
sampleMessages.append(message8)
items = sampleMessages.reversed().map { message in
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperNode, availableReactions: nil)
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperNode, availableReactions: nil, isCentered: false)
}
let width: CGFloat

View File

@ -160,7 +160,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
}
let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil))
items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, isCentered: false))
}
var nodes: [ListViewItemNode] = []

View File

@ -1103,10 +1103,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
let theme = self.presentationData.theme.withUpdated(preview: true)
let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, isCentered: false))
let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil))
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, isCentered: false))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let messageNodes = self.messageNodes {

View File

@ -1016,7 +1016,7 @@ final class MessageStoryRenderer {
let theme = self.presentationData.theme.withUpdated(preview: true)
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil, availableReactions: nil)]
let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil, availableReactions: nil, isCentered: false)]
let inset: CGFloat = 16.0
let width = layout.size.width - inset * 2.0

View File

@ -605,6 +605,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.commitPurposefulAction()
strongSelf.dismissAllTooltips()
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
var openMessageByAction = false
var isLocation = false
for media in message.media {

View File

@ -88,7 +88,7 @@ func chatHistoryEntriesForView(
if let maybeJoinMessage = joinMessage {
if message.timestamp > maybeJoinMessage.timestamp, (!view.holeEarlier || count > 0) {
entries.append(.MessageEntry(maybeJoinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false)))
entries.append(.MessageEntry(maybeJoinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false)))
joinMessage = nil
}
}
@ -154,7 +154,7 @@ func chatHistoryEntriesForView(
} else {
selection = .none
}
groupBucket.append((message, isRead, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: false), entry.location))
groupBucket.append((message, isRead, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: false, isCentered: false), entry.location))
} else {
let selection: ChatHistoryMessageSelection
if let selectedMessages = selectedMessages {
@ -162,7 +162,7 @@ func chatHistoryEntriesForView(
} else {
selection = .none
}
entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId)))
entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId, isCentered: false)))
}
} else {
let selection: ChatHistoryMessageSelection
@ -171,7 +171,7 @@ func chatHistoryEntriesForView(
} else {
selection = .none
}
entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId)))
entries.append(.MessageEntry(message, presentationData, isRead, entry.location, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: entry.attributes.authorIsContact, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: message.index == associatedData.currentlyPlayingMessageId, isCentered: false)))
}
}
@ -181,7 +181,7 @@ func chatHistoryEntriesForView(
}
if let maybeJoinMessage = joinMessage, !view.holeLater {
entries.append(.MessageEntry(maybeJoinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false)))
entries.append(.MessageEntry(maybeJoinMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false)))
joinMessage = nil
}
@ -227,11 +227,11 @@ func chatHistoryEntriesForView(
if messages.count > 1, let groupInfo = messages[0].groupInfo {
var groupMessages: [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes, MessageHistoryEntryLocation?)] = []
for message in messages {
groupMessages.append((message, false, .none, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: false), nil))
groupMessages.append((message, false, .none, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id], isPlaying: false, isCentered: false), nil))
}
entries.insert(.MessageGroupEntry(groupInfo, groupMessages, presentationData), at: 0)
} else {
entries.insert(.MessageEntry(messages[0], presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[messages[0].id], isPlaying: false)), at: 0)
entries.insert(.MessageEntry(messages[0], presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[messages[0].id], isPlaying: false, isCentered: false)), at: 0)
}
let replyCount = view.entries.isEmpty ? 0 : 1
@ -327,7 +327,7 @@ func chatHistoryEntriesForView(
associatedMessageIds: message.associatedMessageIds
)
nextAdMessageId += 1
entries.append(.MessageEntry(updatedMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false)))
entries.append(.MessageEntry(updatedMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false)))
}
}
}

View File

@ -12,18 +12,20 @@ public enum ChatMessageEntryContentType {
}
public struct ChatMessageEntryAttributes: Equatable {
let rank: CachedChannelAdminRank?
let isContact: Bool
let contentTypeHint: ChatMessageEntryContentType
let updatingMedia: ChatUpdatingMessageMedia?
let isPlaying: Bool
var rank: CachedChannelAdminRank?
var isContact: Bool
var contentTypeHint: ChatMessageEntryContentType
var updatingMedia: ChatUpdatingMessageMedia?
var isPlaying: Bool
var isCentered: Bool
init(rank: CachedChannelAdminRank?, isContact: Bool, contentTypeHint: ChatMessageEntryContentType, updatingMedia: ChatUpdatingMessageMedia?, isPlaying: Bool) {
init(rank: CachedChannelAdminRank?, isContact: Bool, contentTypeHint: ChatMessageEntryContentType, updatingMedia: ChatUpdatingMessageMedia?, isPlaying: Bool, isCentered: Bool) {
self.rank = rank
self.isContact = isContact
self.contentTypeHint = contentTypeHint
self.updatingMedia = updatingMedia
self.isPlaying = isPlaying
self.isCentered = isCentered
}
public init() {
@ -32,6 +34,7 @@ public struct ChatMessageEntryAttributes: Equatable {
self.contentTypeHint = .generic
self.updatingMedia = nil
self.isPlaying = false
self.isCentered = false
}
}

View File

@ -2568,6 +2568,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
strongSelf.forEachVisibleItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item, let updatedReaction = newIncomingReactions[item.content.firstMessage.id], let availableReactions = item.associatedData.availableReactions, let targetView = itemNode.targetReactionView(value: updatedReaction) {
for reaction in availableReactions.reactions {
guard let centerAnimation = reaction.centerAnimation else {
continue
}
guard let aroundAnimation = reaction.aroundAnimation else {
continue
}
if reaction.value == updatedReaction {
let standaloneReactionAnimation = StandaloneReactionAnimation()
@ -2582,8 +2589,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
appearAnimation: reaction.appearAnimation,
stillAnimation: reaction.selectAnimation,
listAnimation: reaction.activateAnimation,
applicationAnimation: reaction.effectAnimation
listAnimation: centerAnimation,
applicationAnimation: aroundAnimation
),
targetView: targetView,
hideNode: true,

View File

@ -927,7 +927,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unvote"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
interfaceInteraction.requestUnvoteInMessage(messages[0].id)
f(.dismissWithoutContent)
f(.default)
})))
}
}

View File

@ -373,9 +373,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
}
if false, strongSelf.telegramFile == nil {
if strongSelf.telegramFile == nil {
if let animationNode = strongSelf.animationNode, animationNode.frame.contains(point) {
return .waitForDoubleTap
return .waitForSingleTap
}
}
}
@ -1516,6 +1516,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
} else if case .tap = gesture {
item.controllerInteraction.clickThroughMessage()
} else if case .doubleTap = gesture {
if canAddMessageReactions(message: item.message) {
item.controllerInteraction.updateMessageReaction(item.message, .default)
}
}
}
default:

View File

@ -858,7 +858,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
}
if !strongSelf.backgroundNode.frame.contains(point) {
return .waitForSingleTap
return .waitForDoubleTap
}
}
@ -1321,9 +1321,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
var isItemEdited = false
switch item.content {
case let .message(message, value, _, _, _):
case let .message(message, value, _, attributes, _):
read = value
isItemPinned = message.tags.contains(.pinned)
if attributes.isCentered {
alignment = .center
}
case let .group(messages):
read = messages[0].1
for message in messages {
@ -3037,6 +3040,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
} else if case .tap = gesture {
item.controllerInteraction.clickThroughMessage()
} else if case .doubleTap = gesture {
if canAddMessageReactions(message: item.message) {
item.controllerInteraction.updateMessageReaction(item.message, .default)
}
}
}
default:

View File

@ -47,7 +47,7 @@ private let reactionCountFont = Font.semibold(11.0)
private let reactionFont = Font.regular(12.0)
private final class StatusReactionNode: ASDisplayNode {
let iconView: UIImageView
let iconView: ReactionIconView
private let iconImageDisposable = MetaDisposable()
@ -56,7 +56,7 @@ private final class StatusReactionNode: ASDisplayNode {
private var isSelected: Bool?
override init() {
self.iconView = UIImageView()
self.iconView = ReactionIconView()
super.init()
@ -71,7 +71,8 @@ private final class StatusReactionNode: ASDisplayNode {
if self.value != value {
self.value = value
let defaultImageSize = CGSize(width: 17.0, height: 17.0)
let boundingImageSize = CGSize(width: 17.0, height: 17.0)
let defaultImageSize = CGSize(width: boundingImageSize.width + floor(boundingImageSize.width * 0.5 * 2.0), height: boundingImageSize.height + floor(boundingImageSize.height * 0.5 * 2.0))
let imageSize: CGSize
if let file = file {
self.iconImageDisposable.set((reactionStaticImage(context: context, animation: file, pixelSize: CGSize(width: 72.0, height: 72.0))
@ -82,7 +83,7 @@ private final class StatusReactionNode: ASDisplayNode {
if data.isComplete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
if let image = UIImage(data: dataValue) {
strongSelf.iconView.image = image
strongSelf.iconView.imageView.image = image
}
}
}))
@ -91,7 +92,9 @@ private final class StatusReactionNode: ASDisplayNode {
imageSize = defaultImageSize
}
self.iconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((defaultImageSize.width - imageSize.width) / 2.0), y: floorToScreenPixels((defaultImageSize.height - imageSize.height) / 2.0)), size: imageSize)
let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingImageSize.width - imageSize.width) / 2.0), y: floorToScreenPixels((boundingImageSize.height - imageSize.height) / 2.0)), size: imageSize)
self.iconView.frame = iconFrame
self.iconView.update(size: iconFrame.size, transition: .immediate)
}
}
}

View File

@ -1235,7 +1235,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return PeerSelectionControllerImpl(params)
}
public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)? = nil, clickThroughMessage: (() -> Void)? = nil, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?) -> ListViewItem {
public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)? = nil, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, isCentered: Bool) -> ListViewItem {
let controllerInteraction: ChatControllerInteraction
controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
@ -1300,13 +1300,16 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false), presentationContext: ChatPresentationContext(backgroundNode: backgroundNode as? WallpaperBackgroundNode))
var entryAttributes = ChatMessageEntryAttributes()
entryAttributes.isCentered = isCentered
let content: ChatMessageItemContent
let chatLocation: ChatLocation
if messages.count > 1 {
content = .group(messages: messages.map { ($0, true, .none, ChatMessageEntryAttributes(), nil) })
content = .group(messages: messages.map { ($0, true, .none, entryAttributes, nil) })
chatLocation = .peer(messages.first!.id.peerId)
} else {
content = .message(message: messages.first!, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)
content = .message(message: messages.first!, read: true, selection: .none, attributes: entryAttributes, location: nil)
chatLocation = .peer(messages.first!.id.peerId)
}