#459 General held 76 of ~110 settings (11 screens). Its sections now live on pages named for what people come to do: Account, Appearance, Messages (incl. editor, date and time), Notifications, Calls & audio, Privacy & security (privacy options + the former Devices page), Data & storage (storage, sync, your data), Desktop (desktop app only), Emojis & stickers, Advanced (the former Developer Tools + "show hidden events" / "legacy username colour"), About. The longest page is now ~3 screens (Appearance). Settings opens on Account. Deep links keep working: "devices" lands on Privacy & security, the old "general" on Appearance, the update notice on Desktop. Text that pointed at the old page names is updated. The push-rule editor gets one line explaining the Matrix terms. Remaining Title Case labels on Account and Devices are sentence case. #460 Chat background, seasonal theme and avatar decoration grids sit behind one row: the current choice and a Change button (the first two close after a choice; decorations keep their Save step). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
105 lines
3.9 KiB
TypeScript
105 lines
3.9 KiB
TypeScript
import React, { useState, useSyncExternalStore } from 'react';
|
|
import FocusTrap from 'focus-trap-react';
|
|
import { Box, Button, config, Line, Overlay, OverlayBackdrop, OverlayCenter, Text } from 'folds';
|
|
import { ContainerColor } from '../../styles/ContainerColor.css';
|
|
import { getKeyUploadWatch, subscribeKeyUploadWatch } from '../../utils/keyUploadWatch';
|
|
import { LogoutDialog } from '../../components/LogoutDialog';
|
|
import { stopPropagation } from '../../utils/keyboard';
|
|
|
|
/**
|
|
* [Gitea #201 KE-1] "Encryption on this device is out of sync." Shown while
|
|
* the server keeps rejecting this device's key uploads (keyUploadWatch). The
|
|
* fix is a fresh login, so the action opens the normal logout dialog, which
|
|
* already warns if the device isn't verified / backed up. Hideable for this
|
|
* session only: the problem doesn't go away by itself.
|
|
*/
|
|
export function KeyUploadBanner() {
|
|
const { stuck, lastError } = useSyncExternalStore(subscribeKeyUploadWatch, getKeyUploadWatch);
|
|
const [hidden, setHidden] = useState(false);
|
|
const [details, setDetails] = useState(false);
|
|
const [logout, setLogout] = useState(false);
|
|
|
|
if (!stuck || hidden) return null;
|
|
|
|
return (
|
|
<Box direction="Column" shrink="No">
|
|
<Box
|
|
className={ContainerColor({ variant: 'Critical' })}
|
|
style={{ padding: `${config.space.S100} ${config.space.S300}` }}
|
|
direction="Column"
|
|
alignItems="Center"
|
|
gap="100"
|
|
role="alert"
|
|
>
|
|
<Box alignItems="Center" gap="300" wrap="Wrap" justifyContent="Center">
|
|
<Text size="L400" align="Center">
|
|
Encryption on this device is out of sync with the server. New encrypted messages and
|
|
calls may fail until you sign out and back in.
|
|
</Text>
|
|
<Button
|
|
size="300"
|
|
variant="Critical"
|
|
fill="Solid"
|
|
radii="300"
|
|
onClick={() => setLogout(true)}
|
|
>
|
|
<Text size="B300">Fix: sign out…</Text>
|
|
</Button>
|
|
<Button
|
|
size="300"
|
|
variant="Critical"
|
|
fill="Soft"
|
|
radii="300"
|
|
onClick={() => setDetails((v) => !v)}
|
|
aria-expanded={details}
|
|
>
|
|
<Text size="B300">Details</Text>
|
|
</Button>
|
|
<Button
|
|
size="300"
|
|
variant="Critical"
|
|
fill="None"
|
|
radii="300"
|
|
onClick={() => setHidden(true)}
|
|
>
|
|
<Text size="B300">Hide</Text>
|
|
</Button>
|
|
</Box>
|
|
{details && (
|
|
<Box direction="Column" gap="100" style={{ maxWidth: 640 }}>
|
|
<Text size="T200" align="Center">
|
|
The server already has encryption keys this device thinks it hasn't sent, so it
|
|
rejects every new upload and other people can't start fresh encrypted sessions
|
|
with you. This usually happens after the browser cleared this site's storage
|
|
while you stayed signed in. Before signing out, make sure this device is verified or
|
|
key backup is on (Settings → Privacy & security), so your message history comes back
|
|
when you sign in again.
|
|
</Text>
|
|
{lastError && (
|
|
<Text size="T200" align="Center" priority="300" style={{ wordBreak: 'break-word' }}>
|
|
Server said: {lastError}
|
|
</Text>
|
|
)}
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
<Line variant="Critical" size="300" />
|
|
{logout && (
|
|
<Overlay open backdrop={<OverlayBackdrop />}>
|
|
<OverlayCenter>
|
|
<FocusTrap
|
|
focusTrapOptions={{
|
|
onDeactivate: () => setLogout(false),
|
|
clickOutsideDeactivates: true,
|
|
escapeDeactivates: stopPropagation,
|
|
}}
|
|
>
|
|
<LogoutDialog handleClose={() => setLogout(false)} />
|
|
</FocusTrap>
|
|
</OverlayCenter>
|
|
</Overlay>
|
|
)}
|
|
</Box>
|
|
);
|
|
}
|