mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Fix sticker suggestions menu
This commit is contained in:
parent
3208b7118c
commit
189f0d4306
@ -10,18 +10,22 @@ import StickerResources
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import AnimatedStickerNode
|
import AnimatedStickerNode
|
||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
|
import ShimmerEffect
|
||||||
|
import TelegramPresentationData
|
||||||
|
|
||||||
final class HorizontalStickerGridItem: GridItem {
|
final class HorizontalStickerGridItem: GridItem {
|
||||||
let account: Account
|
let account: Account
|
||||||
let file: TelegramMediaFile
|
let file: TelegramMediaFile
|
||||||
|
let theme: PresentationTheme
|
||||||
let isPreviewed: (HorizontalStickerGridItem) -> Bool
|
let isPreviewed: (HorizontalStickerGridItem) -> Bool
|
||||||
let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Void
|
let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Void
|
||||||
|
|
||||||
let section: GridSection? = nil
|
let section: GridSection? = nil
|
||||||
|
|
||||||
init(account: Account, file: TelegramMediaFile, isPreviewed: @escaping (HorizontalStickerGridItem) -> Bool, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Void) {
|
init(account: Account, file: TelegramMediaFile, theme: PresentationTheme, isPreviewed: @escaping (HorizontalStickerGridItem) -> Bool, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Void) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.file = file
|
self.file = file
|
||||||
|
self.theme = theme
|
||||||
self.isPreviewed = isPreviewed
|
self.isPreviewed = isPreviewed
|
||||||
self.sendSticker = sendSticker
|
self.sendSticker = sendSticker
|
||||||
}
|
}
|
||||||
@ -47,6 +51,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
|||||||
private var currentState: (Account, HorizontalStickerGridItem, CGSize)?
|
private var currentState: (Account, HorizontalStickerGridItem, CGSize)?
|
||||||
private let imageNode: TransformImageNode
|
private let imageNode: TransformImageNode
|
||||||
private var animationNode: AnimatedStickerNode?
|
private var animationNode: AnimatedStickerNode?
|
||||||
|
private var placeholderNode: ShimmerEffectNode?
|
||||||
|
|
||||||
private let stickerFetchedDisposable = MetaDisposable()
|
private let stickerFetchedDisposable = MetaDisposable()
|
||||||
|
|
||||||
@ -76,17 +81,47 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
|
self.placeholderNode = ShimmerEffectNode()
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
self.addSubnode(self.imageNode)
|
self.addSubnode(self.imageNode)
|
||||||
|
if let placeholderNode = self.placeholderNode {
|
||||||
|
placeholderNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
|
self.addSubnode(placeholderNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstTime = true
|
||||||
|
self.imageNode.imageUpdated = { [weak self] image in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if image != nil {
|
||||||
|
strongSelf.removePlaceholder(animated: !firstTime)
|
||||||
|
}
|
||||||
|
firstTime = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.stickerFetchedDisposable.dispose()
|
self.stickerFetchedDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func removePlaceholder(animated: Bool) {
|
||||||
|
if let placeholderNode = self.placeholderNode {
|
||||||
|
self.placeholderNode = nil
|
||||||
|
if !animated {
|
||||||
|
placeholderNode.removeFromSupernode()
|
||||||
|
} else {
|
||||||
|
placeholderNode.alpha = 0.0
|
||||||
|
placeholderNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak placeholderNode] _ in
|
||||||
|
placeholderNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
@ -105,7 +140,11 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
|||||||
animationNode.transform = self.imageNode.transform
|
animationNode.transform = self.imageNode.transform
|
||||||
animationNode.visibility = self.isVisibleInGrid
|
animationNode.visibility = self.isVisibleInGrid
|
||||||
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
|
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
|
||||||
self.addSubnode(animationNode)
|
if let placeholderNode = self.placeholderNode {
|
||||||
|
self.insertSubnode(animationNode, belowSubnode: placeholderNode)
|
||||||
|
} else {
|
||||||
|
self.addSubnode(animationNode)
|
||||||
|
}
|
||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +184,15 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
|||||||
let bounds = self.bounds
|
let bounds = self.bounds
|
||||||
let boundingSize = bounds.insetBy(dx: 2.0, dy: 2.0).size
|
let boundingSize = bounds.insetBy(dx: 2.0, dy: 2.0).size
|
||||||
|
|
||||||
|
if let placeholderNode = self.placeholderNode {
|
||||||
|
let placeholderFrame = CGRect(origin: CGPoint(x: floor((bounds.width - boundingSize.width) / 2.0), y: floor((bounds.height - boundingSize.height) / 2.0)), size: boundingSize)
|
||||||
|
placeholderNode.frame = bounds
|
||||||
|
|
||||||
|
if let theme = self.currentState?.1.theme {
|
||||||
|
placeholderNode.update(backgroundColor: theme.list.plainBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor.mixedWith(theme.list.plainBackgroundColor, alpha: 0.4), shimmeringColor: theme.list.mediaPlaceholderColor.withAlphaComponent(0.3), shapes: [.roundedRect(rect: placeholderFrame, cornerRadius: 10.0)], size: bounds.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let (_, _, mediaDimensions) = self.currentState {
|
if let (_, _, mediaDimensions) = self.currentState {
|
||||||
let imageSize = mediaDimensions.aspectFitted(boundingSize)
|
let imageSize = mediaDimensions.aspectFitted(boundingSize)
|
||||||
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
|
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
|
||||||
@ -160,6 +208,12 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func updateAbsoluteRect(_ absoluteRect: CGRect, within containerSize: CGSize) {
|
||||||
|
if let placeholderNode = self.placeholderNode {
|
||||||
|
placeholderNode.updateAbsoluteRect(absoluteRect, within: containerSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func imageNodeTap(_ recognizer: UITapGestureRecognizer) {
|
@objc func imageNodeTap(_ recognizer: UITapGestureRecognizer) {
|
||||||
if let (_, item, _) = self.currentState, case .ended = recognizer.state {
|
if let (_, item, _) = self.currentState, case .ended = recognizer.state {
|
||||||
self.sendSticker?(.standalone(media: item.file), self, self.bounds)
|
self.sendSticker?(.standalone(media: item.file), self, self.bounds)
|
||||||
|
@ -65,8 +65,8 @@ private struct StickerEntry: Identifiable, Comparable {
|
|||||||
return lhs.index < rhs.index
|
return lhs.index < rhs.index
|
||||||
}
|
}
|
||||||
|
|
||||||
func item(account: Account, stickersInteraction: HorizontalStickersChatContextPanelInteraction, interfaceInteraction: ChatPanelInterfaceInteraction) -> GridItem {
|
func item(account: Account, stickersInteraction: HorizontalStickersChatContextPanelInteraction, interfaceInteraction: ChatPanelInterfaceInteraction, theme: PresentationTheme) -> GridItem {
|
||||||
return HorizontalStickerGridItem(account: account, file: self.file, isPreviewed: { item in
|
return HorizontalStickerGridItem(account: account, file: self.file, theme: theme, isPreviewed: { item in
|
||||||
return false//stickersInteraction.previewedStickerItem == item
|
return false//stickersInteraction.previewedStickerItem == item
|
||||||
}, sendSticker: { file, node, rect in
|
}, sendSticker: { file, node, rect in
|
||||||
let _ = interfaceInteraction.sendSticker(file, node, rect)
|
let _ = interfaceInteraction.sendSticker(file, node, rect)
|
||||||
@ -83,15 +83,15 @@ private struct StickerEntryTransition {
|
|||||||
let scrollToItem: GridNodeScrollToItem?
|
let scrollToItem: GridNodeScrollToItem?
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparedGridEntryTransition(account: Account, from fromEntries: [StickerEntry], to toEntries: [StickerEntry], stickersInteraction: HorizontalStickersChatContextPanelInteraction, interfaceInteraction: ChatPanelInterfaceInteraction) -> StickerEntryTransition {
|
private func preparedGridEntryTransition(account: Account, from fromEntries: [StickerEntry], to toEntries: [StickerEntry], stickersInteraction: HorizontalStickersChatContextPanelInteraction, interfaceInteraction: ChatPanelInterfaceInteraction, theme: PresentationTheme) -> StickerEntryTransition {
|
||||||
let stationaryItems: GridNodeStationaryItems = .none
|
let stationaryItems: GridNodeStationaryItems = .none
|
||||||
let scrollToItem: GridNodeScrollToItem? = nil
|
let scrollToItem: GridNodeScrollToItem? = nil
|
||||||
|
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices
|
let deletions = deleteIndices
|
||||||
let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, stickersInteraction: stickersInteraction, interfaceInteraction: interfaceInteraction), previousIndex: $0.2) }
|
let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, stickersInteraction: stickersInteraction, interfaceInteraction: interfaceInteraction, theme: theme), previousIndex: $0.2) }
|
||||||
let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, stickersInteraction: stickersInteraction, interfaceInteraction: interfaceInteraction)) }
|
let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, stickersInteraction: stickersInteraction, interfaceInteraction: interfaceInteraction, theme: theme)) }
|
||||||
|
|
||||||
return StickerEntryTransition(deletions: deletions, insertions: insertions, updates: updates, updateFirstIndexInSectionOffset: nil, stationaryItems: stationaryItems, scrollToItem: scrollToItem)
|
return StickerEntryTransition(deletions: deletions, insertions: insertions, updates: updates, updateFirstIndexInSectionOffset: nil, stationaryItems: stationaryItems, scrollToItem: scrollToItem)
|
||||||
}
|
}
|
||||||
@ -257,7 +257,7 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
|||||||
self.updateLayout(size: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, bottomInset: validLayout.3, transition: .immediate, interfaceState: validLayout.4)
|
self.updateLayout(size: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, bottomInset: validLayout.3, transition: .immediate, interfaceState: validLayout.4)
|
||||||
}
|
}
|
||||||
|
|
||||||
let transition = preparedGridEntryTransition(account: self.context.account, from: previousEntries, to: entries, stickersInteraction: self.stickersInteraction, interfaceInteraction: self.interfaceInteraction!)
|
let transition = preparedGridEntryTransition(account: self.context.account, from: previousEntries, to: entries, stickersInteraction: self.stickersInteraction, interfaceInteraction: self.interfaceInteraction!, theme: self.theme)
|
||||||
self.enqueueTransition(transition)
|
self.enqueueTransition(transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
private var theme: PresentationTheme
|
||||||
|
|
||||||
private let scrollNode: ASScrollNode
|
private let scrollNode: ASScrollNode
|
||||||
private var items: [TelegramMediaFile] = []
|
private var items: [TelegramMediaFile] = []
|
||||||
@ -36,8 +37,9 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
var updateBackgroundOffset: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
var updateBackgroundOffset: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||||
var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Void)?
|
var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Void)?
|
||||||
|
|
||||||
init(context: AccountContext) {
|
init(context: AccountContext, theme: PresentationTheme) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.theme = theme
|
||||||
|
|
||||||
self.scrollNode = ASScrollNode()
|
self.scrollNode = ASScrollNode()
|
||||||
|
|
||||||
@ -182,7 +184,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
columnIndex += 1
|
columnIndex += 1
|
||||||
if columnIndex == itemsPerRow {
|
if columnIndex == itemsPerRow {
|
||||||
columnIndex = 0
|
columnIndex = 0
|
||||||
topOffset += itemSize
|
topOffset += itemSize + itemSpacing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,6 +197,9 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
var minVisibleY = self.scrollNode.view.bounds.minY
|
var minVisibleY = self.scrollNode.view.bounds.minY
|
||||||
var maxVisibleY = self.scrollNode.view.bounds.maxY
|
var maxVisibleY = self.scrollNode.view.bounds.maxY
|
||||||
|
|
||||||
|
let containerSize = self.scrollNode.view.bounds.size
|
||||||
|
let absoluteOffset: CGFloat = -self.scrollNode.view.contentOffset.y
|
||||||
|
|
||||||
let minActivatedY = minVisibleY
|
let minActivatedY = minVisibleY
|
||||||
let maxActivatedY = maxVisibleY
|
let maxActivatedY = maxVisibleY
|
||||||
|
|
||||||
@ -217,6 +222,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
let item = HorizontalStickerGridItem(
|
let item = HorizontalStickerGridItem(
|
||||||
account: self.context.account,
|
account: self.context.account,
|
||||||
file: item.file,
|
file: item.file,
|
||||||
|
theme: self.theme,
|
||||||
isPreviewed: { _ in
|
isPreviewed: { _ in
|
||||||
return false
|
return false
|
||||||
}, sendSticker: { [weak self] file, node, rect in
|
}, sendSticker: { [weak self] file, node, rect in
|
||||||
@ -235,6 +241,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
self.scrollNode.addSubnode(itemNode)
|
self.scrollNode.addSubnode(itemNode)
|
||||||
}
|
}
|
||||||
itemNode.frame = itemFrame
|
itemNode.frame = itemFrame
|
||||||
|
itemNode.updateAbsoluteRect(itemFrame.offsetBy(dx: 0.0, dy: absoluteOffset), within: containerSize)
|
||||||
itemNode.isVisibleInGrid = isActivated
|
itemNode.isVisibleInGrid = isActivated
|
||||||
validIds.insert(item.file.fileId)
|
validIds.insert(item.file.fileId)
|
||||||
}
|
}
|
||||||
@ -296,7 +303,7 @@ final class InlineReactionSearchPanel: ChatInputContextPanelNode {
|
|||||||
|
|
||||||
context.restoreGState()
|
context.restoreGState()
|
||||||
|
|
||||||
context.setFillColor(UIColor.white.cgColor)
|
context.setFillColor(theme.list.plainBackgroundColor.cgColor)
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
|
||||||
})?.stretchableImage(withLeftCapWidth: Int(backroundDiameter / 2.0 + shadowBlur), topCapHeight: 0)
|
})?.stretchableImage(withLeftCapWidth: Int(backroundDiameter / 2.0 + shadowBlur), topCapHeight: 0)
|
||||||
|
|
||||||
@ -314,7 +321,7 @@ final class InlineReactionSearchPanel: ChatInputContextPanelNode {
|
|||||||
|
|
||||||
self.backgroundContainerNode = ASDisplayNode()
|
self.backgroundContainerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.stickersNode = InlineReactionSearchStickersNode(context: context)
|
self.stickersNode = InlineReactionSearchStickersNode(context: context, theme: theme)
|
||||||
|
|
||||||
super.init(context: context, theme: theme, strings: strings, fontSize: fontSize)
|
super.init(context: context, theme: theme, strings: strings, fontSize: fontSize)
|
||||||
|
|
||||||
@ -330,7 +337,7 @@ final class InlineReactionSearchPanel: ChatInputContextPanelNode {
|
|||||||
|
|
||||||
self.addSubnode(self.containerNode)
|
self.addSubnode(self.containerNode)
|
||||||
|
|
||||||
self.backgroundNode.backgroundColor = .white
|
self.backgroundNode.backgroundColor = theme.list.plainBackgroundColor
|
||||||
|
|
||||||
self.stickersNode.updateBackgroundOffset = { [weak self] offset, transition in
|
self.stickersNode.updateBackgroundOffset = { [weak self] offset, transition in
|
||||||
guard let strongSelf = self, let (_, _) = strongSelf.validLayout else {
|
guard let strongSelf = self, let (_, _) = strongSelf.validLayout else {
|
||||||
@ -350,6 +357,9 @@ final class InlineReactionSearchPanel: ChatInputContextPanelNode {
|
|||||||
}
|
}
|
||||||
let _ = strongSelf.controllerInteraction?.sendSticker(file, true, node, rect)
|
let _ = strongSelf.controllerInteraction?.sendSticker(file, true, node, rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||||
|
self.view.disablesInteractiveKeyboardGestureRecognizer = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateResults(results: [TelegramMediaFile]) {
|
func updateResults(results: [TelegramMediaFile]) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user