Add stickerpack strip expansion

This commit is contained in:
Ilya Laktyushin 2021-07-07 03:12:07 +03:00
parent 21b8a066c2
commit 4a853bf13d
30 changed files with 598 additions and 358 deletions

View File

@ -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()
}

View File

@ -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
}

View File

@ -414,7 +414,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
}
}))
self.listNode.beganInteractiveDragging = { [weak self] in
self.listNode.beganInteractiveDragging = { [weak self] _ in
self?.dismissInput?()
}
}

View File

@ -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 {

View File

@ -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?()
}

View File

@ -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
}

View File

@ -224,7 +224,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
}
}))
self.listNode.beganInteractiveDragging = { [weak self] in
self.listNode.beganInteractiveDragging = { [weak self] _ in
self?.interaction.dismissInput()
}
}

View File

@ -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
}

View File

@ -219,7 +219,7 @@ final class ChannelDiscussionGroupSearchContainerNode: SearchDisplayControllerCo
}
})
self.listNode.beganInteractiveDragging = { [weak self] in
self.listNode.beganInteractiveDragging = { [weak self] _ in
self?.dismissInput?()
}
}

View File

@ -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?()
}
}

View File

@ -581,7 +581,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
}
}
self.listNode.beganInteractiveDragging = { [weak self] in
self.listNode.beganInteractiveDragging = { [weak self] _ in
self?.view.endEditing(true)
}
}

View File

@ -263,7 +263,7 @@ private final class OldChannelsSearchContainerNode: SearchDisplayControllerConte
}
})
self.listNode.beganInteractiveDragging = { [weak self] in
self.listNode.beganInteractiveDragging = { [weak self] _ in
self?.dismissInput?()
}
}

View File

@ -174,7 +174,7 @@ private final class LocalizationListSearchContainerNode: SearchDisplayController
}
})
self.listNode.beganInteractiveDragging = { [weak self] in
self.listNode.beganInteractiveDragging = { [weak self] _ in
self?.dismissInput?()
}
}

View File

@ -1279,7 +1279,7 @@ private final class NotificationExceptionsSearchContainerNode: SearchDisplayCont
}
})
self.listNode.beganInteractiveDragging = { [weak self] in
self.listNode.beganInteractiveDragging = { [weak self] _ in
self?.dismissInput?()
}
}

View File

@ -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?()
}
}

View File

@ -654,7 +654,7 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode {
}
})
self.recentListNode.beganInteractiveDragging = { [weak self] in
self.recentListNode.beganInteractiveDragging = { [weak self] _ in
self?.dismissInput?()
}

View File

@ -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)

View File

@ -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?()

View File

@ -231,7 +231,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode {
}
}))
self.listNode.beganInteractiveDragging = { [weak self] in
self.listNode.beganInteractiveDragging = { [weak self] _ in
self?.dismissInput?()
}

View File

@ -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<Void, NoError>?, (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() {

View File

@ -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<Int64>()
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<Bool>()
private var panelCollapseScrollToIndex: Int?
private let panelExpandedPromise = ValuePromise<Bool>(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<ItemCollectionsView?>(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 {
@ -903,7 +914,7 @@ 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,18 +1425,10 @@ 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)
@ -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))

View File

@ -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))
})
}

View File

@ -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
}

View File

@ -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<Void, NoError>?, (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() {

View File

@ -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<Void, NoError>?, (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, 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)
func updateTheme(theme: PresentationTheme) {
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) {

View File

@ -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<Void, NoError>?, (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,14 +248,41 @@ 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) {

View File

@ -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<Void, NoError>?, (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, 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
func updateTheme(elevated: Bool, theme: PresentationTheme) {
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() {

View File

@ -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

View File

@ -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()
}

View File

@ -284,7 +284,7 @@ class WebSearchControllerNode: ASDisplayNode {
}
})
self.recentQueriesNode.beganInteractiveDragging = { [weak self] in
self.recentQueriesNode.beganInteractiveDragging = { [weak self] _ in
self?.dismissInput?()
}