From ba38eafc7050fba8b5be2cf56448da5bf79e172b Mon Sep 17 00:00:00 2001 From: Salastil Date: Sat, 5 Sep 2026 13:36:46 -0400 Subject: [PATCH] One pin panel, for whichever service has pins The panel and the message-menu action were Matrix-only by spelling rather than by nature - Discord pins the same way and shows the same list. The store field and both calls lose the protocol from their names. One difference survives and is handled where it belongs: a Matrix room's pins arrive as room state, so an empty list is known and the button hides itself, while Discord's have to be asked for - so there the button is offered and the count appears once something has asked, which is what Discord's own client does. Co-Authored-By: Claude Opus 5 --- nobilis | 2 +- .../src/components/ConversationTools.tsx | 20 ++++++++++++------- src/renderer/src/components/MessageRow.tsx | 6 +++--- src/renderer/src/state/store.ts | 19 +++++++++--------- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/nobilis b/nobilis index bdfa712..70911bb 160000 --- a/nobilis +++ b/nobilis @@ -1 +1 @@ -Subproject commit bdfa7120970fc14a3cb4782bdd70de27447c3227 +Subproject commit 70911bbd03a7e509233dcbb0a23fd3b0a3874221 diff --git a/src/renderer/src/components/ConversationTools.tsx b/src/renderer/src/components/ConversationTools.tsx index 9332ca7..198f181 100644 --- a/src/renderer/src/components/ConversationTools.tsx +++ b/src/renderer/src/components/ConversationTools.tsx @@ -255,7 +255,7 @@ interface ThreadSummary { */ function PinnedMessages({ buffer }: { buffer: BufferEntry }): JSX.Element | null { const store = useStore() - const count = (useChat((s) => s.matrixPinned)[buffer.id] || []).length + const known = useChat((s) => s.pinnedMessages)[buffer.id] const [open, setOpen] = useState(false) const [rows, setRows] = useState(null) const button = useRef(null) @@ -268,7 +268,7 @@ function PinnedMessages({ buffer }: { buffer: BufferEntry }): JSX.Element | null setOpen(true) setRows(null) void window.moho - .rpc<{ pinned: Message[] }>('listMatrixPinned', { bufferId: buffer.id }) + .rpc<{ pinned: Message[] }>('listPinned', { bufferId: buffer.id }) .then((answer) => setRows(answer.pinned)) .catch((e: Error) => { store.toast('error', e.message) @@ -277,23 +277,29 @@ function PinnedMessages({ buffer }: { buffer: BufferEntry }): JSX.Element | null } // Nothing pinned is nothing to offer: a button that always opens an empty - // panel is a button people stop pressing. - if (count === 0) return null + // panel is a button people stop pressing. That test needs the list, though, + // and only Matrix sends one unasked - a room's pins are part of its state, + // while Discord's are a question you have to go and ask. So where the + // answer is not known yet the button is offered, which is what Discord's + // own client does, and the count appears once something has asked. + const count = known?.length ?? 0 + if (known && count === 0) return null return ( <> {open && ( setOpen(false)}> -
Pinned in this room
+
Pinned here
{rows === null &&
Looking…
} + {rows?.length === 0 &&
Nothing is pinned.
} {rows?.map((message) => (