mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-04 12:57:45 +00:00
android, desktop: pinned sender avatar follows member updates
This commit is contained in:
1 parent
95725f335f
commit
e951816fd0
2 files changed
+2
-7
No files matched your search
+1
-6
@@ -2513,7 +2513,7 @@ fun BoxScope.ChatItemsList(
|
||||
return mergedItems.value.items.getOrNull(index)?.let { index to it }
|
||||
}
|
||||
if (selectedChatItems.value != null) return@pinned
|
||||
val memberAtTop = remember { derivedStateOf(sameMemberPolicy) { topItem()?.let { (it.second.newest().item.chatDir as? CIDirection.GroupRcv)?.groupMember } } }
|
||||
val memberAtTop = remember { derivedStateOf { topItem()?.let { (it.second.newest().item.chatDir as? CIDirection.GroupRcv)?.groupMember } } }
|
||||
val member = memberAtTop.value ?: return@pinned
|
||||
Box(
|
||||
Modifier
|
||||
@@ -3862,11 +3862,6 @@ private fun shouldShowAvatar(current: ChatItem, older: ChatItem?): Boolean {
|
||||
}
|
||||
}
|
||||
|
||||
// messages of one sender can carry different snapshots of the member
|
||||
private val sameMemberPolicy = object : SnapshotMutationPolicy<GroupMember?> {
|
||||
override fun equivalent(a: GroupMember?, b: GroupMember?): Boolean = a?.memberId == b?.memberId
|
||||
}
|
||||
|
||||
// the list under the stand-in avatar still gets the drags and wheel events that scroll it
|
||||
private data object ShareTouchesWithList : ModifierNodeElement<Modifier.Node>() {
|
||||
override fun create(): Modifier.Node = object : Modifier.Node(), PointerInputModifierNode {
|
||||
|
||||
@@ -19,7 +19,7 @@ One rule on both platforms: the avatar's top is `max(naturalTop, min(belowTopBar
|
||||
### Android and desktop (Compose)
|
||||
|
||||
- **The run's first row moves its own avatar.** A layout modifier on the row's avatar reads its `coordinates` during placement. Compose re-runs such placement whenever an ancestor moves (`notifyChildrenUsingCoordinatesWhilePlacing`), so the avatar follows the scroll in the same frame, with no overlay and no extra state. The avatar draws outside its row; rows are not clipped and older rows are placed after newer ones, so it is drawn and hit-tested above the rows it covers. It stays clickable as before.
|
||||
- **A stand-in when that row is gone.** LazyColumn disposes the first row once it scrolls out, but the run can be taller than the screen. An overlay above the list draws the same avatar at the same position only while the run's first row is not in `visibleItemsInfo`, so exactly one of the two is drawn and the hand-off is seamless. It is composed per member (compared by `memberId`, since messages of one sender carry different member snapshots) in a composable lambda, which has its own restart scope, so its reads don't recompose the chat list; it is shown or hidden during placement, which runs in the same frame as the list.
|
||||
- **A stand-in when that row is gone.** LazyColumn disposes the first row once it scrolls out, but the run can be taller than the screen. An overlay above the list draws the same avatar at the same position only while the run's first row is not in `visibleItemsInfo`, so exactly one of the two is drawn and the hand-off is seamless. It is composed with the top message's member (compared structurally, so a member update such as blocking or a new picture is drawn and passed to member info, and only a hand-off between messages carrying different member snapshots recomposes it) in a composable lambda, which has its own restart scope, so its reads don't recompose the chat list; it is shown or hidden during placement, which runs in the same frame as the list.
|
||||
- **The stand-in is clickable without blocking scrolling.** A no-op pointer-input node with `sharePointerInputWithSiblings() = true` lets the list under it still receive drags and wheel events.
|
||||
- Run geometry (`senderRunBottom`) only walks visible items, so the per-frame cost does not depend on run length.
|
||||
|
||||
|
||||
Reference in new issue
Block a user