mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Update overscroll
This commit is contained in:
parent
baf317921e
commit
80c5d0daf4
@ -2525,7 +2525,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
super.updateNavigationBarLayout(layout, transition: transition)
|
super.updateNavigationBarLayout(layout, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func chatListHeaderView() -> ChatListHeaderComponent.View? {
|
func chatListHeaderView() -> ChatListHeaderComponent.View? {
|
||||||
if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
|
if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
|
||||||
if let componentView = navigationBarView.headerContent.view as? ChatListHeaderComponent.View {
|
if let componentView = navigationBarView.headerContent.view as? ChatListHeaderComponent.View {
|
||||||
return componentView
|
return componentView
|
||||||
@ -3667,6 +3667,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
sourceCornerRadius: transitionView.bounds.height * 0.5,
|
sourceCornerRadius: transitionView.bounds.height * 0.5,
|
||||||
sourceIsAvatar: true
|
sourceIsAvatar: true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Queue.mainQueue().after(0.3, { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.chatListDisplayNode.mainContainerNode.currentItemNode.scroller.panGestureRecognizer.state = .cancelled
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3680,6 +3688,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
|
|
||||||
if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
|
if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
|
||||||
if navigationBarView.storiesUnlocked {
|
if navigationBarView.storiesUnlocked {
|
||||||
|
self.scrollToStories()
|
||||||
|
|
||||||
if let componentView = self.chatListHeaderView() {
|
if let componentView = self.chatListHeaderView() {
|
||||||
if let (transitionView, transitionContentView) = componentView.storyPeerListView()?.transitionViewForItem(peerId: peerId) {
|
if let (transitionView, transitionContentView) = componentView.storyPeerListView()?.transitionViewForItem(peerId: peerId) {
|
||||||
return StoryContainerScreen.TransitionOut(
|
return StoryContainerScreen.TransitionOut(
|
||||||
|
@ -875,6 +875,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
previousItemNode.listNode.updatePeerGrouping = nil
|
previousItemNode.listNode.updatePeerGrouping = nil
|
||||||
previousItemNode.listNode.contentOffsetChanged = nil
|
previousItemNode.listNode.contentOffsetChanged = nil
|
||||||
previousItemNode.listNode.contentScrollingEnded = nil
|
previousItemNode.listNode.contentScrollingEnded = nil
|
||||||
|
previousItemNode.listNode.didBeginInteractiveDragging = nil
|
||||||
previousItemNode.listNode.endedInteractiveDragging = { _ in }
|
previousItemNode.listNode.endedInteractiveDragging = { _ in }
|
||||||
previousItemNode.listNode.shouldStopScrolling = nil
|
previousItemNode.listNode.shouldStopScrolling = nil
|
||||||
previousItemNode.listNode.activateChatPreview = nil
|
previousItemNode.listNode.activateChatPreview = nil
|
||||||
@ -988,6 +989,9 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
guard let validLayout = self.validLayout else {
|
guard let validLayout = self.validLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.didBeginInteractiveDragging?()
|
||||||
|
|
||||||
let tempTopInset: CGFloat
|
let tempTopInset: CGFloat
|
||||||
if validLayout.inlineNavigationLocation != nil {
|
if validLayout.inlineNavigationLocation != nil {
|
||||||
tempTopInset = 0.0
|
tempTopInset = 0.0
|
||||||
@ -1106,6 +1110,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
|
|||||||
var contentOffset: ListViewVisibleContentOffset?
|
var contentOffset: ListViewVisibleContentOffset?
|
||||||
public var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
|
public var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
|
||||||
public var contentScrollingEnded: ((ListView) -> Bool)?
|
public var contentScrollingEnded: ((ListView) -> Bool)?
|
||||||
|
var didBeginInteractiveDragging: (() -> Void)?
|
||||||
var endedInteractiveDragging: ((ListView) -> Void)?
|
var endedInteractiveDragging: ((ListView) -> Void)?
|
||||||
var shouldStopScrolling: ((ListView, CGFloat) -> Bool)?
|
var shouldStopScrolling: ((ListView, CGFloat) -> Bool)?
|
||||||
var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||||
@ -1750,6 +1755,8 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
private var tempDisableStoriesAnimations: Bool = false
|
private var tempDisableStoriesAnimations: Bool = false
|
||||||
private var tempNavigationScrollingTransition: ContainedViewLayoutTransition?
|
private var tempNavigationScrollingTransition: ContainedViewLayoutTransition?
|
||||||
|
|
||||||
|
private var allowOverscrollStoryExpansion: Bool = false
|
||||||
|
|
||||||
private var containerLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, storiesInset: CGFloat)?
|
private var containerLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, storiesInset: CGFloat)?
|
||||||
|
|
||||||
var contentScrollingEnded: ((ListView) -> Bool)?
|
var contentScrollingEnded: ((ListView) -> Bool)?
|
||||||
@ -1806,6 +1813,9 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
self.mainContainerNode.contentScrollingEnded = { [weak self] listView in
|
self.mainContainerNode.contentScrollingEnded = { [weak self] listView in
|
||||||
return self?.contentScrollingEnded(listView: listView, isPrimary: true) ?? false
|
return self?.contentScrollingEnded(listView: listView, isPrimary: true) ?? false
|
||||||
}
|
}
|
||||||
|
self.mainContainerNode.didBeginInteractiveDragging = { [weak self] in
|
||||||
|
self?.didBeginInteractiveDragging(isPrimary: true)
|
||||||
|
}
|
||||||
self.mainContainerNode.endedInteractiveDragging = { [weak self] listView in
|
self.mainContainerNode.endedInteractiveDragging = { [weak self] listView in
|
||||||
self?.endedInteractiveDragging(listView: listView, isPrimary: true)
|
self?.endedInteractiveDragging(listView: listView, isPrimary: true)
|
||||||
}
|
}
|
||||||
@ -2131,6 +2141,15 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
mainDelta = 0.0
|
mainDelta = 0.0
|
||||||
}
|
}
|
||||||
transition.updateSublayerTransformOffset(layer: self.mainContainerNode.layer, offset: CGPoint(x: 0.0, y: -mainDelta))
|
transition.updateSublayerTransformOffset(layer: self.mainContainerNode.layer, offset: CGPoint(x: 0.0, y: -mainDelta))
|
||||||
|
|
||||||
|
if self.inlineStackContainerNode == nil && self.allowOverscrollStoryExpansion {
|
||||||
|
if let controller = self.controller, let componentView = controller.chatListHeaderView(), let storyPeerListView = componentView.storyPeerListView(), let peerId = storyPeerListView.overscrollSelectedId {
|
||||||
|
self.allowOverscrollStoryExpansion = false
|
||||||
|
HapticFeedback().tap()
|
||||||
|
|
||||||
|
controller.openStories(peerId: peerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestNavigationBarLayout(transition: Transition) {
|
func requestNavigationBarLayout(transition: Transition) {
|
||||||
@ -2422,7 +2441,16 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func didBeginInteractiveDragging(isPrimary: Bool) {
|
||||||
|
if isPrimary {
|
||||||
|
self.allowOverscrollStoryExpansion = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func endedInteractiveDragging(listView: ListView, isPrimary: Bool) {
|
private func endedInteractiveDragging(listView: ListView, isPrimary: Bool) {
|
||||||
|
if isPrimary {
|
||||||
|
self.allowOverscrollStoryExpansion = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func contentScrollingEnded(listView: ListView, isPrimary: Bool) -> Bool {
|
private func contentScrollingEnded(listView: ListView, isPrimary: Bool) -> Bool {
|
||||||
@ -2493,6 +2521,9 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
inlineStackContainerNode.contentOffsetChanged = { [weak self] offset in
|
inlineStackContainerNode.contentOffsetChanged = { [weak self] offset in
|
||||||
self?.contentOffsetChanged(offset: offset, isPrimary: false)
|
self?.contentOffsetChanged(offset: offset, isPrimary: false)
|
||||||
}
|
}
|
||||||
|
inlineStackContainerNode.endedInteractiveDragging = { [weak self] listView in
|
||||||
|
self?.didBeginInteractiveDragging(isPrimary: false)
|
||||||
|
}
|
||||||
inlineStackContainerNode.endedInteractiveDragging = { [weak self] listView in
|
inlineStackContainerNode.endedInteractiveDragging = { [weak self] listView in
|
||||||
self?.endedInteractiveDragging(listView: listView, isPrimary: false)
|
self?.endedInteractiveDragging(listView: listView, isPrimary: false)
|
||||||
}
|
}
|
||||||
|
@ -1257,7 +1257,9 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
if let current = self.visibleItemSetViews[slice.peer.id] {
|
if let current = self.visibleItemSetViews[slice.peer.id] {
|
||||||
itemSetView = current
|
itemSetView = current
|
||||||
} else {
|
} else {
|
||||||
itemSetTransition = .immediate
|
itemSetTransition = transition.withAnimation(.none).withUserData(StoryItemSetContainerComponent.TransitionHint(
|
||||||
|
allowSynchronousLoads: !self.visibleItemSetViews.isEmpty
|
||||||
|
))
|
||||||
itemSetView = ItemSetView()
|
itemSetView = ItemSetView()
|
||||||
self.visibleItemSetViews[slice.peer.id] = itemSetView
|
self.visibleItemSetViews[slice.peer.id] = itemSetView
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,14 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class TransitionHint {
|
||||||
|
public let allowSynchronousLoads: Bool
|
||||||
|
|
||||||
|
public init(allowSynchronousLoads: Bool) {
|
||||||
|
self.allowSynchronousLoads = allowSynchronousLoads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum NavigationDirection {
|
public enum NavigationDirection {
|
||||||
case previous
|
case previous
|
||||||
case next
|
case next
|
||||||
@ -960,6 +968,11 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hintAllowSynchronousLoads = true
|
||||||
|
if let hint = transition.userData(TransitionHint.self) {
|
||||||
|
hintAllowSynchronousLoads = hint.allowSynchronousLoads
|
||||||
|
}
|
||||||
|
|
||||||
var validIds: [Int32] = []
|
var validIds: [Int32] = []
|
||||||
var trulyValidIds: [Int32] = []
|
var trulyValidIds: [Int32] = []
|
||||||
|
|
||||||
@ -1080,7 +1093,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
)
|
)
|
||||||
let _ = visibleItem.view.update(
|
let _ = visibleItem.view.update(
|
||||||
transition: itemTransition.withUserData(StoryItemContentComponent.Hint(
|
transition: itemTransition.withUserData(StoryItemContentComponent.Hint(
|
||||||
synchronousLoad: index == centralIndex && itemLayout.contentScaleFraction <= 0.0001
|
synchronousLoad: index == centralIndex && itemLayout.contentScaleFraction <= 0.0001 && hintAllowSynchronousLoads
|
||||||
)),
|
)),
|
||||||
component: AnyComponent(StoryItemContentComponent(
|
component: AnyComponent(StoryItemContentComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
|
@ -348,6 +348,8 @@ public final class StoryPeerListComponent: Component {
|
|||||||
private var currentTitleWidth: CGFloat = 0.0
|
private var currentTitleWidth: CGFloat = 0.0
|
||||||
private var currentActivityFraction: CGFloat = 0.0
|
private var currentActivityFraction: CGFloat = 0.0
|
||||||
|
|
||||||
|
public private(set) var overscrollSelectedId: EnginePeer.Id?
|
||||||
|
|
||||||
private var sharedBlurEffect: NSObject?
|
private var sharedBlurEffect: NSObject?
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
@ -449,19 +451,26 @@ public final class StoryPeerListComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func setLoadingItem(peerId: EnginePeer.Id, signal: Signal<Never, NoError>) {
|
public func setLoadingItem(peerId: EnginePeer.Id, signal: Signal<Never, NoError>) {
|
||||||
self.loadingItemId = peerId
|
var applyLoadingItem = true
|
||||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in
|
|
||||||
self?.state?.updated(transition: .immediate)
|
|
||||||
})
|
|
||||||
|
|
||||||
self.loadingItemDisposable?.dispose()
|
self.loadingItemDisposable?.dispose()
|
||||||
self.loadingItemDisposable = (signal |> deliverOnMainQueue).start(completed: { [weak self] in
|
self.loadingItemDisposable = (signal |> deliverOnMainQueue).start(completed: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.loadingItemId = nil
|
self.loadingItemId = nil
|
||||||
|
applyLoadingItem = false
|
||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .immediate)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if applyLoadingItem {
|
||||||
|
self.loadingItemId = peerId
|
||||||
|
self.state?.updated(transition: .immediate)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public func anchorForTooltip() -> (UIView, CGRect)? {
|
public func anchorForTooltip() -> (UIView, CGRect)? {
|
||||||
@ -773,6 +782,19 @@ public final class StoryPeerListComponent: Component {
|
|||||||
let overscrollFraction: CGFloat = max(0.0, collapsedState.maxFraction - 1.0)
|
let overscrollFraction: CGFloat = max(0.0, collapsedState.maxFraction - 1.0)
|
||||||
let realTimeOverscrollFraction: CGFloat = max(0.0, (1.0 - component.collapseFraction) - 1.0)
|
let realTimeOverscrollFraction: CGFloat = max(0.0, (1.0 - component.collapseFraction) - 1.0)
|
||||||
|
|
||||||
|
var overscrollFocusIndex: Int?
|
||||||
|
for i in 0 ..< self.sortedItems.count {
|
||||||
|
if self.sortedItems[i].peer.id != component.context.account.peerId {
|
||||||
|
overscrollFocusIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let overscrollFocusIndex, overscrollFraction >= 0.7 {
|
||||||
|
self.overscrollSelectedId = self.sortedItems[overscrollFocusIndex].peer.id
|
||||||
|
} else {
|
||||||
|
self.overscrollSelectedId = nil
|
||||||
|
}
|
||||||
|
|
||||||
struct MeasuredItem {
|
struct MeasuredItem {
|
||||||
var itemFrame: CGRect
|
var itemFrame: CGRect
|
||||||
var itemScale: CGFloat
|
var itemScale: CGFloat
|
||||||
@ -808,7 +830,13 @@ public final class StoryPeerListComponent: Component {
|
|||||||
|
|
||||||
let minimizedMaxItemScale: CGFloat = (24.0 + 4.0) / 52.0
|
let minimizedMaxItemScale: CGFloat = (24.0 + 4.0) / 52.0
|
||||||
|
|
||||||
let maximizedItemScale: CGFloat = 1.0 + overscrollFraction * 0.1
|
let overscrollScaleFactor: CGFloat
|
||||||
|
if index == overscrollFocusIndex {
|
||||||
|
overscrollScaleFactor = 0.5
|
||||||
|
} else {
|
||||||
|
overscrollScaleFactor = 0.1
|
||||||
|
}
|
||||||
|
let maximizedItemScale: CGFloat = 1.0 + overscrollFraction * overscrollScaleFactor
|
||||||
|
|
||||||
let minItemScale: CGFloat = minimizedItemScale.interpolate(to: minimizedMaxItemScale, amount: collapsedState.minFraction) * (1.0 - collapsedState.activityFraction) + 0.1 * collapsedState.activityFraction
|
let minItemScale: CGFloat = minimizedItemScale.interpolate(to: minimizedMaxItemScale, amount: collapsedState.minFraction) * (1.0 - collapsedState.activityFraction) + 0.1 * collapsedState.activityFraction
|
||||||
|
|
||||||
@ -824,6 +852,11 @@ public final class StoryPeerListComponent: Component {
|
|||||||
}
|
}
|
||||||
adjustedRegularFrame.origin.x -= effectiveVisibleBounds.minX
|
adjustedRegularFrame.origin.x -= effectiveVisibleBounds.minX
|
||||||
|
|
||||||
|
if let overscrollFocusIndex {
|
||||||
|
let focusIndexOffset: CGFloat = max(-1.0, min(1.0, CGFloat(index - overscrollFocusIndex)))
|
||||||
|
adjustedRegularFrame.origin.x += focusIndexOffset * overscrollFraction * 0.3 * adjustedRegularFrame.width * 0.5
|
||||||
|
}
|
||||||
|
|
||||||
let collapsedItemPosition: CGPoint = collapsedItemFrame.center.interpolate(to: collapsedMaxItemFrame.center, amount: collapsedState.minFraction)
|
let collapsedItemPosition: CGPoint = collapsedItemFrame.center.interpolate(to: collapsedMaxItemFrame.center, amount: collapsedState.minFraction)
|
||||||
|
|
||||||
var itemPosition = collapsedItemPosition.interpolate(to: adjustedRegularFrame.center, amount: min(1.0, collapsedState.maxFraction))
|
var itemPosition = collapsedItemPosition.interpolate(to: adjustedRegularFrame.center, amount: min(1.0, collapsedState.maxFraction))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user