import React, { MouseEventHandler, useCallback, useState } from 'react';
import { useAtom } from 'jotai';
import { CryptoApi, KeyBackupInfo } from 'matrix-js-sdk/lib/crypto-api';
import {
Badge,
Box,
Button,
color,
config,
Icon,
IconButton,
Icons,
Menu,
percent,
PopOut,
ProgressBar,
RectCords,
Spinner,
Text,
} from 'folds';
import FocusTrap from 'focus-trap-react';
import { BackupProgressStatus, backupRestoreProgressAtom } from '../state/backupRestore';
import { InfoCard } from './info-card';
import { AsyncStatus, useAsyncCallback } from '../hooks/useAsyncCallback';
import {
useKeyBackupInfo,
useKeyBackupStatus,
useKeyBackupSync,
useKeyBackupTrust,
} from '../hooks/useKeyBackup';
import { stopPropagation } from '../utils/keyboard';
import { useRestoreBackupOnVerification } from '../hooks/useRestoreBackupOnVerification';
type BackupStatusProps = {
enabled: boolean;
};
function BackupStatus({ enabled }: BackupStatusProps) {
return (
{enabled ? 'Connected' : 'Disconnected'}
);
}
type BackupSyncingProps = {
count: number;
};
function BackupSyncing({ count }: BackupSyncingProps) {
return (
Syncing ({count})
);
}
function BackupProgressFetching() {
return (
Restoring: 0%
);
}
type BackupProgressProps = {
total: number;
downloaded: number;
};
function BackupProgress({ total, downloaded }: BackupProgressProps) {
return (
Restoring: {`${Math.round(percent(0, total, downloaded))}%`}
{downloaded} / {total}
);
}
type BackupTrustInfoProps = {
crypto: CryptoApi;
backupInfo: KeyBackupInfo;
};
function BackupTrustInfo({ crypto, backupInfo }: BackupTrustInfoProps) {
const trust = useKeyBackupTrust(crypto, backupInfo);
if (!trust) return null;
return (
{trust.matchesDecryptionKey ? (
Backup has trusted decryption key.
) : (
Backup does not have trusted decryption key!
)}
{trust.trusted ? (
Backup has trusted by signature.
) : (
Backup does not have trusted signature!
)}
);
}
type BackupRestoreTileProps = {
crypto: CryptoApi;
};
export function BackupRestoreTile({ crypto }: BackupRestoreTileProps) {
const [restoreProgress, setRestoreProgress] = useAtom(backupRestoreProgressAtom);
const restoring =
restoreProgress.status === BackupProgressStatus.Fetching ||
restoreProgress.status === BackupProgressStatus.Loading;
const backupEnabled = useKeyBackupStatus(crypto);
const backupInfo = useKeyBackupInfo(crypto);
const [remainingSession, syncFailure] = useKeyBackupSync();
const [menuCords, setMenuCords] = useState();
const handleMenu: MouseEventHandler = (evt) => {
setMenuCords(evt.currentTarget.getBoundingClientRect());
};
const [restoreState, restoreBackup] = useAsyncCallback(
useCallback(async () => {
await crypto.restoreKeyBackup({
progressCallback(progress) {
setRestoreProgress(progress);
},
});
}, [crypto, setRestoreProgress])
);
const handleRestore = () => {
setMenuCords(undefined);
restoreBackup();
};
return (
{remainingSession === 0 ? (
) : (
)}
setMenuCords(undefined),
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) =>
evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
isKeyBackward: (evt: KeyboardEvent) =>
evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
escapeDeactivates: stopPropagation,
}}
>
}
/>
}
>
{syncFailure && (
{syncFailure}
)}
{!backupEnabled && backupInfo === null && (
No backup present on server!
)}
{!syncFailure && !backupEnabled && backupInfo && (
)}
{restoreState.status === AsyncStatus.Loading && !restoring && }
{restoreProgress.status === BackupProgressStatus.Fetching && }
{restoreProgress.status === BackupProgressStatus.Loading && (
)}
{restoreState.status === AsyncStatus.Error && (
{restoreState.error.message}
)}
);
}
export function AutoRestoreBackupOnVerification() {
useRestoreBackupOnVerification();
return null;
}