From 4a853bf13dfbfc10b13f2acde0e76f5f8e8dd9d2 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 7 Jul 2021 03:12:07 +0300 Subject: [PATCH] Add stickerpack strip expansion --- .../Sources/ChatListSearchListPaneNode.swift | 4 +- .../Sources/Node/ChatListNode.swift | 2 +- .../Sources/ContactsSearchContainerNode.swift | 2 +- submodules/Display/Source/ListView.swift | 15 +- .../Sources/ItemListControllerNode.swift | 2 +- .../LocationPickerControllerNode.swift | 2 +- .../Sources/LocationSearchContainerNode.swift | 2 +- .../Sources/LocationViewControllerNode.swift | 2 +- ...elDiscussionGroupSearchContainerNode.swift | 2 +- .../ChannelMembersSearchContainerNode.swift | 4 +- .../ChannelMembersSearchControllerNode.swift | 2 +- .../Sources/OldChannelsSearch.swift | 2 +- .../LocalizationListControllerNode.swift | 2 +- .../NotificationExceptionControllerNode.swift | 2 +- .../Sources/Search/SettingsSearchItem.swift | 4 +- .../Themes/ThemeGridSearchContentNode.swift | 2 +- .../Sources/ChatControllerNode.swift | 2 +- .../Sources/ChatHistoryListNode.swift | 2 +- .../ChatHistorySearchContainerNode.swift | 2 +- .../ChatMediaInputMetaSectionItemNode.swift | 109 +++++-- .../Sources/ChatMediaInputNode.swift | 284 ++++++++++-------- .../Sources/ChatMediaInputPanelEntries.swift | 132 ++++---- .../ChatMediaInputPeerSpecificItem.swift | 4 +- .../ChatMediaInputRecentGifsItem.swift | 74 +++-- .../Sources/ChatMediaInputSettingsItem.swift | 79 +++-- .../ChatMediaInputStickerPackItem.swift | 109 +++++-- .../Sources/ChatMediaInputTrendingItem.swift | 82 +++-- .../Sources/DrawingStickersScreen.swift | 20 +- .../OverlayAudioPlayerControllerNode.swift | 4 +- .../Sources/WebSearchControllerNode.swift | 2 +- 30 files changed, 598 insertions(+), 358 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index d86eef5c66..eb284e2f4e 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -1518,11 +1518,11 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } }) - self.recentListNode.beganInteractiveDragging = { + self.recentListNode.beganInteractiveDragging = { _ in interaction.dismissInput() } - self.listNode.beganInteractiveDragging = { + self.listNode.beganInteractiveDragging = { _ in interaction.dismissInput() } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 60711e2d13..84a4f05d7b 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1220,7 +1220,7 @@ public final class ChatListNode: ListView { } var startedScrollingAtUpperBound = false - self.beganInteractiveDragging = { [weak self] in + self.beganInteractiveDragging = { [weak self] _ in guard let strongSelf = self else { return } diff --git a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift index a97308a242..d337d880b6 100644 --- a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift @@ -414,7 +414,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo } })) - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } } diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 9599f748a4..a50d1e8eef 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -286,7 +286,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture public final var visibleContentOffsetChanged: (ListViewVisibleContentOffset) -> Void = { _ in } public final var visibleBottomContentOffsetChanged: (ListViewVisibleContentOffset) -> Void = { _ in } - public final var beganInteractiveDragging: () -> Void = { } + public final var beganInteractiveDragging: (CGPoint) -> Void = { _ in } public final var endedInteractiveDragging: () -> Void = { } public final var didEndScrolling: (() -> Void)? @@ -683,7 +683,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } self.scrolledToItem = nil - self.beganInteractiveDragging() + self.beganInteractiveDragging(self.touchesPosition) for itemNode in self.itemNodes { if !itemNode.isLayerBacked { @@ -4039,7 +4039,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture self.updateOverlayHighlight(transition: transition) } - private func itemIndexAtPoint(_ point: CGPoint) -> Int? { + public func itemIndexAtPoint(_ point: CGPoint) -> Int? { for itemNode in self.itemNodes { if itemNode.apparentContentFrame.contains(point) { return itemNode.index @@ -4057,6 +4057,15 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture return nil } + public func indexOf(itemNode: ListViewItemNode) -> Int? { + for listItemNode in self.itemNodes { + if itemNode === listItemNode { + return listItemNode.index + } + } + return nil + } + public func forEachItemNode(_ f: (ASDisplayNode) -> Void) { for itemNode in self.itemNodes { if itemNode.index != nil { diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index 25c3bcc01c..1fc89122e8 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -347,7 +347,7 @@ open class ItemListControllerNode: ASDisplayNode { self?.contentOffsetChanged?(offset, inVoiceOver) } - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in if let strongSelf = self { strongSelf.beganInteractiveDragging?() } diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index a16dfdf565..e43a09a232 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -640,7 +640,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM strongSelf.layoutEmptyResultsPlaceholder(transition: listTransition) } - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in guard let strongSelf = self else { return } diff --git a/submodules/LocationUI/Sources/LocationSearchContainerNode.swift b/submodules/LocationUI/Sources/LocationSearchContainerNode.swift index 28a38b4999..75fe5a5e7c 100644 --- a/submodules/LocationUI/Sources/LocationSearchContainerNode.swift +++ b/submodules/LocationUI/Sources/LocationSearchContainerNode.swift @@ -224,7 +224,7 @@ final class LocationSearchContainerNode: ASDisplayNode { } })) - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.interaction.dismissInput() } } diff --git a/submodules/LocationUI/Sources/LocationViewControllerNode.swift b/submodules/LocationUI/Sources/LocationViewControllerNode.swift index d2bb4fa7cb..0bd8ad9564 100644 --- a/submodules/LocationUI/Sources/LocationViewControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationViewControllerNode.swift @@ -537,7 +537,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan strongSelf.headerNode.updateLayout(layout: layout, navigationBarHeight: navigationBarHeight, topPadding: strongSelf.state.displayingMapModeOptions ? 38.0 : 0.0, offset: 0.0, size: headerFrame.size, transition: listTransition) } - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in guard let strongSelf = self else { return } diff --git a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSearchContainerNode.swift index 352be4ea7c..37ce4689be 100644 --- a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSearchContainerNode.swift @@ -219,7 +219,7 @@ final class ChannelDiscussionGroupSearchContainerNode: SearchDisplayControllerCo } }) - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } } diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift index 0b7c4e97c3..e79adb2cfa 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift @@ -1279,10 +1279,10 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon } }) - self.emptyQueryListNode.beganInteractiveDragging = { [weak self] in + self.emptyQueryListNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } } diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift index bf303f3944..4736c8c03f 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift @@ -581,7 +581,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { } } - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.view.endEditing(true) } } diff --git a/submodules/PeerInfoUI/Sources/OldChannelsSearch.swift b/submodules/PeerInfoUI/Sources/OldChannelsSearch.swift index 4e27823a2f..685ca8cfc0 100644 --- a/submodules/PeerInfoUI/Sources/OldChannelsSearch.swift +++ b/submodules/PeerInfoUI/Sources/OldChannelsSearch.swift @@ -263,7 +263,7 @@ private final class OldChannelsSearchContainerNode: SearchDisplayControllerConte } }) - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } } diff --git a/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift b/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift index 9beb049164..0328661f80 100644 --- a/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift +++ b/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift @@ -174,7 +174,7 @@ private final class LocalizationListSearchContainerNode: SearchDisplayController } }) - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } } diff --git a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift index 66a5dfe0ae..7091dcf186 100644 --- a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift +++ b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift @@ -1279,7 +1279,7 @@ private final class NotificationExceptionsSearchContainerNode: SearchDisplayCont } }) - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } } diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift index 862cb1755b..4c1d7bb5b8 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift @@ -535,11 +535,11 @@ public final class SettingsSearchContainerNode: SearchDisplayControllerContentNo } }) - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } - self.recentListNode.beganInteractiveDragging = { [weak self] in + self.recentListNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridSearchContentNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridSearchContentNode.swift index 39a9bd43b0..1d186c20cc 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridSearchContentNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridSearchContentNode.swift @@ -654,7 +654,7 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode { } }) - self.recentListNode.beganInteractiveDragging = { [weak self] in + self.recentListNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 4c22d96741..e2959758b0 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1248,7 +1248,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let previousInputPanelBackgroundFrame = self.inputPanelBackgroundNode.frame transition.updateFrame(node: self.inputPanelBackgroundNode, frame: apparentInputBackgroundFrame) - self.inputPanelBackgroundNode.update(size: CGSize(width: apparentInputBackgroundFrame.size.width, height: apparentInputBackgroundFrame.size.height + 41.0), transition: transition) + self.inputPanelBackgroundNode.update(size: CGSize(width: apparentInputBackgroundFrame.size.width, height: apparentInputBackgroundFrame.size.height + 41.0 + 31.0), transition: transition) transition.updateFrame(node: self.inputPanelBackgroundSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: apparentInputBackgroundFrame.origin.y), size: CGSize(width: apparentInputBackgroundFrame.size.width, height: UIScreenPixel))) transition.updateFrame(node: self.navigateButtons, frame: apparentNavigateButtonsFrame) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 74d8839abb..863f92baee 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1193,7 +1193,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } }).start() - self.beganInteractiveDragging = { [weak self] in + self.beganInteractiveDragging = { [weak self] _ in self?.isInteractivelyScrollingValue = true self?.isInteractivelyScrollingPromise.set(true) self?.beganDragging?() diff --git a/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift b/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift index bfdfe79955..50ef7caa52 100644 --- a/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift @@ -231,7 +231,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { } })) - self.listNode.beganInteractiveDragging = { [weak self] in + self.listNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() } diff --git a/submodules/TelegramUI/Sources/ChatMediaInputMetaSectionItemNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputMetaSectionItemNode.swift index 4df8a95697..3490c85396 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputMetaSectionItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputMetaSectionItemNode.swift @@ -21,17 +21,19 @@ final class ChatMediaInputMetaSectionItem: ListViewItem { let inputNodeInteraction: ChatMediaInputNodeInteraction let type: ChatMediaInputMetaSectionItemType let theme: PresentationTheme + let expanded: Bool let selectedItem: () -> Void var selectable: Bool { return true } - init(inputNodeInteraction: ChatMediaInputNodeInteraction, type: ChatMediaInputMetaSectionItemType, theme: PresentationTheme, selected: @escaping () -> Void) { + init(inputNodeInteraction: ChatMediaInputNodeInteraction, type: ChatMediaInputMetaSectionItemType, theme: PresentationTheme, expanded: Bool, selected: @escaping () -> Void) { self.inputNodeInteraction = inputNodeInteraction self.type = type self.selectedItem = selected self.theme = theme + self.expanded = expanded } func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -40,11 +42,11 @@ final class ChatMediaInputMetaSectionItem: ListViewItem { Queue.mainQueue().async { node.inputNodeInteraction = self.inputNodeInteraction node.setItem(item: self) - node.updateTheme(theme: self.theme) + node.updateTheme(theme: self.theme, expanded: self.expanded) node.updateIsHighlighted() node.updateAppearanceTransition(transition: .immediate) - node.contentSize = CGSize(width: 41.0, height: 41.0) + node.contentSize = self.expanded ? expandedBoundingSize : boundingSize node.insets = ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem) completion(node, { @@ -58,9 +60,9 @@ final class ChatMediaInputMetaSectionItem: ListViewItem { public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { Queue.mainQueue().async { - completion(ListViewItemNodeLayout(contentSize: node().contentSize, insets: node().insets), { _ in + completion(ListViewItemNodeLayout(contentSize: self.expanded ? expandedBoundingSize : boundingSize, insets: node().insets), { _ in (node() as? ChatMediaInputMetaSectionItemNode)?.setItem(item: self) - (node() as? ChatMediaInputMetaSectionItemNode)?.updateTheme(theme: self.theme) + (node() as? ChatMediaInputMetaSectionItemNode)?.updateTheme(theme: self.theme, expanded: self.expanded) }) } } @@ -70,16 +72,22 @@ final class ChatMediaInputMetaSectionItem: ListViewItem { } } -private let boundingSize = CGSize(width: 41.0, height: 41.0) -private let boundingImageSize = CGSize(width: 30.0, height: 30.0) -private let highlightSize = CGSize(width: 35.0, height: 35.0) +private let boundingSize = CGSize(width: 72.0, height: 41.0) +private let expandedBoundingSize = CGSize(width: 72.0, height: 72.0) +private let boundingImageScale: CGFloat = 0.625 +private let highlightSize = CGSize(width: 56.0, height: 56.0) private let verticalOffset: CGFloat = 3.0 + UIScreenPixel final class ChatMediaInputMetaSectionItemNode: ListViewItemNode { + private let containerNode: ASDisplayNode + private let scalingNode: ASDisplayNode private let imageNode: ASImageNode private let textNodeContainer: ASDisplayNode private let textNode: ImmediateTextNode private let highlightNode: ASImageNode + private let titleNode: ImmediateTextNode + + private var currentExpanded = false var item: ChatMediaInputMetaSectionItem? var currentCollectionId: ItemCollectionId? @@ -88,6 +96,11 @@ final class ChatMediaInputMetaSectionItemNode: ListViewItemNode { var theme: PresentationTheme? init() { + self.containerNode = ASDisplayNode() + self.containerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + + self.scalingNode = ASDisplayNode() + self.highlightNode = ASImageNode() self.highlightNode.isLayerBacked = true self.highlightNode.isHidden = true @@ -105,22 +118,17 @@ final class ChatMediaInputMetaSectionItemNode: ListViewItemNode { self.textNodeContainer.addSubnode(self.textNode) self.textNodeContainer.isUserInteractionEnabled = false - self.highlightNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - highlightSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - highlightSize.height) / 2.0)), size: highlightSize) - - self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - - self.textNodeContainer.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + self.titleNode = ImmediateTextNode() super.init(layerBacked: false, dynamicBounce: false) - self.addSubnode(self.highlightNode) - self.addSubnode(self.imageNode) - self.addSubnode(self.textNodeContainer) + self.addSubnode(self.containerNode) + self.containerNode.addSubnode(self.scalingNode) - let imageSize = CGSize(width: 26.0, height: 26.0) - self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) - - self.textNodeContainer.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0) + 1.0), size: imageSize) + self.scalingNode.addSubnode(self.highlightNode) + self.scalingNode.addSubnode(self.titleNode) + self.scalingNode.addSubnode(self.imageNode) + self.scalingNode.addSubnode(self.textNodeContainer) } override func didLoad() { @@ -139,25 +147,60 @@ final class ChatMediaInputMetaSectionItemNode: ListViewItemNode { } } - func updateTheme(theme: PresentationTheme) { + func updateTheme(theme: PresentationTheme, expanded: Bool) { + let imageSize = CGSize(width: 26.0 * 1.6, height: 26.0 * 1.6) + self.imageNode.frame = CGRect(origin: CGPoint(x: floor((expandedBoundingSize.width - imageSize.width) / 2.0), y: floor((expandedBoundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) + + self.textNodeContainer.frame = CGRect(origin: CGPoint(x: floor((expandedBoundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((expandedBoundingSize.height - imageSize.height) / 2.0) + 1.0), size: imageSize) + if self.theme !== theme { self.theme = theme self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme) + var title = "" if let item = self.item { switch item.type { case .savedStickers: self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelSavedStickersIcon(theme) + title = "Favorites" case .recentStickers: self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelRecentStickersIcon(theme) + title = "Recent" case .stickersMode: self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelStickersModeIcon(theme) + title = "Stickers" case .savedGifs: self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelRecentStickersIcon(theme) + title = "GIFs" case .trendingGifs: self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelTrendingGifsIcon(theme) + title = "Trending" case let .gifEmoji(emoji): var emoji = emoji + switch emoji { + case "😡": + title = "Angry" + case "😮": + title = "Surprised" + case "😂": + title = "Joy" + case "😘": + title = "Kiss" + case "😍": + title = "Hearts" + case "👍": + title = "Thumbs Up" + case "👎": + title = "Thumbs Down" + case "🙄": + title = "Roll-eyes" + case "😎": + title = "Cool" + case "🥳": + title = "Party" + default: + break + } if emoji == "🥳" { if #available(iOSApplicationExtension 12.1, iOS 12.1, *) { } else { @@ -165,12 +208,34 @@ final class ChatMediaInputMetaSectionItemNode: ListViewItemNode { } } self.imageNode.image = nil - self.textNode.attributedText = NSAttributedString(string: emoji, font: Font.regular(27.0), textColor: .black) + self.textNode.attributedText = NSAttributedString(string: emoji, font: Font.regular(43.0), textColor: .black) let textSize = self.textNode.updateLayout(CGSize(width: 100.0, height: 100.0)) self.textNode.frame = CGRect(origin: CGPoint(x: floor((self.textNodeContainer.bounds.width - textSize.width) / 2.0), y: floor((self.textNodeContainer.bounds.height - textSize.height) / 2.0)), size: textSize) } } + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(11.0), textColor: theme.chat.inputPanel.primaryTextColor) } + + self.containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedBoundingSize) + self.scalingNode.bounds = CGRect(origin: CGPoint(), size: expandedBoundingSize) + + let boundsSize = expanded ? expandedBoundingSize : CGSize(width: boundingSize.height, height: boundingSize.height) + let expandScale: CGFloat = expanded ? 1.0 : boundingImageScale + let expandTransition: ContainedViewLayoutTransition = self.currentExpanded != expanded ? .animated(duration: 0.3, curve: .spring) : .immediate + expandTransition.updateTransformScale(node: self.scalingNode, scale: expandScale) + expandTransition.updatePosition(node: self.scalingNode, position: CGPoint(x: boundsSize.width / 2.0, y: boundsSize.height / 2.0 + (expanded ? -2.0 : 3.0))) + + expandTransition.updateAlpha(node: self.titleNode, alpha: expanded ? 1.0 : 0.0) + let titleSize = self.titleNode.updateLayout(CGSize(width: expandedBoundingSize.width - 8.0, height: expandedBoundingSize.height)) + + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((expandedBoundingSize.width - titleSize.width) / 2.0), y: expandedBoundingSize.height - titleSize.height + 2.0), size: titleSize) + let displayTitleFrame = expanded ? titleFrame : CGRect(origin: CGPoint(x: titleFrame.minX, y: self.imageNode.position.y - titleFrame.size.height), size: titleFrame.size) + expandTransition.updateFrameAsPositionAndBounds(node: self.titleNode, frame: displayTitleFrame) + expandTransition.updateTransformScale(node: self.titleNode, scale: expanded ? 1.0 : 0.001) + + self.currentExpanded = expanded + + expandTransition.updateFrame(node: self.highlightNode, frame: expanded ? titleFrame.insetBy(dx: -7.0, dy: -2.0) : CGRect(origin: CGPoint(x: self.imageNode.position.x - highlightSize.width / 2.0, y: self.imageNode.position.y - highlightSize.height / 2.0), size: highlightSize)) } func updateIsHighlighted() { diff --git a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift index a3d3f084ad..e69da972e9 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift @@ -34,6 +34,7 @@ struct ChatMediaInputPanelTransition { let deletions: [ListViewDeleteItem] let insertions: [ListViewInsertItem] let updates: [ListViewUpdateItem] + let scrollToItem: ListViewScrollToItem? } struct ChatMediaInputGridTransition { @@ -47,14 +48,14 @@ struct ChatMediaInputGridTransition { let animated: Bool } -func preparedChatMediaInputPanelEntryTransition(context: AccountContext, from fromEntries: [ChatMediaInputPanelEntry], to toEntries: [ChatMediaInputPanelEntry], inputNodeInteraction: ChatMediaInputNodeInteraction) -> ChatMediaInputPanelTransition { +func preparedChatMediaInputPanelEntryTransition(context: AccountContext, from fromEntries: [ChatMediaInputPanelEntry], to toEntries: [ChatMediaInputPanelEntry], inputNodeInteraction: ChatMediaInputNodeInteraction, scrollToItem: ListViewScrollToItem?) -> ChatMediaInputPanelTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, inputNodeInteraction: inputNodeInteraction), directionHint: nil) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, inputNodeInteraction: inputNodeInteraction), directionHint: nil) } - return ChatMediaInputPanelTransition(deletions: deletions, insertions: insertions, updates: updates) + return ChatMediaInputPanelTransition(deletions: deletions, insertions: insertions, updates: updates, scrollToItem: scrollToItem) } func preparedChatMediaInputGridEntryTransition(account: Account, view: ItemCollectionsView, from fromEntries: [ChatMediaInputGridEntry], to toEntries: [ChatMediaInputGridEntry], update: StickerPacksCollectionUpdate, interfaceInteraction: ChatControllerInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, trendingInteraction: TrendingPaneInteraction) -> ChatMediaInputGridTransition { @@ -152,16 +153,16 @@ func preparedChatMediaInputGridEntryTransition(account: Account, view: ItemColle return ChatMediaInputGridTransition(deletions: deletions, insertions: insertions, updates: updates, updateFirstIndexInSectionOffset: firstIndexInSectionOffset, stationaryItems: stationaryItems, scrollToItem: scrollToItem, updateOpaqueState: opaqueState, animated: animated) } -func chatMediaInputPanelEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, hasUnreadTrending: Bool?, theme: PresentationTheme, hasGifs: Bool = true, hasSettings: Bool = true) -> [ChatMediaInputPanelEntry] { +func chatMediaInputPanelEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, hasUnreadTrending: Bool?, theme: PresentationTheme, hasGifs: Bool = true, hasSettings: Bool = true, expanded: Bool = false) -> [ChatMediaInputPanelEntry] { var entries: [ChatMediaInputPanelEntry] = [] if hasGifs { - entries.append(.recentGifs(theme)) + entries.append(.recentGifs(theme, expanded)) } if let hasUnreadTrending = hasUnreadTrending { - entries.append(.trending(hasUnreadTrending, theme)) + entries.append(.trending(hasUnreadTrending, theme, expanded)) } if let savedStickers = savedStickers, !savedStickers.items.isEmpty { - entries.append(.savedStickers(theme)) + entries.append(.savedStickers(theme, expanded)) } var savedStickerIds = Set() if let savedStickers = savedStickers, !savedStickers.items.isEmpty { @@ -182,40 +183,40 @@ func chatMediaInputPanelEntries(view: ItemCollectionsView, savedStickers: Ordere } } if found { - entries.append(.recentPacks(theme)) + entries.append(.recentPacks(theme, expanded)) } } if let peerSpecificPack = peerSpecificPack { - entries.append(.peerSpecific(theme: theme, peer: peerSpecificPack.peer)) + entries.append(.peerSpecific(theme: theme, peer: peerSpecificPack.peer, expanded: expanded)) } else if case let .available(peer, false) = canInstallPeerSpecificPack { - entries.append(.peerSpecific(theme: theme, peer: peer)) + entries.append(.peerSpecific(theme: theme, peer: peer, expanded: expanded)) } var index = 0 for (_, info, item) in view.collectionInfos { if let info = info as? StickerPackCollectionInfo, item != nil { - entries.append(.stickerPack(index: index, info: info, topItem: item as? StickerPackItem, theme: theme)) + entries.append(.stickerPack(index: index, info: info, topItem: item as? StickerPackItem, theme: theme, expanded: expanded)) index += 1 } } if peerSpecificPack == nil, case let .available(peer, true) = canInstallPeerSpecificPack { - entries.append(.peerSpecific(theme: theme, peer: peer)) + entries.append(.peerSpecific(theme: theme, peer: peer, expanded: expanded)) } if hasSettings { - entries.append(.settings(theme)) + entries.append(.settings(theme, expanded)) } return entries } -func chatMediaInputPanelGifModeEntries(theme: PresentationTheme, reactions: [String]) -> [ChatMediaInputPanelEntry] { +func chatMediaInputPanelGifModeEntries(theme: PresentationTheme, reactions: [String], expanded: Bool) -> [ChatMediaInputPanelEntry] { var entries: [ChatMediaInputPanelEntry] = [] - entries.append(.stickersMode(theme)) - entries.append(.savedGifs(theme)) - entries.append(.trendingGifs(theme)) + entries.append(.stickersMode(theme, expanded)) + entries.append(.savedGifs(theme, expanded)) + entries.append(.trendingGifs(theme, expanded)) for reaction in reactions { - entries.append(.gifEmotion(entries.count, theme, reaction)) + entries.append(.gifEmotion(entries.count, theme, reaction, expanded)) } return entries @@ -451,6 +452,15 @@ final class ChatMediaInputNode: ChatInputNode { private var currentView: ItemCollectionsView? private let dismissedPeerSpecificStickerPack = Promise() + private var panelCollapseScrollToIndex: Int? + private let panelExpandedPromise = ValuePromise(false) + private var panelExpanded: Bool = false { + didSet { + self.panelExpandedPromise.set(self.panelExpanded) + } + } + private var panelCollapseTimer: SwiftSignalKit.Timer? + var requestDisableStickerAnimations: ((Bool) -> Void)? private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat, ChatPresentationInterfaceState, DeviceMetrics, Bool)? @@ -495,6 +505,7 @@ final class ChatMediaInputNode: ChatInputNode { self.collectionListContainer.clipsToBounds = true self.listView = ListView() +// self.listView.clipsToBounds = false self.listView.transform = CATransform3DMakeRotation(-CGFloat(Double.pi / 2.0), 0.0, 0.0, 1.0) self.listView.scroller.panGestureRecognizer.cancelsTouchesInView = false self.listView.accessibilityPageScrolledString = { row, count in @@ -502,6 +513,7 @@ final class ChatMediaInputNode: ChatInputNode { } self.gifListView = ListView() +// self.gifListView.clipsToBounds = false self.gifListView.transform = CATransform3DMakeRotation(-CGFloat(Double.pi / 2.0), 0.0, 0.0, 1.0) self.gifListView.scroller.panGestureRecognizer.cancelsTouchesInView = false self.gifListView.accessibilityPageScrolledString = { row, count in @@ -527,7 +539,7 @@ final class ChatMediaInputNode: ChatInputNode { var getItemIsPreviewedImpl: ((StickerPackItem) -> Bool)? - self.paneArrangement = ChatMediaInputPaneArrangement(panes: [.gifs, .stickers, /*.trending*/], currentIndex: 1, indexTransition: 0.0) + self.paneArrangement = ChatMediaInputPaneArrangement(panes: [.gifs, .stickers], currentIndex: 1, indexTransition: 0.0) super.init() @@ -548,7 +560,6 @@ final class ChatMediaInputNode: ChatInputNode { } } )) - //strongSelf.setCurrentPane(.trending, transition: .animated(duration: 0.25, curve: .spring)) } else if collectionId.namespace == ChatMediaInputPanelAuxiliaryNamespace.savedStickers.rawValue { strongSelf.setCurrentPane(.stickers, transition: .animated(duration: 0.25, curve: .spring), collectionIdHint: collectionId.namespace) strongSelf.currentStickerPacksCollectionPosition = .navigate(index: nil, collectionId: collectionId) @@ -781,7 +792,7 @@ final class ChatMediaInputNode: ChatInputNode { } let trendingInteraction = TrendingPaneInteraction(installPack: { [weak self] info in - guard let strongSelf = self, let info = info as? StickerPackCollectionInfo else { + guard let info = info as? StickerPackCollectionInfo else { return } let _ = (context.engine.stickers.loadedStickerPack(reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) @@ -846,8 +857,8 @@ final class ChatMediaInputNode: ChatInputNode { let previousView = Atomic(value: nil) let transitionQueue = Queue() - let transitions = combineLatest(queue: transitionQueue, itemCollectionsView, peerSpecificPack, context.account.viewTracker.featuredStickerPacks(), self.themeAndStringsPromise.get(), reactions) - |> map { viewAndUpdate, peerSpecificPack, trendingPacks, themeAndStrings, reactions -> (ItemCollectionsView, ChatMediaInputPanelTransition, ChatMediaInputPanelTransition, Bool, ChatMediaInputGridTransition, Bool) in + let transitions = combineLatest(queue: transitionQueue, itemCollectionsView, peerSpecificPack, context.account.viewTracker.featuredStickerPacks(), self.themeAndStringsPromise.get(), reactions, self.panelExpandedPromise.get()) + |> map { viewAndUpdate, peerSpecificPack, trendingPacks, themeAndStrings, reactions, panelExpanded -> (ItemCollectionsView, ChatMediaInputPanelTransition, ChatMediaInputPanelTransition, Bool, ChatMediaInputGridTransition, Bool) in let (view, viewUpdate) = viewAndUpdate let previous = previousView.swap(view) var update = viewUpdate @@ -882,8 +893,8 @@ final class ChatMediaInputNode: ChatInputNode { } } - let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, hasUnreadTrending: hasUnreadTrending, theme: theme) - let gifPaneEntries = chatMediaInputPanelGifModeEntries(theme: theme, reactions: reactions) + let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, hasUnreadTrending: hasUnreadTrending, theme: theme, expanded: panelExpanded) + let gifPaneEntries = chatMediaInputPanelGifModeEntries(theme: theme, reactions: reactions, expanded: panelExpanded) var gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, strings: strings, theme: theme) if view.higher == nil { @@ -901,9 +912,9 @@ final class ChatMediaInputNode: ChatInputNode { } } } - + let (previousPanelEntries, previousGifPaneEntries, previousGridEntries) = previousEntries.swap((panelEntries, gifPaneEntries, gridEntries)) - return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: inputNodeInteraction), preparedChatMediaInputPanelEntryTransition(context: context, from: previousGifPaneEntries, to: gifPaneEntries, inputNodeInteraction: inputNodeInteraction), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: inputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty) + return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: inputNodeInteraction, scrollToItem: nil), preparedChatMediaInputPanelEntryTransition(context: context, from: previousGifPaneEntries, to: gifPaneEntries, inputNodeInteraction: inputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: inputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty) } self.disposable.set((transitions @@ -970,7 +981,6 @@ final class ChatMediaInputNode: ChatInputNode { self.stickerPane.inputNodeInteraction = self.inputNodeInteraction self.gifPane.inputNodeInteraction = self.inputNodeInteraction - //self.trendingPane.inputNodeInteraction = self.inputNodeInteraction paneDidScrollImpl = { [weak self] pane, state, transition in self?.updatePaneDidScroll(pane: pane, state: state, transition: transition) @@ -983,11 +993,59 @@ final class ChatMediaInputNode: ChatInputNode { openGifContextMenuImpl = { [weak self] file, sourceNode, sourceRect, gesture, isSaved in self?.openGifContextMenu(file: file, sourceNode: sourceNode, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved) } + + self.listView.beganInteractiveDragging = { [weak self] position in + if let strongSelf = self, false { + if !strongSelf.panelExpanded, let index = strongSelf.listView.itemIndexAtPoint(CGPoint(x: 0.0, y: position.y)) { + strongSelf.panelCollapseScrollToIndex = index + } + strongSelf.updateIsExpanded(true) + } + } + + self.listView.didEndScrolling = { [weak self] in + if let strongSelf = self, false { + strongSelf.setupCollapseTimer() + } + } + + self.gifListView.beganInteractiveDragging = { [weak self] position in + if let strongSelf = self, false { + if !strongSelf.panelExpanded, let index = strongSelf.gifListView.itemIndexAtPoint(CGPoint(x: 0.0, y: position.y)) { + strongSelf.panelCollapseScrollToIndex = index + } + strongSelf.updateIsExpanded(true) + } + } + + self.gifListView.didEndScrolling = { [weak self] in + if let strongSelf = self, false { + strongSelf.setupCollapseTimer() + } + } } deinit { self.disposable.dispose() self.searchContainerNodeLoadedDisposable.dispose() + self.panelCollapseTimer?.invalidate() + } + + private func updateIsExpanded(_ isExpanded: Bool) { + self.panelCollapseTimer?.invalidate() + + self.panelExpanded = isExpanded + self.updatePaneClippingContainer(size: self.paneClippingContainer.bounds.size, offset: self.currentCollectionListPanelOffset(), transition: .animated(duration: 0.3, curve: .spring)) + } + + private func setupCollapseTimer() { + self.panelCollapseTimer?.invalidate() + + let timer = SwiftSignalKit.Timer(timeout: 1.5, repeat: false, completion: { [weak self] in + self?.updateIsExpanded(false) + }, queue: Queue.mainQueue()) + self.panelCollapseTimer = timer + timer.start() } private func openGifContextMenu(file: MultiplexedVideoNodeFile, sourceNode: ASDisplayNode, sourceRect: CGRect, gesture: ContextGesture, isSaved: Bool) { @@ -1215,7 +1273,7 @@ final class ChatMediaInputNode: ChatInputNode { } } } else { - panes = [strongSelf.gifPane, strongSelf.stickerPane/*, strongSelf.trendingPane*/] + panes = [strongSelf.gifPane, strongSelf.stickerPane] } let panelPoint = strongSelf.view.convert(point, to: strongSelf.collectionListPanel.view) if panelPoint.y < strongSelf.collectionListPanel.frame.maxY { @@ -1367,19 +1425,11 @@ final class ChatMediaInputNode: ChatInputNode { } private func setCurrentPane(_ pane: ChatMediaInputPaneType, transition: ContainedViewLayoutTransition, collectionIdHint: Int32? = nil) { - var transition = transition - if let index = self.paneArrangement.panes.firstIndex(of: pane), index != self.paneArrangement.currentIndex { let previousGifPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs - //let previousTrendingPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .trending self.paneArrangement = self.paneArrangement.withIndexTransition(0.0).withCurrentIndex(index) let updatedGifPanelWasActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs - //let updatedTrendingPanelIsActive = self.paneArrangement.panes[self.paneArrangement.currentIndex] == .trending - - /*if updatedTrendingPanelIsActive != previousTrendingPanelWasActive { - transition = .immediate - }*/ - + if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.validLayout { let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: transition, interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible) self.updateAppearanceTransition(transition: transition) @@ -1396,23 +1446,7 @@ final class ChatMediaInputNode: ChatInputNode { } else if let collectionIdHint = collectionIdHint { self.setHighlightedItemCollectionId(ItemCollectionId(namespace: collectionIdHint, id: 0)) } - /*case .trending: - self.setHighlightedItemCollectionId(ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue, id: 0))*/ } - /*if updatedTrendingPanelIsActive != previousTrendingPanelWasActive { - self.controllerInteraction.updateInputMode { current in - switch current { - case let .media(mode, _): - if updatedTrendingPanelIsActive { - return .media(mode: mode, expanded: .content) - } else { - return .media(mode: mode, expanded: nil) - } - default: - return current - } - } - }*/ } else { if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.validLayout { let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, standardInputHeight: standardInputHeight, inputHeight: inputHeight, maximumHeight: maximumHeight, inputPanelHeight: inputPanelHeight, transition: .animated(duration: 0.25, curve: .spring), interfaceState: interfaceState, deviceMetrics: deviceMetrics, isVisible: isVisible) @@ -1425,10 +1459,6 @@ final class ChatMediaInputNode: ChatInputNode { if self.paneArrangement.panes[self.paneArrangement.currentIndex] == .gifs { self.inputNodeInteraction.highlightedItemCollectionId = collectionId } - } else if collectionId.namespace == ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue { - /*if self.paneArrangement.panes[self.paneArrangement.currentIndex] == .trending { - self.inputNodeInteraction.highlightedItemCollectionId = collectionId - }*/ } else { self.inputNodeInteraction.highlightedStickerItemCollectionId = collectionId if self.paneArrangement.panes[self.paneArrangement.currentIndex] == .stickers { @@ -1469,31 +1499,56 @@ final class ChatMediaInputNode: ChatInputNode { } itemNode.updateIsHighlighted() if itemNode.currentCollectionId == collectionId { - self.listView.ensureItemNodeVisible(itemNode) + if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) { + self.panelCollapseScrollToIndex = targetIndex + self.updateIsExpanded(false) + } else { + self.listView.ensureItemNodeVisible(itemNode) + } ensuredNodeVisible = true } } else if let itemNode = itemNode as? ChatMediaInputMetaSectionItemNode { itemNode.updateIsHighlighted() if itemNode.currentCollectionId == collectionId { - self.listView.ensureItemNodeVisible(itemNode) + if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) { + self.panelCollapseScrollToIndex = targetIndex + self.updateIsExpanded(false) + } else { + self.listView.ensureItemNodeVisible(itemNode) + } ensuredNodeVisible = true } } else if let itemNode = itemNode as? ChatMediaInputRecentGifsItemNode { itemNode.updateIsHighlighted() if itemNode.currentCollectionId == collectionId { - self.listView.ensureItemNodeVisible(itemNode) + if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) { + self.panelCollapseScrollToIndex = targetIndex + self.updateIsExpanded(false) + } else { + self.listView.ensureItemNodeVisible(itemNode) + } ensuredNodeVisible = true } } else if let itemNode = itemNode as? ChatMediaInputTrendingItemNode { itemNode.updateIsHighlighted() if itemNode.currentCollectionId == collectionId { - self.listView.ensureItemNodeVisible(itemNode) + if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) { + self.panelCollapseScrollToIndex = targetIndex + self.updateIsExpanded(false) + } else { + self.listView.ensureItemNodeVisible(itemNode) + } ensuredNodeVisible = true } } else if let itemNode = itemNode as? ChatMediaInputPeerSpecificItemNode { itemNode.updateIsHighlighted() if itemNode.currentCollectionId == collectionId { - self.listView.ensureItemNodeVisible(itemNode) + if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) { + self.panelCollapseScrollToIndex = targetIndex + self.updateIsExpanded(false) + } else { + self.listView.ensureItemNodeVisible(itemNode) + } ensuredNodeVisible = true } } @@ -1504,7 +1559,12 @@ final class ChatMediaInputNode: ChatInputNode { let firstVisibleIndex = currentView.collectionInfos.firstIndex(where: { id, _, _ in return id == firstVisibleCollectionId }) if let targetIndex = targetIndex, let firstVisibleIndex = firstVisibleIndex { let toRight = targetIndex > firstVisibleIndex - self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [], scrollToItem: ListViewScrollToItem(index: targetIndex, position: toRight ? .bottom(0.0) : .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: toRight ? .Down : .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil) + if self.panelExpanded { + self.panelCollapseScrollToIndex = targetIndex + self.updateIsExpanded(false) + } else { + self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [], scrollToItem: ListViewScrollToItem(index: targetIndex, position: toRight ? .bottom(0.0) : .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: toRight ? .Down : .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil) + } } } } @@ -1516,8 +1576,6 @@ final class ChatMediaInputNode: ChatInputNode { return self.stickerPane.collectionListPanelOffset case .gifs: return self.gifPane.collectionListPanelOffset - /*case .trending: - return self.trendingPane.collectionListPanelOffset*/ } } @@ -1611,7 +1669,6 @@ final class ChatMediaInputNode: ChatInputNode { } self.stickerPane.collectionListPanelOffset = 0.0 self.gifPane.collectionListPanelOffset = 0.0 - //self.trendingPane.collectionListPanelOffset = 0.0 self.updateAppearanceTransition(transition: transition) } else { panelHeight = standardInputHeight @@ -1642,11 +1699,6 @@ final class ChatMediaInputNode: ChatInputNode { } } case .trending: - /*self.trendingPane.gridNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? PaneSearchBarPlaceholderNode { - placeholderNode = itemNode - } - }*/ break } } @@ -1666,14 +1718,14 @@ final class ChatMediaInputNode: ChatInputNode { transition.updateFrame(node: self.collectionListPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: collectionListPanelOffset), size: CGSize(width: width, height: 41.0))) transition.updateFrame(node: self.collectionListSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: 41.0 + collectionListPanelOffset), size: CGSize(width: width, height: separatorHeight))) - self.listView.bounds = CGRect(x: 0.0, y: 0.0, width: 41.0, height: width) + self.listView.bounds = CGRect(x: 0.0, y: 0.0, width: 41.0 + 31.0 + 20.0, height: width) transition.updatePosition(node: self.listView, position: CGPoint(x: width / 2.0, y: (41.0 - collectionListPanelOffset) / 2.0)) - self.gifListView.bounds = CGRect(x: 0.0, y: 0.0, width: 41.0, height: width) + self.gifListView.bounds = CGRect(x: 0.0, y: 0.0, width: 41.0 + 31.0 + 20.0, height: width) transition.updatePosition(node: self.gifListView, position: CGPoint(x: width / 2.0, y: (41.0 - collectionListPanelOffset) / 2.0)) let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: 41.0, height: width), insets: UIEdgeInsets(top: 4.0 + leftInset, left: 0.0, bottom: 4.0 + rightInset, right: 0.0), duration: duration, curve: curve) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: 41.0 + 31.0 + 20.0, height: width), insets: UIEdgeInsets(top: 4.0 + leftInset, left: 0.0, bottom: 4.0 + rightInset, right: 0.0), duration: duration, curve: curve) self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) @@ -1721,7 +1773,6 @@ final class ChatMediaInputNode: ChatInputNode { self.gifPane.updateLayout(size: CGSize(width: width - leftInset - rightInset, height: panelHeight), topInset: 41.0, bottomInset: bottomInset, isExpanded: isExpanded, isVisible: isVisible, deviceMetrics: deviceMetrics, transition: transition) self.trendingInteraction?.itemContext.canPlayMedia = isVisible self.stickerPane.updateLayout(size: CGSize(width: width - leftInset - rightInset, height: panelHeight), topInset: 41.0, bottomInset: bottomInset, isExpanded: isExpanded, isVisible: isVisible && visiblePanes.contains(where: { $0.0 == .stickers }), deviceMetrics: deviceMetrics, transition: transition) - //self.trendingPane.updateLayout(size: CGSize(width: width - leftInset - rightInset, height: panelHeight), topInset: 41.0, bottomInset: bottomInset, isExpanded: isExpanded, isVisible: isVisible, deviceMetrics: deviceMetrics, transition: transition) if self.gifPane.supernode != nil { if !visiblePanes.contains(where: { $0.0 == .gifs }) { @@ -1773,31 +1824,6 @@ final class ChatMediaInputNode: ChatInputNode { self.animatingStickerPaneOut = false } - /*if self.trendingPane.supernode != nil { - if !visiblePanes.contains(where: { $0.0 == .trending }) { - if case .animated = transition { - if !self.animatingTrendingPaneOut { - self.animatingTrendingPaneOut = true - var toLeft = false - if let index = self.paneArrangement.panes.firstIndex(of: .trending), index < self.paneArrangement.currentIndex { - toLeft = true - } - transition.animatePosition(node: self.trendingPane, to: CGPoint(x: (toLeft ? -width : width) + width / 2.0, y: self.trendingPane.layer.position.y), removeOnCompletion: false, completion: { [weak self] value in - if let strongSelf = self, value { - strongSelf.animatingTrendingPaneOut = false - strongSelf.trendingPane.removeFromSupernode() - } - }) - } - } else { - self.animatingTrendingPaneOut = false - self.trendingPane.removeFromSupernode() - } - } - } else { - self.animatingTrendingPaneOut = false - }*/ - if !displaySearch, let searchContainerNode = self.searchContainerNode { self.searchContainerNode = nil self.searchContainerNodeLoadedDisposable.set(nil) @@ -1820,12 +1846,6 @@ final class ChatMediaInputNode: ChatInputNode { } } case .trending: - /*self.trendingPane.gridNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? PaneSearchBarPlaceholderNode { - placeholderNode = itemNode - } - } - paneIsEmpty = true*/ break } } @@ -1866,7 +1886,20 @@ final class ChatMediaInputNode: ChatInputNode { } else { options.insert(.AnimateInsertion) } - self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateOpaqueState: nil, completion: { [weak self] _ in + + var scrollToItem: ListViewScrollToItem? + if let targetIndex = self.panelCollapseScrollToIndex { + var position: ListViewScrollPosition + if self.panelExpanded { + position = .center(.top) + } else { + position = .top(self.listView.frame.height / 2.0 + 96.0) + } + scrollToItem = ListViewScrollToItem(index: targetIndex, position: position, animated: true, curve: .Default(duration: nil), directionHint: .Down) + self.panelCollapseScrollToIndex = nil + } + + self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateOpaqueState: nil, completion: { [weak self] _ in if let strongSelf = self { strongSelf.enqueueGridTransition(gridTransition, firstTime: gridFirstTime) if !strongSelf.didSetReady { @@ -1904,7 +1937,6 @@ final class ChatMediaInputNode: ChatInputNode { } self.searchContainerNode?.contentNode.updatePreviewing(animated: animated) - //self.trendingPane.updatePreviewing(animated: animated) } } @@ -1921,11 +1953,6 @@ final class ChatMediaInputNode: ChatInputNode { self.animatingStickerPaneOut = false self.stickerPane.removeFromSupernode() } - /*self.trendingPane.layer.removeAllAnimations() - if self.animatingTrendingPaneOut { - self.animatingTrendingPaneOut = false - self.trendingPane.removeFromSupernode() - }*/ case .changed: if let (width, leftInset, rightInset, bottomInset, standardInputHeight, inputHeight, maximumHeight, inputPanelHeight, interfaceState, deviceMetrics, isVisible) = self.validLayout { let translationX = -recognizer.translation(in: self.view).x @@ -1990,20 +2017,31 @@ final class ChatMediaInputNode: ChatInputNode { } } - let collectionListPanelOffset = self.currentCollectionListPanelOffset() + var collectionListPanelOffset = self.currentCollectionListPanelOffset() + if self.panelExpanded { + collectionListPanelOffset = 0.0 + } + + var listPanelOffset = collectionListPanelOffset * 2.0 self.updateAppearanceTransition(transition: transition) - transition.updateFrame(node: self.collectionListPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: collectionListPanelOffset), size: self.collectionListPanel.bounds.size)) - transition.updateFrame(node: self.collectionListSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: 41.0 + collectionListPanelOffset), size: self.collectionListSeparator.bounds.size)) - transition.updatePosition(node: self.listView, position: CGPoint(x: self.listView.position.x, y: (41.0 - collectionListPanelOffset) / 2.0)) - transition.updatePosition(node: self.gifListView, position: CGPoint(x: self.gifListView.position.x, y: (41.0 - collectionListPanelOffset) / 2.0)) + transition.updateFrame(node: self.collectionListPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: listPanelOffset), size: self.collectionListPanel.bounds.size)) + transition.updatePosition(node: self.listView, position: CGPoint(x: self.listView.position.x, y: (41.0 - listPanelOffset) / 2.0)) + transition.updatePosition(node: self.gifListView, position: CGPoint(x: self.gifListView.position.x, y: (41.0 - listPanelOffset) / 2.0)) self.updatePaneClippingContainer(size: self.paneClippingContainer.bounds.size, offset: collectionListPanelOffset, transition: transition) } private func updatePaneClippingContainer(size: CGSize, offset: CGFloat, transition: ContainedViewLayoutTransition) { - transition.updateFrame(node: self.paneClippingContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: offset + 41.0), size: size)) - transition.updateSublayerTransformOffset(layer: self.paneClippingContainer.layer, offset: CGPoint(x: 0.0, y: -offset - 41.0)) + var offset = offset + var additionalOffset: CGFloat = 0.0 + if self.panelExpanded { + offset = 0.0 + additionalOffset = 31.0 + } + transition.updateFrame(node: self.collectionListSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: offset + 41.0 + additionalOffset), size: self.collectionListSeparator.bounds.size)) + transition.updateFrame(node: self.paneClippingContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: offset + 41.0 + additionalOffset), size: size)) + transition.updateSublayerTransformOffset(layer: self.paneClippingContainer.layer, offset: CGPoint(x: 0.0, y: -offset - 41.0 - additionalOffset)) } private func fixPaneScroll(pane: ChatMediaInputPane, state: ChatMediaInputPaneScrollState) { @@ -2017,12 +2055,14 @@ final class ChatMediaInputNode: ChatInputNode { } } - let collectionListPanelOffset = self.currentCollectionListPanelOffset() + var collectionListPanelOffset = self.currentCollectionListPanelOffset() + if self.panelExpanded { + collectionListPanelOffset = 0.0 + } let transition = ContainedViewLayoutTransition.animated(duration: 0.25, curve: .spring) self.updateAppearanceTransition(transition: transition) transition.updateFrame(node: self.collectionListPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: collectionListPanelOffset), size: self.collectionListPanel.bounds.size)) - transition.updateFrame(node: self.collectionListSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: 41.0 + collectionListPanelOffset), size: self.collectionListSeparator.bounds.size)) transition.updatePosition(node: self.listView, position: CGPoint(x: self.listView.position.x, y: (41.0 - collectionListPanelOffset) / 2.0)) transition.updatePosition(node: self.gifListView, position: CGPoint(x: self.gifListView.position.x, y: (41.0 - collectionListPanelOffset) / 2.0)) diff --git a/submodules/TelegramUI/Sources/ChatMediaInputPanelEntries.swift b/submodules/TelegramUI/Sources/ChatMediaInputPanelEntries.swift index 25336491a5..bb8b80ea07 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputPanelEntries.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputPanelEntries.swift @@ -32,18 +32,18 @@ enum ChatMediaInputPanelEntryStableId: Hashable { } enum ChatMediaInputPanelEntry: Comparable, Identifiable { - case recentGifs(PresentationTheme) - case savedStickers(PresentationTheme) - case recentPacks(PresentationTheme) - case trending(Bool, PresentationTheme) - case settings(PresentationTheme) - case peerSpecific(theme: PresentationTheme, peer: Peer) - case stickerPack(index: Int, info: StickerPackCollectionInfo, topItem: StickerPackItem?, theme: PresentationTheme) + case recentGifs(PresentationTheme, Bool) + case savedStickers(PresentationTheme, Bool) + case recentPacks(PresentationTheme, Bool) + case trending(Bool, PresentationTheme, Bool) + case settings(PresentationTheme, Bool) + case peerSpecific(theme: PresentationTheme, peer: Peer, expanded: Bool) + case stickerPack(index: Int, info: StickerPackCollectionInfo, topItem: StickerPackItem?, theme: PresentationTheme, expanded: Bool) - case stickersMode(PresentationTheme) - case savedGifs(PresentationTheme) - case trendingGifs(PresentationTheme) - case gifEmotion(Int, PresentationTheme, String) + case stickersMode(PresentationTheme, Bool) + case savedGifs(PresentationTheme, Bool) + case trendingGifs(PresentationTheme, Bool) + case gifEmotion(Int, PresentationTheme, String, Bool) var stableId: ChatMediaInputPanelEntryStableId { switch self { @@ -59,7 +59,7 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { return .settings case .peerSpecific: return .peerSpecific - case let .stickerPack(_, info, _, _): + case let .stickerPack(_, info, _, _, _): return .stickerPack(info.id.id) case .stickersMode: return .stickersMode @@ -67,75 +67,75 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { return .savedGifs case .trendingGifs: return .trendingGifs - case let .gifEmotion(_, _, emoji): + case let .gifEmotion(_, _, emoji, _): return .gifEmotion(emoji) } } static func ==(lhs: ChatMediaInputPanelEntry, rhs: ChatMediaInputPanelEntry) -> Bool { switch lhs { - case let .recentGifs(lhsTheme): - if case let .recentGifs(rhsTheme) = rhs, lhsTheme === rhsTheme { + case let .recentGifs(lhsTheme, lhsExpanded): + if case let .recentGifs(rhsTheme, rhsExpanded) = rhs, lhsTheme === rhsTheme, lhsExpanded == rhsExpanded { return true } else { return false } - case let .savedStickers(lhsTheme): - if case let .savedStickers(rhsTheme) = rhs, lhsTheme === rhsTheme { + case let .savedStickers(lhsTheme, lhsExpanded): + if case let .savedStickers(rhsTheme, rhsExpanded) = rhs, lhsTheme === rhsTheme, lhsExpanded == rhsExpanded { return true } else { return false } - case let .recentPacks(lhsTheme): - if case let .recentPacks(rhsTheme) = rhs, lhsTheme === rhsTheme { + case let .recentPacks(lhsTheme, lhsExpanded): + if case let .recentPacks(rhsTheme, rhsExpanded) = rhs, lhsTheme === rhsTheme, lhsExpanded == rhsExpanded { return true } else { return false } - case let .trending(lhsElevated, lhsTheme): - if case let .trending(rhsElevated, rhsTheme) = rhs, lhsTheme === rhsTheme, lhsElevated == rhsElevated { + case let .trending(lhsElevated, lhsTheme, lhsExpanded): + if case let .trending(rhsElevated, rhsTheme, rhsExpanded) = rhs, lhsTheme === rhsTheme, lhsElevated == rhsElevated, lhsExpanded == rhsExpanded { return true } else { return false } - case let .settings(lhsTheme): - if case let .settings(rhsTheme) = rhs, lhsTheme === rhsTheme { + case let .settings(lhsTheme, lhsExpanded): + if case let .settings(rhsTheme, rhsExpanded) = rhs, lhsTheme === rhsTheme, lhsExpanded == rhsExpanded { return true } else { return false } - case let .peerSpecific(lhsTheme, lhsPeer): - if case let .peerSpecific(rhsTheme, rhsPeer) = rhs, lhsTheme === rhsTheme, lhsPeer.isEqual(rhsPeer) { + case let .peerSpecific(lhsTheme, lhsPeer, lhsExpanded): + if case let .peerSpecific(rhsTheme, rhsPeer, rhsExpanded) = rhs, lhsTheme === rhsTheme, lhsPeer.isEqual(rhsPeer), lhsExpanded == rhsExpanded { return true } else { return false } - case let .stickerPack(index, info, topItem, lhsTheme): - if case let .stickerPack(rhsIndex, rhsInfo, rhsTopItem, rhsTheme) = rhs, index == rhsIndex, info == rhsInfo, topItem == rhsTopItem, lhsTheme === rhsTheme { + case let .stickerPack(index, info, topItem, lhsTheme, lhsExpanded): + if case let .stickerPack(rhsIndex, rhsInfo, rhsTopItem, rhsTheme, rhsExpanded) = rhs, index == rhsIndex, info == rhsInfo, topItem == rhsTopItem, lhsTheme === rhsTheme, lhsExpanded == rhsExpanded { return true } else { return false } - case let .stickersMode(lhsTheme): - if case let .stickersMode(rhsTheme) = rhs, lhsTheme === rhsTheme { + case let .stickersMode(lhsTheme, lhsExpanded): + if case let .stickersMode(rhsTheme, rhsExpanded) = rhs, lhsTheme === rhsTheme, lhsExpanded == rhsExpanded { return true } else { return false } - case let .savedGifs(lhsTheme): - if case let .savedGifs(rhsTheme) = rhs, lhsTheme === rhsTheme { + case let .savedGifs(lhsTheme, lhsExpanded): + if case let .savedGifs(rhsTheme, rhsExpanded) = rhs, lhsTheme === rhsTheme, lhsExpanded == rhsExpanded { return true } else { return false } - case let .trendingGifs(lhsTheme): - if case let .trendingGifs(rhsTheme) = rhs, lhsTheme === rhsTheme { + case let .trendingGifs(lhsTheme, lhsExpanded): + if case let .trendingGifs(rhsTheme, rhsExpanded) = rhs, lhsTheme === rhsTheme, lhsExpanded == rhsExpanded { return true } else { return false } - case let .gifEmotion(lhsIndex, lhsTheme, lhsEmoji): - if case let .gifEmotion(rhsIndex, rhsTheme, rhsEmoji) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsEmoji == rhsEmoji { + case let .gifEmotion(lhsIndex, lhsTheme, lhsEmoji, lhsExpanded): + if case let .gifEmotion(rhsIndex, rhsTheme, rhsEmoji, rhsExpanded) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsEmoji == rhsEmoji, lhsExpanded == rhsExpanded { return true } else { return false @@ -156,7 +156,7 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { switch rhs { case .recentGifs, savedStickers: return false - case let .trending(elevated, _) where elevated: + case let .trending(elevated, _, _) where elevated: return false default: return true @@ -165,7 +165,7 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { switch rhs { case .recentGifs, .savedStickers, recentPacks: return false - case let .trending(elevated, _) where elevated: + case let .trending(elevated, _, _) where elevated: return false default: return true @@ -174,16 +174,16 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { switch rhs { case .recentGifs, .savedStickers, recentPacks, .peerSpecific: return false - case let .trending(elevated, _) where elevated: + case let .trending(elevated, _, _) where elevated: return false default: return true } - case let .stickerPack(lhsIndex, lhsInfo, _, _): + case let .stickerPack(lhsIndex, lhsInfo, _, _, _): switch rhs { case .recentGifs, .savedStickers, .recentPacks, .peerSpecific: return false - case let .trending(elevated, _): + case let .trending(elevated, _, _): if elevated { return false } else { @@ -191,7 +191,7 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { } case .settings: return true - case let .stickerPack(rhsIndex, rhsInfo, _, _): + case let .stickerPack(rhsIndex, rhsInfo, _, _, _): if lhsIndex == rhsIndex { return lhsInfo.id.id < rhsInfo.id.id } else { @@ -200,7 +200,7 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { default: return true } - case let .trending(elevated, _): + case let .trending(elevated, _, _): if elevated { switch rhs { case .recentGifs, .trending: @@ -231,11 +231,11 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { default: return true } - case let .gifEmotion(lhsIndex, _, _): + case let .gifEmotion(lhsIndex, _, _, _): switch rhs { case .stickersMode, .savedGifs, .trendingGifs: return false - case let .gifEmotion(rhsIndex, _, _): + case let .gifEmotion(rhsIndex, _, _, _): return lhsIndex < rhsIndex default: return true @@ -251,53 +251,53 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { func item(context: AccountContext, inputNodeInteraction: ChatMediaInputNodeInteraction) -> ListViewItem { switch self { - case let .recentGifs(theme): - return ChatMediaInputRecentGifsItem(inputNodeInteraction: inputNodeInteraction, theme: theme, selected: { + case let .recentGifs(theme, expanded): + return ChatMediaInputRecentGifsItem(inputNodeInteraction: inputNodeInteraction, theme: theme, expanded: expanded, selected: { let collectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.recentGifs.rawValue, id: 0) inputNodeInteraction.navigateToCollectionId(collectionId) }) - case let .savedStickers(theme): - return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .savedStickers, theme: theme, selected: { + case let .savedStickers(theme, expanded): + return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .savedStickers, theme: theme, expanded: expanded, selected: { let collectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.savedStickers.rawValue, id: 0) inputNodeInteraction.navigateToCollectionId(collectionId) }) - case let .recentPacks(theme): - return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .recentStickers, theme: theme, selected: { + case let .recentPacks(theme, expanded): + return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .recentStickers, theme: theme, expanded: expanded, selected: { let collectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.recentStickers.rawValue, id: 0) inputNodeInteraction.navigateToCollectionId(collectionId) }) - case let .trending(elevated, theme): - return ChatMediaInputTrendingItem(inputNodeInteraction: inputNodeInteraction, elevated: elevated, theme: theme, selected: { + case let .trending(elevated, theme, expanded): + return ChatMediaInputTrendingItem(inputNodeInteraction: inputNodeInteraction, elevated: elevated, theme: theme, expanded: expanded, selected: { let collectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue, id: 0) inputNodeInteraction.navigateToCollectionId(collectionId) }) - case let .settings(theme): - return ChatMediaInputSettingsItem(inputNodeInteraction: inputNodeInteraction, theme: theme, selected: { + case let .settings(theme, expanded): + return ChatMediaInputSettingsItem(inputNodeInteraction: inputNodeInteraction, theme: theme, expanded: expanded, selected: { inputNodeInteraction.openSettings() }) - case let .peerSpecific(theme, peer): + case let .peerSpecific(theme, peer, expanded): let collectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.peerSpecific.rawValue, id: 0) - return ChatMediaInputPeerSpecificItem(context: context, inputNodeInteraction: inputNodeInteraction, collectionId: collectionId, peer: peer, theme: theme, selected: { + return ChatMediaInputPeerSpecificItem(context: context, inputNodeInteraction: inputNodeInteraction, collectionId: collectionId, peer: peer, theme: theme, expanded: expanded, selected: { inputNodeInteraction.navigateToCollectionId(collectionId) }) - case let .stickerPack(index, info, topItem, theme): - return ChatMediaInputStickerPackItem(account: context.account, inputNodeInteraction: inputNodeInteraction, collectionId: info.id, collectionInfo: info, stickerPackItem: topItem, index: index, theme: theme, selected: { + case let .stickerPack(index, info, topItem, theme, expanded): + return ChatMediaInputStickerPackItem(account: context.account, inputNodeInteraction: inputNodeInteraction, collectionId: info.id, collectionInfo: info, stickerPackItem: topItem, index: index, theme: theme, expanded: expanded, selected: { inputNodeInteraction.navigateToCollectionId(info.id) }) - case let .stickersMode(theme): - return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .stickersMode, theme: theme, selected: { + case let .stickersMode(theme, expanded): + return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .stickersMode, theme: theme, expanded: expanded, selected: { inputNodeInteraction.navigateBackToStickers() }) - case let .savedGifs(theme): - return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .savedGifs, theme: theme, selected: { + case let .savedGifs(theme, expanded): + return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .savedGifs, theme: theme, expanded: expanded, selected: { inputNodeInteraction.setGifMode(.recent) }) - case let .trendingGifs(theme): - return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .trendingGifs, theme: theme, selected: { + case let .trendingGifs(theme, expanded): + return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .trendingGifs, theme: theme, expanded: expanded, selected: { inputNodeInteraction.setGifMode(.trending) }) - case let .gifEmotion(_, theme, emoji): - return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .gifEmoji(emoji), theme: theme, selected: { + case let .gifEmotion(_, theme, emoji, expanded): + return ChatMediaInputMetaSectionItem(inputNodeInteraction: inputNodeInteraction, type: .gifEmoji(emoji), theme: theme, expanded: expanded, selected: { inputNodeInteraction.setGifMode(.emojiSearch(emoji)) }) } diff --git a/submodules/TelegramUI/Sources/ChatMediaInputPeerSpecificItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputPeerSpecificItem.swift index 834fcb7b06..8d4d0181fc 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputPeerSpecificItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputPeerSpecificItem.swift @@ -15,6 +15,7 @@ final class ChatMediaInputPeerSpecificItem: ListViewItem { let inputNodeInteraction: ChatMediaInputNodeInteraction let collectionId: ItemCollectionId let peer: Peer + let expanded: Bool let selectedItem: () -> Void let theme: PresentationTheme @@ -22,12 +23,13 @@ final class ChatMediaInputPeerSpecificItem: ListViewItem { return true } - init(context: AccountContext, inputNodeInteraction: ChatMediaInputNodeInteraction, collectionId: ItemCollectionId, peer: Peer, theme: PresentationTheme, selected: @escaping () -> Void) { + init(context: AccountContext, inputNodeInteraction: ChatMediaInputNodeInteraction, collectionId: ItemCollectionId, peer: Peer, theme: PresentationTheme, expanded: Bool, selected: @escaping () -> Void) { self.context = context self.inputNodeInteraction = inputNodeInteraction self.collectionId = collectionId self.peer = peer self.selectedItem = selected + self.expanded = expanded self.theme = theme } diff --git a/submodules/TelegramUI/Sources/ChatMediaInputRecentGifsItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputRecentGifsItem.swift index d0926be1a2..76ae81d613 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputRecentGifsItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputRecentGifsItem.swift @@ -11,28 +11,30 @@ import TelegramPresentationData final class ChatMediaInputRecentGifsItem: ListViewItem { let inputNodeInteraction: ChatMediaInputNodeInteraction let selectedItem: () -> Void + let expanded: Bool let theme: PresentationTheme var selectable: Bool { return true } - init(inputNodeInteraction: ChatMediaInputNodeInteraction, theme: PresentationTheme, selected: @escaping () -> Void) { + init(inputNodeInteraction: ChatMediaInputNodeInteraction, theme: PresentationTheme, expanded: Bool, selected: @escaping () -> Void) { self.inputNodeInteraction = inputNodeInteraction self.selectedItem = selected self.theme = theme + self.expanded = expanded } func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { let node = ChatMediaInputRecentGifsItemNode() - node.contentSize = CGSize(width: 41.0, height: 41.0) + node.contentSize = self.expanded ? expandedBoundingSize : boundingSize node.insets = ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem) node.inputNodeInteraction = self.inputNodeInteraction - node.updateTheme(theme: self.theme) node.updateIsHighlighted() node.updateAppearanceTransition(transition: .immediate) Queue.mainQueue().async { + node.updateTheme(theme: self.theme, expanded: self.expanded) completion(node, { return (nil, { _ in }) }) @@ -42,8 +44,8 @@ final class ChatMediaInputRecentGifsItem: ListViewItem { public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { Queue.mainQueue().async { - completion(ListViewItemNodeLayout(contentSize: node().contentSize, insets: ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem)), { _ in - (node() as? ChatMediaInputRecentGifsItemNode)?.updateTheme(theme: self.theme) + completion(ListViewItemNodeLayout(contentSize: self.expanded ? expandedBoundingSize : boundingSize, insets: ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem)), { _ in + (node() as? ChatMediaInputRecentGifsItemNode)?.updateTheme(theme: self.theme, expanded: self.expanded) }) } } @@ -53,14 +55,20 @@ final class ChatMediaInputRecentGifsItem: ListViewItem { } } -private let boundingSize = CGSize(width: 41.0, height: 41.0) -private let boundingImageSize = CGSize(width: 30.0, height: 30.0) -private let highlightSize = CGSize(width: 35.0, height: 35.0) +private let boundingSize = CGSize(width: 72.0, height: 41.0) +private let expandedBoundingSize = CGSize(width: 72.0, height: 72.0) +private let boundingImageScale: CGFloat = 0.625 +private let highlightSize = CGSize(width: 56.0, height: 56.0) private let verticalOffset: CGFloat = 3.0 + UIScreenPixel final class ChatMediaInputRecentGifsItemNode: ListViewItemNode { + private let containerNode: ASDisplayNode + private let scalingNode: ASDisplayNode private let imageNode: ASImageNode private let highlightNode: ASImageNode + private let titleNode: ImmediateTextNode + + private var currentExpanded = false var currentCollectionId: ItemCollectionId? var inputNodeInteraction: ChatMediaInputNodeInteraction? @@ -68,40 +76,68 @@ final class ChatMediaInputRecentGifsItemNode: ListViewItemNode { var theme: PresentationTheme? init() { + self.containerNode = ASDisplayNode() + self.containerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + + self.scalingNode = ASDisplayNode() + self.highlightNode = ASImageNode() self.highlightNode.isLayerBacked = true self.highlightNode.isHidden = true self.imageNode = ASImageNode() self.imageNode.isLayerBacked = true - self.imageNode.contentMode = .center - self.imageNode.contentsScale = UIScreenScale - self.highlightNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - highlightSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - highlightSize.height) / 2.0)), size: highlightSize) - - self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + self.titleNode = ImmediateTextNode() super.init(layerBacked: false, dynamicBounce: false) - self.addSubnode(self.highlightNode) - self.addSubnode(self.imageNode) + self.addSubnode(self.containerNode) + self.containerNode.addSubnode(self.scalingNode) + + self.scalingNode.addSubnode(self.highlightNode) + self.scalingNode.addSubnode(self.titleNode) + self.scalingNode.addSubnode(self.imageNode) self.currentCollectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.recentGifs.rawValue, id: 0) - - let imageSize = CGSize(width: 26.0, height: 26.0) - self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) } deinit { } - func updateTheme(theme: PresentationTheme) { + func updateTheme(theme: PresentationTheme, expanded: Bool) { if self.theme !== theme { self.theme = theme self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme) self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelRecentGifsIconImage(theme) + + self.titleNode.attributedText = NSAttributedString(string: "GIFs", font: Font.regular(11.0), textColor: theme.chat.inputPanel.primaryTextColor) } + + let imageSize = CGSize(width: 26.0 * 1.6, height: 26.0 * 1.6) + self.imageNode.frame = CGRect(origin: CGPoint(x: floor((expandedBoundingSize.width - imageSize.width) / 2.0), y: floor((expandedBoundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) + + self.containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedBoundingSize) + self.scalingNode.bounds = CGRect(origin: CGPoint(), size: expandedBoundingSize) + + let boundsSize = expanded ? expandedBoundingSize : CGSize(width: boundingSize.height, height: boundingSize.height) + let expandScale: CGFloat = expanded ? 1.0 : boundingImageScale + let expandTransition: ContainedViewLayoutTransition = self.currentExpanded != expanded ? .animated(duration: 0.3, curve: .spring) : .immediate + expandTransition.updateTransformScale(node: self.scalingNode, scale: expandScale) + expandTransition.updatePosition(node: self.scalingNode, position: CGPoint(x: boundsSize.width / 2.0, y: boundsSize.height / 2.0 + (expanded ? -2.0 : 3.0))) + + expandTransition.updateAlpha(node: self.titleNode, alpha: expanded ? 1.0 : 0.0) + let titleSize = self.titleNode.updateLayout(CGSize(width: expandedBoundingSize.width - 8.0, height: expandedBoundingSize.height)) + + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((expandedBoundingSize.width - titleSize.width) / 2.0), y: expandedBoundingSize.height - titleSize.height + 2.0), size: titleSize) + let displayTitleFrame = expanded ? titleFrame : CGRect(origin: CGPoint(x: titleFrame.minX, y: self.imageNode.position.y - titleFrame.size.height), size: titleFrame.size) + expandTransition.updateFrameAsPositionAndBounds(node: self.titleNode, frame: displayTitleFrame) + expandTransition.updateTransformScale(node: self.titleNode, scale: expanded ? 1.0 : 0.001) + + self.currentExpanded = expanded + + expandTransition.updateFrame(node: self.highlightNode, frame: expanded ? titleFrame.insetBy(dx: -7.0, dy: -2.0) : CGRect(origin: CGPoint(x: self.imageNode.position.x - highlightSize.width / 2.0, y: self.imageNode.position.y - highlightSize.height / 2.0), size: highlightSize)) } func updateIsHighlighted() { diff --git a/submodules/TelegramUI/Sources/ChatMediaInputSettingsItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputSettingsItem.swift index 15a69c1d51..a1e60e847a 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputSettingsItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputSettingsItem.swift @@ -11,27 +11,29 @@ import TelegramPresentationData final class ChatMediaInputSettingsItem: ListViewItem { let inputNodeInteraction: ChatMediaInputNodeInteraction let selectedItem: () -> Void + let expanded: Bool let theme: PresentationTheme var selectable: Bool { return true } - init(inputNodeInteraction: ChatMediaInputNodeInteraction, theme: PresentationTheme, selected: @escaping () -> Void) { + init(inputNodeInteraction: ChatMediaInputNodeInteraction, theme: PresentationTheme, expanded: Bool, selected: @escaping () -> Void) { self.inputNodeInteraction = inputNodeInteraction self.selectedItem = selected self.theme = theme + self.expanded = expanded } func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { let node = ChatMediaInputSettingsItemNode() - node.contentSize = CGSize(width: 41.0, height: 41.0) + node.contentSize = self.expanded ? expandedBoundingSize : boundingSize node.insets = ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem) node.inputNodeInteraction = self.inputNodeInteraction - node.updateTheme(theme: self.theme) node.updateAppearanceTransition(transition: .immediate) Queue.mainQueue().async { + node.updateTheme(theme: self.theme, expanded: self.expanded) completion(node, { return (nil, { _ in }) }) @@ -41,8 +43,8 @@ final class ChatMediaInputSettingsItem: ListViewItem { public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { Queue.mainQueue().async { - completion(ListViewItemNodeLayout(contentSize: node().contentSize, insets: ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem)), { _ in - (node() as? ChatMediaInputSettingsItemNode)?.updateTheme(theme: self.theme) + completion(ListViewItemNodeLayout(contentSize: self.expanded ? expandedBoundingSize : boundingSize, insets: ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem)), { _ in + (node() as? ChatMediaInputSettingsItemNode)?.updateTheme(theme: self.theme, expanded: self.expanded) }) } } @@ -52,14 +54,19 @@ final class ChatMediaInputSettingsItem: ListViewItem { } } -private let boundingSize = CGSize(width: 41.0, height: 41.0) -private let boundingImageSize = CGSize(width: 30.0, height: 30.0) -private let highlightSize = CGSize(width: 35.0, height: 35.0) +private let boundingSize = CGSize(width: 72.0, height: 41.0) +private let expandedBoundingSize = CGSize(width: 72.0, height: 72.0) +private let boundingImageScale: CGFloat = 0.625 private let verticalOffset: CGFloat = 3.0 + UIScreenPixel final class ChatMediaInputSettingsItemNode: ListViewItemNode { + private let containerNode: ASDisplayNode + private let scalingNode: ASDisplayNode private let buttonNode: HighlightableButtonNode private let imageNode: ASImageNode + private let titleNode: ImmediateTextNode + + private var currentExpanded = false var currentCollectionId: ItemCollectionId? var inputNodeInteraction: ChatMediaInputNodeInteraction? @@ -67,37 +74,59 @@ final class ChatMediaInputSettingsItemNode: ListViewItemNode { var theme: PresentationTheme? init() { + self.containerNode = ASDisplayNode() + self.containerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + + self.scalingNode = ASDisplayNode() + self.buttonNode = HighlightableButtonNode() self.imageNode = ASImageNode() self.imageNode.isLayerBacked = true - self.imageNode.contentMode = .center - self.imageNode.contentsScale = UIScreenScale - - self.buttonNode.frame = CGRect(origin: CGPoint(), size: boundingSize) - - self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - self.imageNode.contentMode = .center - self.imageNode.contentsScale = UIScreenScale + self.titleNode = ImmediateTextNode() + super.init(layerBacked: false, dynamicBounce: false) - self.addSubnode(self.buttonNode) - self.buttonNode.addSubnode(self.imageNode) + self.addSubnode(self.containerNode) + self.containerNode.addSubnode(self.scalingNode) - let imageSize = CGSize(width: 26.0, height: 26.0) - self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) + self.scalingNode.addSubnode(self.buttonNode) + self.scalingNode.addSubnode(self.imageNode) } - - deinit { - } - - func updateTheme(theme: PresentationTheme) { + + func updateTheme(theme: PresentationTheme, expanded: Bool) { + let imageSize = CGSize(width: 26.0 * 1.6, height: 26.0 * 1.6) + self.imageNode.frame = CGRect(origin: CGPoint(x: floor((expandedBoundingSize.width - imageSize.width) / 2.0), y: floor((expandedBoundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) + if self.theme !== theme { self.theme = theme self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelSettingsIconImage(theme) + + self.titleNode.attributedText = NSAttributedString(string: "Settings", font: Font.regular(11.0), textColor: theme.chat.inputPanel.primaryTextColor) } + + self.containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedBoundingSize) + self.scalingNode.bounds = CGRect(origin: CGPoint(), size: expandedBoundingSize) + self.buttonNode.frame = self.scalingNode.bounds + + let boundsSize = expanded ? expandedBoundingSize : CGSize(width: boundingSize.height, height: boundingSize.height) + let expandScale: CGFloat = expanded ? 1.0 : boundingImageScale + let expandTransition: ContainedViewLayoutTransition = self.currentExpanded != expanded ? .animated(duration: 0.3, curve: .spring) : .immediate + expandTransition.updateTransformScale(node: self.scalingNode, scale: expandScale) + expandTransition.updatePosition(node: self.scalingNode, position: CGPoint(x: boundsSize.width / 2.0, y: boundsSize.height / 2.0 + (expanded ? -2.0 : 3.0))) + + expandTransition.updateAlpha(node: self.titleNode, alpha: expanded ? 1.0 : 0.0) + let titleSize = self.titleNode.updateLayout(CGSize(width: expandedBoundingSize.width - 8.0, height: expandedBoundingSize.height)) + + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((expandedBoundingSize.width - titleSize.width) / 2.0), y: expandedBoundingSize.height - titleSize.height + 2.0), size: titleSize) + let displayTitleFrame = expanded ? titleFrame : CGRect(origin: CGPoint(x: titleFrame.minX, y: self.imageNode.position.y - titleFrame.size.height), size: titleFrame.size) + expandTransition.updateFrameAsPositionAndBounds(node: self.titleNode, frame: displayTitleFrame) + expandTransition.updateTransformScale(node: self.titleNode, scale: expanded ? 1.0 : 0.001) + + self.currentExpanded = expanded + } func updateAppearanceTransition(transition: ContainedViewLayoutTransition) { diff --git a/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift index dafba8f3f1..49aea9ed90 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift @@ -22,12 +22,13 @@ final class ChatMediaInputStickerPackItem: ListViewItem { let selectedItem: () -> Void let index: Int let theme: PresentationTheme + let expanded: Bool var selectable: Bool { return true } - init(account: Account, inputNodeInteraction: ChatMediaInputNodeInteraction, collectionId: ItemCollectionId, collectionInfo: StickerPackCollectionInfo, stickerPackItem: StickerPackItem?, index: Int, theme: PresentationTheme, selected: @escaping () -> Void) { + init(account: Account, inputNodeInteraction: ChatMediaInputNodeInteraction, collectionId: ItemCollectionId, collectionInfo: StickerPackCollectionInfo, stickerPackItem: StickerPackItem?, index: Int, theme: PresentationTheme, expanded: Bool, selected: @escaping () -> Void) { self.account = account self.inputNodeInteraction = inputNodeInteraction self.collectionId = collectionId @@ -36,18 +37,19 @@ final class ChatMediaInputStickerPackItem: ListViewItem { self.selectedItem = selected self.index = index self.theme = theme + self.expanded = expanded } func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { let node = ChatMediaInputStickerPackItemNode() - node.contentSize = boundingSize + node.contentSize = self.expanded ? expandedBoundingSize : boundingSize node.insets = ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem) node.inputNodeInteraction = self.inputNodeInteraction Queue.mainQueue().async { completion(node, { return (nil, { _ in - node.updateStickerPackItem(account: self.account, info: self.collectionInfo, item: self.stickerPackItem, collectionId: self.collectionId, theme: self.theme) + node.updateStickerPackItem(account: self.account, info: self.collectionInfo, item: self.stickerPackItem, collectionId: self.collectionId, theme: self.theme, expanded: self.expanded) node.updateAppearanceTransition(transition: .immediate) }) }) @@ -57,8 +59,8 @@ final class ChatMediaInputStickerPackItem: ListViewItem { public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { Queue.mainQueue().async { - completion(ListViewItemNodeLayout(contentSize: node().contentSize, insets: ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem)), { _ in - (node() as? ChatMediaInputStickerPackItemNode)?.updateStickerPackItem(account: self.account, info: self.collectionInfo, item: self.stickerPackItem, collectionId: self.collectionId, theme: self.theme) + completion(ListViewItemNodeLayout(contentSize: self.expanded ? expandedBoundingSize : boundingSize, insets: ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem)), { _ in + (node() as? ChatMediaInputStickerPackItemNode)?.updateStickerPackItem(account: self.account, info: self.collectionInfo, item: self.stickerPackItem, collectionId: self.collectionId, theme: self.theme, expanded: self.expanded) }) } } @@ -68,20 +70,26 @@ final class ChatMediaInputStickerPackItem: ListViewItem { } } -private let boundingSize = CGSize(width: 41.0, height: 41.0) -private let boundingImageSize = CGSize(width: 28.0, height: 28.0) -private let highlightSize = CGSize(width: 35.0, height: 35.0) -private let verticalOffset: CGFloat = 3.0 +private let boundingSize = CGSize(width: 72.0, height: 41.0) +private let expandedBoundingSize = CGSize(width: 72.0, height: 72.0) +private let boundingImageSize = CGSize(width: 45.0, height: 45.0) +private let boundingImageScale: CGFloat = 0.625 +private let highlightSize = CGSize(width: 56.0, height: 56.0) +private let verticalOffset: CGFloat = -3.0 final class ChatMediaInputStickerPackItemNode: ListViewItemNode { + private let containerNode: ASDisplayNode + private let scalingNode: ASDisplayNode private let imageNode: TransformImageNode private var animatedStickerNode: AnimatedStickerNode? private var placeholderNode: StickerShimmerEffectNode? private let highlightNode: ASImageNode + private let titleNode: ImmediateTextNode var inputNodeInteraction: ChatMediaInputNodeInteraction? var currentCollectionId: ItemCollectionId? private var currentThumbnailItem: StickerPackThumbnailItem? + private var currentExpanded = false private var theme: PresentationTheme? private let stickerFetchedDisposable = MetaDisposable() @@ -102,6 +110,11 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode { } init() { + self.containerNode = ASDisplayNode() + self.containerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + + self.scalingNode = ASDisplayNode() + self.highlightNode = ASImageNode() self.highlightNode.isLayerBacked = true self.highlightNode.isHidden = true @@ -110,18 +123,19 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode { self.imageNode.isLayerBacked = !smartInvertColorsEnabled() self.placeholderNode = StickerShimmerEffectNode() - self.placeholderNode?.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - - self.highlightNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - highlightSize.width) / 2.0) + verticalOffset - UIScreenPixel, y: floor((boundingSize.height - highlightSize.height) / 2.0) - UIScreenPixel), size: highlightSize) - - self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - + + self.titleNode = ImmediateTextNode() + super.init(layerBacked: false, dynamicBounce: false) - self.addSubnode(self.highlightNode) - self.addSubnode(self.imageNode) + self.addSubnode(self.containerNode) + self.containerNode.addSubnode(self.scalingNode) + + self.scalingNode.addSubnode(self.highlightNode) + self.scalingNode.addSubnode(self.titleNode) + self.scalingNode.addSubnode(self.imageNode) if let placeholderNode = self.placeholderNode { - self.addSubnode(placeholderNode) + self.scalingNode.addSubnode(placeholderNode) } var firstTime = true @@ -157,11 +171,13 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode { } } - func updateStickerPackItem(account: Account, info: StickerPackCollectionInfo, item: StickerPackItem?, collectionId: ItemCollectionId, theme: PresentationTheme) { + func updateStickerPackItem(account: Account, info: StickerPackCollectionInfo, item: StickerPackItem?, collectionId: ItemCollectionId, theme: PresentationTheme, expanded: Bool) { self.currentCollectionId = collectionId + var themeUpdated = false if self.theme !== theme { self.theme = theme + themeUpdated = true self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme) } @@ -186,22 +202,26 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode { } } + if themeUpdated || self.titleNode.attributedText?.string != info.title { + self.titleNode.attributedText = NSAttributedString(string: info.title, font: Font.regular(11.0), textColor: theme.chat.inputPanel.primaryTextColor) + } + + let boundsSize = expanded ? expandedBoundingSize : CGSize(width: boundingSize.height, height: boundingSize.height) + var imageSize = boundingImageSize + if self.currentThumbnailItem != thumbnailItem { self.currentThumbnailItem = thumbnailItem if let thumbnailItem = thumbnailItem { switch thumbnailItem { case let .still(representation): - let imageSize = representation.dimensions.cgSize.aspectFitted(boundingImageSize) + imageSize = representation.dimensions.cgSize.aspectFitted(boundingImageSize) let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())) imageApply() self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: representation.resource, nilIfEmpty: true)) - self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) case let .animated(resource): - let imageSize = boundingImageSize let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())) imageApply() self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true, nilIfEmpty: true)) - self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false self.imageNode.isHidden = loopAnimatedStickers @@ -212,18 +232,14 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode { } else { animatedStickerNode = AnimatedStickerNode() self.animatedStickerNode = animatedStickerNode - animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) if let placeholderNode = self.placeholderNode { - self.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode) + self.scalingNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode) } else { - self.addSubnode(animatedStickerNode) + self.scalingNode.addSubnode(animatedStickerNode) } animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 80, height: 80, mode: .cached) } animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers - if let animatedStickerNode = self.animatedStickerNode { - animatedStickerNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) - } } if let resourceReference = resourceReference { self.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference).start()) @@ -232,16 +248,43 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode { if let placeholderNode = self.placeholderNode { let imageSize = boundingImageSize - let placeholderFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) - placeholderNode.frame = placeholderFrame - placeholderNode.update(backgroundColor: nil, foregroundColor: theme.chat.inputMediaPanel.stickersSectionTextColor.blitOver(theme.chat.inputPanel.panelBackgroundColor, alpha: 0.4), shimmeringColor: theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor.withMultipliedAlpha(0.2), data: info.immediateThumbnailData, size: imageSize, imageSize: CGSize(width: 100.0, height: 100.0)) } self.updateIsHighlighted() } + + self.containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedBoundingSize) + self.scalingNode.bounds = CGRect(origin: CGPoint(), size: expandedBoundingSize) + + let expandScale: CGFloat = expanded ? 1.0 : boundingImageScale + let expandTransition: ContainedViewLayoutTransition = self.currentExpanded != expanded ? .animated(duration: 0.3, curve: .spring) : .immediate + expandTransition.updateTransformScale(node: self.scalingNode, scale: expandScale) + expandTransition.updatePosition(node: self.scalingNode, position: CGPoint(x: boundsSize.width / 2.0, y: boundsSize.height / 2.0 + (expanded ? -2.0 : 3.0))) + + expandTransition.updateAlpha(node: self.titleNode, alpha: expanded ? 1.0 : 0.0) + let titleSize = self.titleNode.updateLayout(CGSize(width: expandedBoundingSize.width - 8.0, height: expandedBoundingSize.height)) + + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((expandedBoundingSize.width - titleSize.width) / 2.0), y: expandedBoundingSize.height - titleSize.height + 2.0), size: titleSize) + let displayTitleFrame = expanded ? titleFrame : CGRect(origin: CGPoint(x: titleFrame.minX, y: self.imageNode.position.y - titleFrame.size.height), size: titleFrame.size) + expandTransition.updateFrameAsPositionAndBounds(node: self.titleNode, frame: displayTitleFrame) + expandTransition.updateTransformScale(node: self.titleNode, scale: expanded ? 1.0 : 0.001) + + self.currentExpanded = expanded + + self.imageNode.bounds = CGRect(origin: CGPoint(), size: imageSize) + self.imageNode.position = CGPoint(x: expandedBoundingSize.height / 2.0, y: expandedBoundingSize.width / 2.0) + if let animatedStickerNode = self.animatedStickerNode { + animatedStickerNode.frame = self.imageNode.frame + animatedStickerNode.updateLayout(size: self.imageNode.frame.size) + } + if let placeholderNode = self.placeholderNode { + placeholderNode.bounds = CGRect(origin: CGPoint(), size: boundingImageSize) + placeholderNode.position = self.imageNode.position + } + expandTransition.updateFrame(node: self.highlightNode, frame: expanded ? titleFrame.insetBy(dx: -7.0, dy: -2.0) : CGRect(origin: CGPoint(x: self.imageNode.position.x - highlightSize.width / 2.0, y: self.imageNode.position.y - highlightSize.height / 2.0), size: highlightSize)) } - + override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { if let placeholderNode = self.placeholderNode { placeholderNode.updateAbsoluteRect(rect, within: containerSize) diff --git a/submodules/TelegramUI/Sources/ChatMediaInputTrendingItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputTrendingItem.swift index 4b25c0ab63..1c25f037f8 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputTrendingItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputTrendingItem.swift @@ -12,29 +12,31 @@ final class ChatMediaInputTrendingItem: ListViewItem { let inputNodeInteraction: ChatMediaInputNodeInteraction let selectedItem: () -> Void let elevated: Bool + let expanded: Bool let theme: PresentationTheme var selectable: Bool { return true } - init(inputNodeInteraction: ChatMediaInputNodeInteraction, elevated: Bool, theme: PresentationTheme, selected: @escaping () -> Void) { + init(inputNodeInteraction: ChatMediaInputNodeInteraction, elevated: Bool, theme: PresentationTheme, expanded: Bool, selected: @escaping () -> Void) { self.inputNodeInteraction = inputNodeInteraction self.elevated = elevated self.selectedItem = selected + self.expanded = expanded self.theme = theme } func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { let node = ChatMediaInputTrendingItemNode() - node.contentSize = CGSize(width: 41.0, height: 41.0) + node.contentSize = self.expanded ? expandedBoundingSize : boundingSize node.insets = ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem) node.inputNodeInteraction = self.inputNodeInteraction - node.updateTheme(elevated: self.elevated, theme: self.theme) node.updateIsHighlighted() node.updateAppearanceTransition(transition: .immediate) Queue.mainQueue().async { + node.updateTheme(elevated: self.elevated, theme: self.theme, expanded: self.expanded) completion(node, { return (nil, { _ in }) }) @@ -44,8 +46,8 @@ final class ChatMediaInputTrendingItem: ListViewItem { public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { Queue.mainQueue().async { - completion(ListViewItemNodeLayout(contentSize: node().contentSize, insets: ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem)), { _ in - (node() as? ChatMediaInputTrendingItemNode)?.updateTheme(elevated: self.elevated, theme: self.theme) + completion(ListViewItemNodeLayout(contentSize: self.expanded ? expandedBoundingSize : boundingSize, insets: ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem)), { _ in + (node() as? ChatMediaInputTrendingItemNode)?.updateTheme(elevated: self.elevated, theme: self.theme, expanded: self.expanded) }) } } @@ -55,14 +57,20 @@ final class ChatMediaInputTrendingItem: ListViewItem { } } -private let boundingSize = CGSize(width: 41.0, height: 41.0) -private let boundingImageSize = CGSize(width: 30.0, height: 30.0) -private let highlightSize = CGSize(width: 35.0, height: 35.0) +private let boundingSize = CGSize(width: 72.0, height: 41.0) +private let expandedBoundingSize = CGSize(width: 72.0, height: 72.0) +private let boundingImageScale: CGFloat = 0.625 +private let highlightSize = CGSize(width: 56.0, height: 56.0) private let verticalOffset: CGFloat = 3.0 + UIScreenPixel final class ChatMediaInputTrendingItemNode: ListViewItemNode { + private let containerNode: ASDisplayNode + private let scalingNode: ASDisplayNode private let imageNode: ASImageNode private let highlightNode: ASImageNode + private let titleNode: ImmediateTextNode + + private var currentExpanded = false var currentCollectionId: ItemCollectionId? var inputNodeInteraction: ChatMediaInputNodeInteraction? @@ -73,37 +81,43 @@ final class ChatMediaInputTrendingItemNode: ListViewItemNode { let badgeBackground: ASImageNode init() { + self.containerNode = ASDisplayNode() + self.containerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + + self.scalingNode = ASDisplayNode() + self.highlightNode = ASImageNode() self.highlightNode.isLayerBacked = true self.highlightNode.isHidden = true self.imageNode = ASImageNode() self.imageNode.isLayerBacked = true - self.imageNode.contentMode = .center - self.imageNode.contentsScale = UIScreenScale self.badgeBackground = ASImageNode() self.badgeBackground.displaysAsynchronously = false self.badgeBackground.displayWithoutProcessing = true self.badgeBackground.isHidden = true - self.highlightNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - highlightSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - highlightSize.height) / 2.0)), size: highlightSize) - - self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + self.titleNode = ImmediateTextNode() super.init(layerBacked: false, dynamicBounce: false) - self.addSubnode(self.highlightNode) - self.addSubnode(self.imageNode) - self.addSubnode(self.badgeBackground) + self.addSubnode(self.containerNode) + self.containerNode.addSubnode(self.scalingNode) + + self.scalingNode.addSubnode(self.highlightNode) + self.scalingNode.addSubnode(self.titleNode) + self.scalingNode.addSubnode(self.imageNode) + self.scalingNode.addSubnode(self.badgeBackground) self.currentCollectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue, id: 0) } - deinit { - } - - func updateTheme(elevated: Bool, theme: PresentationTheme) { + func updateTheme(elevated: Bool, theme: PresentationTheme, expanded: Bool) { + let imageSize = CGSize(width: 26.0 * 1.85, height: 26.0 * 1.85) + let imageFrame = CGRect(origin: CGPoint(x: floor((expandedBoundingSize.width - imageSize.width) / 2.0), y: floor((expandedBoundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) + self.imageNode.frame = imageFrame + if self.theme !== theme { self.theme = theme @@ -111,19 +125,37 @@ final class ChatMediaInputTrendingItemNode: ListViewItemNode { self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelTrendingIconImage(theme) self.badgeBackground.image = generateFilledCircleImage(diameter: 10.0, color: theme.chat.inputPanel.mediaRecordingDotColor) - let imageSize = CGSize(width: 26.0, height: 26.0) - let imageFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) - self.imageNode.frame = imageFrame - if let image = self.badgeBackground.image { - self.badgeBackground.frame = CGRect(origin: CGPoint(x: imageFrame.maxX - image.size.width - 1.0, y: imageFrame.maxY - image.size.width + 1.0), size: image.size) + self.badgeBackground.frame = CGRect(origin: CGPoint(x: floor(imageFrame.maxX - image.size.width - 7.0), y: 18.0), size: image.size) } + + self.titleNode.attributedText = NSAttributedString(string: "Trending", font: Font.regular(11.0), textColor: theme.chat.inputPanel.primaryTextColor) } if self.elevated != elevated { self.elevated = elevated self.badgeBackground.isHidden = !self.elevated } + + self.containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: expandedBoundingSize) + self.scalingNode.bounds = CGRect(origin: CGPoint(), size: expandedBoundingSize) + + let boundsSize = expanded ? expandedBoundingSize : CGSize(width: boundingSize.height, height: boundingSize.height) + let expandScale: CGFloat = expanded ? 1.0 : boundingImageScale + let expandTransition: ContainedViewLayoutTransition = self.currentExpanded != expanded ? .animated(duration: 0.3, curve: .spring) : .immediate + expandTransition.updateTransformScale(node: self.scalingNode, scale: expandScale) + expandTransition.updatePosition(node: self.scalingNode, position: CGPoint(x: boundsSize.width / 2.0, y: boundsSize.height / 2.0 + (expanded ? -2.0 : 3.0))) + + expandTransition.updateAlpha(node: self.titleNode, alpha: expanded ? 1.0 : 0.0) + let titleSize = self.titleNode.updateLayout(CGSize(width: expandedBoundingSize.width - 8.0, height: expandedBoundingSize.height)) + + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((expandedBoundingSize.width - titleSize.width) / 2.0), y: expandedBoundingSize.height - titleSize.height + 2.0), size: titleSize) + let displayTitleFrame = expanded ? titleFrame : CGRect(origin: CGPoint(x: titleFrame.minX, y: self.imageNode.position.y - titleFrame.size.height), size: titleFrame.size) + expandTransition.updateFrameAsPositionAndBounds(node: self.titleNode, frame: displayTitleFrame) + + self.currentExpanded = expanded + + expandTransition.updateFrame(node: self.highlightNode, frame: expanded ? titleFrame.insetBy(dx: -7.0, dy: -2.0) : CGRect(origin: CGPoint(x: self.imageNode.position.x - highlightSize.width / 2.0, y: self.imageNode.position.y - highlightSize.height / 2.0), size: highlightSize)) } func updateIsHighlighted() { diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift index 0c1a300b7f..9aebe8dbb9 100644 --- a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift +++ b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift @@ -608,24 +608,8 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode { let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, hasUnreadTrending: nil, theme: theme, hasGifs: false, hasSettings: false) let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, hasSearch: false, hasAccessories: false, strings: strings, theme: theme) -// if view.higher == nil { -// var hasTopSeparator = true -// if gridEntries.count == 1, case .search = gridEntries[0] { -// hasTopSeparator = false -// } -// -// var index = 0 -// for item in trendingPacks { -// if !installedPacks.contains(item.info.id) { -// gridEntries.append(.trending(TrendingPanePackEntry(index: index, info: item.info, theme: theme, strings: strings, topItems: item.topItems, installed: installedPacks.contains(item.info.id), unread: item.unread, topSeparator: hasTopSeparator))) -// hasTopSeparator = true -// index += 1 -// } -// } -// } - let (previousPanelEntries, previousGridEntries) = previousStickerEntries.swap((panelEntries, gridEntries)) - return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: stickersInputNodeInteraction), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: stickersInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty) + return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: stickersInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: stickersInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty) } self.disposable.set((stickerTransitions @@ -660,7 +644,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode { let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: nil, recentStickers: nil, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, hasSearch: false, hasAccessories: false, strings: strings, theme: theme) let (previousPanelEntries, previousGridEntries) = previousMaskEntries.swap((panelEntries, gridEntries)) - return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: masksInputNodeInteraction), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: masksInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty) + return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: masksInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: masksInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty) } self.maskDisposable.set((maskTransitions diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 594c76b429..748d69ccf2 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -263,7 +263,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu self.contentNode.addSubnode(self.historyNode) self.contentNode.addSubnode(self.controlsNode) - self.historyNode.beganInteractiveDragging = { [weak self] in + self.historyNode.beganInteractiveDragging = { [weak self] _ in self?.controlsNode.collapse() } @@ -628,7 +628,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu } } - self.historyNode.beganInteractiveDragging = { [weak self] in + self.historyNode.beganInteractiveDragging = { [weak self] _ in self?.controlsNode.collapse() } diff --git a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift index 80bc1ec2bf..b2b279f61d 100644 --- a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift +++ b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift @@ -284,7 +284,7 @@ class WebSearchControllerNode: ASDisplayNode { } }) - self.recentQueriesNode.beganInteractiveDragging = { [weak self] in + self.recentQueriesNode.beganInteractiveDragging = { [weak self] _ in self?.dismissInput?() }