mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
scroll to oldest working
This commit is contained in:
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user