mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-02 09:04:44 +00:00
performance
This commit is contained in:
@@ -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 })
|
||||
|
||||
@@ -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<ClosedRange<Int>>,
|
||||
mergeCategory: CIMergeCategory?,
|
||||
unreadIds: BoxedValue<Set<Int64>>,
|
||||
startIndexInReversedItems: Int
|
||||
startIndexInReversedItems: Int,
|
||||
hash: String
|
||||
)
|
||||
|
||||
func reveal(_ reveal: Bool, _ revealedItems: Binding<Set<Int64>>) {
|
||||
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<T: Hashable>: Equatable, Hashable {
|
||||
|
||||
extension ReverseList.Controller {
|
||||
@MainActor
|
||||
func visibleItemIndexesNonReversed(_ mergedItems: Binding<MergedItems>) -> ClosedRange<Int> {
|
||||
func visibleItemIndexesNonReversed(_ items: [MergedItem]) -> ClosedRange<Int> {
|
||||
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 {
|
||||
|
||||
@@ -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<Int>? = if case let .grouped(_, _, _, rangeInReversed, _, _, _) = merged {
|
||||
let range: ClosedRange<Int>? = if case let .grouped(_, _, _, rangeInReversed, _, _, _, _) = merged {
|
||||
rangeInReversed.boxedValue
|
||||
} else {
|
||||
nil
|
||||
|
||||
@@ -133,6 +133,7 @@ struct ReverseList<Content: View>: 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<Content: View>: 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<Content: View>: UIViewControllerRepresentable {
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, MergedItem>()
|
||||
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<Content: View>: 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<Content: View>: 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<Content: View>: 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<Content: View>: 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<Content: View>: 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<Content: View>: 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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user