mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Various Improvements
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user