mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
201 lines
9.4 KiB
Swift
201 lines
9.4 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import Postbox
|
|
import TelegramCore
|
|
import SwiftSignalKit
|
|
import TelegramPresentationData
|
|
|
|
private func fixGridScrolling(_ gridNode: GridNode) {
|
|
var searchItemNode: GridItemNode?
|
|
var nextItemNode: GridItemNode?
|
|
|
|
gridNode.forEachItemNode { itemNode in
|
|
if let itemNode = itemNode as? PaneSearchBarPlaceholderNode {
|
|
searchItemNode = itemNode
|
|
} else if searchItemNode != nil && nextItemNode == nil, let itemNode = itemNode as? GridItemNode {
|
|
nextItemNode = itemNode
|
|
}
|
|
}
|
|
|
|
if let searchItemNode = searchItemNode {
|
|
let contentInset = gridNode.scrollView.contentInset.top
|
|
let itemFrame = gridNode.convert(searchItemNode.frame, to: gridNode.supernode)
|
|
if itemFrame.contains(CGPoint(x: 0.0, y: contentInset)) {
|
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut)
|
|
|
|
var scrollIndex: Int?
|
|
if itemFrame.minY + itemFrame.height * 0.6 < contentInset {
|
|
for i in 0 ..< gridNode.items.count {
|
|
if let _ = gridNode.items[i] as? StickerPaneTrendingListGridItem {
|
|
scrollIndex = i
|
|
break
|
|
} else if let _ = gridNode.items[i] as? ChatMediaInputStickerGridItem {
|
|
scrollIndex = i
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
for i in 0 ..< gridNode.items.count {
|
|
if let _ = gridNode.items[i] as? PaneSearchBarPlaceholderItem {
|
|
scrollIndex = i
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if let scrollIndex = scrollIndex {
|
|
gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: GridNodeScrollToItem(index: scrollIndex, position: .top(0.0), transition: transition, directionHint: .up, adjustForSection: true, adjustForTopInset: true), updateLayout: nil, itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { _ in })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
final class ChatMediaInputStickerPaneOpaqueState {
|
|
let hasLower: Bool
|
|
|
|
init(hasLower: Bool) {
|
|
self.hasLower = hasLower
|
|
}
|
|
}
|
|
|
|
final class ChatMediaInputStickerPane: ChatMediaInputPane {
|
|
private var isExpanded: Bool?
|
|
private var isPaneVisible = false
|
|
let gridNode: GridNode
|
|
private let paneDidScroll: (ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void
|
|
private let fixPaneScroll: (ChatMediaInputPane, ChatMediaInputPaneScrollState) -> Void
|
|
private var didScrollPreviousOffset: CGFloat?
|
|
private var didScrollPreviousState: ChatMediaInputPaneScrollState?
|
|
|
|
var beganScrolling: (() -> Void)?
|
|
var endedScrolling: (() -> Void)?
|
|
|
|
init(theme: PresentationTheme, strings: PresentationStrings, paneDidScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void, fixPaneScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState) -> Void) {
|
|
self.gridNode = GridNode()
|
|
self.paneDidScroll = paneDidScroll
|
|
self.fixPaneScroll = fixPaneScroll
|
|
|
|
super.init()
|
|
|
|
self.addSubnode(self.gridNode)
|
|
self.gridNode.presentationLayoutUpdated = { [weak self] layout, transition in
|
|
if let strongSelf = self, let opaqueState = strongSelf.gridNode.opaqueState as? ChatMediaInputStickerPaneOpaqueState {
|
|
var offset: CGFloat
|
|
if opaqueState.hasLower {
|
|
offset = -(layout.contentOffset.y + 41.0)
|
|
} else {
|
|
offset = -(layout.contentOffset.y + 41.0)
|
|
offset = min(0.0, offset + 56.0)
|
|
}
|
|
var relativeChange: CGFloat = 0.0
|
|
if let didScrollPreviousOffset = strongSelf.didScrollPreviousOffset {
|
|
relativeChange = offset - didScrollPreviousOffset
|
|
}
|
|
strongSelf.didScrollPreviousOffset = offset
|
|
let state = ChatMediaInputPaneScrollState(absoluteOffset: offset, relativeChange: relativeChange)
|
|
strongSelf.didScrollPreviousState = state
|
|
if !transition.isAnimated {
|
|
strongSelf.paneDidScroll(strongSelf, state, transition)
|
|
}
|
|
}
|
|
}
|
|
self.gridNode.scrollingInitiated = { [weak self] in
|
|
self?.beganScrolling?()
|
|
}
|
|
self.gridNode.scrollingCompleted = { [weak self] in
|
|
if let strongSelf = self {
|
|
if let didScrollPreviousState = strongSelf.didScrollPreviousState {
|
|
strongSelf.fixPaneScroll(strongSelf, didScrollPreviousState)
|
|
}
|
|
fixGridScrolling(strongSelf.gridNode)
|
|
strongSelf.endedScrolling?()
|
|
}
|
|
}
|
|
self.gridNode.setupNode = { [weak self] itemNode in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
if let itemNode = itemNode as? ChatMediaInputStickerGridItemNode {
|
|
itemNode.updateIsPanelVisible(strongSelf.isPaneVisible)
|
|
} else if let itemNode = itemNode as? StickerPaneTrendingListGridItemNode {
|
|
itemNode.updateIsPanelVisible(strongSelf.isPaneVisible)
|
|
}
|
|
}
|
|
self.gridNode.scrollView.alwaysBounceVertical = true
|
|
}
|
|
|
|
override func updateLayout(size: CGSize, topInset: CGFloat, bottomInset: CGFloat, isExpanded: Bool, isVisible: Bool, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition) {
|
|
var changedIsExpanded = false
|
|
if let previousIsExpanded = self.isExpanded {
|
|
if previousIsExpanded != isExpanded {
|
|
changedIsExpanded = true
|
|
}
|
|
}
|
|
self.isExpanded = isExpanded
|
|
|
|
let maxItemSize: CGSize
|
|
if case .tablet = deviceMetrics.type, size.width > 480.0 {
|
|
maxItemSize = CGSize(width: 90.0, height: 96.0)
|
|
} else {
|
|
maxItemSize = CGSize(width: 75.0, height: 80.0)
|
|
}
|
|
|
|
let sideInset: CGFloat = 2.0
|
|
var itemSide: CGFloat = floor((size.width - sideInset * 2.0) / 5.0)
|
|
itemSide = min(itemSide, maxItemSize.width)
|
|
let itemSize = CGSize(width: itemSide, height: max(itemSide, maxItemSize.height))
|
|
|
|
var scrollToItem: GridNodeScrollToItem?
|
|
if changedIsExpanded {
|
|
if isExpanded {
|
|
var scrollIndex: Int?
|
|
for i in 0 ..< self.gridNode.items.count {
|
|
if let _ = self.gridNode.items[i] as? PaneSearchBarPlaceholderItem {
|
|
scrollIndex = i
|
|
break
|
|
}
|
|
}
|
|
if let scrollIndex = scrollIndex {
|
|
scrollToItem = GridNodeScrollToItem(index: scrollIndex, position: .top(0.0), transition: transition, directionHint: .down, adjustForSection: true, adjustForTopInset: true)
|
|
}
|
|
} else {
|
|
var scrollIndex: Int?
|
|
for i in 0 ..< self.gridNode.items.count {
|
|
if let _ = self.gridNode.items[i] as? ChatMediaInputStickerGridItem {
|
|
scrollIndex = i
|
|
break
|
|
}
|
|
}
|
|
if let scrollIndex = scrollIndex {
|
|
scrollToItem = GridNodeScrollToItem(index: scrollIndex, position: .top(0.0), transition: transition, directionHint: .down, adjustForSection: true, adjustForTopInset: true)
|
|
}
|
|
}
|
|
}
|
|
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: scrollToItem, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset, bottom: bottomInset, right: sideInset), preloadSize: isVisible ? 300.0 : 0.0, type: .fixed(itemSize: itemSize, fillWidth: nil, lineSpacing: 0.0, itemSpacing: nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
|
|
|
transition.updateFrame(node: self.gridNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height)))
|
|
|
|
if self.isPaneVisible != isVisible {
|
|
self.isPaneVisible = isVisible
|
|
self.gridNode.forEachItemNode { itemNode in
|
|
if let itemNode = itemNode as? ChatMediaInputStickerGridItemNode {
|
|
itemNode.updateIsPanelVisible(isVisible)
|
|
} else if let itemNode = itemNode as? StickerPaneSearchGlobalItemNode {
|
|
itemNode.updateCanPlayMedia()
|
|
} else if let itemNode = itemNode as? StickerPaneTrendingListGridItemNode {
|
|
itemNode.updateIsPanelVisible(isVisible)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func itemAt(point: CGPoint) -> (ASDisplayNode, StickerPackItem)? {
|
|
if let itemNode = self.gridNode.itemNodeAtPoint(self.view.convert(point, to: self.gridNode.view)) as? ChatMediaInputStickerGridItemNode, let stickerPackItem = itemNode.stickerPackItem {
|
|
return (itemNode, stickerPackItem)
|
|
}
|
|
return nil
|
|
}
|
|
}
|