Files
cinny/src/app/features/settings/general/StorageUsage.tsx
T

104 lines
3.6 KiB
TypeScript
Raw Normal View History

import React, { useCallback, useEffect, useState } from 'react';
import { Badge, Box, Button, Text } from 'folds';
import { SequenceCard } from '../../../components/sequence-card';
import { SequenceCardStyle } from '../styles.css';
import { SettingTile } from '../../../components/setting-tile';
import { bytesToSize } from '../../../utils/common';
import {
percentOf,
readStorageUsage,
requestPersistentStorage,
StorageUsage as Usage,
storageUsageSupported,
} from '../../../utils/storageUsage';
/**
* [Gitea #120] One tile: how much this device stores for Lotus, whether the
* browser has promised to keep it, and a way to ask. Hidden entirely when
* `navigator.storage.estimate()` is unavailable rather than showing zeros.
*/
export function StorageUsage() {
const [usage, setUsage] = useState<Usage | undefined>();
const [requesting, setRequesting] = useState(false);
const refresh = useCallback(() => {
readStorageUsage()
.then(setUsage)
.catch(() => setUsage(undefined));
}, []);
useEffect(() => {
if (storageUsageSupported()) refresh();
}, [refresh]);
if (!usage) return null;
const handlePersist = async () => {
setRequesting(true);
await requestPersistentStorage();
setRequesting(false);
refresh();
};
const breakdown: string[] = [];
if (typeof usage.indexedDB === 'number')
breakdown.push(
`${bytesToSize(usage.indexedDB)} in IndexedDB (sync cache, encryption keys, search index)`,
);
if (typeof usage.caches === 'number')
breakdown.push(`${bytesToSize(usage.caches)} offline app files`);
return (
<Box direction="Column" gap="100">
<Text size="L400">Storage</Text>
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
<SettingTile
title={`${bytesToSize(usage.usage)} used on this device`}
description={
<Box direction="Column" gap="100">
<Text size="T200">
{percentOf(usage.usage, usage.quota)}% of the {bytesToSize(usage.quota)} the browser
allows this site.
{breakdown.length > 0 && ` ${breakdown.join(' · ')}.`}
</Text>
<Text size="T200">
Images and videos you have viewed are kept in the browser&apos;s own cache, which it
manages and empties itself; they are not counted here. Encryption keys are never
cleared from this page — losing them makes older encrypted messages unreadable on
this device. Cached search results can be cleared from Message Search.
</Text>
</Box>
}
after={
usage.persisted === undefined ? undefined : (
<Box direction="Column" alignItems="End" gap="100">
<Badge
variant={usage.persisted ? 'Success' : 'Warning'}
fill="Soft"
radii="300"
size="400"
>
<Text size="L400">{usage.persisted ? 'Protected' : 'May be evicted'}</Text>
</Badge>
{!usage.persisted && (
<Button
size="300"
variant="Secondary"
fill="Soft"
radii="300"
outlined
disabled={requesting}
onClick={handlePersist}
>
<Text size="B300">Keep my data</Text>
</Button>
)}
</Box>
)
}
/>
</SequenceCard>
</Box>
);
}