scroll to oldest working

This commit is contained in:
Diogo
2024-10-15 16:31:45 +01:00
parent a7844b3f7b
commit 7adfe631bf
2 changed files with 58 additions and 20 deletions
+21 -14
View File
@@ -23,7 +23,8 @@ struct ChatView: View {
@Environment(\.scenePhase) var scenePhase
@State @ObservedObject var chat: Chat
@StateObject private var scrollModel = ReverseListScrollModel()
@State private var itemSection: Dictionary<Int64, ChatSection> = Dictionary()
@State private var itemSection: [Int64: ChatSection] = [:]
@State private var activeSection: ChatSection = .bottom
@State private var showChatInfoSheet: Bool = false
@State private var showAddMembersSheet: Bool = false
@State private var composeState = ComposeState()
@@ -188,7 +189,7 @@ struct ChatView: View {
if !isLoading,
im.reversedChatItems.count <= loadItemsPerPage,
filtered(im.reversedChatItems).count < 10 {
loadChatItems(chat.chatInfo, .toOldest)
loadChatItems(chat.chatInfo, .toOldest, .bottom, nil)
}
}
.environmentObject(scrollModel)
@@ -419,7 +420,7 @@ struct ChatView: View {
let cInfo = chat.chatInfo
let mergedItems = filtered(im.reversedChatItems)
return GeometryReader { g in
ReverseList(items: mergedItems, scrollState: $scrollModel.state, itemSection: $itemSection ) { ci in
ReverseList(items: mergedItems, scrollState: $scrollModel.state, itemSection: $itemSection, activeSection: $activeSection ) { ci in
let voiceNoFrame = voiceWithoutFrame(ci)
let maxWidth = cInfo.chatType == .group
? voiceNoFrame
@@ -439,8 +440,8 @@ struct ChatView: View {
forwardedChatItems: $forwardedChatItems
)
.id(ci.id) // Required to trigger `onAppear` on iOS15
} loadPage: { direction in
loadChatItems(cInfo, direction)
} loadPage: { (direction, section, chatItemId) in
loadChatItems(cInfo, direction, section, chatItemId)
} loadItemsAround: { chatItemId in
loadItemsAround(cInfo, chatItemId)
}
@@ -842,7 +843,7 @@ struct ChatView: View {
}
}
private func loadChatItems(_ cInfo: ChatInfo, _ direction: ChatScrollDirection) {
private func loadChatItems(_ cInfo: ChatInfo, _ direction: ChatScrollDirection, _ section: ChatSection, _ chatItem: ChatItem?) {
Task {
if loadingItems || firstPage { return }
loadingItems = true
@@ -852,9 +853,9 @@ struct ChatView: View {
// Load additional items until the page is +50 large after merging
while chatItemsAvailable && filtered(reversedPage).count < loadItemsPerPage {
let pagination: ChatPagination =
if direction == .toOldest, let lastItem = reversedPage.last ?? im.reversedChatItems.last {
if direction == .toOldest, let lastItem = chatItem ?? reversedPage.last ?? im.reversedChatItems.last {
.before(chatItemId: lastItem.id, count: loadItemsPerPage)
} else if direction == .toLatest, let firstItem = reversedPage.first ?? im.reversedChatItems.first {
} else if direction == .toLatest, let firstItem = chatItem ?? reversedPage.first ?? im.reversedChatItems.first {
// TODO: Replace with anchor.
.after(chatItemId: firstItem.id, count: loadItemsPerPage)
} else {
@@ -870,13 +871,19 @@ struct ChatView: View {
reversedPage.append(contentsOf: chatItems.reversed())
}
await MainActor.run {
if reversedPage.count == 0 {
if reversedPage.count == 0 {
firstPage = true
} else {
var reversedPageToAppend = Array<ChatItem>()
reversedPage.forEach { ci in
itemSection[ci.id] = .bottom
if itemSection[ci.id] == nil {
reversedPageToAppend.append(ci)
itemSection[ci.id] = section
} else {
// TODO: Maybe merge
}
}
im.reversedChatItems.append(contentsOf: reversedPage)
im.reversedChatItems.append(contentsOf: reversedPageToAppend)
}
loadingItems = false
}
@@ -889,9 +896,9 @@ struct ChatView: View {
private func loadItemsAround(_ cInfo: ChatInfo, _ chatItemId: Int64) {
Task {
if loadingItems { return }
if loadingItems || itemSection[chatItemId] != nil { return }
loadingItems = true
do {
do {
var reversedPage = Array<ChatItem>()
var chatItemsAvailable = true
// Load additional items until the page is +50 large after merging
@@ -913,7 +920,7 @@ struct ChatView: View {
)
chatItems.append(contentsOf: afterItems)
}
chatItemsAvailable = !chatItems.isEmpty
reversedPage.append(contentsOf: chatItems.reversed())
}
+37 -6
View File
@@ -28,11 +28,11 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
@Binding var scrollState: ReverseListScrollModel.State
@Binding var itemSection: Dictionary<Int64, ChatSection>
@Binding var activeSection: ChatSection
/// Closure, that returns user interface for a given item
let content: (ChatItem) -> Content
let loadPage: (ChatScrollDirection) -> Void
let loadPage: (ChatScrollDirection, ChatSection, ChatItem?) -> Void
let loadItemsAround: (Int64) -> Void
func makeUIViewController(context: Context) -> Controller {
@@ -92,8 +92,12 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
self.dataSource = UITableViewDiffableDataSource<ChatSection, ChatItem>(
tableView: tableView
) { (tableView, indexPath, item) -> UITableViewCell? in
if self.representer.scrollState == .atDestination, indexPath.item > self.itemCount - 8, self.itemCount > 8 {
self.representer.loadPage(.toOldest)
if let section = self.dataSource.sectionIdentifier(for: indexPath.section) {
let itemCount = self.getTotalItemsInItemSection(indexPath: indexPath)
if self.representer.activeSection == section, indexPath.item > itemCount - 8, itemCount > 8 {
let lastItem = self.getLastItemInItemSection(indexPath: indexPath)
self.representer.loadPage(.toOldest, section, lastItem)
}
}
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath)
if #available(iOS 16.0, *) {
@@ -178,7 +182,7 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
/// Scrolls up
func scrollToItem(id: Int64) {
if let loadedIndex = representer.items.firstIndex(where: { $0.id == id }) {
if let loadedIndex = self.representer.items.firstIndex(where: { $0.id == id }) {
let ci = representer.items[loadedIndex]
if let indexPath = dataSource.indexPath(for: ci),
let section = dataSource.sectionIdentifier(for: indexPath.section) {
@@ -186,7 +190,10 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
}
} else {
Task {
self.representer.loadItemsAround(id)
if (self.representer.itemSection[id] == nil) {
self.representer.activeSection = .destination
self.representer.loadItemsAround(id)
}
}
}
}
@@ -290,6 +297,30 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
relativeFrame.minY < tableView.frame.height - InvertedTableView.inset
} else { false }
}
private func getTotalItemsInItemSection(indexPath: IndexPath) -> Int {
return self.tableView.numberOfRows(inSection: indexPath.section)
}
private func getLastItemInItemSection(indexPath: IndexPath) -> ChatItem? {
let numberOfRows = self.getTotalItemsInItemSection(indexPath: indexPath)
return if numberOfRows > 0,
let lastItem = self.dataSource.itemIdentifier(for: IndexPath(row: numberOfRows - 1, section: indexPath.section)) {
lastItem
} else {
nil
}
}
private func getFirstItemInItemSection(indexPath: IndexPath) -> ChatItem? {
let firstIndexPath = IndexPath(item: 0, section: indexPath.section)
return if let firstItem = self.dataSource.itemIdentifier(for: firstIndexPath) {
firstItem
} else {
nil
}
}
}
/// `UIHostingConfiguration` back-port for iOS14 and iOS15