mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Stories
This commit is contained in:
parent
ceda80c3a5
commit
2a9b47f029
@ -16,6 +16,7 @@ swift_library(
|
||||
"//submodules/ComponentFlow:ComponentFlow",
|
||||
"//submodules/AnimationUI:AnimationUI",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user