From d7a851d61fa05cc3b2f1c484511f47ad0ffbe447 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 24 Jul 2022 16:14:16 +0200 Subject: [PATCH 1/8] Gif improvements --- .../EntityKeyboardTopPanelComponent.swift | 2 +- .../Sources/GifPagerContentComponent.swift | 43 +++-- .../TelegramUI/Sources/ChatController.swift | 4 +- .../Sources/ChatControllerInteraction.swift | 4 +- .../Sources/ChatEntityKeyboardInputNode.swift | 180 ++++++++++++++++-- .../Sources/ChatMediaInputGifPane.swift | 2 +- .../Sources/ChatMediaInputNode.swift | 4 +- .../Sources/GifPaneSearchContentNode.swift | 2 +- 8 files changed, 201 insertions(+), 40 deletions(-) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index 7286fe13ab..1d760286e5 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -1821,7 +1821,7 @@ final class EntityKeyboardTopPanelComponent: Component { } let isRound: Bool - if let string = activeContentItemId.base as? String, (string == "recent" || string == "static") { + if let string = activeContentItemId.base as? String, (string == "recent" || string == "static" || string == "trending") { isRound = true } else { isRound = false diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/GifPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GifPagerContentComponent.swift index 8171d8976e..2194d5b83f 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/GifPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GifPagerContentComponent.swift @@ -134,12 +134,12 @@ public final class GifPagerContentComponent: Component { public final class InputInteraction { public let performItemAction: (Item, UIView, CGRect) -> Void - public let openGifContextMenu: (TelegramMediaFile, UIView, CGRect, ContextGesture, Bool) -> Void + public let openGifContextMenu: (Item, UIView, CGRect, ContextGesture, Bool) -> Void public let loadMore: (String) -> Void public init( performItemAction: @escaping (Item, UIView, CGRect) -> Void, - openGifContextMenu: @escaping (TelegramMediaFile, UIView, CGRect, ContextGesture, Bool) -> Void, + openGifContextMenu: @escaping (Item, UIView, CGRect, ContextGesture, Bool) -> Void, loadMore: @escaping (String) -> Void ) { self.performItemAction = performItemAction @@ -149,17 +149,22 @@ public final class GifPagerContentComponent: Component { } public final class Item: Equatable { - public let file: TelegramMediaFile + public let file: FileMediaReference + public let contextResult: (ChatContextResultCollection, ChatContextResult)? - public init(file: TelegramMediaFile) { + public init(file: FileMediaReference, contextResult: (ChatContextResultCollection, ChatContextResult)?) { self.file = file + self.contextResult = contextResult } public static func ==(lhs: Item, rhs: Item) -> Bool { if lhs === rhs { return true } - if lhs.file.fileId != rhs.file.fileId { + if lhs.file.media.fileId != rhs.file.media.fileId { + return false + } + if (lhs.contextResult == nil) != (rhs.contextResult != nil) { return false } @@ -242,10 +247,16 @@ public final class GifPagerContentComponent: Component { self.horizontalSpacing = 1.0 self.verticalSpacing = 1.0 - let itemHorizontalSpace = width - self.containerInsets.left - self.containerInsets.right - self.itemSize = floor((width - self.horizontalSpacing * 2.0) / 3.0) + let defaultItemSize: CGFloat = 120.0 + + let itemHorizontalSpace = width - self.containerInsets.left - self.containerInsets.right + var itemsPerRow = Int(floor((itemHorizontalSpace) / (defaultItemSize))) + itemsPerRow = max(3, itemsPerRow) + + self.itemsPerRow = itemsPerRow + + self.itemSize = floor((itemHorizontalSpace - self.horizontalSpacing * CGFloat(itemsPerRow - 1)) / CGFloat(itemsPerRow)) - self.itemsPerRow = Int((itemHorizontalSpace + self.horizontalSpacing) / (self.itemSize + self.horizontalSpacing)) let numRowsInGroup = (itemCount + (self.itemsPerRow - 1)) / self.itemsPerRow self.contentSize = CGSize(width: width, height: self.containerInsets.top + self.containerInsets.bottom + CGFloat(numRowsInGroup) * self.itemSize + CGFloat(max(0, numRowsInGroup - 1)) * self.verticalSpacing) } @@ -265,7 +276,7 @@ public final class GifPagerContentComponent: Component { ) ) - if column == self.itemsPerRow - 1 { + if column == self.itemsPerRow - 1 && index < self.itemCount - 1 { rect.size.width = self.width - self.containerInsets.right - rect.minX } @@ -321,7 +332,7 @@ public final class GifPagerContentComponent: Component { self.item = item self.onUpdateDisplayPlaceholder = onUpdateDisplayPlaceholder - super.init(context: context, file: item?.file, synchronousLoad: attemptSynchronousLoad) + super.init(context: context, file: item?.file.media, synchronousLoad: attemptSynchronousLoad) if item == nil { self.updateDisplayPlaceholder(displayPlaceholder: true, duration: 0.0) @@ -453,6 +464,8 @@ public final class GifPagerContentComponent: Component { self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + self.isMultipleTouchEnabled = false + self.useSublayerTransformForActivation = false self.shouldBegin = { [weak self] point in guard let strongSelf = self else { @@ -475,7 +488,7 @@ public final class GifPagerContentComponent: Component { return } let rect = strongSelf.scrollView.convert(itemLayer.frame, to: strongSelf) - component.inputInteraction.openGifContextMenu(item.file, strongSelf, rect, gesture, true) + component.inputInteraction.openGifContextMenu(item, strongSelf, rect, gesture, component.subject == .recent) } } @@ -483,16 +496,16 @@ public final class GifPagerContentComponent: Component { fatalError("init(coder:) has not been implemented") } - private func openGifContextMenu(file: TelegramMediaFile, sourceView: UIView, sourceRect: CGRect, gesture: ContextGesture, isSaved: Bool) { + private func openGifContextMenu(item: Item, sourceView: UIView, sourceRect: CGRect, gesture: ContextGesture, isSaved: Bool) { guard let component = self.component else { return } - component.inputInteraction.openGifContextMenu(file, sourceView, sourceRect, gesture, isSaved) + component.inputInteraction.openGifContextMenu(item, sourceView, sourceRect, gesture, isSaved) } @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { - if let component = self.component, let item = self.item(atPoint: recognizer.location(in: self)), let itemView = self.visibleItemLayers[.media(item.file.fileId)] { + if let component = self.component, let item = self.item(atPoint: recognizer.location(in: self)), let itemView = self.visibleItemLayers[.media(item.file.media.fileId)] { component.inputInteraction.performItemAction(item, self, self.scrollView.convert(itemView.frame, to: self)) } } @@ -629,7 +642,7 @@ public final class GifPagerContentComponent: Component { let itemId: ItemKey if index < component.items.count { item = component.items[index] - itemId = .media(component.items[index].file.fileId) + itemId = .media(component.items[index].file.media.fileId) } else if component.isLoading || component.loadMoreToken != nil { itemId = .placeholder(index) } else { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 7f5f65bc9d..a396a9928b 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1741,7 +1741,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } return true - }, sendBotContextResultAsGif: { [weak self] collection, result, sourceNode, sourceRect, silentPosting in + }, sendBotContextResultAsGif: { [weak self] collection, result, sourceView, sourceRect, silentPosting in guard let strongSelf = self else { return false } @@ -1749,7 +1749,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return false } if let _ = strongSelf.presentationInterfaceState.slowmodeState, strongSelf.presentationInterfaceState.subject != .scheduledMessages { - strongSelf.interfaceInteraction?.displaySlowmodeTooltip(sourceNode.view, sourceRect) + strongSelf.interfaceInteraction?.displaySlowmodeTooltip(sourceView, sourceRect) return false } diff --git a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift index 95c6a9da12..2267e19977 100644 --- a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift @@ -75,7 +75,7 @@ public final class ChatControllerInteraction { let sendMessage: (String) -> Void let sendSticker: (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?) -> Bool let sendGif: (FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool - let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect, Bool) -> Bool + let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, UIView, CGRect, Bool) -> Bool let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void let requestMessageActionUrlAuth: (String, MessageActionUrlSubject) -> Void let activateSwitchInline: (PeerId?, String) -> Void @@ -179,7 +179,7 @@ public final class ChatControllerInteraction { sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?) -> Bool, sendGif: @escaping (FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool, - sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect, Bool) -> Bool, + sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, UIView, CGRect, Bool) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageActionUrlSubject) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, diff --git a/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift index a55fcff8de..a427216356 100644 --- a/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift @@ -616,7 +616,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { guard let controllerInteraction = controllerInteraction else { return } - let _ = controllerInteraction.sendGif(.savedGif(media: item.file), view, rect, false, false) + let _ = controllerInteraction.sendGif(item.file, view, rect, false, false) }, openGifContextMenu: { _, _, _, _, _ in }, @@ -776,7 +776,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { var items: [GifPagerContentComponent.Item] = [] for gifItem in savedGifs { items.append(GifPagerContentComponent.Item( - file: gifItem.contents.get(RecentMediaItem.self)!.media + file: .savedGif(media: gifItem.contents.get(RecentMediaItem.self)!.media), + contextResult: nil )) } return EntityKeyboardGifContent( @@ -800,7 +801,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { if let trendingGifs = trendingGifs { for file in trendingGifs.files { items.append(GifPagerContentComponent.Item( - file: file.file.media + file: file.file, + contextResult: file.contextResult )) } } else { @@ -829,7 +831,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { if let result = result { for file in result.files { items.append(GifPagerContentComponent.Item( - file: file.file.media + file: file.file, + contextResult: file.contextResult )) } loadMoreToken = result.nextOffset @@ -893,7 +896,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { var existingIds = Set() for item in componentValue.component.items { items.append(item) - existingIds.insert(item.file.fileId) + existingIds.insert(item.file.media.fileId) } var loadMoreToken: String? @@ -904,7 +907,10 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { continue } existingIds.insert(file.file.media.fileId) - items.append(GifPagerContentComponent.Item(file: file.file.media)) + items.append(GifPagerContentComponent.Item( + file: file.file, + contextResult: file.contextResult + )) } if !result.isComplete { loadMoreToken = result.nextOffset @@ -1392,13 +1398,18 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { guard let controllerInteraction = controllerInteraction else { return } - let _ = controllerInteraction.sendGif(.savedGif(media: item.file), view, rect, false, false) + + if let (collection, result) = item.contextResult { + let _ = controllerInteraction.sendBotContextResultAsGif(collection, result, view, rect, false) + } else { + let _ = controllerInteraction.sendGif(item.file, view, rect, false, false) + } }, - openGifContextMenu: { [weak self] file, sourceView, sourceRect, gesture, isSaved in + openGifContextMenu: { [weak self] item, sourceView, sourceRect, gesture, isSaved in guard let strongSelf = self else { return } - strongSelf.openGifContextMenu(file: file, sourceView: sourceView, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved) + strongSelf.openGifContextMenu(item: item, sourceView: sourceView, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved) }, loadMore: { [weak self] token in guard let strongSelf = self, let gifContext = strongSelf.gifContext else { @@ -1602,9 +1613,142 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { return (expandedHeight, 0.0) } - private func openGifContextMenu(file: TelegramMediaFile, sourceView: UIView, sourceRect: CGRect, gesture: ContextGesture, isSaved: Bool) { + private func openGifContextMenu(item: GifPagerContentComponent.Item, sourceView: UIView, sourceRect: CGRect, gesture: ContextGesture, isSaved: Bool) { + let file = item + let canSaveGif: Bool - if file.fileId.namespace == Namespaces.Media.CloudFile { + if file.file.media.fileId.namespace == Namespaces.Media.CloudFile { + canSaveGif = true + } else { + canSaveGif = false + } + + let _ = (self.context.engine.stickers.isGifSaved(id: file.file.media.fileId) + |> deliverOnMainQueue).start(next: { [weak self] isGifSaved in + guard let strongSelf = self else { + return + } + var isGifSaved = isGifSaved + if !canSaveGif { + isGifSaved = false + } + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + + let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file.file.media], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:]) + + let gallery = GalleryController(context: strongSelf.context, source: .standaloneMessage(message), streamSingleVideo: true, replaceRootController: { _, _ in + }, baseNavigationController: nil) + gallery.setHintWillBePresentedInPreviewingContext(true) + + var items: [ContextMenuItem] = [] + items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Send, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + f(.default) + if isSaved { + let _ = self?.controllerInteraction?.sendGif(file.file, sourceView, sourceRect, false, false) + } else if let (collection, result) = file.contextResult { + let _ = self?.controllerInteraction?.sendBotContextResultAsGif(collection, result, sourceView, sourceRect, false) + } + }))) + + if let currentState = strongSelf.currentState { + let interfaceState = currentState.interfaceState + + var isScheduledMessages = false + if case .scheduledMessages = interfaceState.subject { + isScheduledMessages = true + } + if !isScheduledMessages { + if case let .peer(peerId) = interfaceState.chatLocation { + if peerId != self?.context.account.peerId && peerId.namespace != Namespaces.Peer.SecretChat { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_SendSilently, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + f(.default) + if isSaved { + let _ = self?.controllerInteraction?.sendGif(file.file, sourceView, sourceRect, true, false) + } else if let (collection, result) = file.contextResult { + let _ = self?.controllerInteraction?.sendBotContextResultAsGif(collection, result, sourceView, sourceRect, true) + } + }))) + } + + if isSaved { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/ScheduleIcon"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + f(.default) + + let _ = self?.controllerInteraction?.sendGif(file.file, sourceView, sourceRect, false, true) + }))) + } + } + } + } + + if isSaved || isGifSaved { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor) + }, action: { _, f in + f(.dismissWithoutContent) + + guard let strongSelf = self else { + return + } + let _ = removeSavedGif(postbox: strongSelf.context.account.postbox, mediaId: file.file.media.fileId).start() + }))) + } else if canSaveGif && !isGifSaved { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Preview_SaveGif, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor) + }, action: { _, f in + f(.dismissWithoutContent) + + guard let strongSelf = self else { + return + } + + let context = strongSelf.context + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let _ = (toggleGifSaved(account: context.account, fileReference: file.file, saved: true) + |> deliverOnMainQueue).start(next: { result in + guard let strongSelf = self else { + return + } + switch result { + case .generic: + strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil) + case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + let text: String + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { + text = presentationData.strings.Premium_MaxSavedGifsFinalText + } else { + text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string + } + strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: false, animateInAsReplacement: false, action: { action in + guard let strongSelf = self else { + return false + } + + if case .info = action { + let controller = PremiumIntroScreen(context: context, source: .savedGifs) + strongSelf.controllerInteraction?.navigationController()?.pushViewController(controller) + return true + } + return false + }), nil) + } + }) + }))) + } + + let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceView: sourceView, sourceRect: sourceRect)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + strongSelf.controllerInteraction?.presentGlobalOverlayController(contextController, nil) + }) + + /*let canSaveGif: Bool + if item.file.fileId.namespace == Namespaces.Media.CloudFile { canSaveGif = true } else { canSaveGif = false @@ -1639,15 +1783,19 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { }*/ }))) - /*if let (_, _, _, _, _, _, _, _, interfaceState, _, _, _) = strongSelf.validLayout { + if let currentState = strongSelf.currentState { + let interfaceState = currentState.interfaceState + var isScheduledMessages = false if case .scheduledMessages = interfaceState.subject { isScheduledMessages = true } if !isScheduledMessages { if case let .peer(peerId) = interfaceState.chatLocation { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + if peerId != self?.context.account.peerId && peerId.namespace != Namespaces.Peer.SecretChat { - items.append(.action(ContextMenuActionItem(text: strongSelf.strings.Conversation_SendMessage_SendSilently, icon: { theme in + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_SendSilently, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in f(.default) @@ -1660,7 +1808,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { } if isSaved { - items.append(.action(ContextMenuActionItem(text: strongSelf.strings.Conversation_SendMessage_ScheduleMessage, icon: { theme in + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/ScheduleIcon"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in f(.default) @@ -1670,7 +1818,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { } } } - }*/ + } if isSaved || isGifSaved { items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in @@ -1724,7 +1872,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceView: sourceView, sourceRect: sourceRect)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) strongSelf.controllerInteraction?.presentGlobalOverlayController(contextController, nil) - }) + })*/ } } diff --git a/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift b/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift index 288c9d53ba..f8d1b0a914 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift @@ -236,7 +236,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { multiplexedNode.fileSelected = { [weak self] file, sourceNode, sourceRect in if let (collection, result) = file.contextResult { - let _ = self?.controllerInteraction.sendBotContextResultAsGif(collection, result, sourceNode, sourceRect, false) + let _ = self?.controllerInteraction.sendBotContextResultAsGif(collection, result, sourceNode.view, sourceRect, false) } else { let _ = self?.controllerInteraction.sendGif(file.file, sourceNode.view, sourceRect, false, false) } diff --git a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift index e827bb3be6..afc1423c61 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift @@ -1470,7 +1470,7 @@ final class ChatMediaInputNode: ChatInputNode { if isSaved { let _ = self?.controllerInteraction.sendGif(file.file, sourceNode.view, sourceRect, false, false) } else if let (collection, result) = file.contextResult { - let _ = self?.controllerInteraction.sendBotContextResultAsGif(collection, result, sourceNode, sourceRect, false) + let _ = self?.controllerInteraction.sendBotContextResultAsGif(collection, result, sourceNode.view, sourceRect, false) } }))) @@ -1489,7 +1489,7 @@ final class ChatMediaInputNode: ChatInputNode { if isSaved { let _ = self?.controllerInteraction.sendGif(file.file, sourceNode.view, sourceRect, true, false) } else if let (collection, result) = file.contextResult { - let _ = self?.controllerInteraction.sendBotContextResultAsGif(collection, result, sourceNode, sourceRect, true) + let _ = self?.controllerInteraction.sendBotContextResultAsGif(collection, result, sourceNode.view, sourceRect, true) } }))) } diff --git a/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift b/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift index 6e55f0cbb4..845e95f258 100644 --- a/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift +++ b/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift @@ -331,7 +331,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode { multiplexedNode.fileSelected = { [weak self] file, sourceNode, sourceRect in if let (collection, result) = file.contextResult { - let _ = self?.controllerInteraction.sendBotContextResultAsGif(collection, result, sourceNode, sourceRect, false) + let _ = self?.controllerInteraction.sendBotContextResultAsGif(collection, result, sourceNode.view, sourceRect, false) } else { let _ = self?.controllerInteraction.sendGif(file.file, sourceNode.view, sourceRect, false, false) } From 99207385b15cf8ffef50215f16b3173b4420ac59 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 24 Jul 2022 16:23:12 +0200 Subject: [PATCH 2/8] Emoji improvements --- .../Sources/DefaultDayPresentationTheme.swift | 2 +- .../EntityKeyboard/Sources/EntityKeyboard.swift | 5 ++++- .../Sources/EntityKeyboardTopPanelComponent.swift | 14 +++++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index 3ad0c85faa..bc6bb3a410 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -863,7 +863,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio panelIconColor: UIColor(rgb: 0x858e99), panelHighlightedIconBackgroundColor: UIColor(rgb: 0x858e99, alpha: 0.2), panelHighlightedIconColor: UIColor(rgb: 0x4D5561), - panelContentVibrantOverlayColor: day ? UIColor(white: 0.0, alpha: 0.3) : UIColor(white: 0.6, alpha: 0.65), + panelContentVibrantOverlayColor: day ? UIColor(white: 0.0, alpha: 0.3) : UIColor(white: 0.65, alpha: 0.65), stickersBackgroundColor: UIColor(rgb: 0xe8ebf0), stickersSectionTextColor: UIColor(rgb: 0x9099a2), stickersSearchBackgroundColor: UIColor(rgb: 0xd9dbe1), diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift index ed42e4b1cd..738ed544e1 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift @@ -285,6 +285,7 @@ public final class EntityKeyboardComponent: Component { contentTopPanels.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(EntityKeyboardTopPanelComponent( theme: component.theme, items: topGifItems, + containerSideInset: component.containerInsets.left, forceActiveItemId: defaultActiveGifItemId, activeContentItemIdUpdated: gifsContentItemIdUpdated, reorderItems: { _ in @@ -352,7 +353,7 @@ public final class EntityKeyboardComponent: Component { if let file = itemGroup.items[0].file { topStickerItems.append(EntityKeyboardTopPanelComponent.Item( id: itemGroup.supergroupId, - isReorderable: true, + isReorderable: !itemGroup.isFeatured, content: AnyComponent(EntityKeyboardAnimationTopPanelComponent( context: stickerContent.context, file: file, @@ -375,6 +376,7 @@ public final class EntityKeyboardComponent: Component { contentTopPanels.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(EntityKeyboardTopPanelComponent( theme: component.theme, items: topStickerItems, + containerSideInset: component.containerInsets.left, defaultActiveItemId: stickerContent.itemGroups.first?.groupId, activeContentItemIdUpdated: stickersContentItemIdUpdated, reorderItems: { [weak self] items in @@ -478,6 +480,7 @@ public final class EntityKeyboardComponent: Component { contentTopPanels.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardTopPanelComponent( theme: component.theme, items: topEmojiItems, + containerSideInset: component.containerInsets.left, activeContentItemIdUpdated: emojiContentItemIdUpdated, reorderItems: { [weak self] items in guard let strongSelf = self else { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index 1d760286e5..9028825dd1 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -1010,6 +1010,7 @@ final class EntityKeyboardTopPanelComponent: Component { let theme: PresentationTheme let items: [Item] + let containerSideInset: CGFloat let defaultActiveItemId: AnyHashable? let forceActiveItemId: AnyHashable? let activeContentItemIdUpdated: ActionSlot<(AnyHashable, AnyHashable?, Transition)> @@ -1018,6 +1019,7 @@ final class EntityKeyboardTopPanelComponent: Component { init( theme: PresentationTheme, items: [Item], + containerSideInset: CGFloat, defaultActiveItemId: AnyHashable? = nil, forceActiveItemId: AnyHashable? = nil, activeContentItemIdUpdated: ActionSlot<(AnyHashable, AnyHashable?, Transition)>, @@ -1025,6 +1027,7 @@ final class EntityKeyboardTopPanelComponent: Component { ) { self.theme = theme self.items = items + self.containerSideInset = containerSideInset self.defaultActiveItemId = defaultActiveItemId self.forceActiveItemId = forceActiveItemId self.activeContentItemIdUpdated = activeContentItemIdUpdated @@ -1038,6 +1041,9 @@ final class EntityKeyboardTopPanelComponent: Component { if lhs.items != rhs.items { return false } + if lhs.containerSideInset != rhs.containerSideInset { + return false + } if lhs.defaultActiveItemId != rhs.defaultActiveItemId { return false } @@ -1064,7 +1070,7 @@ final class EntityKeyboardTopPanelComponent: Component { } let topInset: CGFloat = -3.0 - let sideInset: CGFloat = 7.0 + let sideInset: CGFloat let itemSize: CGSize let staticItemSize: CGSize let staticExpandedItemSize: CGSize @@ -1074,7 +1080,9 @@ final class EntityKeyboardTopPanelComponent: Component { let isExpanded: Bool let items: [Item] - init(isExpanded: Bool, height: CGFloat, items: [ItemDescription]) { + init(isExpanded: Bool, containerSideInset: CGFloat, height: CGFloat, items: [ItemDescription]) { + self.sideInset = containerSideInset + 7.0 + self.isExpanded = isExpanded self.itemSize = self.isExpanded ? CGSize(width: 54.0, height: 68.0) : CGSize(width: 28.0, height: 28.0) self.staticItemSize = self.itemSize @@ -1665,7 +1673,7 @@ final class EntityKeyboardTopPanelComponent: Component { } let previousItemLayout = self.itemLayout - let itemLayout = ItemLayout(isExpanded: isExpanded, height: availableSize.height, items: self.items.map { item -> ItemLayout.ItemDescription in + let itemLayout = ItemLayout(isExpanded: isExpanded, containerSideInset: component.containerSideInset, height: availableSize.height, items: self.items.map { item -> ItemLayout.ItemDescription in let isStatic = item.id == AnyHashable("static") return ItemLayout.ItemDescription( isStatic: isStatic, From ea6929fef6cc15753f11766736551c4982fe1331 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 24 Jul 2022 17:31:47 +0200 Subject: [PATCH 3/8] Emoji improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 17 +++++ ...ArchivedStickerPacksNoticeController.swift | 10 ++- .../AttachmentTextInputPanelNode.swift | 22 ++++++ .../Sources/PagerComponent.swift | 1 + .../ContainedViewLayoutTransition.swift | 8 +-- submodules/Display/Source/NavigationBar.swift | 2 +- .../ArchivedStickerPacksController.swift | 11 ++- .../FeaturedStickerPacksController.swift | 12 +++- .../InstalledStickerPacksController.swift | 70 ++++++++++++++++--- .../Sources/ChatControllerNode.swift | 8 +-- .../Sources/ChatHistoryEntriesForView.swift | 2 +- .../Sources/ChatTextInputPanelNode.swift | 48 ++++++++++--- .../Sources/TouchDownGestureRecognizer.swift | 22 +++--- 13 files changed, 191 insertions(+), 42 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 0c3d34a763..f208f82f9f 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7883,8 +7883,13 @@ Sorry for the inconvenience."; "EmojiPackActionInfo.AddedTitle" = "Emoji Added"; "EmojiPackActionInfo.AddedText" = "%@ has been added to your emoji."; "EmojiPackActionInfo.RemovedTitle" = "Emoji Removed"; +"EmojiPackActionInfo.ArchivedTitle" = "Emoji Archived"; "EmojiPackActionInfo.RemovedText" = "%@ is no longer in your emoji."; +"MaskPackActionInfo.RemovedTitle" = "Masks Removed"; +"MaskPackActionInfo.ArchivedTitle" = "Masks Archived"; +"MaskPackActionInfo.RemovedText" = "%@ is no longer in your masks."; + "WebApp.ShareMyPhoneNumber" = "Share My Phone Number"; "WebApp.ShareMyPhoneNumberConfirmation" = "Are you sure you want to share your phone number **%1$@** with **%2$@**?"; @@ -7893,3 +7898,15 @@ Sorry for the inconvenience."; "Emoji.ClearRecent" = "Clear Recent Emoji"; "Premium.AnimatedEmoji.Proceed" = "Unlock Animated Emoji"; + +"EmojiPacksSettings.Title" = "Emoji"; + +"EmojiStickerSettings.Info" = "Artists are welcome to add their own emoji sets using our @stickers bot.\n\nTap on a message to view and add the whole set."; + +"StickerPack.MaskCount_1" = "1 mask"; +"StickerPack.MaskCount_any" = "%@ masks"; + +"StickerPack.EmojiCount_1" = "1 emoji"; +"StickerPack.EmojiCount_any" = "%@ emoji"; + +"StickerSettings.EmojiContextInfo" = "If you archive an emoji set, you can quickly restore it later from the Archived Emoji section."; diff --git a/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift b/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift index bacd63fd78..8e81d93b98 100644 --- a/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift +++ b/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift @@ -127,7 +127,15 @@ private final class ArchivedStickersNoticeAlertContentNode: AlertContentNode { var index: Int = 0 var entries: [ArchivedStickersNoticeEntry] = [] for pack in archivedStickerPacks { - entries.append(ArchivedStickersNoticeEntry(index: index, info: pack.0, topItem: pack.1, count: presentationData.strings.StickerPack_StickerCount(pack.0.count))) + let countTitle: String + if pack.0.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks { + countTitle = presentationData.strings.StickerPack_EmojiCount(pack.0.count) + } else if pack.0.id.namespace == Namespaces.ItemCollection.CloudMaskPacks { + countTitle = presentationData.strings.StickerPack_MaskCount(pack.0.count) + } else { + countTitle = presentationData.strings.StickerPack_StickerCount(pack.0.count) + } + entries.append(ArchivedStickersNoticeEntry(index: index, info: pack.0, topItem: pack.1, count: countTitle)) index += 1 } diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index ae4ea05956..ab164cbba7 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -405,6 +405,17 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS strongSelf.ensureFocused() } } + recognizer.waitForTouchUp = { [weak self] in + guard let strongSelf = self, let textInputNode = strongSelf.textInputNode else { + return true + } + + if textInputNode.textView.isFirstResponder { + return true + } else { + return false + } + } self.textInputBackgroundNode.view.addGestureRecognizer(recognizer) self.emojiViewProvider = { [weak self] emoji in @@ -525,6 +536,17 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS strongSelf.ensureFocused() } } + recognizer.waitForTouchUp = { [weak self] in + guard let strongSelf = self, let textInputNode = strongSelf.textInputNode else { + return true + } + + if textInputNode.textView.isFirstResponder { + return true + } else { + return false + } + } textInputNode.view.addGestureRecognizer(recognizer) textInputNode.textView.accessibilityHint = self.textPlaceholderNode.attributedText?.string diff --git a/submodules/Components/PagerComponent/Sources/PagerComponent.swift b/submodules/Components/PagerComponent/Sources/PagerComponent.swift index a020e249f8..f5d31dfa3a 100644 --- a/submodules/Components/PagerComponent/Sources/PagerComponent.swift +++ b/submodules/Components/PagerComponent/Sources/PagerComponent.swift @@ -430,6 +430,7 @@ public final class PagerComponent() @@ -437,7 +437,8 @@ final class CustomEmojiContainerView: UIView { continue } - let size = CGSize(width: 24.0, height: 24.0) + let itemSize: CGFloat = floor(24.0 * fontSize / 17.0) + let size = CGSize(width: itemSize, height: itemSize) view.frame = CGRect(origin: CGPoint(x: floor(rect.midX - size.width / 2.0), y: floor(rect.midY - size.height / 2.0)), size: size) @@ -896,6 +897,17 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { strongSelf.ensureFocused() } } + recognizer.waitForTouchUp = { [weak self] in + guard let strongSelf = self, let textInputNode = strongSelf.textInputNode else { + return true + } + + if textInputNode.textView.isFirstResponder { + return true + } else { + return false + } + } self.textInputBackgroundNode.isUserInteractionEnabled = true self.textInputBackgroundNode.view.addGestureRecognizer(recognizer) @@ -992,6 +1004,26 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { strongSelf.ensureFocusedOnTap() } } + recognizer.waitForTouchUp = { [weak self] in + guard let strongSelf = self, let textInputNode = strongSelf.textInputNode else { + return true + } + + if textInputNode.textView.isFirstResponder { + return true + } else if let (_, _, _, bottomInset, _, _, metrics, _, _) = strongSelf.validLayout { + let textFieldWaitsForTouchUp: Bool + if case .regular = metrics.widthClass, bottomInset.isZero { + textFieldWaitsForTouchUp = true + } else { + textFieldWaitsForTouchUp = false + } + + return textFieldWaitsForTouchUp + } else { + return false + } + } textInputNode.view.addGestureRecognizer(recognizer) self.touchDownGestureRecognizer = recognizer @@ -1081,14 +1113,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat { let previousAdditionalSideInsets = self.validLayout?.4 self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary, isMediaInputExpanded) - - let textFieldWaitsForTouchUp: Bool - if case .regular = metrics.widthClass, bottomInset.isZero { - textFieldWaitsForTouchUp = true - } else { - textFieldWaitsForTouchUp = false - } - self.touchDownGestureRecognizer?.waitForTouchUp = textFieldWaitsForTouchUp var transition = transition var additionalOffset: CGFloat = 0.0 @@ -2146,6 +2170,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { var rects: [CGRect] = [] var customEmojiRects: [(CGRect, ChatTextInputTextCustomEmojiAttribute)] = [] + let fontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + if let attributedText = textInputNode.attributedText { let beginning = textInputNode.textView.beginningOfDocument attributedText.enumerateAttributes(in: NSMakeRange(0, attributedText.length), options: [], using: { attributes, range, _ in @@ -2229,7 +2255,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { self.customEmojiContainerView = customEmojiContainerView } - customEmojiContainerView.update(emojiRects: customEmojiRects) + customEmojiContainerView.update(fontSize: fontSize, emojiRects: customEmojiRects) } else if let customEmojiContainerView = self.customEmojiContainerView { customEmojiContainerView.removeFromSuperview() self.customEmojiContainerView = nil diff --git a/submodules/TouchDownGesture/Sources/TouchDownGestureRecognizer.swift b/submodules/TouchDownGesture/Sources/TouchDownGestureRecognizer.swift index 2b5a981b6c..22ed7f1421 100644 --- a/submodules/TouchDownGesture/Sources/TouchDownGestureRecognizer.swift +++ b/submodules/TouchDownGesture/Sources/TouchDownGestureRecognizer.swift @@ -6,7 +6,8 @@ public class TouchDownGestureRecognizer: UIGestureRecognizer, UIGestureRecognize public var touchDown: (() -> Void)? private var touchLocation: CGPoint? - public var waitForTouchUp = false + public var waitForTouchUp: (() -> Bool)? + private var isWaitingForTouchUp: Bool = false override public init(target: Any?, action: Selector?) { super.init(target: target, action: action) @@ -14,6 +15,13 @@ public class TouchDownGestureRecognizer: UIGestureRecognizer, UIGestureRecognize self.delegate = self } + override public func reset() { + self.touchLocation = nil + self.isWaitingForTouchUp = false + + super.reset() + } + public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } @@ -21,7 +29,8 @@ public class TouchDownGestureRecognizer: UIGestureRecognizer, UIGestureRecognize override public func touchesBegan(_ touches: Set, with event: UIEvent) { super.touchesBegan(touches, with: event) - if self.waitForTouchUp { + if let waitForTouchUp = self.waitForTouchUp, waitForTouchUp() { + self.isWaitingForTouchUp = true if let touch = touches.first { self.touchLocation = touch.location(in: self.view) } @@ -49,14 +58,9 @@ public class TouchDownGestureRecognizer: UIGestureRecognizer, UIGestureRecognize override public func touchesEnded(_ touches: Set, with event: UIEvent) { super.touchesEnded(touches, with: event) - if let touchDown = self.touchDown, self.waitForTouchUp { + if let touchDown = self.touchDown, self.isWaitingForTouchUp { + self.isWaitingForTouchUp = false touchDown() } } - - override public func reset() { - self.touchLocation = nil - - super.reset() - } } From df6e87cc3d1597bdd2ca7986ad56cb7c638ecd79 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 24 Jul 2022 18:18:59 +0200 Subject: [PATCH 4/8] Emoji fixes --- .../Sources/AnimationCache.swift | 4 ++ .../Resources/Animations/anim_botToKey.tgs | Bin 1774 -> 0 bytes .../Resources/Animations/anim_keyToBot.tgs | Bin 1763 -> 0 bytes .../Resources/Animations/anim_keyToSmile.tgs | Bin 1506 -> 0 bytes .../Animations/anim_keyToSticker.tgs | Bin 2072 -> 0 bytes ...botToKey.json => input_anim_botToKey.json} | 0 ...keyToBot.json => input_anim_keyToBot.json} | 0 ...oSmile.json => input_anim_keyToSmile.json} | 0 ...cker.json => input_anim_keyToSticker.json} | 0 ...eToKey.json => input_anim_smileToKey.json} | 0 ...er.json => input_anim_smileToSticker.json} | 0 ...oKey.json => input_anim_stickerToKey.json} | 0 ...le.json => input_anim_stickerToSmile.json} | 0 .../Sources/ChatTextInputPanelNode.swift | 32 ++++----- .../Source/UIKitRuntimeUtils/UIKitUtils.m | 63 +++++++----------- 15 files changed, 44 insertions(+), 55 deletions(-) delete mode 100644 submodules/TelegramUI/Resources/Animations/anim_botToKey.tgs delete mode 100644 submodules/TelegramUI/Resources/Animations/anim_keyToBot.tgs delete mode 100644 submodules/TelegramUI/Resources/Animations/anim_keyToSmile.tgs delete mode 100644 submodules/TelegramUI/Resources/Animations/anim_keyToSticker.tgs rename submodules/TelegramUI/Resources/Animations/{anim_botToKey.json => input_anim_botToKey.json} (100%) rename submodules/TelegramUI/Resources/Animations/{anim_keyToBot.json => input_anim_keyToBot.json} (100%) rename submodules/TelegramUI/Resources/Animations/{anim_keyToSmile.json => input_anim_keyToSmile.json} (100%) rename submodules/TelegramUI/Resources/Animations/{anim_keyToSticker.json => input_anim_keyToSticker.json} (100%) rename submodules/TelegramUI/Resources/Animations/{anim_smileToKey.json => input_anim_smileToKey.json} (100%) rename submodules/TelegramUI/Resources/Animations/{anim_smiletosticker.json => input_anim_smileToSticker.json} (100%) rename submodules/TelegramUI/Resources/Animations/{anim_stickerToKey.json => input_anim_stickerToKey.json} (100%) rename submodules/TelegramUI/Resources/Animations/{anim_stickertosmile.json => input_anim_stickerToSmile.json} (100%) diff --git a/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift b/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift index efb5fd4379..93d442d7f9 100644 --- a/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift +++ b/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift @@ -456,6 +456,10 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter { } func add(with drawingBlock: (AnimationCacheItemDrawingSurface) -> Double?, proposedWidth: Int, proposedHeight: Int) { + if proposedWidth == 0 || proposedHeight == 0 { + self.isFailed = true + return + } if self.isFailed || self.isFinished { return } diff --git a/submodules/TelegramUI/Resources/Animations/anim_botToKey.tgs b/submodules/TelegramUI/Resources/Animations/anim_botToKey.tgs deleted file mode 100644 index 20939237d65e0771360b3201dd1797e2184878b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1774 zcmVWlo*MOBq~+d4U!rNGSjj{b zN0)Up&3PQHZlWn%c5o3Yj=n}ynL_)@T(=vrIoq`#+ug_O#?&GKqWLCmKa$_U{B{29(cYu_$Qpz~GHFu!8a}tpkMgn&rY0+L!UQ~hs5az-m zt`^K@Jm%C`xyF5bhjwz@@P1B{OaU4Agh3WT49+D+;*^?DNuyx?jB0LFwZk3iHCKz9 zB>}I2jy~0Z?5ZU&wzLu`Xs#kR+;y`bH!+S`fEU`$W{F^23LOu`MGd(1JE?Du9%mzx zc+>~zf6@BbQA&kQ;p$AOFR`7Ve;!TS`t$O>zd&}ToE6p_@AX~Rg*|qb{r-5w+RhMK zB2F@vnMtNFpF}c2MnccO$jw|cD+{J;6XxDLDZee+tBHPAH(O}^^vlohCVwr@+g&~R zpjUj7Oy1YK&o`iO1&e&SST{=ySugeFIzrcjgo}k4CbNq>mIE_Pf;q1DOw7GJm|-Fq ziGB#YTo^tRyn7B5J;k7Uw!tZ3jK8kd>+u-h*NdC0)j33|bw-11+I>MvFUVe5(eZzM z-uMLrn;rxq0PAyl*8j%KW{qXh$Kyj=@2;2o5FxEmOj`Z$-wZJEa%)(IY!MnQKUjOX zhv#T1&L0L^*VpEH%t$Xmvlx>o`waY1FSgC)uHHl%#vy@hAyh#6+*DcvrKxcv2=D!W`yo&Rxlx6_+)mX zvlE^D=yaAJpU$!e=q!8HboT8hv!3CJ&fX@S!P!i7Y{qqG$7gUn%R4qB;fc>qe0Jiq z1NkgFKA)+O&;H!5cH4)j?5PPPACW*$=f}s?d7*N~m57C^7|*JX8KKZg@MA8HBdKXS z`_q@4oMqfNdm>vtfOG79TFhhkH^e>mKJ6M9miItVJXXZT!}V}9Z9{o(q9!0-n8i|k z#*)aXcm+O#+f-}iISm3=-RsfT7-z|E<}Ck}bcaBCd<1N$1OUZvL80*XMxPxPecF#+ z9y|KPOMt|!b&UNMm&9ep9u(AJD9Anv+5doo3)ZeUV&I^$xv>X<{21?udt7_kP#mzK zuq>|k66$z+i9Fm(klUBqOjss=hRuXsNy+0>c$x4f&bk=NEM+p6wxD!X278i$5X(xz zGNKb!mKBv0!E#qcOa_e~TMr&IV^rgyka0n$>xBBkQJ?eV*>0k}d$Vs%TQ%5pd07jZ z%a1w&Sc-}8-E#^5uo_~k@LE!o2nPo+#cac>lXhWIek21Key(+`ES-hYo zsA+95qRL&mx=r7M>Z2%_kg|5&dG;NXU*nd%TBo5;bV<{2w2_3?M%%a#1`Q@;naH>g zLkR=5J`EU252eJPRou~7X+i7aaXa&p0sO^;TCf0p4xK^!< zYS;=i11I4Cz2U>jk(0!Dp#c&*Leg*h+TN*Rj=%U8csP2}riGDlrq1U%}aEXmKiNpQRQLo3a-C#$&|@-P>j(EbtL7MDq>W`R+;H9 z#u2i7q6<6;eR}u=$oT0jF=H~yUS5{^0AA4xk&2}}BWn^#R@%Iln35)QGB2yO z2^wOYeoGq+k{Ywv4&xHospC-2Q(|2u{Wea5l;?Zj!9YInN+X(4-Qo(%Kq*}=(h4R$ z(I6eQ2mE4!SC7j^J+2)#M)g5LqEGAc(1cV)+{NQ1q~GsP=D!m%40D)|HjpF)_Axo> zVs$Ag;B=rDUO|4NQ|PF4hlFPt6Hb^Dg%Nc9WZ4-CeFqt*Orpj(ef(5`s0<7%lZ(ML z$RM#65Oqa9>1xI@^7*TOr)ES<*X-lH{`&E^%)bK~i|wcNa-veRaZX=u;|$`>qQ0j8 QT{XY|7Z*CJcgZyX04+~zO8@`> diff --git a/submodules/TelegramUI/Resources/Animations/anim_keyToBot.tgs b/submodules/TelegramUI/Resources/Animations/anim_keyToBot.tgs deleted file mode 100644 index 2cc3eed1040ecb071672b5497be60d50950139cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1763 zcmV<91|0bxiwFpEh0$UF18Ze@RBu9WbS`RhZ*BnXTitKlHW2?;3_VkXC`y#%w+-w1 zI;`7X0;5n8r}q2_S#1{t`QLZEBPmi==GJk%AT@%7(tYvnc)A}cI}g#9XdLCd?)-d}I(=JsQ|`FOo)LHqXh7S-zAZrbjm z@zsPy%ldxW=<5f^p08mPENbtgab}FYTQ29@-E@=|U}x7Le*tO_(MJENQE>s}L>u@A zF2o~?nt*3H32?TCBaj1=&ou$K35O}~aRjlAqbfsHKyA^Mc3M<|Ljuf@Ks+s|TqP`_ zo4LDrWDiuO2V2}02#YkWh=-P>ResL2`?~J5E5SNtB!<}3EH~aqq6~Rko{up z3KnNPlUh#jROT#}T#yp51TPet5L$+KpnMaJ+vfB1vA;srUBMG6?2LGW<~t(Mtjqfx zHfY1TjfB$p45L#eT5O4dr?yxg&C~H)-Ofh(`CP5x?x$aVemDASded&2(FeWSqj>bb z*?iuDzzi1nu3qk@7_DCByJdu?_lk`PGmU!?hSnd}D4XDFUx|rV13*`Xu8ZG)W|F<9 zp{xI#o?4jZdW9Mo->>uKayY*CO}(AXZy-V~GZ^5mJrtny0_?RB9sAeQ#lGJMxRgQg z%Nu&W|Hfgp#1`o1;zQeP7Slu7AgRH8wEBVH?&ZU)sr}Yt8_!_N!G4o?xg2b}>ggmG z+U#OTNvD5T4{>r^`+qd`dUv;JR*_EQK3J?FR0#!Ox~oa0>1zWS>^~=yJ+#}=dZHzr z)d08BCvYo&mAI7_ygUlGtl|Z?PJ>%17d~z!ytKHL@Lc+^rCAr)x^(p)4aR>U*viij zTk;9mQm+fP2zHJ*ZuJx|u=Q-%%6S1Sci_?&nLBV9raN*KT!_nw>jGUD=z8POrOpps z*%Q!}y-Mi%{u5VE@d90^f-X37i4M6EP3*7>j$NulE@ED=>w;Yu?0U1TaQ{pjVnqmR7;h}~Kc5Yw`gopRzU%TV1aiIq%qQ0}grM29 zzCG>xR-1DLDOW!fUT~FE`aNG(;J!4;3;foHEbJRWS($H#1T45n(@Njxxp|M6Asft5 zyDuu`+CB0`BK7GuH|ly`J1s+TbO87aW$~>e*t9157Rg{t7A@Eqx13kxbr+8fr$_gl z2_3b_X6FvWBzI%$Z^B|5NE{|M4!mU@C|bL*7pvRh3jp8@v~r%3Q6N;BAbDC!BpA>( zjE+v4oubfJtkhI}uNb?h?JcphCjhbZ6N& zOm>Y~vTBWnHqj)x;n$w8ZKRF)VA8-LTVbWGFF%wpP;1j9BkpA>_Pz4Tth7n%;_-`& zR+ZjZR9q;WPblx+-MY%i&AQ6mt1c~C8PV`AkPM836ZEDJog*W$u|gBXc80ir?@JF< z-6;<=C)4UKYIARH0PR{?Z`(!?{wo5Xm&Bet^p>J+ zURt0?+P)YsmH5?#BBtv=X@ZPGJ4v){}PzZuH8i@rwl$R&BA zA`xBI(L9wRTHQo*xa{DfoQS?fbE8GHt+x*g>;4)&M372k?!%X6Om*=p`9Jb#e_Ai{fcWT_!QS< zi7`%SxJ!k>d$_Zx0~xG7kLLUOOLgy$>E4(7Z{>bD(|~@nh2P%&_`^@LKdbZouAY6w z;AipdeZBi~0}GZA?`64eDokVl$?2lkr;c8WID|@95o7!sS8-D4iqc0iV2;ax9%sOu zJ_8|C)1JR>&tE?6{Oxhkn{o8^)X~Si0*KqQ7935pEE#_Z#HPRAxb1?zgG2CHA9!p zOilUxd9~hGH8`b}lTI(!5rIAEqR<8X^A?(6y8!x`FG8Lq0MF+QDG3(rx}L`KzAkT; zt8)N^YbNpRvHRT|;8}EeJZ$-);rCj-R{~P%%3+ClX!J=R4l+Zx*HcEAnWmi5xNzRT z*X6dk+|`>12LKZNv0d%95abr3%pv!_r}+%}{yveqaak-5=GKPi5%Poy9ZaBlIDvks zZmWkT&`|=OI538k7m3O&4u8t&?%?4j>h9@AHGYuncKWif2k*Y zk`Cbybz;Ix4?fg{_o@<(yDuc!0Ow+gIVV}=aprs@xl@xI=fbm`J{`-MAueRB-aJgoZ$gWr4xFjIrBzyZ#4H>XwD94E_@>S0z8)r80efy zUlYgSryGe$O?cXSd?4wacdYaezk}!7rkdY{rBdCmS*zoR-yiyeEOi_L1G@lE75t%; z2zPN$KHUB}$p`B75%Mt)KbQFQeH*Ia1-pCx*5_P!fARCQrqb{8)W?0jyRIH%Dqi)* zRVU>tH{_~iRbIRpRlOpbN>W+ikYO_(H*}H}eo(P4LyeM#=MPJNbb<9u9k8yY4xUfo z)sd|HoCA(qzrihg4Nl2ta5?tK4jD&FT=CFB-*IohA(lXd!Tm6jQJljR^pg`00Ev!6 zgWtFpPCJUiXzwgoxkR&5<@{n+baI7vB1b8I9!TiuryHp9fnh=J^5HIQ5C;ngs$idd z^?P~!69(SIL7#y5voWKHY^~=3;Jb&BIhHYQHwf*mE`yU`1<@Rb_&76Mu<`3#kSzq)2fmq!C0|KLY Io^K%l0N2s+!2kdN diff --git a/submodules/TelegramUI/Resources/Animations/anim_keyToSticker.tgs b/submodules/TelegramUI/Resources/Animations/anim_keyToSticker.tgs deleted file mode 100644 index 8863943a7d6036c5ea9c41e9e5d27a7024c4c4e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2072 zcmV+z2 z=;f(E(c6n*1gfpXTd#Kwt(rCn^6#AsDN*unojR?PqD5j$B!|P9!{N-KlDF0C>b!EI z6{@PMmqT^#WnFFVt8;kl;h|hz{Z^eDT~{~!*xkX5+y2eZ`);^?G45X+#?94jKfwI! z>uZ|W9S;3?sLn4g>uTG*=?8p&I}LAk@Kkj*zNyY_QuyO;v)_S=!$3o~Fyn2t$6plS>zfaUjUSM0&@!xr=%($+e3I z{q>RAAp06+ecc#g3nr5-=(d3mxBJ=ZoE0egLk)s z)@NUQ{^i+E{nfZ1&b~(g&YH8YhyAbjAaDadzwEY$9-%DH&y0Y6egv|s3;_F|L1FhF z^Cd%gDFJh%6H-hVUizn=GBzUTg%ry*e02-xRGAiHX&{nyj}%lF%V`!w(E+I#oh z-Zw=6G}B(!)Sq!_rpR~%1@|-*+!_V{5m1<%%{xvQOwi=mcmx6eGz9z_f$;1I6osI% zMI02Rpb7682jOWbgf$B7M?hh6*3NM-mxCs}UmUc583FSA?hfbOQCTEq-}J+){tQ(f zj#t>PHrufuKq*NcRr+#Uk+Bch6tJO`b2BNL1c2A1gv6W@?D_eQ2FWZ~b$i^NuZQmb zW^)Awq0CWxMr1+v5A3;|aePSqGVv=YUm65UiJfH;bBYA}(@q9ddwXmWB6H}Djkvtu zKMdXO@Nz%gRY(LEw}2hqJV5>`5t{^IB6>%#TgS!agGHG4y}XRDS_Hcr z`rSy2e!>@%w4rb!rBk(!BEn+|i>KWS<{+ul&vTGo9yraaaxbri~1%Px&d zsc~Ln?bgce1te>{nuTQ(42AY);oC&>ly|}!m}u~e7J;P#=G;(gDHOGnNH9Vt38O_D z@SBelfp#E75j?o6q+oWaB5e261m#Y)v%L3077AfOB$&J&kInX^xeZv6wjWhDFh{v! z>A53Ymu3|Fbp72b1)meX1&vC>Q!~jykrwFj@s+HQLN3 zPZlSGv%M+qW}`ucRT1OPDnlQCFpvs+F}P9)%CrGdB4AM{UqqWi$5Po&Mo%`UXp>6V z7B5ax#MW})Dq(2zT_Ch#D}sSI4G^f2V%-P@9XMBO>H%S^YMj}+)HWDixkDSeu|U=n zbUIuRI)KU+?4@%FSh(7Pw4@v$g@$-4O3+Oq0D?+$1agE~vZUw;R#>2D zjl;;Z%aALo?1?IY)%EvNg_18nkd%QOpso)iXCI!aWf7TlX^gZdGE+2b%h(0=GNM## za)dAGAC1rla~TW0q^HmVi!9ciiK610I0SK|R0E-EIePP_Jj+=$ayChAP$?DXWGi9k zVB>6?4g+SJsSVkO^a~0VXj*Kd*=|8aPvn+Q4K6z64zpL_MC~E=vDxMi9#(2BBY{(W zYC}#UxZq$xoLe}tnktls5|vw^5{@?17f%gO4dd4^UH%J6yZ)c8|4Z1qU9$C?zPtWs zxH^IUFNpDum zTC-BpnN_YevO>0FCPzyJrQ%|}r7ihWMOMG`WaZ})f)bo&VbILPC%sMK&PPBGz1&$Lz=f1F1Hh4Ti1 zNIVnXLaP{}Rwsz~0s+-jAx2Izfg>VZgb7XvWtaqu+os!8_28C6H-g8=2!HTbekaOe~*GY-SrV5UeUAKT-g-vFvkU ziVC!IGLypsZXuE`b&_|#Mb->Ll8Q0h2%w1LeKK;X*03Q#L`U2K?r;zsNfcUoZb8Kk zEl9S3|FJg!-Gy2nErp1MCD2VA^P~m#C&obKpPIn)V>;e=y#%v>{ie1p+YZQ?#+!=< zB#yD-Pm~3OB@0mhf4ci1GVmRs(d}Ms`!lEic}j4JAF+m8{!dDm@BRRABNC<(B>(`n CjOg|N diff --git a/submodules/TelegramUI/Resources/Animations/anim_botToKey.json b/submodules/TelegramUI/Resources/Animations/input_anim_botToKey.json similarity index 100% rename from submodules/TelegramUI/Resources/Animations/anim_botToKey.json rename to submodules/TelegramUI/Resources/Animations/input_anim_botToKey.json diff --git a/submodules/TelegramUI/Resources/Animations/anim_keyToBot.json b/submodules/TelegramUI/Resources/Animations/input_anim_keyToBot.json similarity index 100% rename from submodules/TelegramUI/Resources/Animations/anim_keyToBot.json rename to submodules/TelegramUI/Resources/Animations/input_anim_keyToBot.json diff --git a/submodules/TelegramUI/Resources/Animations/anim_keyToSmile.json b/submodules/TelegramUI/Resources/Animations/input_anim_keyToSmile.json similarity index 100% rename from submodules/TelegramUI/Resources/Animations/anim_keyToSmile.json rename to submodules/TelegramUI/Resources/Animations/input_anim_keyToSmile.json diff --git a/submodules/TelegramUI/Resources/Animations/anim_keyToSticker.json b/submodules/TelegramUI/Resources/Animations/input_anim_keyToSticker.json similarity index 100% rename from submodules/TelegramUI/Resources/Animations/anim_keyToSticker.json rename to submodules/TelegramUI/Resources/Animations/input_anim_keyToSticker.json diff --git a/submodules/TelegramUI/Resources/Animations/anim_smileToKey.json b/submodules/TelegramUI/Resources/Animations/input_anim_smileToKey.json similarity index 100% rename from submodules/TelegramUI/Resources/Animations/anim_smileToKey.json rename to submodules/TelegramUI/Resources/Animations/input_anim_smileToKey.json diff --git a/submodules/TelegramUI/Resources/Animations/anim_smiletosticker.json b/submodules/TelegramUI/Resources/Animations/input_anim_smileToSticker.json similarity index 100% rename from submodules/TelegramUI/Resources/Animations/anim_smiletosticker.json rename to submodules/TelegramUI/Resources/Animations/input_anim_smileToSticker.json diff --git a/submodules/TelegramUI/Resources/Animations/anim_stickerToKey.json b/submodules/TelegramUI/Resources/Animations/input_anim_stickerToKey.json similarity index 100% rename from submodules/TelegramUI/Resources/Animations/anim_stickerToKey.json rename to submodules/TelegramUI/Resources/Animations/input_anim_stickerToKey.json diff --git a/submodules/TelegramUI/Resources/Animations/anim_stickertosmile.json b/submodules/TelegramUI/Resources/Animations/input_anim_stickerToSmile.json similarity index 100% rename from submodules/TelegramUI/Resources/Animations/anim_stickertosmile.json rename to submodules/TelegramUI/Resources/Animations/input_anim_stickerToSmile.json diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 8656ef430b..a5986e03e5 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -207,60 +207,60 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { case .keyboard: if let previousInputMode = previousInputMode { if case .stickers = previousInputMode { - animationName = "anim_stickerToKey" + animationName = "input_anim_stickerToKey" animationMode = .animating(loop: false) } else if case .emoji = previousInputMode { - animationName = "anim_smileToKey" + animationName = "input_anim_smileToKey" animationMode = .animating(loop: false) } else if case .bot = previousInputMode { - animationName = "anim_botToKey" + animationName = "input_anim_botToKey" animationMode = .animating(loop: false) } else { - animationName = "anim_stickerToKey" + animationName = "input_anim_stickerToKey" } } else { - animationName = "anim_stickerToKey" + animationName = "input_anim_stickerToKey" } case .stickers: if let previousInputMode = previousInputMode { if case .keyboard = previousInputMode { - animationName = "anim_keyToSticker" + animationName = "input_anim_keyToSticker" animationMode = .animating(loop: false) } else if case .emoji = previousInputMode { - animationName = "anim_smileToSticker" + animationName = "input_anim_smileToSticker" animationMode = .animating(loop: false) colorKeys = emojiColorKeys } else { - animationName = "anim_keyToSticker" + animationName = "input_anim_keyToSticker" } } else { - animationName = "anim_keyToSticker" + animationName = "input_anim_keyToSticker" } case .emoji: if let previousInputMode = previousInputMode { if case .keyboard = previousInputMode { - animationName = "anim_keyToSmile" + animationName = "input_anim_keyToSmile" animationMode = .animating(loop: false) } else if case .stickers = previousInputMode { - animationName = "anim_stickerToSmile" + animationName = "input_anim_stickerToSmile" animationMode = .animating(loop: false) colorKeys = emojiColorKeys } else { - animationName = "anim_keyToSmile" + animationName = "input_anim_keyToSmile" } } else { - animationName = "anim_keyToSmile" + animationName = "input_anim_keyToSmile" } case .bot: if let previousInputMode = previousInputMode { if case .keyboard = previousInputMode { - animationName = "anim_keyToBot" + animationName = "input_anim_keyToBot" animationMode = .animating(loop: false) } else { - animationName = "anim_keyToBot" + animationName = "input_anim_keyToBot" } } else { - animationName = "anim_keyToBot" + animationName = "input_anim_keyToBot" } } } else { diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m index a58c63b7c8..9241d2297b 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m @@ -169,46 +169,31 @@ void applySmoothRoundedCornersImpl(CALayer * _Nonnull layer) { } } -/*@interface _UIPortalView : UIView - -@property(nonatomic, getter=_isGeometryFrozen, setter=_setGeometryFrozen:) _Bool _geometryFrozen; // @synthesize _geometryFrozen=__geometryFrozen; -@property(nonatomic) _Bool forwardsClientHitTestingToSourceView; // @synthesize forwardsClientHitTestingToSourceView=_forwardsClientHitTestingToSourceView; -@property(copy, nonatomic) NSString * _Nullable name; // @synthesize name=_name; -@property(nonatomic) __weak UIView * _Nullable sourceView; // @synthesize sourceView=_sourceView; -- (void)setCenter:(struct CGPoint)arg1; -- (void)setBounds:(struct CGRect)arg1; -- (void)setFrame:(struct CGRect)arg1; -- (void)setHidden:(_Bool)arg1; -@property(nonatomic) _Bool allowsHitTesting; // @dynamic allowsHitTesting; -@property(nonatomic) _Bool allowsBackdropGroups; // @dynamic allowsBackdropGroups; -@property(nonatomic) _Bool matchesPosition; // @dynamic matchesPosition; -@property(nonatomic) _Bool matchesTransform; // @dynamic matchesTransform; -@property(nonatomic) _Bool matchesAlpha; // @dynamic matchesAlpha; -@property(nonatomic) _Bool hidesSourceView; // @dynamic hidesSourceView; -- (instancetype _Nonnull)initWithFrame:(struct CGRect)arg1; -- (instancetype _Nonnull)initWithSourceView:(UIView * _Nullable)arg1; - -@end*/ - UIView * _Nullable makePortalView() { - static Class portalViewClass = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - portalViewClass = NSClassFromString([@[@"_", @"UI", @"Portal", @"View"] componentsJoinedByString:@""]); - }); - if (!portalViewClass) { + if (@available(iOS 12.0, *)) { + static Class portalViewClass = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + portalViewClass = NSClassFromString([@[@"_", @"UI", @"Portal", @"View"] componentsJoinedByString:@""]); + }); + if (!portalViewClass) { + return nil; + } + UIView *view = [[portalViewClass alloc] init]; + if (!view) { + return nil; + } + + if (@available(iOS 13.0, *)) { + view.forwardsClientHitTestingToSourceView = false; + } + view.matchesPosition = true; + view.matchesTransform = true; + view.matchesAlpha = false; + view.allowsHitTesting = false; + + return view; + } else { return nil; } - UIView *view = [[portalViewClass alloc] init]; - if (!view) { - return nil; - } - - view.forwardsClientHitTestingToSourceView = false; - view.matchesPosition = true; - view.matchesTransform = true; - view.matchesAlpha = false; - view.allowsHitTesting = false; - - return view; } From 68ab84bed46b7861ec7c1dcdb8de11470c537631 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 24 Jul 2022 19:10:37 +0200 Subject: [PATCH 5/8] Don't apply animation if not yet finished --- .../Sources/LottieAnimationComponent.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/submodules/Components/LottieAnimationComponent/Sources/LottieAnimationComponent.swift b/submodules/Components/LottieAnimationComponent/Sources/LottieAnimationComponent.swift index ea7b60c4dd..c584daf0e8 100644 --- a/submodules/Components/LottieAnimationComponent/Sources/LottieAnimationComponent.swift +++ b/submodules/Components/LottieAnimationComponent/Sources/LottieAnimationComponent.swift @@ -141,6 +141,7 @@ public final class LottieAnimationComponent: Component { } if let animationView = self.animationView, animationView.isAnimationPlaying { + updateComponent = false self.currentCompletion = { [weak self] in guard let strongSelf = self else { return @@ -154,6 +155,7 @@ public final class LottieAnimationComponent: Component { self.animationView?.removeFromSuperview() self.didPlayToCompletion = false + self.currentCompletion = nil if let url = getAppBundle().url(forResource: component.animation.name, withExtension: "json"), let animation = Animation.filepath(url.path) { let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable)) From 58c710200729e376a0a9e8d19f583606b47dcc61 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 24 Jul 2022 20:23:40 +0200 Subject: [PATCH 6/8] Fix text input layout --- .../Source/ASTextKitComponents.mm | 4 +++- .../Sources/ChatTextInputPanelNode.swift | 16 +++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/submodules/AsyncDisplayKit/Source/ASTextKitComponents.mm b/submodules/AsyncDisplayKit/Source/ASTextKitComponents.mm index 7bfee153fa..bba12025a8 100644 --- a/submodules/AsyncDisplayKit/Source/ASTextKitComponents.mm +++ b/submodules/AsyncDisplayKit/Source/ASTextKitComponents.mm @@ -172,8 +172,10 @@ // If our text-view's width is already the constrained width, we can use our existing TextKit stack for this sizing calculation. // Otherwise, we create a temporary stack to size for `constrainedWidth`. + UIEdgeInsets additionalInsets = UIEdgeInsetsZero; if (CGRectGetWidth(components.textView.threadSafeBounds) != constrainedWidth) { - components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, CGFLOAT_MAX)]; + additionalInsets = self.textView.textContainerInset; + components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth - additionalInsets.left - additionalInsets.right, CGFLOAT_MAX)]; } // Force glyph generation and layout, which may not have happened yet (and isn't triggered by -usedRectForTextContainer:). diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index a5986e03e5..928cadb6c7 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -581,13 +581,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } func updateInputTextState(_ state: ChatTextInputState, keepSendButtonEnabled: Bool, extendedSearchLayout: Bool, accessoryItems: [ChatTextInputAccessoryItem], animated: Bool) { - if state.inputText.length != 0 && self.textInputNode == nil { - self.loadTextInputNode() - } - - if let textInputNode = self.textInputNode, let currentState = self.presentationInterfaceState { - self.updatingInputState = true - + if let currentState = self.presentationInterfaceState { var updateAccessoryButtons = false if accessoryItems.count == self.accessoryItemButtons.count { for i in 0 ..< accessoryItems.count { @@ -624,6 +618,14 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } self.accessoryItemButtons = updatedButtons } + } + + if state.inputText.length != 0 && self.textInputNode == nil { + self.loadTextInputNode() + } + + if let textInputNode = self.textInputNode, let _ = self.presentationInterfaceState { + self.updatingInputState = true var textColor: UIColor = .black var accentTextColor: UIColor = .blue From c95c59661aea64547279fff251375e20444f4338 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 24 Jul 2022 20:55:19 +0200 Subject: [PATCH 7/8] Emoji improvements --- .../Sources/EmojiPagerContentComponent.swift | 34 ++++++++++++++++--- .../EntityKeyboardTopPanelComponent.swift | 10 ++++-- .../Sources/MultiAnimationRenderer.swift | 29 +++++++++++----- .../ChatInterfaceStateContextQueries.swift | 4 ++- .../Sources/ChatTextInputPanelNode.swift | 14 ++++---- 5 files changed, 68 insertions(+), 23 deletions(-) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index eb5f007d65..f36990b98a 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -1205,7 +1205,11 @@ private final class GroupExpandActionButton: UIButton { self.currentTextLayout = (title, color, textConstrainedWidth, textSize) } - let size = CGSize(width: textSize.width + 10.0 * 2.0, height: 28.0) + var sideInset: CGFloat = 10.0 + if textSize.width > 24.0 { + sideInset = 6.0 + } + let size = CGSize(width: textSize.width + sideInset * 2.0, height: 28.0) let textFrame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floor((size.height - textSize.height) / 2.0)), size: textSize) self.textLayer.frame = textFrame @@ -2834,6 +2838,7 @@ public final class EmojiPagerContentComponent: Component { let groupHeaderPoint = self.scrollView.convert(locationInScrollView, to: groupHeader) if let clearIconLayer = groupHeader.clearIconLayer, clearIconLayer.frame.insetBy(dx: -4.0, dy: -4.0).contains(groupHeaderPoint) { component.inputInteractionHolder.inputInteraction?.clearGroup(id) + return } else { if groupHeader.tapGesture(recognizer) { return @@ -3019,8 +3024,16 @@ public final class EmojiPagerContentComponent: Component { let contentAnimation = transition.userData(ContentAnimation.self) var transitionHintInstalledGroupId: AnyHashable? - if let contentAnimation = contentAnimation, case let .groupInstalled(groupId) = contentAnimation.type { - transitionHintInstalledGroupId = groupId + var transitionHintExpandedGroupId: AnyHashable? + if let contentAnimation = contentAnimation { + switch contentAnimation.type { + case let .groupInstalled(groupId): + transitionHintInstalledGroupId = groupId + case let .groupExpanded(groupId): + transitionHintExpandedGroupId = groupId + default: + break + } } for groupItems in itemLayout.visibleItems(for: effectiveVisibleBounds) { @@ -3504,8 +3517,19 @@ public final class EmojiPagerContentComponent: Component { for (id, button) in self.visibleGroupExpandActionButtons { if !validGroupExpandActionButtons.contains(id) { removedGroupExpandActionButtonIds.append(id) - button.removeFromSuperview() - button.tintContainerLayer.removeFromSuperlayer() + + if !transition.animation.isImmediate && transitionHintExpandedGroupId == id { + button.alpha = 0.0 + button.layer.animateScale(from: 1.0, to: 0.5, duration: 0.2) + let tintContainerLayer = button.tintContainerLayer + button.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak button, weak tintContainerLayer] _ in + button?.removeFromSuperview() + tintContainerLayer?.removeFromSuperlayer() + }) + } else { + button.removeFromSuperview() + button.tintContainerLayer.removeFromSuperlayer() + } } } for id in removedGroupExpandActionButtonIds { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index 9028825dd1..694a8b0956 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -700,7 +700,7 @@ final class EntityKeyboardStaticStickersPanelComponent: Component { for i in 0 ..< items.count { if AnyHashable(items[i].rawValue) == scrollToItem { let itemFrame = itemLayout.frame(at: i) - self.scrollView.scrollRectToVisible(itemFrame.insetBy(dx: -itemLayout.sideInset, dy: 0.0), animated: true) + self.scrollView.scrollRectToVisible(itemFrame.insetBy(dx: -itemLayout.sideInset - (itemLayout.itemSpacing + itemFrame.width) * 2.0, dy: 0.0), animated: true) break } } @@ -1898,7 +1898,13 @@ final class EntityKeyboardTopPanelComponent: Component { for i in 0 ..< component.items.count { if component.items[i].id == itemId { let itemFrame = itemLayout.containerFrame(at: i) - self.scrollView.scrollRectToVisible(itemFrame.insetBy(dx: -2.0, dy: 0.0), animated: true) + let expandedInset: CGFloat + if itemLayout.isExpanded { + expandedInset = -2.0 + } else { + expandedInset = -itemLayout.sideInset - (itemFrame.width + itemLayout.itemSpacing) * 2.0 + } + self.scrollView.scrollRectToVisible(itemFrame.insetBy(dx: expandedInset, dy: 0.0), animated: true) break } } diff --git a/submodules/TelegramUI/Components/MultiAnimationRenderer/Sources/MultiAnimationRenderer.swift b/submodules/TelegramUI/Components/MultiAnimationRenderer/Sources/MultiAnimationRenderer.swift index 126894163b..85d5dc1f55 100644 --- a/submodules/TelegramUI/Components/MultiAnimationRenderer/Sources/MultiAnimationRenderer.swift +++ b/submodules/TelegramUI/Components/MultiAnimationRenderer/Sources/MultiAnimationRenderer.swift @@ -535,26 +535,37 @@ public final class MultiAnimationRendererImpl: MultiAnimationRenderer { private var groupContext: GroupContext? private var frameSkip: Int - private var displayLink: ConstantDisplayLinkAnimator? + private var displayTimer: Foundation.Timer? private(set) var isPlaying: Bool = false { didSet { if self.isPlaying != oldValue { if self.isPlaying { - if self.displayLink == nil { - self.displayLink = ConstantDisplayLinkAnimator { [weak self] in + if self.displayTimer == nil { + final class TimerTarget: NSObject { + private let f: () -> Void + + init(_ f: @escaping () -> Void) { + self.f = f + } + + @objc func timerEvent() { + self.f() + } + } + let displayTimer = Foundation.Timer(timeInterval: CGFloat(self.frameSkip) / 60.0, target: TimerTarget { [weak self] in guard let strongSelf = self else { return } strongSelf.animationTick() - } - self.displayLink?.frameInterval = self.frameSkip - self.displayLink?.isPaused = false + }, selector: #selector(TimerTarget.timerEvent), userInfo: nil, repeats: true) + self.displayTimer = displayTimer + RunLoop.main.add(displayTimer, forMode: .common) } } else { - if let displayLink = self.displayLink { - self.displayLink = nil - displayLink.invalidate() + if let displayTimer = self.displayTimer { + self.displayTimer = nil + displayTimer.invalidate() } } } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift index b4dc54ec9c..4971c3243e 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift @@ -376,7 +376,9 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee switch attribute { case let .CustomEmoji(_, alt, _): if !alt.isEmpty, let keyword = allEmoticons[alt] { - result.append((alt, item.file, keyword)) + if !item.file.isPremiumEmoji || hasPremium { + result.append((alt, item.file, keyword)) + } } default: break diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 928cadb6c7..6d5a77b118 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -60,11 +60,11 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { self.addSubnode(self.iconImageNode) switch item { - case .input, .botInput: - self.iconImageNode.isHidden = true - self.animationView = ComponentView() - default: - break + case .input, .botInput: + self.iconImageNode.isHidden = true + self.animationView = ComponentView() + default: + break } if let text = text { @@ -84,9 +84,11 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { if highlighted { strongSelf.layer.removeAnimation(forKey: "opacity") strongSelf.alpha = 0.4 + strongSelf.layer.allowsGroupOpacity = true } else { strongSelf.alpha = 1.0 strongSelf.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + strongSelf.layer.allowsGroupOpacity = false } } } @@ -269,7 +271,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { var colors: [String: UIColor] = [:] for colorKey in colorKeys { - colors[colorKey] = self.theme.chat.inputPanel.inputControlColor + colors[colorKey] = self.theme.chat.inputPanel.inputControlColor.blitOver(self.theme.chat.inputPanel.inputBackgroundColor, alpha: 1.0) } let _ = animationView.update( From c1595c9de9e45ffe70700af69306458441433c2d Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 24 Jul 2022 21:38:41 +0200 Subject: [PATCH 8/8] Emoji improvements --- .../Sources/DefaultDayPresentationTheme.swift | 2 +- .../ChatInterfaceStateAccessoryPanels.swift | 2 +- .../Sources/ChatTextInputPanelNode.swift | 14 +- .../Sources/ForwardAccessoryPanelNode.swift | 174 ++++++++++-------- .../Sources/PeerSelectionControllerNode.swift | 2 +- 5 files changed, 112 insertions(+), 82 deletions(-) diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index bc6bb3a410..703c677673 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -849,7 +849,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio inputStrokeColor: UIColor(rgb: 0x000000, alpha: 0.1), inputPlaceholderColor: UIColor(rgb: 0xbebec0), inputTextColor: UIColor(rgb: 0x000000), - inputControlColor: UIColor(rgb: 0xa0a7b0), + inputControlColor: UIColor(rgb: 0x868D98), actionControlFillColor: defaultDayAccentColor, actionControlForegroundColor: UIColor(rgb: 0xffffff), primaryTextColor: UIColor(rgb: 0x000000), diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift index a0a8432548..7e5e257aa2 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift @@ -60,7 +60,7 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS forwardPanelNode.updateThemeAndStrings(theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, forwardOptionsState: chatPresentationInterfaceState.interfaceState.forwardOptionsState) return forwardPanelNode } else { - let panelNode = ForwardAccessoryPanelNode(context: context, messageIds: forwardMessageIds, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, fontSize: chatPresentationInterfaceState.fontSize, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder, forwardOptionsState: chatPresentationInterfaceState.interfaceState.forwardOptionsState) + let panelNode = ForwardAccessoryPanelNode(context: context, messageIds: forwardMessageIds, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, fontSize: chatPresentationInterfaceState.fontSize, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder, forwardOptionsState: chatPresentationInterfaceState.interfaceState.forwardOptionsState, animationCache: chatControllerInteraction?.presentationContext.animationCache, animationRenderer: chatControllerInteraction?.presentationContext.animationRenderer) panelNode.interfaceInteraction = interfaceInteraction return panelNode } diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 6d5a77b118..6b6a608c10 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -119,7 +119,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { fatalError("init(coder:) has not been implemented") } - static func imageAndInsets(item: ChatTextInputAccessoryItem, theme: PresentationTheme, strings: PresentationStrings) -> (UIImage?, String?, String, CGFloat, UIEdgeInsets) { + private static func imageAndInsets(item: ChatTextInputAccessoryItem, theme: PresentationTheme, strings: PresentationStrings) -> (UIImage?, String?, String, CGFloat, UIEdgeInsets) { switch item { case let .input(isEnabled, inputMode), let .botInput(isEnabled, inputMode): switch inputMode { @@ -149,7 +149,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { } } - static func calculateWidth(item: ChatTextInputAccessoryItem, image: UIImage?, text: String?, strings: PresentationStrings) -> CGFloat { + private static func calculateWidth(item: ChatTextInputAccessoryItem, image: UIImage?, text: String?, strings: PresentationStrings) -> CGFloat { switch item { case .input, .botInput, .silentPost, .commands, .scheduledMessages: return 32.0 @@ -175,7 +175,9 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { self.iconImageNode.frame = imageFrame if let animationView = self.animationView { - let animationFrame = imageFrame.insetBy(dx: -4.0, dy: -4.0) + let iconSize: CGSize = CGSize(width: 32.0, height: 32.0) + let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0) - bottomInset), size: iconSize) + let animationFrame = iconFrame.insetBy(dx: -4.0, dy: -4.0) var previousInputMode: ChatTextInputAccessoryItem.InputMode? var inputMode: ChatTextInputAccessoryItem.InputMode? @@ -274,7 +276,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { colors[colorKey] = self.theme.chat.inputPanel.inputControlColor.blitOver(self.theme.chat.inputPanel.inputBackgroundColor, alpha: 1.0) } - let _ = animationView.update( + let animationSize = animationView.update( transition: .immediate, component: AnyComponent(LottieAnimationComponent( animation: LottieAnimationComponent.AnimationItem( @@ -282,7 +284,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { mode: animationMode ), colors: colors, - size: animationFrame.size + size: CGSize(width: 32.0, height: 32.0) )), environment: {}, containerSize: animationFrame.size @@ -292,7 +294,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode { if view.superview == nil { self.view.addSubview(view) } - view.frame = animationFrame + view.frame = CGRect(origin: CGPoint(x: animationFrame.minX + floor((animationFrame.width - animationSize.width) / 2.0), y: animationFrame.minY + floor((animationFrame.height - animationSize.height) / 2.0)), size: animationSize) } } } diff --git a/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift index 4c6f222d5b..064cb6b28d 100644 --- a/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift @@ -15,67 +15,70 @@ import TextFormat import Markdown import TelegramNotices import ChatPresentationInterfaceState +import TextNodeWithEntities +import AnimationCache +import MultiAnimationRenderer -func textStringForForwardedMessage(_ message: Message, strings: PresentationStrings) -> (String, Bool) { +func textStringForForwardedMessage(_ message: Message, strings: PresentationStrings) -> (text: String, entities: [MessageTextEntity], isMedia: Bool) { for media in message.media { switch media { - case _ as TelegramMediaImage: - return (strings.Message_Photo, true) - case let file as TelegramMediaFile: - if file.isVideoSticker || file.isAnimatedSticker { - return (strings.Message_Sticker, true) - } - var fileName: String = strings.Message_File - for attribute in file.attributes { - switch attribute { - case .Sticker: - return (strings.Message_Sticker, true) - case let .FileName(name): - fileName = name - case let .Audio(isVoice, _, title, performer, _): - if isVoice { - return (strings.Message_Audio, true) - } else { - if let title = title, let performer = performer, !title.isEmpty, !performer.isEmpty { - return (title + " — " + performer, true) - } else if let title = title, !title.isEmpty { - return (title, true) - } else if let performer = performer, !performer.isEmpty { - return (performer, true) - } else { - return (strings.Message_Audio, true) - } - } - case .Video: - if file.isAnimated { - return (strings.Message_Animation, true) - } else { - return (strings.Message_Video, true) - } - default: - break + case _ as TelegramMediaImage: + return (strings.Message_Photo, [], true) + case let file as TelegramMediaFile: + if file.isVideoSticker || file.isAnimatedSticker { + return (strings.Message_Sticker, [], true) + } + var fileName: String = strings.Message_File + for attribute in file.attributes { + switch attribute { + case .Sticker: + return (strings.Message_Sticker, [], true) + case let .FileName(name): + fileName = name + case let .Audio(isVoice, _, title, performer, _): + if isVoice { + return (strings.Message_Audio, [], true) + } else { + if let title = title, let performer = performer, !title.isEmpty, !performer.isEmpty { + return (title + " — " + performer, [], true) + } else if let title = title, !title.isEmpty { + return (title, [], true) + } else if let performer = performer, !performer.isEmpty { + return (performer, [], true) + } else { + return (strings.Message_Audio, [], true) + } } + case .Video: + if file.isAnimated { + return (strings.Message_Animation, [], true) + } else { + return (strings.Message_Video, [], true) + } + default: + break } - return (fileName, true) - case _ as TelegramMediaContact: - return (strings.Message_Contact, true) - case let game as TelegramMediaGame: - return (game.title, true) - case _ as TelegramMediaMap: - return (strings.Message_Location, true) - case _ as TelegramMediaAction: - return ("", true) - case _ as TelegramMediaPoll: - return (strings.ForwardedPolls(1), true) - case let dice as TelegramMediaDice: - return (dice.emoji, true) - case let invoice as TelegramMediaInvoice: - return (invoice.title, true) - default: - break + } + return (fileName, [], true) + case _ as TelegramMediaContact: + return (strings.Message_Contact, [], true) + case let game as TelegramMediaGame: + return (game.title, [], true) + case _ as TelegramMediaMap: + return (strings.Message_Location, [], true) + case _ as TelegramMediaAction: + return ("", [], true) + case _ as TelegramMediaPoll: + return (strings.ForwardedPolls(1), [], true) + case let dice as TelegramMediaDice: + return (dice.emoji, [], true) + case let invoice as TelegramMediaInvoice: + return (invoice.title, [], true) + default: + break } } - return (message.text, false) + return (message.text, message.textEntitiesAttribute?.entities ?? [], false) } final class ForwardAccessoryPanelNode: AccessoryPanelNode { @@ -89,7 +92,8 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { let lineNode: ASImageNode let iconNode: ASImageNode let titleNode: ImmediateTextNode - let textNode: ImmediateTextNode + let textNode: ImmediateTextNodeWithEntities + private var originalText: NSAttributedString? private let actionArea: AccessibilityAreaNode @@ -102,7 +106,7 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { private var validLayout: (size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState)? - init(context: AccountContext, messageIds: [MessageId], theme: PresentationTheme, strings: PresentationStrings, fontSize: PresentationFontSize, nameDisplayOrder: PresentationPersonNameOrder, forwardOptionsState: ChatInterfaceForwardOptionsState?) { + init(context: AccountContext, messageIds: [MessageId], theme: PresentationTheme, strings: PresentationStrings, fontSize: PresentationFontSize, nameDisplayOrder: PresentationPersonNameOrder, forwardOptionsState: ChatInterfaceForwardOptionsState?, animationCache: AnimationCache?, animationRenderer: MultiAnimationRenderer?) { self.context = context self.messageIds = messageIds self.theme = theme @@ -131,7 +135,7 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { self.titleNode.maximumNumberOfLines = 1 self.titleNode.displaysAsynchronously = false - self.textNode = ImmediateTextNode() + self.textNode = ImmediateTextNodeWithEntities() self.textNode.maximumNumberOfLines = 1 self.textNode.displaysAsynchronously = false @@ -148,6 +152,16 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { self.addSubnode(self.textNode) self.addSubnode(self.actionArea) + if let animationCache = animationCache, let animationRenderer = animationRenderer { + self.textNode.arguments = TextNodeWithEntities.Arguments( + context: context, + cache: animationCache, + renderer: animationRenderer, + placeholderColor: theme.list.mediaPlaceholderColor, + attemptSynchronous: false + ) + } + self.messageDisposable.set((context.account.postbox.messagesAtIds(messageIds) |> deliverOnMainQueue).start(next: { [weak self] messages in if let strongSelf = self { @@ -157,7 +171,7 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { var authors = "" var uniquePeerIds = Set() var title = "" - var text = "" + var text = NSMutableAttributedString(string: "") var sourcePeer: (Bool, String)? for message in messages { if let author = message.forwardInfo?.author ?? message.effectiveAuthor, !uniquePeerIds.contains(author.id) { @@ -178,11 +192,27 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { if messages.count == 1 { title = strongSelf.strings.Conversation_ForwardOptions_ForwardTitleSingle - let (string, _) = textStringForForwardedMessage(messages[0], strings: strings) - text = "\(authors): \(string)" + let (string, entities, _) = textStringForForwardedMessage(messages[0], strings: strings) + + text = NSMutableAttributedString(attributedString: NSAttributedString(string: "\(authors): ", font: Font.regular(15.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor)) + + let additionalText = NSMutableAttributedString(attributedString: NSAttributedString(string: string, font: Font.regular(15.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor)) + for entity in entities { + switch entity.type { + case let .CustomEmoji(_, fileId): + let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound) + if range.lowerBound >= 0 && range.upperBound <= additionalText.length { + additionalText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(stickerPack: nil, fileId: fileId, file: messages[0].associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile), range: range) + } + default: + break + } + } + + text.append(additionalText) } else { title = strongSelf.strings.Conversation_ForwardOptions_ForwardTitle(Int32(messages.count)) - text = strongSelf.strings.Conversation_ForwardFrom(authors).string + text = NSMutableAttributedString(attributedString: NSAttributedString(string: strongSelf.strings.Conversation_ForwardFrom(authors).string, font: Font.regular(15.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor)) } strongSelf.messages = messages @@ -190,7 +220,9 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { strongSelf.authors = authors strongSelf.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor) - strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor) + strongSelf.textNode.attributedText = text + strongSelf.originalText = text + strongSelf.textNode.visibility = true let headerString: String if messages.count == 1 { @@ -273,25 +305,21 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { let filteredMessages = self.messages - var authors = self.authors ?? "" - if forwardOptionsState?.hideNames == true { - authors = self.strings.DialogList_You - } - var title = "" - var text = "" - if filteredMessages.count == 1, let message = filteredMessages.first { + if filteredMessages.count == 1 { title = self.strings.Conversation_ForwardOptions_ForwardTitleSingle - let (string, _) = textStringForForwardedMessage(message, strings: strings) - text = "\(authors): \(string)" } else { title = self.strings.Conversation_ForwardOptions_ForwardTitle(Int32(filteredMessages.count)) - text = self.strings.Conversation_ForwardFrom(authors).string } self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(15.0), textColor: self.theme.chat.inputPanel.panelControlAccentColor) - self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: self.theme.chat.inputPanel.secondaryTextColor) + if let attributedText = self.textNode.attributedText { + let updatedText = NSMutableAttributedString(attributedString: attributedText) + updatedText.addAttribute(.foregroundColor, value: self.theme.chat.inputPanel.secondaryTextColor, range: NSRange(location: 0, length: updatedText.length)) + self.textNode.attributedText = updatedText + self.originalText = updatedText + } if let (size, inset, interfaceState) = self.validLayout { self.updateState(size: size, inset: inset, interfaceState: interfaceState) diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index ac30fa69f4..798e6d568a 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -386,7 +386,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { func beginSelection() { if let _ = self.textInputPanelNode { } else { - let forwardAccessoryPanelNode = ForwardAccessoryPanelNode(context: self.context, messageIds: self.forwardedMessageIds, theme: self.presentationData.theme, strings: self.presentationData.strings, fontSize: self.presentationData.chatFontSize, nameDisplayOrder: self.presentationData.nameDisplayOrder, forwardOptionsState: self.presentationInterfaceState.interfaceState.forwardOptionsState) + let forwardAccessoryPanelNode = ForwardAccessoryPanelNode(context: self.context, messageIds: self.forwardedMessageIds, theme: self.presentationData.theme, strings: self.presentationData.strings, fontSize: self.presentationData.chatFontSize, nameDisplayOrder: self.presentationData.nameDisplayOrder, forwardOptionsState: self.presentationInterfaceState.interfaceState.forwardOptionsState, animationCache: nil, animationRenderer: nil) forwardAccessoryPanelNode.interfaceInteraction = self.interfaceInteraction self.addSubnode(forwardAccessoryPanelNode) self.forwardAccessoryPanelNode = forwardAccessoryPanelNode