mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Initial implementation of channel overscroll navigation
This commit is contained in:
@@ -17,6 +17,7 @@ import ListMessageItem
|
||||
import AccountContext
|
||||
import ChatInterfaceState
|
||||
import ChatListUI
|
||||
import ComponentFlow
|
||||
|
||||
extension ChatReplyThreadMessage {
|
||||
var effectiveTopId: MessageId {
|
||||
@@ -546,6 +547,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
let topVisibleMessageRange = ValuePromise<ChatTopVisibleMessageRange?>(nil, ignoreRepeated: true)
|
||||
|
||||
var isSelectionGestureEnabled = true
|
||||
|
||||
private var overscrollView: ComponentHostView<Empty>?
|
||||
var nextChannelToRead: EnginePeer?
|
||||
var offerNextChannelToRead: Bool = false
|
||||
private var currentOverscrollExpandProgress: CGFloat = 0.0
|
||||
private var feedback: HapticFeedback?
|
||||
var openNextChannelToRead: ((EnginePeer) -> Void)?
|
||||
|
||||
private let clientId: Atomic<Int32>
|
||||
|
||||
@@ -1115,11 +1123,14 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
if strongSelf.tagMask == nil {
|
||||
var atBottom = false
|
||||
var offsetFromBottom: CGFloat?
|
||||
switch offset {
|
||||
case let .known(offsetValue):
|
||||
if offsetValue.isLessThanOrEqualTo(0.0) {
|
||||
atBottom = true
|
||||
offsetFromBottom = offsetValue
|
||||
}
|
||||
//print("offsetValue: \(offsetValue)")
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -1130,6 +1141,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
strongSelf.isScrollAtBottomPositionUpdated?()
|
||||
}
|
||||
|
||||
strongSelf.maybeUpdateOverscrollAction(offset: offsetFromBottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1150,10 +1163,22 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
self?.isInteractivelyScrollingPromise.set(true)
|
||||
self?.beganDragging?()
|
||||
}
|
||||
|
||||
self.endedInteractiveDragging = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let channel = strongSelf.nextChannelToRead, strongSelf.currentOverscrollExpandProgress >= 0.99 {
|
||||
strongSelf.openNextChannelToRead?(channel)
|
||||
}
|
||||
}
|
||||
|
||||
self.didEndScrolling = { [weak self] in
|
||||
self?.isInteractivelyScrollingValue = false
|
||||
self?.isInteractivelyScrollingPromise.set(false)
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.isInteractivelyScrollingValue = false
|
||||
strongSelf.isInteractivelyScrollingPromise.set(false)
|
||||
}
|
||||
|
||||
let selectionRecognizer = ChatHistoryListSelectionRecognizer(target: self, action: #selector(self.selectionPanGesture(_:)))
|
||||
@@ -1177,6 +1202,64 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
public func setLoadStateUpdated(_ f: @escaping (ChatHistoryNodeLoadState, Bool) -> Void) {
|
||||
self.loadStateUpdated = f
|
||||
}
|
||||
|
||||
private func maybeUpdateOverscrollAction(offset: CGFloat?) {
|
||||
if let offset = offset, offset < 0.0, self.offerNextChannelToRead {
|
||||
let overscrollView: ComponentHostView<Empty>
|
||||
if let current = self.overscrollView {
|
||||
overscrollView = current
|
||||
} else {
|
||||
overscrollView = ComponentHostView<Empty>()
|
||||
overscrollView.layer.sublayerTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
|
||||
self.overscrollView = overscrollView
|
||||
self.view.addSubview(overscrollView)
|
||||
}
|
||||
|
||||
let expandProgress: CGFloat = min(1.0, max(-offset, 0.0) / 110.0)
|
||||
|
||||
let text: String
|
||||
if self.nextChannelToRead != nil {
|
||||
if expandProgress >= 0.99 {
|
||||
//TODO:localize
|
||||
text = "Release to go to the next unread channel"
|
||||
} else {
|
||||
text = "Swipe up to go to the next unread channel"
|
||||
}
|
||||
|
||||
let previousType = self.currentOverscrollExpandProgress >= 0.99
|
||||
let currentType = expandProgress >= 0.99
|
||||
|
||||
if previousType != currentType {
|
||||
if self.feedback == nil {
|
||||
self.feedback = HapticFeedback()
|
||||
}
|
||||
self.feedback?.tap()
|
||||
}
|
||||
|
||||
self.currentOverscrollExpandProgress = expandProgress
|
||||
} else {
|
||||
text = "You have no unread channels"
|
||||
}
|
||||
|
||||
let overscrollSize = overscrollView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(ChatOverscrollControl(
|
||||
text: text,
|
||||
backgroundColor: selectDateFillStaticColor(theme: self.currentPresentationData.theme.theme, wallpaper: self.currentPresentationData.theme.wallpaper),
|
||||
foregroundColor: bubbleVariableColor(variableColor: self.currentPresentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: self.currentPresentationData.theme.wallpaper),
|
||||
peer: self.nextChannelToRead,
|
||||
context: self.context,
|
||||
expandProgress: expandProgress
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: self.bounds.width, height: 200.0)
|
||||
)
|
||||
overscrollView.frame = CGRect(origin: CGPoint(x: floor((self.bounds.width - overscrollSize.width) / 2.0), y: -offset + self.insets.top - overscrollSize.height - 10.0), size: overscrollSize)
|
||||
} else if let overscrollView = self.overscrollView {
|
||||
self.overscrollView = nil
|
||||
overscrollView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
func refreshPollActionsForVisibleMessages() {
|
||||
let _ = self.clientId.swap(nextClientId)
|
||||
|
||||
Reference in New Issue
Block a user