From 2fcf0859bfa6056143bf71801178755b74f8a747 Mon Sep 17 00:00:00 2001 From: Salastil Date: Wed, 2 Sep 2026 08:22:51 -0400 Subject: [PATCH] Invite into a Matrix room, and stop implying a tick means more than it does A field rather than a picker, because a Matrix address names a person globally - there is no roster to choose from and nobody to look up first. Offered on every Matrix room rather than gated on permission: the server decides and says so plainly, and guessing here would only hide the action from somebody who could have used it. And the sessions panel now says what verifying actually does. moho does no cross-signing, so a verification is recorded between these two sessions and never published - Element goes on showing the device as unverified. Somebody not told that would reasonably conclude one of the two clients is broken, which is a worse outcome than the missing feature. Co-Authored-By: Claude Opus 5 --- nobilis | 2 +- .../src/components/ConversationTools.tsx | 62 +++++++++++++++++++ .../src/components/MatrixAccountTools.tsx | 10 +++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/nobilis b/nobilis index 78019ca..1ef66ab 160000 --- a/nobilis +++ b/nobilis @@ -1 +1 @@ -Subproject commit 78019ca762692ef055c99d20d32998bfb969a220 +Subproject commit 1ef66ab532e23ffa4fdeeb5bde12452d87bcaae2 diff --git a/src/renderer/src/components/ConversationTools.tsx b/src/renderer/src/components/ConversationTools.tsx index 865615e..9eb5559 100644 --- a/src/renderer/src/components/ConversationTools.tsx +++ b/src/renderer/src/components/ConversationTools.tsx @@ -6,6 +6,63 @@ import { formatFullTime } from '../lib/util' import type { BufferEntry } from '../state/store' import type { Message } from '../../../shared/wire' +/** + * Asking somebody into a Matrix room. + * + * A field rather than a picker, because a Matrix address names a person + * globally - there is no roster to choose from and nobody to look up first. + * Offered on every Matrix room rather than gated on permission: the server + * decides, and it says so plainly enough that guessing here would only mean + * hiding the action from somebody who could have used it. + */ +function InviteToRoom({ buffer }: { buffer: BufferEntry }): JSX.Element { + const store = useStore() + const [open, setOpen] = useState(false) + const [who, setWho] = useState('') + const [busy, setBusy] = useState(false) + + const invite = (): void => { + const userId = who.trim() + if (!userId) return + setBusy(true) + void window.moho + .rpc('inviteMatrixMember', { accountId: buffer.accountId, bufferId: buffer.id, userId }) + .then(() => { + store.toast('info', `Invited ${userId}`) + setWho('') + setOpen(false) + }) + .catch((e: Error) => store.toast('error', e.message)) + .finally(() => setBusy(false)) + } + + if (!open) { + return setOpen(true)} /> + } + + return ( +
+ + setWho(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Escape') { + setWho('') + setOpen(false) + } + if (e.key === 'Enter') invite() + }} + onBlur={() => !who.trim() && setOpen(false)} + /> +
+ ) +} + /** * What a channel's restrictions mean, for the ones worth explaining. * @@ -160,6 +217,11 @@ export function ConversationTools({ buffer }: { buffer: BufferEntry }): JSX.Elem the conversation being read rather than on the window. */} {isDiscord && isDm && } + {/* The same job on Matrix, where it needs no picker: a Matrix address + names somebody whether or not this account has ever met them, so + there is no friend list to choose from and nothing to look up. */} + {buffer.accountId.startsWith('matrix:') && } +
Your other logged-in devices. Verify one to let encrypted history decrypt across both.
+ {/* Said because the tick is otherwise a promise this cannot keep. + moho does no cross-signing, so a verification here is recorded + between these two sessions and never published - Element will + still show the device as unverified, and somebody who was not + told that would reasonably conclude one of the two clients is + broken. */} +
+ Verifying marks a session as trusted in moho. moho does not do cross-signing, + so other clients will still show it unverified. +