Repository navigation
Stop holding hidden conversations (GRYT-1379) - #217
Merged
Merged
Conversation
Hiding a conversation was a row on the server. `dm:setHidden` set `conversation_members.hidden_at`, `listConversationsForUser` filtered those rows out, and a new message cleared it for everybody. Sivert wants it on the device instead. It's a visual thing, and the server shouldn't hold it. So the list stops filtering on `hidden_at`, and `dm:setHidden` and the `dm:hidden` event are gone. A one-shot migration clears every value the old code wrote. Without that clear, anything hidden before today stays filtered out and the new client can never draw it, since it can't show what it isn't sent. The column stays, unused. Drop it and an older binary rolling back writes to a column that isn't there. Keeping it costs one TEXT column per membership row. Blocking used the same column to take the blocked person's conversation off the blocker's list. With the filter gone that write does nothing, so it goes too, and the client hides the row locally instead. Same effect, and you can find it again under Hidden rather than it being gone for good. Hide in a client older than this stops doing anything once a server updates. Co-Authored-By: Claude Opus 5 <[email protected]>
This was referenced Sep 23, 2026
sivert-io
added a commit
to Gryt-chat/docs
that referenced
this pull request
Sep 23, 2026
…YT-1379) (#129) `dm:setHidden` and `dm:hidden` are gone from the server: hiding a conversation is the client's own now, kept on the device and sent nowhere. The page said otherwise, which fails the socket coverage check on the server's pull request. Gryt-chat/server#217 and Gryt-chat/client#649. Co-authored-by: Claude Opus 5 <[email protected]>
sivert-io
added a commit
to Gryt-chat/client
that referenced
this pull request
Sep 23, 2026
* Hide a conversation on this device (GRYT-1379) Hiding was the server's answer until now, so it followed you between devices and there was no way to see what you had hidden. Sivert wants it the other way round: "it should purely be a visual thing", and "i dont want the server to hold it". So it lives in localStorage, one entry per server and account, holding each conversation id and the moment it was hidden. Nothing is sent anywhere. Every read and write is wrapped, since storage throws in a private window, and an empty answer draws the whole list rather than a broken one. A conversation comes back when its `last_message_at` is newer than the moment it was hidden. That falls out of the list the server already sends, so a message that arrived while the app was shut brings it back the same as a live one, and the stored entry is dropped once it's back. The moment is clamped to now when it is read, or a clock that went backwards would hide a conversation for good. Under the Messages list there's a Hidden row with a count and a chevron, which exists only while something is hidden. It expands in place and draws those conversations back. A dimmed row opens and reads normally without unhiding, and its menu offers Show in list. Whether the group is open is remembered per device, and it opens by itself while the conversation being read is one of the hidden ones. Hiding is instant, with an Undo in a toast for five seconds and no confirm. It also marks the conversation read, so a badge cannot be left counting for a row nobody can see. Blocking used to take the conversation off your list on the server, through the same column. The server half of this drops that, so the client does it here when a block lands. The server half is Gryt-chat/server#217. Co-Authored-By: Claude Opus 5 <[email protected]> * Screenshots for the GRYT-1379 PR Co-Authored-By: Claude Opus 5 <[email protected]> * Take the screenshots back out of the tree (GRYT-1379) They stay reachable by the commit that added them, which is what the pull request body links to. Co-Authored-By: Claude Opus 5 <[email protected]> --------- Co-authored-by: Claude Opus 5 <[email protected]>
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.
Hiding a conversation was a row on the server.
dm:setHiddensetconversation_members.hidden_at,listConversationsForUserfiltered those rows out, and a new message cleared it for everybody. Sivert wants it on the device instead: "it should purely be a visual thing", and "i dont want the server to hold it".So the server lets go of it, and the client does the hiding on its own: Gryt-chat/client#649. The socket reference documented both events, so Gryt-chat/docs#129 drops them. Task: https://tasks.sivert.io/tasks/1456
What changes
listConversationsForUserstops filtering onhidden_at, so the list holds every conversation again.dm:setHiddenand thedm:hiddenevent are gone, along withsetConversationHiddenandclearConversationHidden.dm:openno longer unhides, andchat:sendno longer unhides everybody in the conversation. The first message in a new conversation still announces it, which is what that block was doing as well.hidden_atthe old code wrote, stamped inschema_metaso it runs once.Without that clear, anything hidden before today would stay filtered out for good: the new client can only draw what it is sent, and it is never sent those rows.
The column stays
conversation_members.hidden_atis kept, unused. Dropping it is a rebuild of the table in SQLite, and an older binary rolling back would then write to a column that isn't there. Keeping it costs one TEXT column per membership row and nothing reads it.What to look at
Blocking.
user:blockused to callhideConversationsBetween, which took the blocked person's conversation off the blocker's list through the same column. With the filter gone that write does nothing, so leaving it in would be a line that reads as working and isn't. I deleted it, and the client hides the row locally when a block lands instead. Same effect, and it can be found again under Hidden rather than being gone for good. That's the part I'm least sure about, since it's the one place the two halves have to agree.One thing I expected to lose and didn't: today a blocked person's message already unhides the blocker's row, because
clearConversationHiddenunhid everybody in the conversation without asking who. The client rule has the same hole, so blocking behaves as it did.chat:send. ThewasEmptyannounce loop is the same set it always sent; it just no longer unions in whoeverclearConversationHiddenreported.The rest is shape rather than behaviour.
Tested
yarn test(1454 tests),yarn test:examples,yarn build,npx eslint ., the comment-length and self-hosted-config checks, all green.The tests that covered hiding now cover letting go of it:
dm:setHiddenis not registered, a conversation still carryinghidden_atis listed, and the migration clears the column once and leaves the marker behind so a second run doesn't.Also run against a real client: a throwaway server built from this branch, the client's Playwright suite on the other end, two accounts hiding and unhiding conversations between them.
The reference check
reference / Compare the reference pages against the sourceis red here and stays red until Gryt-chat/docs#129 lands:check-socket-coverage.mjsfails hard on an event the page describes and the server doesn't have. The docs PR is green on its own, so it goes first.verifyis green here.Accepted
Once a server updates, Hide in an older client does nothing. It writes
hidden_atthat nothing reads, and the row stays in the list.Don't merge this one. It's yours to read first.
🤖 Generated with Claude Code