Initial implementation of channel overscroll navigation

This commit is contained in:
Ali
2021-07-27 17:36:35 +02:00
parent 6cec47b5f7
commit e85f3884d4
27 changed files with 2940 additions and 2 deletions

View File

@@ -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)