mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-23 06:22:43 +00:00
change
This commit is contained in:
1 parent
83f478e7ba
commit
e2b9e162eb
5 files changed
+111
-38
No files matched your search
@@ -11,17 +11,45 @@ import SimpleXChat
|
||||
|
||||
func loadLastItems(_ loadingMoreItems: Binding<Bool>, _ chatInfo: ChatInfo) {
|
||||
//return ()
|
||||
if ItemsModel.shared.chatState.totalAfter == 0 {
|
||||
return
|
||||
}
|
||||
loadingMoreItems.wrappedValue = true
|
||||
Task {
|
||||
try? await Task.sleep(nanoseconds: 2500_000000)
|
||||
if ItemsModel.shared.chatState.totalAfter == 0 || ChatModel.shared.chatId != chatInfo.id {
|
||||
try? await Task.sleep(nanoseconds: 1500_000000)
|
||||
if ChatModel.shared.chatId != chatInfo.id {
|
||||
await MainActor.run {
|
||||
loadingMoreItems.wrappedValue = false
|
||||
}
|
||||
return
|
||||
}
|
||||
await MainActor.run {
|
||||
loadingMoreItems.wrappedValue = true
|
||||
}
|
||||
await apiLoadMessages(chatInfo.chatType, chatInfo.apiId, ChatPagination.last(count: 50), ItemsModel.shared.chatState)
|
||||
await MainActor.run {
|
||||
loadingMoreItems.wrappedValue = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func preloadItems(_ mergedItems: MergedItems, _ state: ListState, _ ignoreLoadingRequests: Binding<[Int64]>, _ loadItems: @escaping (ChatPagination) -> Void) {
|
||||
let remaining = ChatPagination.UNTIL_PRELOAD_COUNT
|
||||
let firstVisibleIndex = state.firstVisibleItemIndex
|
||||
|
||||
preloadItemsAfter()
|
||||
|
||||
func preloadItemsAfter() {
|
||||
let items: [ChatItem] = ItemsModel.shared.reversedChatItems.reversed()
|
||||
let splits = mergedItems.splits
|
||||
let split = splits.last(where: { $0.indexRangeInParentItems.contains(firstVisibleIndex) })
|
||||
// we're inside a splitRange (top --- [end of the splitRange --- we're here --- start of the splitRange] --- bottom)
|
||||
logger.debug("LALAL PRELOAD AFTER \(String(describing: split)) \(firstVisibleIndex) \(items.count - 1 - (split?.indexRangeInReversed.lowerBound ?? -1))")
|
||||
if let split, split.indexRangeInParentItems.lowerBound + remaining > firstVisibleIndex {
|
||||
let index = items.count - 1 - split.indexRangeInReversed.lowerBound
|
||||
if index >= 0 {
|
||||
let loadFromItemId = items[index].id
|
||||
loadItems(.after(chatItemId: loadFromItemId, count: ChatPagination.PRELOAD_COUNT))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,6 +47,7 @@ struct ChatView: View {
|
||||
@State private var selectedChatItems: Set<Int64>? = nil
|
||||
@State private var showDeleteSelectedMessages: Bool = false
|
||||
@State private var allowToDeleteSelectedMessagesForAll: Bool = false
|
||||
@State private var ignoreLoadingRequests: [Int64] = []
|
||||
|
||||
@AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
|
||||
|
||||
@@ -429,12 +430,19 @@ struct ChatView: View {
|
||||
.map { $0.element }
|
||||
}
|
||||
|
||||
var searchValueIsEmpty: Bool {
|
||||
get {
|
||||
searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func chatItemsList() -> some View {
|
||||
let cInfo = chat.chatInfo
|
||||
return GeometryReader { g in
|
||||
let _ = logger.debug("LALAL RELOAD \(im.reversedChatItems.count)")
|
||||
ReverseList(items: im.reversedChatItems, mergedItems: $mergedItems, revealedItems: $revealedItems, unreadCount: Binding.constant(chat.chatStats.unreadCount), scrollState: $scrollModel.state) { index, mergedItem in
|
||||
// LALAL CAN I CHANGE BINDING LIKE THIS IN ignoreLoadingRequests?
|
||||
ReverseList(items: im.reversedChatItems, mergedItems: $mergedItems, revealedItems: $revealedItems, unreadCount: Binding.constant(chat.chatStats.unreadCount), scrollState: $scrollModel.state, ignoreLoadingRequests: searchValueIsEmpty ? $ignoreLoadingRequests : Binding.constant([])) { index, mergedItem in
|
||||
let ci = switch mergedItem {
|
||||
case let .single(item, _): item.item
|
||||
case let .grouped(items, _, _, _, _, _, _): items.boxedValue.last!.item
|
||||
@@ -892,7 +900,7 @@ struct ChatView: View {
|
||||
var chatItemsAvailable = true
|
||||
var itemsCountChanged = false
|
||||
// Load additional items until the page is +50 large after merging
|
||||
while chatItemsAvailable && filtered(im.reversedChatItems).count < loadItemsPerPage {
|
||||
//while chatItemsAvailable && filtered(im.reversedChatItems).count < loadItemsPerPage {
|
||||
let oldCount = im.reversedChatItems.count
|
||||
await apiLoadMessages(
|
||||
cInfo.chatType,
|
||||
@@ -904,7 +912,8 @@ struct ChatView: View {
|
||||
)
|
||||
itemsCountChanged = im.reversedChatItems.count != oldCount
|
||||
chatItemsAvailable = itemsCountChanged
|
||||
}
|
||||
//}
|
||||
logger.debug("LALAL ITEMCOUNTCHANGED \(itemsCountChanged) \(oldCount) \(im.reversedChatItems.count)")
|
||||
await MainActor.run {
|
||||
if !itemsCountChanged {
|
||||
firstPage = true
|
||||
|
||||
@@ -547,6 +547,11 @@ struct ComposeView: View {
|
||||
if case let .voicePreview(_, duration) = composeState.preview {
|
||||
voiceMessageRecordingTime = TimeInterval(duration)
|
||||
}
|
||||
// Task {
|
||||
// for i in 201 ... 400 {
|
||||
// await sendMessageAsync("\(i)", live: false, ttl: nil)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
@Binding var unreadCount: Int
|
||||
|
||||
@Binding var scrollState: ReverseListScrollModel.State
|
||||
|
||||
@State private var itemsUpdaterTask: Task<Void, Never>? = nil
|
||||
@Binding var ignoreLoadingRequests: [Int64]
|
||||
|
||||
/// Closure, that returns user interface for a given item
|
||||
/// Index, merged item
|
||||
@@ -45,26 +44,14 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
controller.scroll(to: 0, position: .top)
|
||||
}
|
||||
} else {
|
||||
itemsUpdaterTask?.cancel()
|
||||
// when tableView is dragging and new items are added, scroll position cannot be set correctly
|
||||
// so it's better to just wait until dragging ends
|
||||
if controller.tableView.isDragging && !controller.tableView.isDecelerating {
|
||||
DispatchQueue.main.async {
|
||||
itemsUpdaterTask = Task {
|
||||
while controller.tableView.isDragging && !controller.tableView.isDecelerating {
|
||||
do {
|
||||
try await Task.sleep(nanoseconds: 100_000000)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
await MainActor.run {
|
||||
controller.update(items: items)
|
||||
}
|
||||
}
|
||||
|
||||
if controller.tableView.isDragging/* && !controller.tableView.isDecelerating*/ {
|
||||
controller.runBlockOnEndDecelerating = {
|
||||
controller.update(items: items)
|
||||
}
|
||||
} else {
|
||||
controller.runBlockOnEndDecelerating = nil
|
||||
controller.update(items: items)
|
||||
}
|
||||
}
|
||||
@@ -88,6 +75,8 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
private let updateFloatingButtons = PassthroughSubject<Void, Never>()
|
||||
private var bag = Set<AnyCancellable>()
|
||||
|
||||
var runBlockOnEndDecelerating: (() -> Void)? = nil
|
||||
|
||||
private var scrollToRowOnAppear = 0
|
||||
|
||||
init(representer: ReverseList) {
|
||||
@@ -113,15 +102,26 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
)
|
||||
}
|
||||
|
||||
var prevFirstVisible = -1
|
||||
|
||||
// 3. Configure data source
|
||||
self.dataSource = UITableViewDiffableDataSource<Section, Int>(
|
||||
tableView: tableView
|
||||
) { (tableView, indexPath, item) -> UITableViewCell? in
|
||||
if indexPath.item > self.itemCount - 8 {
|
||||
//if indexPath.item > self.itemCount - 8 {
|
||||
logger.debug("LALAL ITEM \(indexPath.item)")
|
||||
let pagination = ChatPagination.last(count: 0)
|
||||
self.representer.loadItems(pagination, { self.visibleItemIndexesNonReversed(Binding.constant(self.representer.mergedItems)) })
|
||||
}
|
||||
Task {
|
||||
// do not put it outside Task because it will be stackoverflow error
|
||||
if let state = self.getListState(), prevFirstVisible != state.firstVisibleItemIndex {
|
||||
prevFirstVisible = state.firstVisibleItemIndex
|
||||
logger.debug("LALAL LOADING before")
|
||||
preloadItems(self.representer.mergedItems, state, self.representer.$ignoreLoadingRequests) { pagination in
|
||||
logger.debug("LALAL LOADING INSIDE")
|
||||
self.representer.loadItems(pagination, { self.visibleItemIndexesNonReversed(Binding.constant(self.representer.mergedItems)) })
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath)
|
||||
if #available(iOS 16.0, *) {
|
||||
cell.contentConfiguration = UIHostingConfiguration { self.representer.content(indexPath.item, self.itemsInPrevSnapshot[item]!) }
|
||||
@@ -186,6 +186,11 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
NotificationCenter.default.post(name: .chatViewWillBeginScrolling, object: nil)
|
||||
}
|
||||
|
||||
override public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
runBlockOnEndDecelerating?()
|
||||
runBlockOnEndDecelerating = nil
|
||||
}
|
||||
|
||||
/// depending on tableView layout phase conditions, it can already have known size or not. Not possible to correctly scroll to required
|
||||
/// item if the size is unknown
|
||||
func scrollToRowWhenKnowSize(_ row: Int) {
|
||||
@@ -253,13 +258,17 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
func update(items: [ChatItem]) {
|
||||
var prevSnapshot = dataSource.snapshot()
|
||||
let wasCount = prevSnapshot.numberOfItems
|
||||
let insertedOneNewestItem = wasCount != 0 && representer.mergedItems.items.count - wasCount == 1 && prevSnapshot.itemIdentifiers.first!.hashValue == self.representer.mergedItems.items[1].hashValue
|
||||
let willBeCount = representer.mergedItems.items.count
|
||||
let c = 13
|
||||
let insertedOneNewestItem = wasCount != 0 && willBeCount - wasCount == c && prevSnapshot.itemIdentifiers.first!.hashValue == self.representer.mergedItems.items[c].hashValue
|
||||
logger.debug("LALAL WAS \(wasCount) will be \(self.representer.mergedItems.items.count)")
|
||||
//self.representer.mergedItems = MergedItems.create(items, representer.unreadCount, representer.revealedItems, ItemsModel.shared.chatState)
|
||||
let snapshot: NSDiffableDataSourceSnapshot<Section, Int>
|
||||
let itemsInCurrentSnapshot: Dictionary<Int, MergedItem>
|
||||
if insertedOneNewestItem {
|
||||
prevSnapshot.insertItems([representer.mergedItems.items.first!.hashValue], beforeItem: prevSnapshot.itemIdentifiers.first!)
|
||||
for i in 0 ..< c {
|
||||
prevSnapshot.insertItems([representer.mergedItems.items[c - i].hashValue], beforeItem: prevSnapshot.itemIdentifiers.first!)
|
||||
}
|
||||
var new = itemsInPrevSnapshot
|
||||
new[representer.mergedItems.items.first!.hashValue] = representer.mergedItems.items.first!
|
||||
itemsInCurrentSnapshot = new
|
||||
@@ -305,8 +314,10 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
}
|
||||
} else if wasCount != snapshot.numberOfItems {
|
||||
logger.debug("LALAL drag \(self.tableView.isDragging), decel \(self.tableView.isDecelerating)")
|
||||
// tableView.panGestureRecognizer.isEnabled = false
|
||||
// logger.debug("LALAL drag2 \(self.tableView.isDragging), decel \(self.tableView.isDecelerating)")
|
||||
if tableView.isDecelerating {
|
||||
// CATransaction.begin()
|
||||
itemsInPrevSnapshot = itemsInCurrentSnapshot
|
||||
tableView.beginUpdates()
|
||||
dataSource.apply(
|
||||
@@ -314,16 +325,18 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
animatingDifferences: false
|
||||
)
|
||||
tableView.endUpdates()
|
||||
// CATransaction.commit()
|
||||
tableView.panGestureRecognizer.isEnabled = true
|
||||
} else {
|
||||
itemsInPrevSnapshot = itemsInCurrentSnapshot
|
||||
dataSource.apply(
|
||||
snapshot,
|
||||
animatingDifferences: false
|
||||
)
|
||||
if snapshot.itemIdentifiers[0] == prevSnapshot.itemIdentifiers[0] {
|
||||
//if snapshot.itemIdentifiers[0] == prevSnapshot.itemIdentifiers[0] {
|
||||
// added new items to top
|
||||
|
||||
} else {
|
||||
//} else {
|
||||
// added new items to bottom
|
||||
// logger.debug("LALAL WAS HEIGHT \(wasContentHeight) now \(self.tableView.contentSize.height), offset was \(wasOffset), now \(self.tableView.contentOffset.y), will be \(self.tableView.contentOffset.y + (self.tableView.contentSize.height - wasContentHeight)), countDiff \(countDiff), wasVisibleRow \(wasFirstVisibleRow), wasFirstVisibleOffset \(wasFirstVisibleOffset)")
|
||||
self.tableView.scrollToRow(
|
||||
@@ -337,10 +350,11 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
)
|
||||
let state = getListState()!
|
||||
logger.debug("LALAL NOW FIRST VISIBLE \(state.firstVisibleItemIndex) \(state.firstVisibleItemOffset)")
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
updateFloatingButtons.send()
|
||||
return
|
||||
}
|
||||
|
||||
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
@@ -348,8 +362,9 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
}
|
||||
|
||||
func getListState() -> ListState? {
|
||||
logger.debug("LALAL VISIBLE ROWS \((self.tableView.indexPathsForVisibleRows ?? []).map({ $0.row }))")
|
||||
if let visibleRows = tableView.indexPathsForVisibleRows,
|
||||
visibleRows.last?.item ?? 0 < representer.mergedItems.items.count {
|
||||
visibleRows.last?.row ?? 0 < representer.mergedItems.items.count {
|
||||
let scrollOffset: Double = tableView.contentOffset.y + InvertedTableView.inset
|
||||
let topItemDate: Date? =
|
||||
if let lastVisible = visibleRows.last(where: { isVisible(indexPath: $0) }) {
|
||||
@@ -364,11 +379,11 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||
let lastVisible = visibleRows.last(where: { isVisible(indexPath: $0) })
|
||||
let bottomItemId: ChatItem.ID? =
|
||||
if let firstVisible {
|
||||
representer.mergedItems.items[firstVisible.item].newest().item.id
|
||||
representer.mergedItems.items[firstVisible.row].newest().item.id
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
return ListState(scrollOffset: scrollOffset, topItemDate: topItemDate, bottomItemId: bottomItemId, firstVisibleItemIndex: firstVisible?.item ?? 0, lastVisibleItemIndex: lastVisible?.item ?? 0, firstVisibleItemOffset: firstVisibleOffset ?? 0)
|
||||
return ListState(scrollOffset: scrollOffset, topItemDate: topItemDate, bottomItemId: bottomItemId, firstVisibleItemIndex: firstVisible?.row ?? 0, lastVisibleItemIndex: lastVisible?.row ?? 0, firstVisibleItemOffset: firstVisibleOffset ?? 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -537,4 +552,16 @@ class InvertedTableView: UITableView {
|
||||
override var adjustedContentInset: UIEdgeInsets {
|
||||
Self.insets
|
||||
}
|
||||
|
||||
// override var contentOffset: CGPoint {
|
||||
// get { super.contentOffset }
|
||||
// set {
|
||||
// logger.debug("LALAL SET OFFSET \(newValue.y)")
|
||||
// if newValue.y <= 0 {
|
||||
// return
|
||||
// }
|
||||
// super.contentOffset = newValue
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -1183,6 +1183,10 @@ struct NewUser: Encodable {
|
||||
}
|
||||
|
||||
public enum ChatPagination {
|
||||
public static let INITIAL_COUNT = 50
|
||||
public static let PRELOAD_COUNT = 50
|
||||
public static let UNTIL_PRELOAD_COUNT = 50
|
||||
|
||||
case last(count: Int)
|
||||
case after(chatItemId: Int64, count: Int)
|
||||
case before(chatItemId: Int64, count: Int)
|
||||
|
||||
Reference in new issue
Block a user