Now that the unread fanout runs in a job, it can run after a member has
already opened the room and moved on to another one. Their sidebar would
then mark the room unread for a message they have seen.
The job now notifies only members who still have the room unread or are
in it now. Room#receive marks members who aren't in the room unread, and
opening the room clears it, so a member who caught up in the meantime is
skipped. When the
job runs right away this is the same set of people as before, except
members who have hidden the room, whose sidebar doesn't list it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JVFo3Lt9T8M5NR7KxVvsZ2
Since the unread rooms stream was scoped per user, posting a message
publishes the room id once to each member of the room. Each publish is a
round trip to Redis, made one after another inside the request, so the time
to post a message grows with the size of the room and in a big room is far
more than the rest of the request.
The fanout now runs in Message::BroadcastUnreadRoomJob, so the poster no
longer waits for it. Who gets the notice and what it says are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NuTtwJKhb77Dv7EQqv2C3C
* Preload only uncached messages and reduce rendering overhead
* Keep benchmark summaries without raw JSON results
* Use Ruby benchmark drivers and keep generated results out of the repo
UnreadRoomsChannel streamed from a hardcoded global name, and every message
published its room id to it. Any authenticated user, including one with no
memberships at all, could subscribe and watch which rooms were active and exactly
when, across every closed room and direct conversation on the account. The browser
filters ids it doesn't recognise, but that happens after delivery.
Its sibling ReadRoomsChannel is already scoped per user; this mirrors it, and
message creation fans the notice out to the room's members instead of broadcasting
it to everyone. No message content was exposed either way, only the timing.
This adds the ability to ban a user by their IP address.
When an admin is viewing a user profile, a new "Ban user" button is
present. Clicking on that will:
- Create a ban on the IP addresses that were tracked for that user's
sessions
- Remove all the messages authored by that user
- Log the user out immediately
In addition, that user will no longer be shown in most user lists in the
app. They are still shown to admins, in account settings. Viewing their
profile from there will now show a "Remove ban" button which can be used
to restore their access (it doesn't restore their messages though --
those are already gone -- it just removes the blocks so they can log in
again).