diff --git a/nobilis b/nobilis
index 873a908..4f7ba9b 160000
--- a/nobilis
+++ b/nobilis
@@ -1 +1 @@
-Subproject commit 873a9083d274e96e95ae98355e6ba1fb5a63ed09
+Subproject commit 4f7ba9bc214a739defdf160cdcc9b40a760b6f68
diff --git a/src/renderer/src/components/BufferList.tsx b/src/renderer/src/components/BufferList.tsx
index 577e31e..852b3ed 100644
--- a/src/renderer/src/components/BufferList.tsx
+++ b/src/renderer/src/components/BufferList.tsx
@@ -23,6 +23,7 @@ import type { BufferGroup } from '../../../shared/wire'
import {
bufferDisplayName,
bufferKindGlyph,
+ serviceRoomGlyph,
classes,
resolveMediaUrl,
serviceIcon
@@ -808,6 +809,11 @@ function BufferRow({
) : buffer.avatarUrl ? (
+ ) : 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.
+
) : (
)
diff --git a/src/renderer/src/lib/buffermenu.ts b/src/renderer/src/lib/buffermenu.ts
index 086dd5c..ba10a00 100644
--- a/src/renderer/src/lib/buffermenu.ts
+++ b/src/renderer/src/lib/buffermenu.ts
@@ -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[]))
]
}
diff --git a/src/renderer/src/lib/util.ts b/src/renderer/src/lib/util.ts
index 97bceaf..e91cd62 100644
--- a/src/renderer/src/lib/util.ts
+++ b/src/renderer/src/lib/util.ts
@@ -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
diff --git a/src/shared/wire.ts b/src/shared/wire.ts
index dcdb493..02f5417 100644
--- a/src/shared/wire.ts
+++ b/src/shared/wire.ts
@@ -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