Various Improvements

This commit is contained in:
Ilya Laktyushin
2021-08-07 22:55:08 +03:00
parent 6b3103bafc
commit 2e989d1369
46 changed files with 1743 additions and 254 deletions

View File

@@ -7,6 +7,7 @@ import TelegramCore
import SwiftSignalKit
import TelegramPresentationData
import TelegramUIPreferences
import TelegramNotices
import MergeLists
import AccountContext
import StickerPackPreviewUI
@@ -65,7 +66,7 @@ func preparedChatMediaInputGridEntryTransition(account: Account, view: ItemColle
case .initial:
for i in (0 ..< toEntries.count).reversed() {
switch toEntries[i] {
case .search, .peerSpecificSetup, .trending:
case .search, .trendingList, .peerSpecificSetup, .trending:
break
case .sticker:
scrollToItem = GridNodeScrollToItem(index: i, position: .top(0.0), transition: .immediate, directionHint: .down, adjustForSection: true, adjustForTopInset: true)
@@ -140,7 +141,7 @@ func preparedChatMediaInputGridEntryTransition(account: Account, view: ItemColle
var firstIndexInSectionOffset = 0
if !toEntries.isEmpty {
switch toEntries[0].index {
case .search, .peerSpecificSetup, .trending:
case .search, .trendingList, .peerSpecificSetup, .trending:
break
case let .collectionIndex(index):
firstIndexInSectionOffset = Int(index.itemIndex.index)
@@ -152,14 +153,11 @@ 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, expanded: Bool = false) -> [ChatMediaInputPanelEntry] {
func chatMediaInputPanelEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, theme: PresentationTheme, hasGifs: Bool = true, hasSettings: Bool = true, expanded: Bool = false) -> [ChatMediaInputPanelEntry] {
var entries: [ChatMediaInputPanelEntry] = []
if hasGifs {
entries.append(.recentGifs(theme, expanded))
}
if let hasUnreadTrending = hasUnreadTrending {
entries.append(.trending(hasUnreadTrending, theme, expanded))
}
if let savedStickers = savedStickers, !savedStickers.items.isEmpty {
entries.append(.savedStickers(theme, expanded))
}
@@ -221,7 +219,7 @@ func chatMediaInputPanelGifModeEntries(theme: PresentationTheme, reactions: [Str
return entries
}
func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, hasSearch: Bool = true, hasAccessories: Bool = true, strings: PresentationStrings, theme: PresentationTheme) -> [ChatMediaInputGridEntry] {
func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, trendingPacks: [FeaturedStickerPackItem], dismissedTrendingStickerPacks: [ItemCollectionId.Id]? = nil, hasSearch: Bool = true, hasAccessories: Bool = true, strings: PresentationStrings, theme: PresentationTheme) -> [ChatMediaInputGridEntry] {
var entries: [ChatMediaInputGridEntry] = []
if hasSearch && view.lower == nil {
@@ -249,6 +247,14 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered
}
}
var trendingIsDismissed = false
if let dismissedTrendingStickerPacks = dismissedTrendingStickerPacks, trendingPacks.map { $0.info.id.id } == dismissedTrendingStickerPacks {
trendingIsDismissed = true
}
if !trendingIsDismissed {
entries.append(.trendingList(theme: theme, strings: strings, packs: trendingPacks))
}
if let recentStickers = recentStickers, !recentStickers.items.isEmpty {
let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.recentStickers.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_FrequentlyUsed.uppercased(), shortName: "", thumbnail: nil, immediateThumbnailData: nil, hash: 0, count: 0)
var addedCount = 0
@@ -346,6 +352,8 @@ final class ChatMediaInputNodeInteraction {
let navigateBackToStickers: () -> Void
let setGifMode: (ChatMediaInputGifMode) -> Void
let openSettings: () -> Void
let openTrending: (ItemCollectionId?) -> Void
let dismissTrendingPacks: ([ItemCollectionId]) -> Void
let toggleSearch: (Bool, ChatMediaInputSearchMode?, String) -> Void
let openPeerSpecificSettings: () -> Void
let dismissPeerSpecificSettings: () -> Void
@@ -360,11 +368,13 @@ final class ChatMediaInputNodeInteraction {
var displayStickerPlaceholder = true
var displayStickerPackManageControls = true
init(navigateToCollectionId: @escaping (ItemCollectionId) -> Void, navigateBackToStickers: @escaping () -> Void, setGifMode: @escaping (ChatMediaInputGifMode) -> Void, openSettings: @escaping () -> Void, toggleSearch: @escaping (Bool, ChatMediaInputSearchMode?, String) -> Void, openPeerSpecificSettings: @escaping () -> Void, dismissPeerSpecificSettings: @escaping () -> Void, clearRecentlyUsedStickers: @escaping () -> Void) {
init(navigateToCollectionId: @escaping (ItemCollectionId) -> Void, navigateBackToStickers: @escaping () -> Void, setGifMode: @escaping (ChatMediaInputGifMode) -> Void, openSettings: @escaping () -> Void, openTrending: @escaping (ItemCollectionId?) -> Void, dismissTrendingPacks: @escaping ([ItemCollectionId]) -> Void, toggleSearch: @escaping (Bool, ChatMediaInputSearchMode?, String) -> Void, openPeerSpecificSettings: @escaping () -> Void, dismissPeerSpecificSettings: @escaping () -> Void, clearRecentlyUsedStickers: @escaping () -> Void) {
self.navigateToCollectionId = navigateToCollectionId
self.navigateBackToStickers = navigateBackToStickers
self.setGifMode = setGifMode
self.openSettings = openSettings
self.openTrending = openTrending
self.dismissTrendingPacks = dismissTrendingPacks
self.toggleSearch = toggleSearch
self.openPeerSpecificSettings = openPeerSpecificSettings
self.dismissPeerSpecificSettings = dismissPeerSpecificSettings
@@ -451,14 +461,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 {
private var panelFocusScrollToIndex: Int?
private var panelFocusInitialPosition: CGPoint?
private let panelIsFocusedPromise = ValuePromise<Bool>(false)
private var panelIsFocused: Bool = false {
didSet {
self.panelExpandedPromise.set(self.panelExpanded)
self.panelIsFocusedPromise.set(self.panelIsFocused)
}
}
private var panelCollapseTimer: SwiftSignalKit.Timer?
private var panelFocusTimer: SwiftSignalKit.Timer?
var requestDisableStickerAnimations: ((Bool) -> Void)?
@@ -494,17 +505,17 @@ final class ChatMediaInputNode: ChatInputNode {
self.themeAndStringsPromise = Promise((theme, strings))
self.collectionListPanel = ASDisplayNode()
self.collectionListPanel.clipsToBounds = true
// self.collectionListPanel.clipsToBounds = true
self.collectionListSeparator = ASDisplayNode()
self.collectionListSeparator.isLayerBacked = true
self.collectionListSeparator.backgroundColor = theme.chat.inputMediaPanel.panelSeparatorColor
self.collectionListContainer = CollectionListContainerNode()
self.collectionListContainer.clipsToBounds = true
// self.collectionListContainer.clipsToBounds = true
self.listView = ListView()
// self.listView.clipsToBounds = false
self.listView.useSingleDimensionTouchPoint = true
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
@@ -512,7 +523,7 @@ final class ChatMediaInputNode: ChatInputNode {
}
self.gifListView = ListView()
// self.gifListView.clipsToBounds = false
self.gifListView.useSingleDimensionTouchPoint = true
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
@@ -550,6 +561,7 @@ final class ChatMediaInputNode: ChatInputNode {
} else if collectionId.namespace == ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue {
strongSelf.controllerInteraction.navigationController()?.pushViewController(FeaturedStickersScreen(
context: strongSelf.context,
highlightedPackId: nil,
sendSticker: {
fileReference, sourceNode, sourceRect in
if let strongSelf = self {
@@ -609,6 +621,23 @@ final class ChatMediaInputNode: ChatInputNode {
controller.navigationPresentation = .modal
strongSelf.controllerInteraction.navigationController()?.pushViewController(controller)
}
}, openTrending: { [weak self] packId in
if let strongSelf = self {
strongSelf.controllerInteraction.navigationController()?.pushViewController(FeaturedStickersScreen(
context: strongSelf.context,
highlightedPackId: packId,
sendSticker: {
fileReference, sourceNode, sourceRect in
if let strongSelf = self {
return strongSelf.controllerInteraction.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect)
} else {
return false
}
}
))
}
}, dismissTrendingPacks: { packIds in
let _ = ApplicationSpecificNotice.setDismissedTrendingStickerPacks(accountManager: context.sharedContext.accountManager, values: packIds.map { $0.id }).start()
}, toggleSearch: { [weak self] value, searchMode, query in
if let strongSelf = self {
if let searchMode = searchMode, value {
@@ -636,8 +665,8 @@ final class ChatMediaInputNode: ChatInputNode {
if let strongSelf = self {
strongSelf.controllerInteraction.updateInputMode { current in
switch current {
case let .media(mode, _):
return .media(mode: mode, expanded: .search(searchMode))
case let .media(mode, _, focused):
return .media(mode: mode, expanded: .search(searchMode), focused: focused)
default:
return current
}
@@ -648,8 +677,8 @@ final class ChatMediaInputNode: ChatInputNode {
} else {
strongSelf.controllerInteraction.updateInputMode { current in
switch current {
case let .media(mode, _):
return .media(mode: mode, expanded: nil)
case let .media(mode, _, focused):
return .media(mode: mode, expanded: nil, focused: focused)
default:
return current
}
@@ -860,8 +889,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, self.panelExpandedPromise.get())
|> map { viewAndUpdate, peerSpecificPack, trendingPacks, themeAndStrings, reactions, panelExpanded -> (ItemCollectionsView, ChatMediaInputPanelTransition, ChatMediaInputPanelTransition, Bool, ChatMediaInputGridTransition, Bool) in
let transitions = combineLatest(queue: transitionQueue, itemCollectionsView, peerSpecificPack, context.account.viewTracker.featuredStickerPacks(), self.themeAndStringsPromise.get(), reactions, self.panelIsFocusedPromise.get(), ApplicationSpecificNotice.dismissedTrendingStickerPacks(accountManager: context.sharedContext.accountManager))
|> map { viewAndUpdate, peerSpecificPack, trendingPacks, themeAndStrings, reactions, panelExpanded, dismissedTrendingStickerPacks -> (ItemCollectionsView, ChatMediaInputPanelTransition, ChatMediaInputPanelTransition, Bool, ChatMediaInputGridTransition, Bool) in
let (view, viewUpdate) = viewAndUpdate
let previous = previousView.swap(view)
var update = viewUpdate
@@ -884,21 +913,10 @@ final class ChatMediaInputNode: ChatInputNode {
for info in view.collectionInfos {
installedPacks.insert(info.0)
}
var hasUnreadTrending: Bool?
for pack in trendingPacks {
if hasUnreadTrending == nil {
hasUnreadTrending = false
}
if pack.unread {
hasUnreadTrending = true
break
}
}
let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, hasUnreadTrending: hasUnreadTrending, theme: theme, expanded: panelExpanded)
let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, 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)
var gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, trendingPacks: trendingPacks, dismissedTrendingStickerPacks: dismissedTrendingStickerPacks, strings: strings, theme: theme)
if view.higher == nil {
var hasTopSeparator = true
@@ -998,32 +1016,79 @@ final class ChatMediaInputNode: ChatInputNode {
}
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
if let strongSelf = self {
strongSelf.panelFocusTimer?.invalidate()
var position = position
var index = strongSelf.listView.itemIndexAtPoint(CGPoint(x: 0.0, y: position.y))
if index == nil {
position.y += 10.0
index = strongSelf.listView.itemIndexAtPoint(CGPoint(x: 0.0, y: position.y))
}
if let index = index {
strongSelf.panelFocusScrollToIndex = index
strongSelf.panelFocusInitialPosition = position
}
strongSelf.interfaceInteraction?.updateTextInputStateAndMode { inputTextState, inputMode in
if case let .media(mode, expanded, _) = inputMode {
return (inputTextState, .media(mode: mode, expanded: expanded, focused: true))
} else {
return (inputTextState, inputMode)
}
}
strongSelf.updateIsExpanded(true)
}
}
self.listView.didEndScrolling = { [weak self] in
if let strongSelf = self, false {
strongSelf.setupCollapseTimer()
self.listView.endedInteractiveDragging = { [weak self] position in
if let strongSelf = self {
strongSelf.panelFocusInitialPosition = position
}
}
self.listView.didEndScrolling = { [weak self] decelerated in
if let strongSelf = self {
if decelerated {
strongSelf.panelFocusScrollToIndex = nil
strongSelf.panelFocusInitialPosition = nil
}
strongSelf.setupCollapseTimer(timeout: decelerated ? 0.5 : 1.5)
}
}
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
if let strongSelf = self {
strongSelf.panelFocusTimer?.invalidate()
var position = position
var index = strongSelf.gifListView.itemIndexAtPoint(CGPoint(x: 0.0, y: position.y))
if index == nil {
position.y += 10.0
index = strongSelf.gifListView.itemIndexAtPoint(CGPoint(x: 0.0, y: position.y))
}
if let index = index {
strongSelf.panelFocusScrollToIndex = index
}
strongSelf.interfaceInteraction?.updateTextInputStateAndMode { inputTextState, inputMode in
if case let .media(mode, expanded, _) = inputMode {
return (inputTextState, .media(mode: mode, expanded: expanded, focused: true))
} else {
return (inputTextState, inputMode)
}
}
strongSelf.updateIsExpanded(true)
}
}
self.gifListView.didEndScrolling = { [weak self] in
if let strongSelf = self, false {
strongSelf.setupCollapseTimer()
self.gifListView.endedInteractiveDragging = { [weak self] position in
if let strongSelf = self {
strongSelf.panelFocusInitialPosition = position
}
}
self.gifListView.didEndScrolling = { [weak self] decelerated in
if let strongSelf = self {
if decelerated {
strongSelf.panelFocusScrollToIndex = nil
strongSelf.panelFocusInitialPosition = nil
}
strongSelf.setupCollapseTimer(timeout: decelerated ? 0.5 : 1.5)
}
}
}
@@ -1031,23 +1096,31 @@ final class ChatMediaInputNode: ChatInputNode {
deinit {
self.disposable.dispose()
self.searchContainerNodeLoadedDisposable.dispose()
self.panelCollapseTimer?.invalidate()
self.panelFocusTimer?.invalidate()
}
private func updateIsExpanded(_ isExpanded: Bool) {
self.panelCollapseTimer?.invalidate()
self.panelExpanded = isExpanded
guard self.panelIsFocused != isExpanded else {
return
}
self.panelIsFocused = isExpanded
self.updatePaneClippingContainer(size: self.paneClippingContainer.bounds.size, offset: self.currentCollectionListPanelOffset(), transition: .animated(duration: 0.3, curve: .spring))
}
private func setupCollapseTimer() {
self.panelCollapseTimer?.invalidate()
private func setupCollapseTimer(timeout: Double) {
self.panelFocusTimer?.invalidate()
let timer = SwiftSignalKit.Timer(timeout: 1.5, repeat: false, completion: { [weak self] in
self?.updateIsExpanded(false)
let timer = SwiftSignalKit.Timer(timeout: timeout, repeat: false, completion: { [weak self] in
self?.interfaceInteraction?.updateTextInputStateAndMode { inputTextState, inputMode in
if case let .media(mode, expanded, _) = inputMode {
return (inputTextState, .media(mode: mode, expanded: expanded, focused: false))
} else {
return (inputTextState, inputMode)
}
}
}, queue: Queue.mainQueue())
self.panelCollapseTimer = timer
self.panelFocusTimer = timer
timer.start()
}
@@ -1502,10 +1575,19 @@ final class ChatMediaInputNode: ChatInputNode {
}
itemNode.updateIsHighlighted()
if itemNode.currentCollectionId == collectionId {
if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelCollapseScrollToIndex = targetIndex
self.updateIsExpanded(false)
if self.panelIsFocused, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelFocusScrollToIndex = targetIndex
self.panelFocusInitialPosition = nil
self.interfaceInteraction?.updateTextInputStateAndMode { inputTextState, inputMode in
if case let .media(mode, expanded, _) = inputMode {
return (inputTextState, .media(mode: mode, expanded: expanded, focused: false))
} else {
return (inputTextState, inputMode)
}
}
} else {
self.panelFocusScrollToIndex = nil
self.panelFocusInitialPosition = nil
self.listView.ensureItemNodeVisible(itemNode)
}
ensuredNodeVisible = true
@@ -1513,10 +1595,19 @@ final class ChatMediaInputNode: ChatInputNode {
} else if let itemNode = itemNode as? ChatMediaInputMetaSectionItemNode {
itemNode.updateIsHighlighted()
if itemNode.currentCollectionId == collectionId {
if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelCollapseScrollToIndex = targetIndex
self.updateIsExpanded(false)
if self.panelIsFocused, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelFocusScrollToIndex = targetIndex
self.panelFocusInitialPosition = nil
self.interfaceInteraction?.updateTextInputStateAndMode { inputTextState, inputMode in
if case let .media(mode, expanded, _) = inputMode {
return (inputTextState, .media(mode: mode, expanded: expanded, focused: false))
} else {
return (inputTextState, inputMode)
}
}
} else {
self.panelFocusScrollToIndex = nil
self.panelFocusInitialPosition = nil
self.listView.ensureItemNodeVisible(itemNode)
}
ensuredNodeVisible = true
@@ -1524,10 +1615,19 @@ final class ChatMediaInputNode: ChatInputNode {
} else if let itemNode = itemNode as? ChatMediaInputRecentGifsItemNode {
itemNode.updateIsHighlighted()
if itemNode.currentCollectionId == collectionId {
if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelCollapseScrollToIndex = targetIndex
self.updateIsExpanded(false)
if self.panelIsFocused, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelFocusScrollToIndex = targetIndex
self.panelFocusInitialPosition = nil
self.interfaceInteraction?.updateTextInputStateAndMode { inputTextState, inputMode in
if case let .media(mode, expanded, _) = inputMode {
return (inputTextState, .media(mode: mode, expanded: expanded, focused: false))
} else {
return (inputTextState, inputMode)
}
}
} else {
self.panelFocusScrollToIndex = nil
self.panelFocusInitialPosition = nil
self.listView.ensureItemNodeVisible(itemNode)
}
ensuredNodeVisible = true
@@ -1535,10 +1635,19 @@ final class ChatMediaInputNode: ChatInputNode {
} else if let itemNode = itemNode as? ChatMediaInputTrendingItemNode {
itemNode.updateIsHighlighted()
if itemNode.currentCollectionId == collectionId {
if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelCollapseScrollToIndex = targetIndex
self.updateIsExpanded(false)
if self.panelIsFocused, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelFocusScrollToIndex = targetIndex
self.panelFocusInitialPosition = nil
self.interfaceInteraction?.updateTextInputStateAndMode { inputTextState, inputMode in
if case let .media(mode, expanded, _) = inputMode {
return (inputTextState, .media(mode: mode, expanded: expanded, focused: false))
} else {
return (inputTextState, inputMode)
}
}
} else {
self.panelFocusScrollToIndex = nil
self.panelFocusInitialPosition = nil
self.listView.ensureItemNodeVisible(itemNode)
}
ensuredNodeVisible = true
@@ -1546,10 +1655,19 @@ final class ChatMediaInputNode: ChatInputNode {
} else if let itemNode = itemNode as? ChatMediaInputPeerSpecificItemNode {
itemNode.updateIsHighlighted()
if itemNode.currentCollectionId == collectionId {
if self.panelExpanded, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelCollapseScrollToIndex = targetIndex
self.updateIsExpanded(false)
if self.panelIsFocused, let targetIndex = self.listView.indexOf(itemNode: itemNode) {
self.panelFocusScrollToIndex = targetIndex
self.panelFocusInitialPosition = nil
self.interfaceInteraction?.updateTextInputStateAndMode { inputTextState, inputMode in
if case let .media(mode, expanded, _) = inputMode {
return (inputTextState, .media(mode: mode, expanded: expanded, focused: false))
} else {
return (inputTextState, inputMode)
}
}
} else {
self.panelFocusScrollToIndex = nil
self.panelFocusInitialPosition = nil
self.listView.ensureItemNodeVisible(itemNode)
}
ensuredNodeVisible = true
@@ -1562,10 +1680,19 @@ 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
if self.panelExpanded {
self.panelCollapseScrollToIndex = targetIndex
self.updateIsExpanded(false)
if self.panelIsFocused {
self.panelFocusScrollToIndex = targetIndex
self.panelFocusInitialPosition = nil
self.interfaceInteraction?.updateTextInputStateAndMode { inputTextState, inputMode in
if case let .media(mode, expanded, _) = inputMode {
return (inputTextState, .media(mode: mode, expanded: expanded, focused: false))
} else {
return (inputTextState, inputMode)
}
}
} else {
self.panelFocusScrollToIndex = nil
self.panelFocusInitialPosition = nil
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)
}
}
@@ -1643,7 +1770,7 @@ final class ChatMediaInputNode: ChatInputNode {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool) -> (CGFloat, CGFloat) {
var searchMode: ChatMediaInputSearchMode?
if let (_, _, _, _, _, _, _, _, interfaceState, _, _) = self.validLayout, case let .media(_, maybeExpanded) = interfaceState.inputMode, let expanded = maybeExpanded, case let .search(mode) = expanded {
if let (_, _, _, _, _, _, _, _, interfaceState, _, _) = self.validLayout, case let .media(_, maybeExpanded, _) = interfaceState.inputMode, let expanded = maybeExpanded, case let .search(mode) = expanded {
searchMode = mode
}
@@ -1659,8 +1786,12 @@ final class ChatMediaInputNode: ChatInputNode {
let separatorHeight = UIScreenPixel
let panelHeight: CGFloat
var isFocused = false
var isExpanded: Bool = false
if case let .media(_, maybeExpanded) = interfaceState.inputMode, let expanded = maybeExpanded {
if case let .media(_, _, focused) = interfaceState.inputMode {
isFocused = focused
}
if case let .media(_, maybeExpanded, _) = interfaceState.inputMode, let expanded = maybeExpanded {
isExpanded = true
switch expanded {
case .content:
@@ -1677,6 +1808,8 @@ final class ChatMediaInputNode: ChatInputNode {
panelHeight = standardInputHeight
}
self.updateIsExpanded(isFocused)
if displaySearch {
if let searchContainerNode = self.searchContainerNode {
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: -inputPanelHeight), size: CGSize(width: width, height: panelHeight + inputPanelHeight))
@@ -1721,10 +1854,10 @@ 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 + 31.0 + 20.0, height: width)
self.listView.bounds = CGRect(x: 0.0, y: 0.0, width: 41.0 + 31.0 + 40.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 + 31.0 + 20.0, height: width)
self.gifListView.bounds = CGRect(x: 0.0, y: 0.0, width: 41.0 + 31.0 + 40.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)
@@ -1891,15 +2024,24 @@ final class ChatMediaInputNode: ChatInputNode {
}
var scrollToItem: ListViewScrollToItem?
if let targetIndex = self.panelCollapseScrollToIndex {
if let targetIndex = self.panelFocusScrollToIndex {
var position: ListViewScrollPosition
if self.panelExpanded {
position = .center(.top)
if self.panelIsFocused {
if let initialPosition = self.panelFocusInitialPosition {
position = .top(96.0 + (initialPosition.y - self.listView.frame.height / 2.0) * 0.5)
} else {
position = .top(96.0)
}
} else {
position = .top(self.listView.frame.height / 2.0 + 96.0)
if let initialPosition = self.panelFocusInitialPosition {
position = .top(self.listView.frame.height / 2.0 + 96.0 + (initialPosition.y - self.listView.frame.height / 2.0))
} else {
position = .top(self.listView.frame.height / 2.0 + 96.0)
}
self.panelFocusScrollToIndex = nil
self.panelFocusInitialPosition = nil
}
scrollToItem = ListViewScrollToItem(index: targetIndex, position: position, animated: true, curve: .Default(duration: nil), directionHint: .Down)
self.panelCollapseScrollToIndex = nil
scrollToItem = ListViewScrollToItem(index: targetIndex, position: position, animated: true, curve: .Spring(duration: 0.4), directionHint: .Down, displayLink: true)
}
self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateOpaqueState: nil, completion: { [weak self] _ in
@@ -1993,7 +2135,7 @@ final class ChatMediaInputNode: ChatInputNode {
private var isExpanded: Bool {
var isExpanded: Bool = false
if let validLayout = self.validLayout, case let .media(_, maybeExpanded) = validLayout.8.inputMode, maybeExpanded != nil {
if let validLayout = self.validLayout, case let .media(_, maybeExpanded, _) = validLayout.8.inputMode, maybeExpanded != nil {
isExpanded = true
}
return isExpanded
@@ -2021,7 +2163,7 @@ final class ChatMediaInputNode: ChatInputNode {
}
var collectionListPanelOffset = self.currentCollectionListPanelOffset()
if self.panelExpanded {
if self.panelIsFocused {
collectionListPanelOffset = 0.0
}
@@ -2038,9 +2180,8 @@ final class ChatMediaInputNode: ChatInputNode {
private func updatePaneClippingContainer(size: CGSize, offset: CGFloat, transition: ContainedViewLayoutTransition) {
var offset = offset
var additionalOffset: CGFloat = 0.0
if self.panelExpanded {
if self.panelIsFocused {
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))
@@ -2059,7 +2200,7 @@ final class ChatMediaInputNode: ChatInputNode {
}
var collectionListPanelOffset = self.currentCollectionListPanelOffset()
if self.panelExpanded {
if self.panelIsFocused {
collectionListPanelOffset = 0.0
}
@@ -2073,6 +2214,15 @@ final class ChatMediaInputNode: ChatInputNode {
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.panelIsFocused {
let convertedPoint = CGPoint(x: max(0.0, point.y), y: point.x)
if let result = self.listView.hitTest(convertedPoint, with: event) {
return result
}
if let result = self.gifListView.hitTest(convertedPoint, with: event) {
return result
}
}
if let searchContainerNode = self.searchContainerNode {
if let result = searchContainerNode.hitTest(point.offsetBy(dx: -searchContainerNode.frame.minX, dy: -searchContainerNode.frame.minY), with: event) {
return result