diff --git a/nobilis b/nobilis index 2505f58..412c232 160000 --- a/nobilis +++ b/nobilis @@ -1 +1 @@ -Subproject commit 2505f58e15cb4bc6caba05d672d207d4fa38677e +Subproject commit 412c23201d53eb3d70498495867ac6537c2dfe47 diff --git a/src/renderer/src/app.css b/src/renderer/src/app.css index bcfbda1..325f6a5 100644 --- a/src/renderer/src/app.css +++ b/src/renderer/src/app.css @@ -5418,6 +5418,23 @@ video.lightbox-media { flex: none; } +/* A recovery code, shown once. Wide-spaced and selectable because it is meant + to be copied or written out by hand, and a run of base58 read off a screen + is easy to get wrong. */ +.recovery-code { + display: block; + margin-top: 6px; + padding: 8px 10px; + border-radius: 6px; + background: var(--surface-container-lowest); + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 13px; + letter-spacing: 0.06em; + line-height: 1.7; + overflow-wrap: anywhere; + user-select: all; +} + /* --- Exporting a conversation ----------------------------------------- */ .export-choice { diff --git a/src/renderer/src/components/MatrixAccountTools.tsx b/src/renderer/src/components/MatrixAccountTools.tsx index ee0ed01..e18dc91 100644 --- a/src/renderer/src/components/MatrixAccountTools.tsx +++ b/src/renderer/src/components/MatrixAccountTools.tsx @@ -57,6 +57,10 @@ export function MatrixAccountTools({ account }: { account: Account }): JSX.Eleme // server of its own. Seeded from the account and only sent when it changes, // so opening this page does not rewrite a setting nobody touched. const [rtcFocus, setRtcFocus] = useState(account.rtcFocusUrl ?? '') + const [dehydrating, setDehydrating] = useState(false) + const [storageSecret, setStorageSecret] = useState('') + // The recovery code, held only long enough to be read once. + const [newCode, setNewCode] = useState('') const [restoreKey, setRestoreKey] = useState('') const [busy, setBusy] = useState('') const [deleting, setDeleting] = useState(null) @@ -115,6 +119,72 @@ export function MatrixAccountTools({ account }: { account: Account }): JSX.Eleme return (
+
+
+
Keep a spare device for room keys
+
+ Leaves an encrypted, unused device on the homeserver so keys sent while you are + signed out are not lost. A new sign-in collects them with your recovery code. + Element calls this a dehydrated device. +
+
+ {account.dehydration ? ( + On + ) : ( + + )} +
+ {!account.dehydration && ( +
+ +
+ setStorageSecret(e.target.value)} + /> +
+
+ )} + {/* Shown once and never again: it is not kept anywhere this daemon can + read it back, which is the point of it. */} + {newCode && ( +
+ +
+
+ Write this down. It is the only way to collect those keys on a new sign-in, and + it is not stored anywhere. +
+ {newCode} +
+ +
+ )}
Sliding sync
diff --git a/src/shared/wire.ts b/src/shared/wire.ts index 7d4ea57..ccb884a 100644 --- a/src/shared/wire.ts +++ b/src/shared/wire.ts @@ -61,6 +61,11 @@ export interface Account { * the account talks to its server, so it is not taken automatically. */ slidingSync?: boolean + /** + * Matrix only: whether this account keeps a dehydrated device, so room keys + * shared while a login did not exist are not simply lost. + */ + dehydration?: boolean } export interface SneedChatRoom {