This commit is contained in:
Ali 2023-06-13 17:59:19 +03:00
parent ceda80c3a5
commit 2a9b47f029
6 changed files with 130 additions and 10 deletions

View File

@ -16,6 +16,7 @@ swift_library(
"//submodules/ComponentFlow:ComponentFlow",
"//submodules/AnimationUI:AnimationUI",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/Components/MultilineTextComponent",
],
visibility = [
"//visibility:public",

View File

@ -4,6 +4,8 @@ import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import ComponentFlow
import MultilineTextComponent
public protocol SparseItemGridLayer: CALayer {
func update(size: CGSize)
@ -218,12 +220,16 @@ public final class SparseItemGrid: ASDisplayNode {
public let holeAnchors: [HoleAnchor]
public let count: Int
public let itemBinding: SparseItemGridBinding
public let headerText: String?
public let snapTopInset: Bool
public init(items: [Item], holeAnchors: [HoleAnchor], count: Int, itemBinding: SparseItemGridBinding) {
public init(items: [Item], holeAnchors: [HoleAnchor], count: Int, itemBinding: SparseItemGridBinding, headerText: String?, snapTopInset: Bool) {
self.items = items
self.holeAnchors = holeAnchors
self.count = count
self.itemBinding = itemBinding
self.headerText = headerText
self.snapTopInset = snapTopInset
}
func item(at index: Int) -> Item? {
@ -470,15 +476,21 @@ public final class SparseItemGrid: ASDisplayNode {
private var previousScrollOffset: CGFloat = 0.0
var coveringInsetOffset: CGFloat = 0.0
var offset: CGFloat {
return self.scrollView.contentOffset.y
}
let coveringOffsetUpdated: (Viewport, ContainedViewLayoutTransition) -> Void
let offsetUpdated: (Viewport, ContainedViewLayoutTransition) -> Void
private var decelerationAnimator: ConstantDisplayLinkAnimator?
init(theme: PresentationTheme, zoomLevel: ZoomLevel, maybeLoadHoleAnchor: @escaping (HoleAnchor, HoleLocation) -> Void, coveringOffsetUpdated: @escaping (Viewport, ContainedViewLayoutTransition) -> Void) {
init(theme: PresentationTheme, zoomLevel: ZoomLevel, maybeLoadHoleAnchor: @escaping (HoleAnchor, HoleLocation) -> Void, coveringOffsetUpdated: @escaping (Viewport, ContainedViewLayoutTransition) -> Void, offsetUpdated: @escaping (Viewport, ContainedViewLayoutTransition) -> Void) {
self.theme = theme
self.zoomLevel = zoomLevel
self.maybeLoadHoleAnchor = maybeLoadHoleAnchor
self.coveringOffsetUpdated = coveringOffsetUpdated
self.offsetUpdated = offsetUpdated
self.scrollView = ScrollView()
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
@ -526,6 +538,9 @@ public final class SparseItemGrid: ASDisplayNode {
if let layout = self.layout, let _ = self.items {
let offset = scrollView.contentOffset.y
self.offsetUpdated(self, .immediate)
let delta = offset - self.previousScrollOffset
self.previousScrollOffset = offset
@ -587,7 +602,7 @@ public final class SparseItemGrid: ASDisplayNode {
}
private func snapCoveringInsetOffset(animated: Bool) {
if let layout = self.layout, let _ = self.items {
if let layout = self.layout, let items = self.items, items.snapTopInset {
let offset = self.scrollView.contentOffset.y
if offset < layout.containerLayout.insets.top {
if offset <= layout.containerLayout.insets.top / 2.0 {
@ -1188,9 +1203,14 @@ public final class SparseItemGrid: ASDisplayNode {
return self.fromViewport.coveringInsetOffset * (1.0 - self.currentProgress) + self.toViewport.coveringInsetOffset * self.currentProgress
}
let coveringOffsetUpdated: (ContainedViewLayoutTransition) -> Void
var offset: CGFloat {
return self.fromViewport.offset * (1.0 - self.currentProgress) + self.toViewport.offset * self.currentProgress
}
init(interactiveState: InteractiveState?, layout: ContainerLayout, anchorItemIndex: Int, transitionAnchorPoint: CGPoint, from fromViewport: Viewport, to toViewport: Viewport, coveringOffsetUpdated: @escaping (ContainedViewLayoutTransition) -> Void) {
let coveringOffsetUpdated: (ContainedViewLayoutTransition) -> Void
let offsetUpdated: (ContainedViewLayoutTransition) -> Void
init(interactiveState: InteractiveState?, layout: ContainerLayout, anchorItemIndex: Int, transitionAnchorPoint: CGPoint, from fromViewport: Viewport, to toViewport: Viewport, coveringOffsetUpdated: @escaping (ContainedViewLayoutTransition) -> Void, offsetUpdated: @escaping (ContainedViewLayoutTransition) -> Void) {
self.interactiveState = interactiveState
self.layout = layout
self.anchorItemIndex = anchorItemIndex
@ -1198,6 +1218,7 @@ public final class SparseItemGrid: ASDisplayNode {
self.fromViewport = fromViewport
self.toViewport = toViewport
self.coveringOffsetUpdated = coveringOffsetUpdated
self.offsetUpdated = offsetUpdated
super.init()
@ -1327,6 +1348,7 @@ public final class SparseItemGrid: ASDisplayNode {
}
self.coveringOffsetUpdated(transition)
self.offsetUpdated(transition)
}
}
@ -1351,6 +1373,8 @@ public final class SparseItemGrid: ASDisplayNode {
private var currentViewportTransition: ViewportTransition?
private let scrollingArea: SparseItemGridScrollingArea
private var headerText: ComponentView<Empty>?
private var initialZoomLevel: ZoomLevel?
private var isLoadingHole: Bool = false
@ -1491,6 +1515,8 @@ public final class SparseItemGrid: ASDisplayNode {
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
}, coveringOffsetUpdated: { [weak self] viewport, transition in
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
}, offsetUpdated: { [weak self] viewport, transition in
self?.offsetUpdated(viewport: viewport, transition: transition)
})
nextViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
@ -1501,6 +1527,8 @@ public final class SparseItemGrid: ASDisplayNode {
let nextInteractiveState = ViewportTransition.InteractiveState(anchorLocation: anchorLocation, initialScale: startScale, targetScale: nextScale)
let currentViewportTransition = ViewportTransition(interactiveState: nextInteractiveState, layout: containerLayout, anchorItemIndex: currentViewportTransition.anchorItemIndex, transitionAnchorPoint: currentViewportTransition.transitionAnchorPoint, from: boundaryViewport, to: nextViewport, coveringOffsetUpdated: { [weak self] transition in
self?.transitionCoveringOffsetUpdated(transition: transition)
}, offsetUpdated: { [weak self] transition in
self?.transitionOffsetUpdated(transition: transition)
})
currentViewportTransition.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
self.insertSubnode(currentViewportTransition, belowSubnode: self.scrollingArea)
@ -1541,6 +1569,8 @@ public final class SparseItemGrid: ASDisplayNode {
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
}, coveringOffsetUpdated: { [weak self] viewport, transition in
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
}, offsetUpdated: { [weak self] viewport, transition in
self?.offsetUpdated(viewport: viewport, transition: transition)
})
nextViewport.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
@ -1548,6 +1578,8 @@ public final class SparseItemGrid: ASDisplayNode {
let currentViewportTransition = ViewportTransition(interactiveState: interactiveState, layout: containerLayout, anchorItemIndex: anchorItemIndex, transitionAnchorPoint: anchorLocation, from: previousViewport, to: nextViewport, coveringOffsetUpdated: { [weak self] transition in
self?.transitionCoveringOffsetUpdated(transition: transition)
}, offsetUpdated: { [weak self] transition in
self?.transitionOffsetUpdated(transition: transition)
})
currentViewportTransition.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
self.insertSubnode(currentViewportTransition, belowSubnode: self.scrollingArea)
@ -1597,6 +1629,45 @@ public final class SparseItemGrid: ASDisplayNode {
public func update(size: CGSize, insets: UIEdgeInsets, useSideInsets: Bool, scrollIndicatorInsets: UIEdgeInsets, lockScrollingAtTop: Bool, fixedItemHeight: CGFloat?, fixedItemAspect: CGFloat?, items: Items, theme: PresentationTheme, synchronous: SparseItemGrid.Synchronous) {
self.theme = theme
var headerInset: CGFloat = 0.0
if let headerTextValue = items.headerText {
let headerText: ComponentView<Empty>
if let current = self.headerText {
headerText = current
} else {
headerText = ComponentView()
self.headerText = headerText
}
let headerTextSize = headerText.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: headerTextValue, font: Font.regular(15.0), textColor: theme.list.itemSecondaryTextColor)),
horizontalAlignment: .center,
maximumNumberOfLines: 0
)),
environment: {},
containerSize: CGSize(width: size.width - 16.0 * 2.0, height: 400.0)
)
let headerTextFrame = CGRect(origin: CGPoint(x: floor((size.width - headerTextSize.width) * 0.5), y: insets.top + 18.0), size: headerTextSize)
if let headerTextView = headerText.view {
if headerTextView.superview == nil {
headerTextView.layer.anchorPoint = CGPoint()
self.view.addSubview(headerTextView)
}
headerTextView.center = headerTextFrame.origin
headerTextView.bounds = CGRect(origin: CGPoint(), size: headerTextFrame.size)
}
headerInset += 18.0 + headerTextSize.height + 18.0
} else if let headerText = self.headerText {
self.headerText = nil
headerText.view?.removeFromSuperview()
}
var insets = insets
insets.top += headerInset
let containerLayout = ContainerLayout(size: size, insets: insets, useSideInsets: useSideInsets, scrollIndicatorInsets: scrollIndicatorInsets, lockScrollingAtTop: lockScrollingAtTop, fixedItemHeight: fixedItemHeight, fixedItemAspect: fixedItemAspect)
self.containerLayout = containerLayout
self.items = items
@ -1605,6 +1676,7 @@ public final class SparseItemGrid: ASDisplayNode {
self.tapRecognizer?.isEnabled = fixedItemHeight == nil
self.pinchRecognizer?.isEnabled = fixedItemHeight == nil
if self.currentViewport == nil {
let currentViewport = Viewport(theme: self.theme, zoomLevel: self.initialZoomLevel ?? ZoomLevel(rawValue: 3), maybeLoadHoleAnchor: { [weak self] holeAnchor, location in
guard let strongSelf = self else {
@ -1613,6 +1685,8 @@ public final class SparseItemGrid: ASDisplayNode {
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
}, coveringOffsetUpdated: { [weak self] viewport, transition in
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
}, offsetUpdated: { [weak self] viewport, transition in
self?.offsetUpdated(viewport: viewport, transition: transition)
})
self.currentViewport = currentViewport
self.insertSubnode(currentViewport, belowSubnode: self.scrollingArea)
@ -1689,6 +1763,8 @@ public final class SparseItemGrid: ASDisplayNode {
strongSelf.maybeLoadHoleAnchor(holeAnchor: holeAnchor, location: location)
}, coveringOffsetUpdated: { [weak self] viewport, transition in
self?.coveringOffsetUpdated(viewport: viewport, transition: transition)
}, offsetUpdated: { [weak self] viewport, transition in
self?.offsetUpdated(viewport: viewport, transition: transition)
})
self.currentViewport = currentViewport
self.insertSubnode(currentViewport, belowSubnode: self.scrollingArea)
@ -1705,6 +1781,8 @@ public final class SparseItemGrid: ASDisplayNode {
let currentViewportTransition = ViewportTransition(interactiveState: nil, layout: containerLayout, anchorItemIndex: anchorItemIndex, transitionAnchorPoint: anchorLocation, from: previousViewport, to: currentViewport, coveringOffsetUpdated: { [weak self] transition in
self?.transitionCoveringOffsetUpdated(transition: transition)
}, offsetUpdated: { [weak self] transition in
self?.transitionOffsetUpdated(transition: transition)
})
currentViewportTransition.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
self.insertSubnode(currentViewportTransition, belowSubnode: self.scrollingArea)
@ -1748,6 +1826,30 @@ public final class SparseItemGrid: ASDisplayNode {
return
}
items.itemBinding.coveringInsetOffsetUpdated(transition: transition)
if let headerTextView = self.headerText?.view {
headerTextView.layer.transform = CATransform3DMakeTranslation(0.0, -self.coveringInsetOffset, 0.0)
}
}
private func offsetUpdated(viewport: Viewport, transition: ContainedViewLayoutTransition) {
if self.currentViewportTransition != nil {
return
}
if let headerTextView = self.headerText?.view {
headerTextView.layer.transform = CATransform3DMakeTranslation(0.0, -viewport.offset, 0.0)
}
}
private func transitionOffsetUpdated(transition: ContainedViewLayoutTransition) {
guard let currentViewportTransition = self.currentViewportTransition else {
return
}
if let headerTextView = self.headerText?.view {
headerTextView.layer.transform = CATransform3DMakeTranslation(0.0, -currentViewportTransition.offset, 0.0)
}
}
public func forEachVisibleItem(_ f: (SparseItemGridDisplayItem) -> Void) {

View File

@ -298,7 +298,7 @@ public final class ChatListNavigationBar: Component {
storiesUnlockedOffsetFraction = 1.0
} else {
storiesOffsetFraction = 1.0 - (clippedStoriesOverscrollOffset / defaultStoriesOffsetDistance)
storiesUnlockedOffsetFraction = 0.0
storiesUnlockedOffsetFraction = 1.0
}
} else {
storiesOffsetFraction = 1.0
@ -514,11 +514,11 @@ public final class ChatListNavigationBar: Component {
self.applyScrollFractionAnimator?.invalidate()
self.applyScrollFractionAnimator = nil
let storiesUnlocked = component.storiesUnlocked
self.storiesOffsetStartFraction = self.storiesOffsetFraction
self.storiesUnlockedStartFraction = self.storiesUnlockedFraction
let storiesUnlocked = component.storiesUnlocked
self.applyScrollFraction = 0.0
self.applyScrollUnlockedFraction = 0.0
self.applyScrollFractionAnimator = DisplayLinkAnimator(duration: duration * UIView.animationDurationFactor(), from: 0.0, to: 1.0, update: { [weak self] value in

View File

@ -1548,11 +1548,19 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
return
}
var headerText: String?
if strongSelf.isArchive {
//TODO:localize
headerText = "Only you can see archived stories unless you choose to save them to your profile."
}
let items = SparseItemGrid.Items(
items: mappedItems,
holeAnchors: mappedHoles,
count: totalCount,
itemBinding: strongSelf.itemGridBinding
itemBinding: strongSelf.itemGridBinding,
headerText: headerText,
snapTopInset: false
)
let currentSynchronous = synchronous && firstTime

View File

@ -1874,7 +1874,9 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
items: mappedItems,
holeAnchors: mappedHoles,
count: list.totalCount,
itemBinding: strongSelf.itemGridBinding
itemBinding: strongSelf.itemGridBinding,
headerText: nil,
snapTopInset: true
)
let currentSynchronous = synchronous && firstTime

View File

@ -295,10 +295,17 @@ final class StoryItemSetViewListComponent: Component {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let navigationPanelView = self.navigationPanel.view {
if let result = navigationPanelView.hitTest(self.convert(point, to: navigationPanelView), with: event) {
if result !== navigationPanelView {
return result
}
}
}
if let navigationLeftButtonView = self.navigationLeftButton.view {
if let result = navigationLeftButtonView.hitTest(self.convert(point, to: navigationLeftButtonView), with: event) {
return result
}
}
if !self.backgroundView.frame.contains(point) {
if !self.backgroundView.frame.contains(point) && !self.navigationBarBackground.frame.contains(point) {
return nil
}