tracking positions correctly on scroll

This commit is contained in:
Diogo
2024-10-31 11:42:22 +00:00
parent 83ce1f5808
commit f87a4a43c9
2 changed files with 52 additions and 26 deletions
@@ -23,7 +23,8 @@ data class ChatSectionAreaBoundary (
data class ChatSection (
val items: MutableList<SectionItems>,
val area: ChatSectionArea,
val boundary: ChatSectionAreaBoundary
val boundary: ChatSectionAreaBoundary,
val itemPositions: MutableMap<Long, Int>
)
data class SectionItems (
@@ -31,7 +32,6 @@ data class SectionItems (
val items: MutableList<ChatItem>,
val revealed: Boolean,
val showAvatar: MutableSet<Long>,
val itemPositions: MutableMap<Long, Int>
)
data class ChatSectionLoad (
@@ -104,8 +104,7 @@ fun List<ChatItem>.putIntoSections(revealedItems: Set<Long>): List<ChatSection>
it.add(first.id)
}
}
},
itemPositions = mutableMapOf(first.id to 0),
}
)
} else {
return emptyList()
@@ -117,12 +116,14 @@ fun List<ChatItem>.putIntoSections(revealedItems: Set<Long>): List<ChatSection>
ChatSection(
items = mutableListOf(recent),
area = area,
boundary = ChatSectionAreaBoundary(minIndex = 0, maxIndex = 0, area = area)
boundary = ChatSectionAreaBoundary(minIndex = 0, maxIndex = 0, area = area),
itemPositions = mutableMapOf(recent.items[0].id to 0)
)
)
var prev = this[0]
var index = 0
var positionInList = 0;
while (index < size) {
if (index == 0) {
index++
@@ -133,6 +134,7 @@ fun List<ChatItem>.putIntoSections(revealedItems: Set<Long>): List<ChatSection>
val existingSection = sections.find { it.area == itemArea }
if (existingSection == null) {
positionInList++
val newSection = SectionItems(
mergeCategory = item.mergeCategory,
items = mutableListOf<ChatItem>().also { it.add(item) },
@@ -140,22 +142,30 @@ fun List<ChatItem>.putIntoSections(revealedItems: Set<Long>): List<ChatSection>
showAvatar = mutableSetOf<Long>().also {
it.add(item.id)
},
itemPositions = mutableMapOf(item.id to index),
)
sections.add(
ChatSection(items = mutableListOf(newSection), area = itemArea, boundary = ChatSectionAreaBoundary(minIndex = index, maxIndex = index, area = itemArea))
ChatSection(
items = mutableListOf(newSection),
area = itemArea,
boundary = ChatSectionAreaBoundary(minIndex = index, maxIndex = index, area = itemArea),
itemPositions = mutableMapOf(item.id to positionInList)
)
)
} else {
recent = existingSection.items.last()
val category = item.mergeCategory
if (recent.mergeCategory == category) {
if (category == null || recent.revealed || revealedItems.contains(item.id)) {
positionInList++
}
if (item.chatDir is CIDirection.GroupRcv && prev.chatDir is CIDirection.GroupRcv && item.chatDir.groupMember.memberId != (prev.chatDir as CIDirection.GroupRcv).groupMember.memberId) {
recent.showAvatar.add(item.id)
}
recent.items.add(item)
recent.itemPositions[item.id] = index
existingSection.itemPositions[item.id] = positionInList
} else {
positionInList++
val newSectionItems = SectionItems(
mergeCategory = item.mergeCategory,
items = mutableListOf<ChatItem>().also { it.add(item) },
@@ -165,8 +175,8 @@ fun List<ChatItem>.putIntoSections(revealedItems: Set<Long>): List<ChatSection>
it.add(item.id)
}
},
itemPositions = mutableMapOf(item.id to index),
)
existingSection.itemPositions[item.id] = positionInList
existingSection.items.add(newSectionItems)
}
existingSection.boundary.maxIndex = index
@@ -178,6 +188,17 @@ fun List<ChatItem>.putIntoSections(revealedItems: Set<Long>): List<ChatSection>
return sections
}
fun List<ChatSection>.chatItemPosition(chatItemId: Long): Int? {
for (section in this) {
val position = section.itemPositions[chatItemId]
if (position != null) {
return position
}
}
return null
}
fun List<ChatSection>.dropTemporarySections() {
val bottomSection = this.find { it.area == ChatSectionArea.Bottom }
if (bottomSection != null) {
@@ -1020,10 +1020,13 @@ fun BoxWithConstraintsScope.ChatItemsList(
.collect {
revealedItems.value = setOf()
preloadItemsEnabled.value = true
val firstUnreadItemIndex = reversedChatItems.indexOfLast { it.isRcvNew }
if (firstUnreadItemIndex != -1) {
listState.scrollToItem(scrollPosition(firstUnreadItemIndex), -maxHeightRounded)
val firstUnreadItem = chatModel.chatItems[firstUnreadItemIndex]
val firstUnreadItem = reversedChatItems.findLast { it.isRcvNew }
if (firstUnreadItem != null) {
val firstUnreadItemIndexIdx = sections.find { it.area == ChatSectionArea.Current }?.itemPositions?.get(firstUnreadItem.id)
if (firstUnreadItemIndexIdx != null) {
listState.scrollToItem(scrollPosition(firstUnreadItemIndexIdx), -maxHeightRounded)
}
if (chatModel.chatItemsSectionArea[firstUnreadItem.id] != ChatSectionArea.Bottom) {
withBGApi {
val chat = chatController.apiGetChat(rh = remoteHostId, type = chatInfo.chatType, id = chatInfo.apiId)
@@ -1052,10 +1055,10 @@ fun BoxWithConstraintsScope.ChatItemsList(
}
val scrollToItem: (Long) -> Unit = { itemId: Long ->
val index = reversedChatItems.indexOfFirst { it.id == itemId }
val index = sections.chatItemPosition(itemId)
preloadItemsEnabled.value = false
if (index != -1) {
if (index != null) {
scope.launch {
listState.animateScrollToItem(scrollPosition(index), -maxHeightRounded)
preloadItemsEnabled.value = true
@@ -1072,18 +1075,20 @@ fun BoxWithConstraintsScope.ChatItemsList(
}
val chatSectionLoad = ChatSectionLoad(0, ChatSectionArea.Destination)
apiLoadMessagesAroundItem(rhId = remoteHostId, chatModel = chatModel, chatInfo = chatInfo, aroundItemId = itemId, chatSectionLoad = chatSectionLoad)
val idx = reversedChatItems.indexOfFirst { it.id == itemId }
scope.launch {
listState.animateScrollToItem(scrollPosition(idx), -maxHeightRounded)
withContext(Dispatchers.Main) {
if (!itemsToDrop.isNullOrEmpty()) {
itemsToDrop.forEach {
chatModel.chatItemsSectionArea.remove(it.id)
val idx = sections.chatItemPosition(itemId)
if (idx != null) {
listState.animateScrollToItem(scrollPosition(idx), -maxHeightRounded)
withContext(Dispatchers.Main) {
if (!itemsToDrop.isNullOrEmpty()) {
itemsToDrop.forEach {
chatModel.chatItemsSectionArea.remove(it.id)
}
chatModel.chatItems.value.removeIf { chatModel.chatItemsSectionArea[it.id] == null }
val newIdx = reversedChatItems.indexOfFirst { it.id == itemId }
listState.scrollToItem(scrollPosition(newIdx), -maxHeightRounded)
}
chatModel.chatItems.value.removeIf { chatModel.chatItemsSectionArea[it.id] == null }
val newIdx = reversedChatItems.indexOfFirst { it.id == itemId }
listState.scrollToItem(scrollPosition(newIdx), -maxHeightRounded)
}
}
preloadItemsEnabled.value = true
@@ -1364,7 +1369,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
// index here is just temporary, should be removed at all or put in the section items
val prevItem = area.getPreviousShownItem(sIdx, i)
val nextItem = area.getNextShownItem(sIdx, i)
ChatViewListItem(section.itemPositions[cItem.id] ?: -1, section, cItem, prevItem, nextItem)
ChatViewListItem(area.itemPositions[cItem.id] ?: -1, section, cItem, prevItem, nextItem)
}
} else {
val item = section.items.first()
@@ -1372,7 +1377,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
// here you make one collapsed item from multiple items (should be already in section items)
val prevItem = area.getPreviousShownItem(sIdx, section.items.lastIndex)
val nextItem = area.getNextShownItem(sIdx, section.items.lastIndex)
ChatViewListItem(section.itemPositions[item.id] ?: -1, section, item, prevItem, nextItem)
ChatViewListItem(area.itemPositions[item.id] ?: -1, section, item, prevItem, nextItem)
}
}
}