Skip to content

πŸ”„οΈ Improve unread marker display - #6484

Merged
AndyScherzinger merged 9 commits into
masterfrom
feat/noid/msgUnreadMarker
Aug 18, 2026
Merged

πŸ”„οΈ Improve unread marker display#6484
AndyScherzinger merged 9 commits into
masterfrom
feat/noid/msgUnreadMarker

Conversation

@AndyScherzinger

@AndyScherzinger AndyScherzinger commented Aug 12, 2026

Copy link
Copy Markdown
Member

When entering a conversation, especially when using #6454 the read-state is not always correct / read / propagated.

This PR addresses this, implementing read-status propagation

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • πŸ”– Capability is checked or not needed
  • πŸ”™ Backport requests are created or not needed: /backport to stable-xx.x
  • πŸ“… Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

πŸ€– AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@AndyScherzinger AndyScherzinger added enhancement New feature or request 2. developing Work in progress labels Aug 12, 2026
@AndyScherzinger
AndyScherzinger force-pushed the feat/noid/msgPreFetching branch from f978b99 to 1a1f664 Compare August 13, 2026 15:07
@AndyScherzinger
AndyScherzinger force-pushed the feat/noid/msgUnreadMarker branch from 74641aa to f0bc26d Compare August 13, 2026 21:42
@AndyScherzinger
AndyScherzinger force-pushed the feat/noid/msgPreFetching branch from 1a1f664 to 3f6a24d Compare August 13, 2026 21:43
@AndyScherzinger
AndyScherzinger force-pushed the feat/noid/msgUnreadMarker branch from f0bc26d to a137536 Compare August 13, 2026 21:43
@AndyScherzinger AndyScherzinger added 3. to review Waiting for reviews AI assisted and removed 2. developing Work in progress labels Aug 13, 2026
@AndyScherzinger AndyScherzinger added this to the 25.0.0 milestone Aug 13, 2026
@AndyScherzinger
AndyScherzinger requested a review from mahibi August 13, 2026 21:47
@AndyScherzinger
AndyScherzinger marked this pull request as ready for review August 13, 2026 21:47
@AndyScherzinger AndyScherzinger changed the title Improve unread marker πŸ”„οΈ Improve unread marker Aug 14, 2026
@AndyScherzinger AndyScherzinger changed the title πŸ”„οΈ Improve unread marker πŸ”„οΈ Improve unread marker display Aug 14, 2026
Base automatically changed from feat/noid/msgPreFetching to master August 14, 2026 15:44
@AndyScherzinger
AndyScherzinger force-pushed the feat/noid/msgUnreadMarker branch from a137536 to 8c9e228 Compare August 14, 2026 15:47
@github-actions

Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/31816236588/artifacts/9225804811
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@AndyScherzinger
AndyScherzinger force-pushed the feat/noid/msgUnreadMarker branch from 8c9e228 to 4f75c5e Compare August 14, 2026 17:22
@github-actions

Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/31823716011/artifacts/9228623568
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@mahibi
mahibi force-pushed the feat/noid/msgUnreadMarker branch from 4f75c5e to 4711f95 Compare August 18, 2026 10:14
@github-actions

Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/32125764257/artifacts/9323067893
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@mahibi

mahibi commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Reviewed and tested, looks good @AndyScherzinger πŸ‘

I added 3 commits. If these make sense to you, it can be merged.

@github-actions

Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/32150518162/artifacts/9334362137
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

The unread marker can only be placed correctly when the chat's visible
window - the latest chat block - reaches back to the last read message.
For a room without any cached chat block the catch-up fetched the newest
page, so any backlog larger than one page left a chat block floating
entirely above the unread boundary and the marker ended up pinned to the
oldest message of that block, in the middle of the unread messages.

When the unread backlog is at least one page but still closable within
the backlog rounds, anchor the initial fetch at lastReadMessage
(including the last read message itself) and close the rest of the
backlog from there, so the created chat block contains the boundary.
Smaller backlogs fit into the newest page anyway and oversized backlogs
could not be closed either way, so both keep the newest-messages fetch.

The room list prefetch passes the conversation's lastReadMessage and
unread count along, so background-prefetched rooms open with a window
the marker can be placed in.

Assisted-by: Claude Code:claude-fable-5
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
The initial load decided the cache was usable when the newest cached
message id was at least the conversation's lastReadMessage. That check
compares against the top of the latest chat block, not its extent: a
block created by a capped newest-messages fetch (e.g. the background
prefetch) floats entirely above the unread boundary and trivially
passes, so the open path only closed the backlog above the block and
the unread marker stayed pinned to the block's oldest message, in the
middle of the unread messages.

Decide on containment instead: the cache is only trusted when the
latest chat block reaches back to lastReadMessage (or has no history
below it). Otherwise the initial window is re-fetched via
initialCatchUp, anchored at the unread boundary when the backlog is
large.

This also changes the handling of a cache whose latest block lies
completely below lastReadMessage (read further on another device):
instead of fetching the newest page into a floating block, the backlog
is closed from the newest cached message, keeping the window contiguous
across the boundary.

Assisted-by: Claude Code:claude-fable-5
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
buildChatItems latched firstUnreadMessageId on the first emission as
"the first visible message newer than lastReadMessage". When the
visible window did not reach back to the last read message - all
visible messages unread - this resolved to the window's oldest message,
far above the true boundary, and the cached value kept the marker there
even after scrolling up loaded the older unread messages.

Only latch the marker when a message at or below lastReadMessage is
visible, proving the boundary is inside the window. Until then no
marker is shown instead of a wrong one. Temporary messages have
negative ids and don't count as proof.

Assisted-by: Claude Code:claude-fable-5
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
The catch-up triggered by a push notification fetched the newest page
for rooms without any cached chat block, so a large unread backlog left
a chat block floating above the unread boundary β€” the same window shape
that misplaces the unread marker on chat open.

Look up the pushed room in the local conversations cache and pass its
lastReadMessage and unread count to catchUpRoom, so the initial fetch
is anchored at the boundary when the backlog calls for it. The boundary
is room-level state and therefore not passed for thread catch-ups.

Assisted-by: Claude Code:claude-fable-5
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Covers loadInitialMessages' choice between trusting the cached window
and re-fetching it: the backlog is closed from the newest cached message
when the latest chat block reaches the last read message (or has no
history below it), while a block floating above the boundary and an
empty cache are repaired with a fetch anchored at lastReadMessage for
large backlogs and a newest-messages fetch for small ones.

Assisted-by: Claude Code:claude-fable-5
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
AndyScherzinger and others added 2 commits August 18, 2026 20:13
Extracts the latch condition from buildChatItems into the pure
findFirstUnreadMessageId (no behavior change) and covers it: the marker
position is only derived when a message at or below lastReadMessage is
visible, windows floating entirely above the boundary and temporary
messages with negative ids yield no marker, and a deleted or expired
boundary message is handled by any older visible message.

Assisted-by: Claude Code:claude-fable-5
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
The check only guards against a block whose oldest message skipped past
lastReadMessage entirely β€” it doesn't verify the block's newest message
actually reaches that far, so a stale-but-not-floating block also passes
it. blockNotFloatingAboveLastReadMessage describes what's actually being tested.

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
@AndyScherzinger
AndyScherzinger force-pushed the feat/noid/msgUnreadMarker branch from e5ec21f to 6ee450c Compare August 18, 2026 18:24
mahibi added 2 commits August 18, 2026 20:27
fetchNewMessages defaulted to fromMessageId=0 when no HTTP sync had
happened yet for the conversation, so the insurance request tried to
close the backlog from the very start of the room's history instead of
skipping until there is something to verify against. For a busy room
this can never close within tryCloseBacklog's round budget, burning
five requests before falling back anyway.

Assisted-by: Claude Code:claude-sonnet-5
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This also calms down the loading bar.

Fix 1:
When entering a chat, in onResume in ChatViewModel,
fetchNewMessagesWithRetry was called.
This is too aggressive pulling and should only be used when waiting for uploads to be finished.
It is also not satisfied when the response is empty and will try again.
UX wise this caused that the loading bar was shown for a long time.

fetchNewMessagesWithRetry was replaced with chatRepository.fetchNewMessages() which does a single fetch which is enough in this case.

Fix 2:
As onResume is executed on every opening of a chat, it added unnecessary requests on top of the loadInitialMessages handling. As a guard to only execute it when coming back from background, isReturningFromBackground was introduced.
In this way the scenario from #6313 is still fulfilled but too many requests on startup are avoided.

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
@AndyScherzinger
AndyScherzinger force-pushed the feat/noid/msgUnreadMarker branch from 6ee450c to 20790c7 Compare August 18, 2026 18:30
@AndyScherzinger

AndyScherzinger commented Aug 18, 2026

Copy link
Copy Markdown
Member Author

Nice improvements @mahibi πŸ‘

All great - will merge after CI did its thing πŸš€

@github-actions

Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/32171372510/artifacts/9339772535
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@AndyScherzinger
AndyScherzinger merged commit ca0aa85 into master Aug 18, 2026
18 of 24 checks passed
@AndyScherzinger
AndyScherzinger deleted the feat/noid/msgUnreadMarker branch August 18, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants