diff --git a/apps/ios/Shared/Views/Chat/ChatItemsLoader.swift b/apps/ios/Shared/Views/Chat/ChatItemsLoader.swift index 61f5d45043..3dabc4e14c 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemsLoader.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemsLoader.swift @@ -62,23 +62,29 @@ func apiLoadMessages( chatState.unreadAfterNewestLoaded = navInfo.afterUnread } case let .before(paginationChatItemId, _): + logger.debug("LALALA 0") newItems.append(contentsOf: oldItems) let indexInCurrentItems = oldItems.firstIndex(where: { $0.id == paginationChatItemId }) guard let indexInCurrentItems else { return } let (newIds, _) = mapItemsToIds(chat.chatItems) + logger.debug("LALALA 1") let wasSize = newItems.count let visibleItemIndexes = await MainActor.run { visibleItemIndexesNonReversed() } + logger.debug("LALALA 2") let modifiedSplits = removeDuplicatesAndModifySplitsOnBeforePagination( unreadAfterItemId, &newItems, newIds, chatState.splits, visibleItemIndexes ) + logger.debug("LALALA 3") let insertAt = max((indexInCurrentItems - (wasSize - newItems.count) + modifiedSplits.trimmedIds.count), 0) newItems.insert(contentsOf: chat.chatItems, at: insertAt) + logger.debug("LALALA 4") let newReversed: [ChatItem] = newItems.reversed() await MainActor.run { ItemsModel.shared.reversedChatItems = newReversed chatState.splits = modifiedSplits.newSplits chatState.moveUnreadAfterItem(modifiedSplits.oldUnreadSplitIndex, modifiedSplits.newUnreadSplitIndex, oldItems) } + logger.debug("LALALA 5") case let .after(paginationChatItemId, _): newItems.append(contentsOf: oldItems) let indexInCurrentItems = oldItems.firstIndex(where: { $0.id == paginationChatItemId }) diff --git a/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift b/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift index 00403a4766..27cd8f12f1 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift @@ -49,7 +49,7 @@ struct MergedItems { } let revealed = item.mergeCategory == nil || revealedItems.contains(item.id) - if recent != nil, case let .grouped(items, _, _, _, mergeCategory, unreadIds, _) = recent, mergeCategory == category, let first = items.boxedValue.first, !revealedItems.contains(first.item.id) && !itemIsSplit { + if recent != nil, case let .grouped(items, _, _, _, mergeCategory, unreadIds, _, _) = recent, mergeCategory == category, let first = items.boxedValue.first, !revealedItems.contains(first.item.id) && !itemIsSplit { let listItem = ListItem(item: item, prevItem: prev, nextItem: next, unreadBefore: unreadBefore) items.boxedValue.append(listItem) @@ -79,13 +79,15 @@ struct MergedItems { rangeInReversed: lastRangeInReversedForMergedItems!, mergeCategory: item.mergeCategory, unreadIds: BoxedValue(item.isRcvNew ? Set(arrayLiteral: item.id) : Set()), - startIndexInReversedItems: index + startIndexInReversedItems: index, + hash: listItem.genHash() ) } else { lastRangeInReversedForMergedItems = nil recent = MergedItem.single( item: listItem, - startIndexInReversedItems: index + startIndexInReversedItems: index, + hash: listItem.genHash() ) } mergedItems.append(recent!) @@ -117,19 +119,25 @@ struct MergedItems { enum MergedItem: Hashable, Equatable { // equatable and hashable implementations allows to NSDiffableDataSourceSnapshot to see the difference and correcrly scroll items we want. Without any of it, the scroll position will be random in ReverseList static func == (lhs: Self, rhs: Self) -> Bool { - lhs.hashValue == rhs.hashValue + lhs.hash == rhs.hash } - var hashValue: Int { self.newest().item.hashValue } - func hash(into hasher: inout Hasher) { - hasher.combine("\(self.newest().item)") + hasher.combine(hash) + } + + var hash: String { + switch self { + case .single(_, _, let hash): hash + case .grouped(_, _, _, _, _, _, _, let hash): hash + } } // the item that is always single, cannot be grouped and always revealed case single( item: ListItem, - startIndexInReversedItems: Int + startIndexInReversedItems: Int, + hash: String ) /** The item that can contain multiple items or just one depending on revealed state. When the whole group of merged items is revealed, @@ -146,11 +154,12 @@ enum MergedItem: Hashable, Equatable { rangeInReversed: BoxedValue>, mergeCategory: CIMergeCategory?, unreadIds: BoxedValue>, - startIndexInReversedItems: Int + startIndexInReversedItems: Int, + hash: String ) func reveal(_ reveal: Bool, _ revealedItems: Binding>) { - if case .grouped(let items, _, let revealedIdsWithinGroup, _, _, _, _) = self { + if case .grouped(let items, _, let revealedIdsWithinGroup, _, _, _, _, _) = self { var newRevealed = revealedItems.wrappedValue var i = 0 if reveal { @@ -172,37 +181,37 @@ enum MergedItem: Hashable, Equatable { var startIndexInReversedItems: Int { get { switch self { - case let .single(_, startIndexInReversedItems): startIndexInReversedItems - case let .grouped(_, _, _, _, _, _, startIndexInReversedItems): startIndexInReversedItems + case let .single(_, startIndexInReversedItems, _): startIndexInReversedItems + case let .grouped(_, _, _, _, _, _, startIndexInReversedItems, _): startIndexInReversedItems } } } func hasUnread() -> Bool { switch self { - case let .single(item, _): item.item.isRcvNew - case let .grouped(_, _, _, _, _, unreadIds, _): !unreadIds.boxedValue.isEmpty + case let .single(item, _, _): item.item.isRcvNew + case let .grouped(_, _, _, _, _, unreadIds, _, _): !unreadIds.boxedValue.isEmpty } } func newest() -> ListItem { switch self { - case let .single(item, _): item - case let .grouped(items, _, _, _, _, _, _): items.boxedValue[0] + case let .single(item, _, _): item + case let .grouped(items, _, _, _, _, _, _, _): items.boxedValue[0] } } func oldest() -> ListItem { switch self { - case let .single(item, _): item - case let .grouped(items, _, _, _, _, _, _): items.boxedValue[items.boxedValue.count - 1] + case let .single(item, _, _): item + case let .grouped(items, _, _, _, _, _, _, _): items.boxedValue[items.boxedValue.count - 1] } } func lastIndexInReversed() -> Int { switch self { case .single: startIndexInReversedItems - case let .grouped(items, _, _, _, _, _, _): startIndexInReversedItems + items.boxedValue.count - 1 + case let .grouped(items, _, _, _, _, _, _, _): startIndexInReversedItems + items.boxedValue.count - 1 } } } @@ -223,6 +232,10 @@ struct ListItem: Hashable { let nextItem: ChatItem? // how many unread items before (older than) this one (excluding this one) let unreadBefore: Int + + func genHash() -> String { + "\(item.meta.itemId) \(item.meta.updatedAt.hashValue) \(item.reactions.count) \(item.meta.isRcvNew) \(unreadBefore)" + } } class ActiveChatState { @@ -288,13 +301,12 @@ class BoxedValue: Equatable, Hashable { extension ReverseList.Controller { @MainActor - func visibleItemIndexesNonReversed(_ mergedItems: Binding) -> ClosedRange { + func visibleItemIndexesNonReversed(_ items: [MergedItem]) -> ClosedRange { let zero = 0 ... 0 - if mergedItems.wrappedValue.items.count == 0 { + if items.isEmpty { return zero } - let listState = getListState(mergedItems.wrappedValue.items) ?? ListState() - let items = mergedItems.wrappedValue.items + let listState = getListState(items) ?? ListState() let newest = items.count > listState.firstVisibleItemIndex ? items[listState.firstVisibleItemIndex].startIndexInReversedItems : nil let oldest = items.count > listState.firstVisibleItemIndex ? items[listState.lastVisibleItemIndex].lastIndexInReversed() : nil guard let newest, let oldest else { diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 01c0a3797c..ce3fd5d506 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -447,8 +447,8 @@ struct ChatView: View { // LALAL CAN I CHANGE BINDING LIKE THIS IN ignoreLoadingRequests? ReverseList(mergedItems: $mergedItems, revealedItems: $revealedItems, unreadCount: Binding.constant(chat.chatStats.unreadCount), scrollState: $scrollModel.state, loadingMoreItems: $loadingMoreItems, allowLoadMoreItems: $allowLoadMoreItems, 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 + case let .single(item, _, _): item.item + case let .grouped(items, _, _, _, _, _, _, _): items.boxedValue.last!.item } let voiceNoFrame = voiceWithoutFrame(ci) let maxWidth = cInfo.chatType == .group @@ -675,7 +675,7 @@ struct ChatView: View { .onTapGesture { scrollModel.scrollToBottom() } } } - .if(loadingMoreItems) { $0.hidden() } + .disabled(loadingMoreItems) .padding() .frame(maxWidth: .infinity, alignment: .trailing) } @@ -1044,7 +1044,7 @@ struct ChatView: View { let last = isLastItem ? im.reversedChatItems.last : nil let listItem = merged.newest() let item = listItem.item - let range: ClosedRange? = if case let .grouped(_, _, _, rangeInReversed, _, _, _) = merged { + let range: ClosedRange? = if case let .grouped(_, _, _, rangeInReversed, _, _, _, _) = merged { rangeInReversed.boxedValue } else { nil diff --git a/apps/ios/Shared/Views/Chat/ReverseList.swift b/apps/ios/Shared/Views/Chat/ReverseList.swift index 26f9c15f08..8d8cb40111 100644 --- a/apps/ios/Shared/Views/Chat/ReverseList.swift +++ b/apps/ios/Shared/Views/Chat/ReverseList.swift @@ -133,6 +133,7 @@ struct ReverseList: UIViewControllerRepresentable { cell.transform = .verticalFlip cell.selectionStyle = .none cell.backgroundColor = .clear + _ = logger.debug("LALAL ASK FOR CELL \(indexPath.row)") return cell } @@ -255,11 +256,14 @@ struct ReverseList: UIViewControllerRepresentable { } func update(_ items: [MergedItem]) { + logger.debug("LALAL STEP 0 \(items.count)") let prevSnapshot = dataSource.snapshot() + logger.debug("LALAL STEP 1") let wasCount = prevSnapshot.numberOfItems let willBeCount = items.count let c = 1 let insertedSeveralNewestItems = wasCount != 0 && willBeCount - wasCount == c && prevSnapshot.itemIdentifiers.first!.hashValue == items[c].hashValue + logger.debug("LALAL STEP 2") logger.debug("LALAL WAS \(wasCount) will be \(items.count)") if items == prevSnapshot.itemIdentifiers { logger.debug("LALAL SAME ITEMS, not rebuilding the tableview") @@ -270,7 +274,7 @@ struct ReverseList: UIViewControllerRepresentable { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) snapshot.appendItems(items) - + logger.debug("LALAL STEP 3") dataSource.defaultRowAnimation = .none let wasContentHeight = tableView.contentSize.height @@ -279,6 +283,7 @@ struct ReverseList: UIViewControllerRepresentable { let wasFirstVisibleRow = listState?.firstVisibleItemIndex ?? 0//tableView.indexPathsForVisibleRows?.first?.row ?? 0 let wasFirstVisibleOffset = listState?.firstVisibleItemOffset ?? 0 // Sets content offset on initial load + logger.debug("LALAL STEP 4") if wasCount == 0 { dataSource.apply( snapshot, @@ -292,6 +297,7 @@ struct ReverseList: UIViewControllerRepresentable { animated: false ) } + logger.debug("LALAL STEP 5 0") } else if wasCount != snapshot.numberOfItems { logger.debug("LALAL drag \(self.tableView.isDragging), decel \(self.tableView.isDecelerating)") if useSmoothScrolling && tableView.isDecelerating { @@ -303,6 +309,7 @@ struct ReverseList: UIViewControllerRepresentable { ) tableView.endUpdates() tableView.panGestureRecognizer.isEnabled = true + logger.debug("LALAL STEP 5 1") } else { // remember current translation var translationToApply: CGPoint? = nil @@ -354,9 +361,17 @@ struct ReverseList: UIViewControllerRepresentable { self.tableView.setContentOffset(CGPointMake(o.x + t.x, o.y + t.y), animated: true) } } + logger.debug("LALAL STEP 5 2") } + } else { + dataSource.apply( + snapshot, + animatingDifferences: false + ) + logger.debug("LALAL STEP 5 3") } updateFloatingButtons.send() + logger.debug("LALAL STEP 6") } override func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -479,7 +494,7 @@ struct ReverseList: UIViewControllerRepresentable { if index == nil { let pagination = ChatPagination.around(chatItemId: itemId, count: ChatPagination.PRELOAD_COUNT * 2) let oldSize = ItemsModel.shared.reversedChatItems.count - _ = await self.representer.loadItems(true, pagination, { self.visibleItemIndexesNonReversed(Binding.constant(self.representer.mergedItems))}) + _ = await self.representer.loadItems(true, pagination, { self.visibleItemIndexesNonReversed(self.representer.mergedItems.items)}) var repeatsLeft = 50 while oldSize == ItemsModel.shared.reversedChatItems.count && repeatsLeft > 0 { try await Task.sleep(nanoseconds: 20_000000) @@ -511,9 +526,10 @@ struct ReverseList: UIViewControllerRepresentable { logger.debug("LALAL LOADING before \(String(describing: self.representer.scrollState))") prevFirstVisible = state.firstVisibleItemIndex Task { - await preloadItems(self.representer.mergedItems, self.representer.allowLoadMoreItems, state, self.representer.$ignoreLoadingRequests) { pagination in + let mergedItems = self.representer.mergedItems + await preloadItems(mergedItems, self.representer.allowLoadMoreItems, state, self.representer.$ignoreLoadingRequests) { pagination in logger.debug("LALAL LOADING INSIDE") - let triedToLoad = await self.representer.loadItems(false, pagination, { self.visibleItemIndexesNonReversed(Binding.constant(self.representer.mergedItems)) }) + let triedToLoad = await self.representer.loadItems(false, pagination, { self.visibleItemIndexesNonReversed(mergedItems.items) }) return triedToLoad } }