Mark the server's own room, and stop offering to leave it

Its own glyph in the list, because the fact worth knowing is that it is
not one of the others - a notice about a quota is easy to scroll past
when it arrives in what looks like a room a stranger made.

The Leave entry becomes a sentence saying the server keeps it open,
rather than disappearing. An absent entry reads as a client that forgot
and leaves somebody hunting for the gesture; this says what is actually
true.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-13 13:37:49 -04:00
co-authored by Claude Opus 5
parent 8febda2af1
commit e6c7a40af2
5 changed files with 50 additions and 9 deletions
+1 -1
Submodule nobilis updated: 873a9083d2...4f7ba9bc21
@@ -23,6 +23,7 @@ import type { BufferGroup } from '../../../shared/wire'
import {
bufferDisplayName,
bufferKindGlyph,
serviceRoomGlyph,
classes,
resolveMediaUrl,
serviceIcon
@@ -808,6 +809,11 @@ function BufferRow({
<Avatar name={buffer.name} url={buffer.avatarUrl} size={22} status={status} />
) : buffer.avatarUrl ? (
<img className="buffer-avatar" src={resolveMediaUrl(buffer.avatarUrl)} alt="" />
) : buffer.serviceRoom ? (
// The server's own room. Marked here because the fact worth knowing is
// that it is not one of the others - a notice about a quota is easy to
// scroll past when it arrives in what looks like a room a stranger made.
<Icon name={serviceRoomGlyph()} size={15} />
) : (
<Icon name={bufferKindGlyph(buffer.kind)} size={15} />
)
+21 -8
View File
@@ -228,13 +228,26 @@ export function bufferMenuEntries({
{ label: 'Hide', icon: 'visibility_off', onClick: onHide },
...(buffer.kind === 'server'
? []
: ([
{
label: leaveLabel(buffer, account?.service),
icon: 'close',
danger: true,
onClick: onClose
}
] as MenuEntry[]))
: buffer.serviceRoom
? // The server's own room, which the server will not let anybody
// leave. Said rather than hidden: an entry that is simply absent
// reads as a client that forgot, where this reads as the fact it
// is - and hiding it would leave somebody hunting for the gesture.
([
{
label: 'Your server keeps this room open',
icon: 'lock',
disabled: true,
onClick: () => {}
}
] as MenuEntry[])
: ([
{
label: leaveLabel(buffer, account?.service),
icon: 'close',
danger: true,
onClick: onClose
}
] as MenuEntry[]))
]
}
+13
View File
@@ -175,6 +175,19 @@ export function bufferKindGlyph(kind: string): string {
return 'tag'
}
/**
* The mark on a conversation that is the service talking rather than a
* person: Matrix's server notices room.
*
* Its own glyph rather than the channel hash, because the thing worth knowing
* about that room is precisely that it is not one of the others - a message
* about a quota or an account restriction is easy to scroll past when it
* arrives in what looks like a room a stranger made.
*/
export function serviceRoomGlyph(): string {
return 'campaign'
}
/**
* Deterministic per-nick colour, so the same person keeps the same colour
* across sessions and buffers. Hue-only variation against a fixed saturation
+9
View File
@@ -127,6 +127,15 @@ export interface Buffer {
* just stops competing for the top of the rail.
*/
lowPriority?: boolean
/**
* This conversation is the service itself talking to the account.
*
* Matrix's server notices room - a terms-of-service change, a quota, an
* account restriction. Marked because it otherwise looks exactly like an
* ordinary room from a stranger, and because it cannot be left: the server
* refuses, so offering to leave it is offering something that fails.
*/
serviceRoom?: boolean
/**
* Set while the buffer exists but the service has not yet said what is in
* it - a Matrix room between joining it and its first sync. The list draws