mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Emoji preview
This commit is contained in:
@@ -192,7 +192,7 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
}
|
}
|
||||||
self.boundsChangeTrackerLayer = boundsChangeTrackerLayer
|
self.boundsChangeTrackerLayer = boundsChangeTrackerLayer
|
||||||
|
|
||||||
let gestureRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
/*let gestureRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||||
gestureRecognizer.longTap = { [weak self] point, _ in
|
gestureRecognizer.longTap = { [weak self] point, _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@@ -217,7 +217,20 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.containerNode.view.addGestureRecognizer(gestureRecognizer)
|
self.containerNode.view.addGestureRecognizer(gestureRecognizer)*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func targetItem(at point: CGPoint) -> (TelegramMediaFile, CALayer)? {
|
||||||
|
if let (item, _) = self.item(atPoint: point), let file = item.itemFile {
|
||||||
|
let itemId = EmojiPagerContentComponent.View.ItemLayer.Key(
|
||||||
|
groupId: 0,
|
||||||
|
itemId: .animation(.file(file.fileId))
|
||||||
|
)
|
||||||
|
if let itemLayer = self.visibleItemLayers[itemId] {
|
||||||
|
return (file, itemLayer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
@objc private func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
private let requestDismiss: () -> Void
|
private let requestDismiss: () -> Void
|
||||||
private let presentInGlobalOverlay: (ViewController, Any?) -> Void
|
private let presentInGlobalOverlay: (ViewController, Any?) -> Void
|
||||||
private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
||||||
|
private let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?
|
||||||
private let backgroundNode: ASImageNode
|
private let backgroundNode: ASImageNode
|
||||||
private let gridNode: GridNode
|
private let gridNode: GridNode
|
||||||
private let actionAreaBackgroundNode: NavigationBackgroundNode
|
private let actionAreaBackgroundNode: NavigationBackgroundNode
|
||||||
@@ -172,6 +173,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
self.presentInGlobalOverlay = presentInGlobalOverlay
|
self.presentInGlobalOverlay = presentInGlobalOverlay
|
||||||
self.expandProgressUpdated = expandProgressUpdated
|
self.expandProgressUpdated = expandProgressUpdated
|
||||||
self.sendSticker = sendSticker
|
self.sendSticker = sendSticker
|
||||||
|
self.sendEmoji = sendEmoji
|
||||||
|
|
||||||
self.backgroundNode = ASImageNode()
|
self.backgroundNode = ASImageNode()
|
||||||
self.backgroundNode.displaysAsynchronously = true
|
self.backgroundNode.displaysAsynchronously = true
|
||||||
@@ -494,6 +496,8 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let itemNode = strongSelf.gridNode.itemNodeAtPoint(point) as? StickerPackEmojisItemNode, let targetItem = itemNode.targetItem(at: strongSelf.gridNode.view.convert(point, to: itemNode.view)) {
|
||||||
|
return strongSelf.emojiSuggestionPeekContent(itemLayer: targetItem.1, file: targetItem.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -523,6 +527,195 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
}, activateBySingleTap: true))
|
}, activateBySingleTap: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func emojiSuggestionPeekContent(itemLayer: CALayer, file: TelegramMediaFile) -> Signal<(UIView, CGRect, PeekControllerContent)?, NoError> {
|
||||||
|
let context = self.context
|
||||||
|
|
||||||
|
var collectionId: ItemCollectionId?
|
||||||
|
for attribute in file.attributes {
|
||||||
|
if case let .CustomEmoji(_, _, _, packReference) = attribute {
|
||||||
|
switch packReference {
|
||||||
|
case let .id(id, _):
|
||||||
|
collectionId = ItemCollectionId(namespace: Namespaces.ItemCollection.CloudEmojiPacks, id: id)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
||||||
|
if let collectionId {
|
||||||
|
bubbleUpEmojiOrStickersets.append(collectionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
let accountPeerId = context.account.peerId
|
||||||
|
|
||||||
|
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: accountPeerId))
|
||||||
|
|> map { peer -> Bool in
|
||||||
|
var hasPremium = false
|
||||||
|
if case let .user(user) = peer, user.isPremium {
|
||||||
|
hasPremium = true
|
||||||
|
}
|
||||||
|
return hasPremium
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue
|
||||||
|
|> map { [weak self, weak itemLayer] hasPremium -> (UIView, CGRect, PeekControllerContent)? in
|
||||||
|
guard let strongSelf = self, let itemLayer = itemLayer else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var menuItems: [ContextMenuItem] = []
|
||||||
|
menuItems.removeAll()
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
var isLocked = false
|
||||||
|
if !hasPremium {
|
||||||
|
isLocked = file.isPremiumEmoji
|
||||||
|
/*if isLocked && chatPeerId == context.account.peerId {
|
||||||
|
isLocked = false
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
let sendEmoji: (TelegramMediaFile) -> Void = { file in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var text = "."
|
||||||
|
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
|
||||||
|
loop: for attribute in file.attributes {
|
||||||
|
switch attribute {
|
||||||
|
case let .CustomEmoji(_, _, displayText, stickerPackReference):
|
||||||
|
text = displayText
|
||||||
|
|
||||||
|
var packId: ItemCollectionId?
|
||||||
|
if case let .id(id, _) = stickerPackReference {
|
||||||
|
packId = ItemCollectionId(namespace: Namespaces.ItemCollection.CloudEmojiPacks, id: id)
|
||||||
|
}
|
||||||
|
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: packId, fileId: file.fileId.id, file: file)
|
||||||
|
break loop
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let emojiAttribute {
|
||||||
|
self.sendEmoji?(text, emojiAttribute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let setStatus: (TelegramMediaFile) -> Void = { file in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let context = self.context
|
||||||
|
|
||||||
|
let _ = context.engine.accountData.setEmojiStatus(file: file, expirationDate: nil).start()
|
||||||
|
|
||||||
|
var animateInAsReplacement = false
|
||||||
|
animateInAsReplacement = false
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let undoController = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, title: nil, text: "Your emoji status has been updated.", undoText: nil, customAction: nil), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return false })
|
||||||
|
controller.present(undoController, in: .window(.root))
|
||||||
|
}
|
||||||
|
let copyEmoji: (TelegramMediaFile) -> Void = { file in
|
||||||
|
var text = "."
|
||||||
|
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
|
||||||
|
loop: for attribute in file.attributes {
|
||||||
|
switch attribute {
|
||||||
|
case let .CustomEmoji(_, _, displayText, _):
|
||||||
|
text = displayText
|
||||||
|
|
||||||
|
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: file.fileId.id, file: file)
|
||||||
|
break loop
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let _ = emojiAttribute {
|
||||||
|
storeMessageTextInPasteboard(text, entities: [MessageTextEntity(range: 0 ..< (text as NSString).length, type: .CustomEmoji(stickerPack: nil, fileId: file.fileId.id))])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
if strongSelf.sendEmoji != nil {
|
||||||
|
menuItems.append(.action(ContextMenuActionItem(text: "Send Emoji", icon: { theme in
|
||||||
|
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) {
|
||||||
|
return generateImage(image.size, rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
if let cgImage = image.cgImage {
|
||||||
|
context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}, action: { _, f in
|
||||||
|
sendEmoji(file)
|
||||||
|
f(.default)
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
menuItems.append(.action(ContextMenuActionItem(text: "Set as Status", icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Smile"), color: theme.actionSheet.primaryTextColor)
|
||||||
|
}, action: { _, f in
|
||||||
|
f(.default)
|
||||||
|
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasPremium {
|
||||||
|
setStatus(file)
|
||||||
|
} else {
|
||||||
|
var replaceImpl: ((ViewController) -> Void)?
|
||||||
|
let controller = PremiumDemoScreen(context: context, subject: .animatedEmoji, action: {
|
||||||
|
let controller = PremiumIntroScreen(context: context, source: .animatedEmoji)
|
||||||
|
replaceImpl?(controller)
|
||||||
|
})
|
||||||
|
replaceImpl = { [weak controller] c in
|
||||||
|
controller?.replace(with: c)
|
||||||
|
}
|
||||||
|
strongSelf.controller?.push(controller)
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
menuItems.append(.action(ContextMenuActionItem(text: "Copy Emoji", icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.actionSheet.primaryTextColor)
|
||||||
|
}, action: { _, f in
|
||||||
|
copyEmoji(file)
|
||||||
|
f(.default)
|
||||||
|
})))
|
||||||
|
|
||||||
|
if menuItems.isEmpty {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = StickerPreviewPeekContent(context: context, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked, menu: menuItems, openPremiumIntro: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let premiumController = PremiumIntroScreen(context: context, source: .stickers)
|
||||||
|
controller.push(premiumController)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (strongSelf.view, itemLayer.convert(itemLayer.bounds, to: strongSelf.view.layer), content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updatePresentationData(_ presentationData: PresentationData) {
|
func updatePresentationData(_ presentationData: PresentationData) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user