This commit is contained in:
Diogo
2024-11-06 10:15:57 +00:00
parent 547dbc5271
commit ff7fcaf7f3
4 changed files with 53 additions and 20 deletions
+6 -1
View File
@@ -52,8 +52,13 @@ class ItemsModel: ObservableObject {
static let shared = ItemsModel()
private let publisher = ObservableObjectPublisher()
private var bag = Set<AnyCancellable>()
var chatItemIds = Set<ChatItem.ID>()
var reversedChatItems: [ChatItem] = [] {
willSet { publisher.send() }
willSet {
chatItemIds.removeAll()
chatItemIds.formUnion(newValue.map { $0.id })
publisher.send()
}
}
var itemAdded = false {
willSet { publisher.send() }
@@ -346,6 +346,7 @@ struct FramedItemView: View {
private func loadItemsAround(_ cInfo: ChatInfo, _ chatItemId: Int64) async -> [ChatItem]? {
do {
let im = ItemsModel.shared
var reversedPage = Array<ChatItem>()
let pagination: ChatPagination = .around(chatItemId: chatItemId, count: loadItemsPerPage * 2)
let (chatItems, gap) = try await apiGetChatItems(
@@ -355,13 +356,15 @@ struct FramedItemView: View {
search: ""
)
reversedPage.append(contentsOf: chatItems.reversed())
let dedupedChatItems = chatItems.filter { !im.chatItemIds.contains($0.id) }
reversedPage.append(contentsOf: dedupedChatItems.reversed())
await MainActor.run {
ItemsModel.shared.reversedChatItems.append(contentsOf: reversedPage)
if let size = gap {
ItemsModel.shared.gap = ChatGap(index: reversedPage.count, size: size)
let itemCount = im.reversedChatItems.count
if let size = gap, size - itemCount > 0 {
im.gap = ChatGap(index: itemCount, size: size - itemCount)
}
im.reversedChatItems.append(contentsOf: reversedPage)
}
return reversedPage
+35 -11
View File
@@ -443,8 +443,8 @@ struct ChatView: View {
forwardedChatItems: $forwardedChatItems
)
.id(ci.id) // Required to trigger `onAppear` on iOS15
} loadPage: {
loadChatItems(cInfo)
} loadPage: { pagination in
loadChatItems(cInfo, pagination)
}
.opacity(ItemsModel.shared.isLoading ? 0 : 1)
.padding(.vertical, -InvertedTableView.inset)
@@ -857,7 +857,7 @@ struct ChatView: View {
}
}
private func loadChatItems(_ cInfo: ChatInfo) {
private func loadChatItems(_ cInfo: ChatInfo, _ pagination: ChatPagination = .initial(count: loadItemsPerPage)) {
Task {
if loadingItems || firstPage { return }
loadingItems = true
@@ -866,26 +866,50 @@ struct ChatView: View {
var chatItemsAvailable = true
// Load additional items until the page is +50 large after merging
while chatItemsAvailable && filtered(reversedPage).count < loadItemsPerPage {
let pagination: ChatPagination =
if let lastItem = reversedPage.last ?? im.reversedChatItems.last {
.before(chatItemId: lastItem.id, count: loadItemsPerPage)
} else {
.last(count: loadItemsPerPage)
}
let chatPagination: ChatPagination = switch pagination {
case let .before(chatItemId, count): .before(chatItemId: reversedPage.last?.id ?? chatItemId, count: count)
case let .last(count): .last(count: count)
case let .initial(count): .initial(count: count)
case let .after(chatItemId, count):
.after(chatItemId: reversedPage.first?.id ?? chatItemId, count: count)
case .around(_, _): throw RuntimeError("Unsupported pagination type for loading chat items: \(pagination)")
}
let (chatItems, _) = try await apiGetChatItems(
type: cInfo.chatType,
id: cInfo.apiId,
pagination: pagination,
pagination: chatPagination,
search: searchText
)
chatItemsAvailable = !chatItems.isEmpty
reversedPage.append(contentsOf: chatItems.reversed())
}
let dedupedreversePage = reversedPage.filter { !im.chatItemIds.contains($0.id) }
await MainActor.run {
if reversedPage.count == 0 {
firstPage = true
} else {
im.reversedChatItems.append(contentsOf: reversedPage)
switch pagination {
case .before, .last, .initial:
im.reversedChatItems.append(contentsOf: dedupedreversePage)
case let .after(chatItemId, _):
let index = im.reversedChatItems.firstIndex { $0.id == chatItemId }
logger.error("[scrolling] setting: \(dedupedreversePage.count) \(index ?? -1)")
if let index {
if let gap = im.gap {
let size = gap.size - dedupedreversePage.count
if size > 0 {
im.gap = ChatGap(index: gap.index, size: size)
} else {
im.gap = nil
}
}
im.reversedChatItems.insert(contentsOf: dedupedreversePage, at: index)
}
case .around(_, _): break
}
}
loadingItems = false
}
+5 -4
View File
@@ -20,7 +20,7 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
/// Closure, that returns user interface for a given item
let content: (ChatItem) -> Content
let loadPage: () -> Void
let loadPage: (_ pagination: ChatPagination) -> Void
func makeUIViewController(context: Context) -> Controller {
Controller(representer: self)
@@ -41,7 +41,7 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
controller.scrollToUnread(to: items.first(where: { $0.id == id }))
}
} else {
logger.error("[scrolling] not scrolling gap: (\(gap?.index ?? -1), \(gap?.size ?? 0)")
logger.error("[scrolling] not scrolling gap: (\(gap?.index ?? -1), \(gap?.size ?? 0))")
controller.update(items: items, gap: gap)
}
}
@@ -85,11 +85,12 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
if self.representer.scrollState == .atDestination {
if indexPath.item > self.itemCount - preloadItem, let item = self.getItemAtPath(indexPath: IndexPath(row: self.itemCount - 1, section: 0)) {
logger.error("[scrolling] requesting page \(item.text)")
self.representer.loadPage()
self.representer.loadPage(.before(chatItemId: item.id, count: loadItemsPerPage))
} else if let item = self.getFirstItemBeforePlacholder(indexPath) {
logger.error("[scrolling] needs items in top \(item.text)")
// TODO: Cleanup, this should never be possible
} else if let item = self.getFirstItemAfterPlacholder(indexPath) {
logger.error("[scrolling] needs items in bottom \(item.text)")
self.representer.loadPage(.after(chatItemId: item.id, count: loadItemsPerPage))
}
}
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath)