Fix read receipts for open chats and one sided message deletes - #95
Merged
Conversation
Follow up to #92, which the reporter says did not fix #90. The recipient only ever marked a conversation seen on a page load or a page of getMessages. A message that arrives over the websocket lands in a chat that is already open and never refetches, so nothing marked it, and the sender's tick stayed on one until somebody reloaded. That is the remaining half of "sometimes it works": the reload that fixes it has to be the recipient's. The poll that #92 added now marks the open conversation seen, which is the same thing a page load does. It runs immediately rather than after a full interval, and skips while the tab is hidden so a chat left open in a background tab does not report messages nobody looked at as read. The conversation query built `(A) or (B) and (C)`. SQL binds `and` tighter than `or`, so the delete filter only ever applied to received messages and a sender deleting their own message saw it return on the next load. It only looked deleted once the recipient deleted it too, which hard deletes the row. Grouping the two halves of the conversation fixes it in both getMessages and the Inertia index. Refs #90 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #90
Follow up to #92, which @Mueeza-517 reports did not fix #90, plus the second bug raised in the same comment.
Read receipts, the half #92 missed
#92 got the reporting right. The sender polls, and the server tells it which of its messages have been read. What it did not fix is that nothing marks a message seen while the conversation is already open.
seenis only ever flipped in two places:index()when the page loads with auser_id, andgetMessages()on page 1. A message that arrives over Pusher is appended to an open chat that never refetches, so it is displayed to the recipient and staysseen = 0forever.That is the rest of "sometimes it works, sometimes it does not". With both chats open the tick never turns blue, and the reload that fixes it has to be the recipient's, not the sender's. Reloading the sender's page only refetches the same unread state.
checkNewMessages()now marks the open conversation seen, which is the same thing a page load does, on a request that was already being made. On the client the poll fires immediately instead of after a full 10s interval, and skips whiledocument.visibilityState !== 'visible', so a chat left open in a background tab does not report messages nobody looked at as read.Messages coming back after the sender deletes them
Operator precedence. The conversation query chained
where(A)->orWhere(B)->where(C), which renders as:andbinds tighter thanor, so this isA or (B and C). The delete filter never applied to the sender's own messages. Deleting for yourself did writedeleted_by_sender, it just had no effect on what you were shown. It only appeared to work once the recipient deleted too, because that path hard deletes the row.Grouping the two halves of the conversation into one closure fixes it. Same bug and same fix in
getMessages()and in the Inertiaindex().Verification
tests/Feature/MessengerDeleteAndSeenTest.php, 7 tests. 3 fail onmainand pass here:The other 4 pin behaviour that must not change: deleting for yourself leaves the message on the other side, the recipient's own delete still hides it, no third party message leaks into a conversation, and a poll with no conversation open marks nothing seen.
Full suite: 126 tests, 13 failures, all pre-existing (
Auth\PasswordResetTest,PasswordUpdateTest,RegistrationTest,ProfileTest,UserCreationTest, which are Laravel starter stubs) and the same 13 as before this branch.npm run buildandtsc --noEmitclean.Worth checking separately
@Mueeza-517 tested on
zerp.pk. The comment landed about 5 hours after #92 merged, so it is worth confirming the deployment there actually had #92 on it. The gaps above are real either way, but it changes what the screenshots were showing.Not addressed here: a distinct grey "delivered" tick still needs a delivery ack from the recipient's client that does not exist, and none of this covers the case where Pusher is not configured at all, since nothing else fetches new messages.