mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Reaction improvements
This commit is contained in:
parent
d7a5983255
commit
8f23d13f1b
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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] = []
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user