From 92eda6fae643037ac5ca19612bf8ac7e0edc2a9f Mon Sep 17 00:00:00 2001 From: Avently <7953703+avently@users.noreply.github.com> Date: Mon, 20 Jan 2025 08:05:08 -0800 Subject: [PATCH] changes --- .../Shared/Views/Chat/ChatItemsMerger.swift | 18 ++++++++- apps/ios/Shared/Views/Chat/ChatView.swift | 24 ++++++++++-- apps/ios/Shared/Views/Chat/ReverseList.swift | 38 +++++++++++++------ 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift b/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift index f194e07b71..9a9a40049a 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift @@ -21,6 +21,7 @@ struct MergedItems { return MergedItems(items: [], splits: [], indexInParentItems: [:], snapshot: NSDiffableDataSourceSnapshot()) } + logger.debug("LALAL STEP 0") let unreadAfterItemId = chatState.unreadAfterItemId let itemSplits = chatState.splits var mergedItems: [MergedItem] = [] @@ -108,6 +109,7 @@ struct MergedItems { indexInParentItems[item.id] = visibleItemIndexInParent index += 1 } + logger.debug("LALAL STEP 7") var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([ReverseListSection.main]) snapshot.appendItems(mergedItems) @@ -238,8 +240,22 @@ struct ListItem: Hashable { // how many unread items before (older than) this one (excluding this one) let unreadBefore: Int + private func chatDirHash(_ chatDir: CIDirection?) -> Int { + guard let chatDir else { return 0 } + return switch chatDir { + case .directSnd: 0 + case .directRcv: 1 + case .groupSnd: 2 + case let .groupRcv(mem): "\(mem.groupMemberId) \(mem.displayName) \(mem.memberStatus.rawValue) \(mem.memberRole.rawValue) \(mem.image?.hash ?? 0)".hash + case .localSnd: 4 + case .localRcv: 5 + } + } + + // using meta.hashValue instead of parts of it gives 120ms vs 90ms for MergedItems list generation for 1800 items (+ snapshot time generation for UITableView in MergedItems). + // so better to use partial meta here func genHash() -> String { - "\(item.meta.itemId) \(item.meta.updatedAt.hashValue) \(item.reactions.count) \(item.meta.isRcvNew) \(item.text.hash) \(unreadBefore) \(prevItem?.id ?? -1) \(nextItem?.id ?? -2)" + "\(item.meta.itemId) \(item.meta.updatedAt.hashValue) \(item.meta.itemEdited) \(item.meta.itemDeleted?.hashValue ?? 0) \(item.meta.itemTimed?.hashValue ?? 0) \(item.meta.itemStatus.hashValue) \(item.meta.sentViaProxy ?? false) \(item.mergeCategory?.hashValue ?? 0) \(chatDirHash(item.chatDir)) \(item.reactions.hashValue) \(item.meta.isRcvNew) \(item.text.hash) \(item.file?.hashValue ?? 0) \(item.quotedItem?.itemId ?? 0) \(unreadBefore) \(prevItem?.id ?? 0) \(chatDirHash(prevItem?.chatDir)) \(prevItem?.mergeCategory?.hashValue ?? 0) \(nextItem?.id ?? 0) \(chatDirHash(nextItem?.chatDir)) \(nextItem?.mergeCategory?.hashValue ?? 0)" } } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index ce3fd5d506..994a36ad90 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -48,6 +48,7 @@ struct ChatView: View { @State private var allowToDeleteSelectedMessagesForAll: Bool = false @State private var allowLoadMoreItems: Bool = false @State private var ignoreLoadingRequests: [Int64] = [] + @State private var updateMergedItemsTask: Task? = nil @AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial @@ -473,6 +474,7 @@ struct ChatView: View { forwardedChatItems: $forwardedChatItems, reveal: { reveal in mergedItem.reveal(reveal, $revealedItems) + updateMergedItemsTask?.cancel() mergedItems = MergedItems.create(ItemsModel.shared.reversedChatItems, chat.chatStats.unreadCount, revealedItems, ItemsModel.shared.chatState) } ) @@ -494,12 +496,23 @@ struct ChatView: View { } } .onChange(of: im.reversedChatItems) { items in - mergedItems = MergedItems.create(items, chat.chatStats.unreadCount, revealedItems, ItemsModel.shared.chatState) + updateMergedItemsTask?.cancel() + updateMergedItemsTask = Task { + let items = MergedItems.create(items, chat.chatStats.unreadCount, revealedItems, ItemsModel.shared.chatState) + if Task.isCancelled { + return + } + await MainActor.run { + mergedItems = items + } + } } .onChange(of: revealedItems) { revealed in + updateMergedItemsTask?.cancel() mergedItems = MergedItems.create(im.reversedChatItems, chat.chatStats.unreadCount, revealed, ItemsModel.shared.chatState) } .onChange(of: chat.chatStats.unreadCount) { unreadCount in + updateMergedItemsTask?.cancel() mergedItems = MergedItems.create(im.reversedChatItems, unreadCount, revealedItems, ItemsModel.shared.chatState) } .onChange(of: chat.id) { _ in @@ -913,7 +926,9 @@ struct ChatView: View { private func loadChatItems(_ cInfo: ChatInfo, _ pagination: ChatPagination, _ visibleItemIndexesNonReversed: @escaping () -> ClosedRange = { 0 ... 0 }) async -> Bool { if loadingMoreItems { return false } - loadingMoreItems = true + await MainActor.run { + loadingMoreItems = true + } var chatItemsAvailable = true var itemsCountChanged = false // Load additional items until the page is +50 large after merging @@ -1049,7 +1064,7 @@ struct ChatView: View { } else { nil } - let showAvatar = if case .grouped = merged { shouldShowAvatar(item, listItem.nextItem) } else { true } + let showAvatar = shouldShowAvatar(item, listItem.nextItem) let itemSeparation: ItemSeparation let prevItemSeparationLargeGap: Bool let single = switch merged { @@ -1084,7 +1099,8 @@ struct ChatView: View { } .onAppear { // LALAL - //return () + return () + if markedRead { return } else { diff --git a/apps/ios/Shared/Views/Chat/ReverseList.swift b/apps/ios/Shared/Views/Chat/ReverseList.swift index a81331e18e..f3e327c1d9 100644 --- a/apps/ios/Shared/Views/Chat/ReverseList.swift +++ b/apps/ios/Shared/Views/Chat/ReverseList.swift @@ -61,7 +61,7 @@ struct ReverseList: UIViewControllerRepresentable { } else { // 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 !mergedItems.splits.isEmpty && controller.tableView.isDragging/* && !controller.tableView.isDecelerating*/ { + if !controller.useSmoothScrolling && !mergedItems.splits.isEmpty && controller.tableView.isDragging/* && !controller.tableView.isDecelerating*/ { controller.runBlockOnEndDecelerating = { Task { let prevSnapshot = controller.dataSource.snapshot() @@ -71,9 +71,9 @@ struct ReverseList: UIViewControllerRepresentable { controller.updateFloatingButtons.send() return } - await MainActor.run { + //await MainActor.run { controller.update(mergedItems.items, mergedItems.snapshot, prevSnapshot) - } + //} } } } else { @@ -89,7 +89,9 @@ struct ReverseList: UIViewControllerRepresentable { controller.updateFloatingButtons.send() return } - controller.update(mergedItems.items, mergedItems.snapshot, prevSnapshot) + //await MainActor.run { + controller.update(mergedItems.items, mergedItems.snapshot, prevSnapshot) + //} } } } @@ -114,7 +116,8 @@ struct ReverseList: UIViewControllerRepresentable { private var runBlockOnEndScrolling: (() -> Void)? = nil - private let useSmoothScrolling = false + let useSmoothScrolling = false + var preloading = false init(representer: ReverseList) { self.representer = representer @@ -305,6 +308,7 @@ struct ReverseList: UIViewControllerRepresentable { let wasFirstVisibleOffset = listState?.firstVisibleItemOffset ?? 0 // Sets content offset on initial load logger.debug("LALAL STEP 4") + tableView.panGestureRecognizer.isEnabled = true if wasCount == 0 { dataSource.apply( snapshot, @@ -399,9 +403,11 @@ struct ReverseList: UIViewControllerRepresentable { updateFloatingButtons.send() if let listState = self.getListState(representer.mergedItems.items) { - if runBlockOnEndDecelerating != nil && nearSplit(listState, representer.mergedItems) { - stopScrolling() + if let block = runBlockOnEndDecelerating, nearSplit(remaining: 40, listState, representer.mergedItems) { + tableView.panGestureRecognizer.isEnabled = false + block() } + logger.debug("LALAL IN SPLIT OR NO \(self.nearSplit(remaining: 40, listState, self.representer.mergedItems)) \(String(describing: self.representer.mergedItems.splits))") preloadIfNeeded(listState) } } @@ -414,14 +420,16 @@ struct ReverseList: UIViewControllerRepresentable { } } - func stopScrolling() { + func stopScrolling(disable: Bool = false) { tableView.setContentOffset(self.tableView.contentOffset, animated: false) + if disable { + tableView.panGestureRecognizer.isEnabled = false + } } - func nearSplit(_ listState: ListState, _ mergedItems: MergedItems) -> Bool { + func nearSplit(remaining: Int, _ listState: ListState, _ mergedItems: MergedItems) -> Bool { if mergedItems.splits.isEmpty { return false } - let remaining = 10 let firstVisibleIndex = listState.firstVisibleItemIndex let lastVisibleIndex = listState.lastVisibleItemIndex for split in mergedItems.splits { @@ -435,6 +443,10 @@ struct ReverseList: UIViewControllerRepresentable { let containsInRange = split.indexRangeInParentItems.contains(firstVisibleIndex) if containsInRange { if lastVisibleIndex > (split.indexRangeInParentItems.upperBound - remaining) { + // situation when there is nothing to load from top + if self.representer.ignoreLoadingRequests.contains(where: { $0 == mergedItems.items.last?.oldest().item.id } ) { + return false + } return true } if firstVisibleIndex < (split.indexRangeInParentItems.lowerBound + remaining) { @@ -543,10 +555,14 @@ struct ReverseList: UIViewControllerRepresentable { } func preloadIfNeeded(_ state: ListState) { - if representer.scrollState == .atDestination, prevFirstVisible != state.firstVisibleItemIndex { + if representer.scrollState == .atDestination, prevFirstVisible != state.firstVisibleItemIndex, !preloading { logger.debug("LALAL LOADING before \(String(describing: self.representer.scrollState))") prevFirstVisible = state.firstVisibleItemIndex Task { + preloading = true + defer { + preloading = false + } let mergedItems = self.representer.mergedItems await preloadItems(mergedItems, self.representer.allowLoadMoreItems, state, self.representer.$ignoreLoadingRequests) { pagination in logger.debug("LALAL LOADING INSIDE")