import clsx from "clsx"; import * as React from "react"; import { initRegistration } from "../../utils"; import { AUTH_HOST, browserSupportsWebAuthn, checkResponse, } from "../../utils/webauthn"; import { PasskeyLogo } from "../Login"; import * as styles from "./styles.module.css"; type Authenticator = { id: string; name: string; lastUsed: string; }; type Classes = { iconButton?: string; button?: string; li?: string; editable?: string; input?: string; }; export function Passkeys({ className, classes = {}, ...props }: { className?: string; classes: Classes; }) { const [loading, setLoading] = React.useState(true); const [passkeys, setPasskeys] = React.useState([]); const [supportsWebauthn, setSupportsWebauthn] = React.useState(true); React.useEffect(() => { setSupportsWebauthn(browserSupportsWebAuthn()); }, []); const reload = React.useCallback(() => { fetch(`${AUTH_HOST}/me`, { credentials: "include", }) .then(checkResponse) .then((data) => { setPasskeys(data.authenticators); }) .finally(() => { setLoading(false); }); }, []); React.useEffect(() => { reload(); }, [reload]); return (
{loading ? (
Loading...
) : ( )}
); } function Editable({ name, lastUsed, onChange, classes = {}, }: { name: string; lastUsed: string; onChange: (value: string) => void; classes: Classes; }) { const [value, setValue] = React.useState(name); const [editing, setEditing] = React.useState(false); const relativeTime = React.useCallback((date) => { if (!date) return ""; const formatter = new Intl.RelativeTimeFormat(undefined, { numeric: "auto", }); const DIVISIONS = [ { amount: 60, name: "seconds" }, { amount: 60, name: "minutes" }, { amount: 24, name: "hours" }, { amount: 7, name: "days" }, { amount: 4.34524, name: "weeks" }, { amount: 12, name: "months" }, { amount: Number.POSITIVE_INFINITY, name: "years" }, ]; let duration = (date.getTime() - Date.now()) / 1000; for (let i = 0; i <= DIVISIONS.length; i++) { const division = DIVISIONS[i]; if (Math.abs(duration) < division.amount) { return formatter.format(Math.round(duration), division.name as any); } duration /= division.amount; } }, []); return (
{editing ? ( setValue(e.target.value)} onBlur={() => { setEditing(false); onChange(value); }} /> ) : ( setEditing(true)} > {name} (used {relativeTime(new Date(lastUsed))}) )}
); }