Skip to content

fix(chat): keep X-Chat-Last-Common-Read from the read marker response - #6846

Open
ToteMeiSter wants to merge 2 commits into
nextcloud:masterfrom
ToteMeiSter:fix/last-common-read-from-read-response
Open

ToteMeiSter wants to merge 2 commits into
nextcloud:masterfrom
ToteMeiSter:fix/last-common-read-from-read-response

Conversation

@ToteMeiSter

@ToteMeiSter ToteMeiSter commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The response to POST chat/<token>/read carries the last message read by all participants in the X-Chat-Last-Common-Read header. ReadMarkerSyncWorker dropped it (setChatReadMarker returned only the body), so the two check marks of the user's own messages stayed behind until the next chat sync or room list refresh.

Change

  • setChatReadMarker returns the whole Response<GenericOverall>,
  • ReadMarkerSyncWorker reads the header (lastCommonReadOf) and stores it in the conversation entry via ConversationListUpdater.updateLastCommonRead (new ConversationsDao.updateLastCommonRead); the open chat already combines conversation.lastCommonReadMessage into its status icons,
  • because a non-2xx answer is no longer an Rx error, the worker and MarkAsReadReceiver check isSuccessful explicitly (a failed answer still retries/does not cancel the notification, as before).

How to test

  • Unit: ./gradlew :app:testGenericDebugUnitTest --tests "com.nextcloud.talk.jobs.ReadMarkerSyncWorkerTest" (header parsing, missing/broken header, value reaches the DAO).
  • Manual: A and B in one conversation, B reads the chat: A sees two check marks without reloading the room list.

The worker's doWork() itself is not unit tested (WorkManager injection).

🏁 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

🤖 AI (if applicable)

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

The change and the tests were prepared with AI assistance (Claude Code, claude-sonnet-5-5).

@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

📝 Walkthrough

Walkthrough

The read-marker API and network source now return Retrofit HTTP responses. The notification receiver cancels notifications only after successful responses. The sync worker checks response status, parses the X-Chat-Last-Common-Read header, and stores valid integer values. Conversation refreshes record request times and coordinate local-state preservation and database writes with a mutex. Tests cover response handling, common-read updates, and refresh races.


Priority: ➖ Normal

Merge Risk: 🔵 Low · up to e9055

Check marks may briefly regress when refresh responses arrive out of order. The issue is localized and recoverable, but the ordering fix should be considered before merging.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 15.71% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 70 functions across 15 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check Passed The title clearly summarizes the main change: preserving the X-Chat-Last-Common-Read header from the read-marker response.
Description check Passed The description explains the problem, implementation, testing, manual validation, and AI involvement. It omits the optional screenshots and TODO sections, and the backport and milestone checklist item…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.

  • Fix all pre-merge checks with AI
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/src/test/java/com/nextcloud/talk/jobs/ReadMarkerSyncWorkerTest.kt (1)

44-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a worker-level success-path test.

ReadMarkerSyncWorkerTest tests header parsing and calls ConversationListUpdater directly. It does not execute ReadMarkerSyncWorker or verify that a successful response calls updateLastCommonRead("$userId@$roomToken", value). A regression that removes the worker call can therefore pass the current tests while the user's own-message check marks remain stale. Add a focused worker test, or a test seam that still exercises this worker-to-updater call.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 302bbf35-93ce-4a9a-95f6-b82e39493d93
📥 Commits

Reviewing files that changed from the base of the PR and between acc10f9 and ac50daf.

📒 Files selected for processing (9)
  • app/src/main/java/com/nextcloud/talk/api/NcApi.java
  • app/src/main/java/com/nextcloud/talk/chat/data/network/ChatNetworkDataSource.kt
  • app/src/main/java/com/nextcloud/talk/chat/data/network/RetrofitChatNetwork.kt
  • app/src/main/java/com/nextcloud/talk/conversationlist/data/network/ConversationListUpdater.kt
  • app/src/main/java/com/nextcloud/talk/data/database/dao/ConversationsDao.kt
  • app/src/main/java/com/nextcloud/talk/jobs/ReadMarkerSyncWorker.kt
  • app/src/main/java/com/nextcloud/talk/receivers/MarkAsReadReceiver.kt
  • app/src/main/java/com/nextcloud/talk/utils/preview/ComposePreviewUtilsDaos.kt
  • app/src/test/java/com/nextcloud/talk/jobs/ReadMarkerSyncWorkerTest.kt

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.

Comment thread app/src/main/java/com/nextcloud/talk/jobs/ReadMarkerSyncWorker.kt Outdated
@ToteMeiSter

Copy link
Copy Markdown
Contributor Author

Review: #6846 (review)

@coderabbitai Addressed in e9055e7: ReadMarkerSyncWorkerTest now runs the worker itself (TestListenableWorkerBuilder under Robolectric, the same way ConversationsSyncWorkerTest does it). It checks that a successful response calls updateLastCommonRead("<userId>@<roomToken>", value), that a response without the header stores nothing but still counts as delivered, that an HTTP 500 stores nothing and retries, and that a failing database write does not resend the delivered marker. The former updater-to-DAO test lives in ConversationListUpdaterCommonReadTest.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b2bb1429-9bf0-4765-b1b4-506d25b2dfb5
📥 Commits

Reviewing files that changed from the base of the PR and between ac50daf and e9055e7.

📒 Files selected for processing (10)
  • app/src/main/java/com/nextcloud/talk/conversationlist/data/network/ConversationListUpdater.kt
  • app/src/main/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepository.kt
  • app/src/main/java/com/nextcloud/talk/data/database/dao/ConversationsDao.kt
  • app/src/main/java/com/nextcloud/talk/jobs/ReadMarkerSyncWorker.kt
  • app/src/test/java/com/nextcloud/talk/conversationlist/data/network/ConversationListFreshnessIntegrationTest.kt
  • app/src/test/java/com/nextcloud/talk/conversationlist/data/network/ConversationListUpdaterCommonReadTest.kt
  • app/src/test/java/com/nextcloud/talk/conversationlist/data/network/ConversationListUpdaterPinTest.kt
  • app/src/test/java/com/nextcloud/talk/conversationlist/data/network/ConversationListUpdaterUnreadTest.kt
  • app/src/test/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepositoryTest.kt
  • app/src/test/java/com/nextcloud/talk/jobs/ReadMarkerSyncWorkerTest.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/com/nextcloud/talk/data/database/dao/ConversationsDao.kt
  • app/src/main/java/com/nextcloud/talk/conversationlist/data/network/ConversationListUpdater.kt

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

@coderabbitai

coderabbitai Bot commented Oct 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
for file in \
  app/src/test/java/com/nextcloud/talk/jobs/ReadMarkerSyncWorkerTest.kt \
  app/src/test/java/com/nextcloud/talk/conversationlist/data/network/ConversationListUpdaterCommonReadTest.kt
do
  ast-grep outline "$file" --items all
  cat -n "$file"
done

Length of output: 16428


@ToteMeiSter Thanks for the update. I verified that ReadMarkerSyncWorkerTest builds the worker under Robolectric and calls send(). It covers the updater call, success without a header, retry on HTTP 500, and success after a database write failure.

ConversationListUpdaterCommonReadTest separately verifies the updater-to-DAO call. This addresses the test-coverage concern. I inspected the tests but did not run them.


🧠 Learnings used
Learnt from: ToteMeiSter
Repo: nextcloud/talk-android

Timestamp: 2026-10-11T14:26:47.595Z
Learning: In Nextcloud Talk Android, app/src/main/java/com/nextcloud/talk/jobs/ReadMarkerSyncWorker.kt must not retry a successfully delivered read-marker POST when storing X-Chat-Last-Common-Read fails. This storage write is an optimization; the next room-list sync supplies the value. Log storage failures and return Result.success(), but rethrow CancellationException to preserve coroutine cancellation.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

The answer to POST chat/<token>/read carries the last message read by all
participants in the X-Chat-Last-Common-Read header. The read marker worker
dropped it, so the read check marks of the own messages stayed behind until
the next chat sync or room list refresh.

Return the whole response from setChatReadMarker, write the header value to
the conversation entry and let the open chat pick it up from there. A non-2xx
answer is now treated as a failed attempt explicitly, as the response is no
longer thrown as an error.

Assisted-by: Claude-Code:claude-sonnet-5-5
Signed-off-by: Krainov Gleb <krajnov.g@kontentplus.ru>
A room list response requested before the read marker answer reached the
client can still carry an older X-Chat-Last-Common-Read value, and applying it
after the answer lowered the read check marks again until the next sync. The
read marker pending guard cannot catch this, it only covers lastReadMessage.

Remember when the answer arrived and give preservePendingLocalState the start
time of the request that produced the response: a response requested before
that moment cannot lower the value, one requested after it applies unchanged
and ends the guard. A sync holds a lock from its merge through its write and
the answer takes the same lock, so an answer cannot slip in between the two
and be overwritten. The value written from an answer may be lower than the
stored one, as marking a chat as unread lowers it on the server.

A failure while storing the value no longer leaves the worker, the marker has
been delivered and must not be sent again. The worker test now runs the worker.

Assisted-by: Claude-Code:claude-opus-5-5
Signed-off-by: Krainov Gleb <krajnov.g@kontentplus.ru>
@ToteMeiSter
ToteMeiSter force-pushed the fix/last-common-read-from-read-response branch from e9055e7 to 9a3375a Compare October 18, 2026 12:36
@github-actions

Copy link
Copy Markdown
Contributor

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants