Files
cinny/src/app/pages/client/ClockSkewBanner.tsx
T

83 lines
2.8 KiB
TypeScript
Raw Normal View History

import React, { useCallback, useState } from 'react';
import { useAtomValue } from 'jotai';
import { Box, Button, config, Line, Text } from 'folds';
import { clockSkewAtom } from '../../state/clockSkew';
import { ContainerColor } from '../../styles/ContainerColor.css';
import { clockFixHint, describeSkewVsServer, detectClockFixPlatform } from '../../utils/clockSkew';
const DISMISS_KEY = 'lotus-clock-skew-dismissed-until';
const DISMISS_MS = 24 * 60 * 60 * 1000;
const readDismissedUntil = (): number => {
try {
const raw = localStorage.getItem(DISMISS_KEY);
const n = raw ? Number(raw) : 0;
return Number.isFinite(n) ? n : 0;
} catch {
return 0;
}
};
/**
* [Gitea #158] "Your computer's clock is 14 minutes ahead of the server."
* Same slot and style as the sync banners. Shown while the skew monitor is
* over its threshold; the direction matters, so it is said. Dismissable for
* 24 h; never auto-corrects anything.
*/
export function ClockSkewBanner() {
const { skewMs, warning } = useAtomValue(clockSkewAtom);
const [dismissedUntil, setDismissedUntil] = useState(readDismissedUntil);
const [showHint, setShowHint] = useState(false);
const dismiss = useCallback(() => {
const until = Date.now() + DISMISS_MS;
try {
localStorage.setItem(DISMISS_KEY, String(until));
} catch {
// storage unavailable — dismiss for this session only
}
setDismissedUntil(until);
}, []);
if (!warning || skewMs === null || Date.now() < dismissedUntil) return null;
return (
<Box direction="Column" shrink="No">
<Box
className={ContainerColor({ variant: 'Warning' })}
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">
Your computer&apos;s clock is <b>{describeSkewVsServer(skewMs)}</b>. Encrypted messages
and voice calls will fail until it is fixed.
</Text>
<Button
size="300"
variant="Warning"
fill="Soft"
radii="300"
onClick={() => setShowHint((v) => !v)}
aria-expanded={showHint}
>
<Text size="B300">How to fix</Text>
</Button>
<Button size="300" variant="Warning" fill="None" radii="300" onClick={dismiss}>
<Text size="B300">Dismiss for 24 h</Text>
</Button>
</Box>
{showHint && (
<Text size="T200" align="Center">
{clockFixHint(detectClockFixPlatform(navigator.userAgent))}
</Text>
)}
</Box>
<Line variant="Warning" size="300" />
</Box>
);
}