87 lines
3.6 KiB
TypeScript
87 lines
3.6 KiB
TypeScript
import { lazy, Suspense, useCallback, useState } from 'react';
|
|
import { CompactRow } from '@/features/item/CompactRow';
|
|
import { useDownload } from '@/features/item/download';
|
|
import { formatRemaining } from '@/lib/format';
|
|
import { useShareSession } from '@/store/shareSession';
|
|
import { Button } from '@/ui/Button';
|
|
import { CopyButton } from '@/ui/CopyButton';
|
|
import { Icon } from '@/ui/Icon';
|
|
import { QrCode } from '@/ui/QrCode';
|
|
import { ShareBar } from './ShareBar';
|
|
import styles from './ShareView.module.css';
|
|
|
|
const PreviewSheet = lazy(() => import('@/features/item/PreviewSheet'));
|
|
|
|
const noop = () => {};
|
|
|
|
/** 已封存的分享:只读。访客与创建者看到的清单相同,创建者多一张「分享给别人」卡片 */
|
|
export function ShareView() {
|
|
const share = useShareSession((s) => s.share);
|
|
const ids = useShareSession((s) => s.order);
|
|
const integrityError = useShareSession((s) => s.integrityError);
|
|
const error = useShareSession((s) => s.error);
|
|
const download = useDownload();
|
|
const [sheetId, setSheetId] = useState<string>();
|
|
const [showQr, setShowQr] = useState(true);
|
|
const closeSheet = useCallback(() => setSheetId(undefined), []);
|
|
|
|
if (!share) return null;
|
|
const remaining = formatRemaining(share.expiresAt);
|
|
|
|
async function downloadAll() {
|
|
const { byId } = useShareSession.getState();
|
|
for (const id of ids) {
|
|
const item = byId[id];
|
|
if (item) await download(item);
|
|
await new Promise((r) => setTimeout(r, 300)); // 浏览器会拦截太密集的多文件下载
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className={styles.page}>
|
|
<ShareBar
|
|
title={share.mine ? '我的分享' : '收到的分享'}
|
|
badge={<span className={styles.badge}><Icon name="lock" size={12} /> 已封存 · 只读</span>}
|
|
/>
|
|
<main className={styles.main}>
|
|
{integrityError ? <p className={styles.error} role="alert">{integrityError}</p> : null}
|
|
{error ? <p className={styles.error} role="alert">{error}</p> : null}
|
|
{share.mine ? (
|
|
<section className={styles.share} aria-label="分享给别人">
|
|
<h2 className={styles.h}>分享给别人</h2>
|
|
<code className={styles.link}>{share.link}</code>
|
|
<div className={styles.row}>
|
|
<CopyButton text={share.link} variant="primary" label="复制链接" />
|
|
<Button variant="ghost" icon="scan" aria-expanded={showQr} onClick={() => setShowQr((v) => !v)}>
|
|
{showQr ? '收起二维码' : '二维码'}
|
|
</Button>
|
|
</div>
|
|
{showQr ? <QrCode text={share.link} label="分享链接二维码" /> : null}
|
|
<p className={styles.dim}>
|
|
访客只能预览和下载。内容已封存,你也不能再修改或删除,{remaining}后自动清除。持有链接的人都能下载。
|
|
</p>
|
|
</section>
|
|
) : (
|
|
<p className={styles.note}>
|
|
这是一个只读分享:内容不会再被修改,{remaining}后自动清除。
|
|
</p>
|
|
)}
|
|
|
|
<div className={styles.head}>
|
|
<span>{ids.length} 项内容</span>
|
|
<Button variant="primary" icon="download" onClick={() => void downloadAll()} disabled={ids.length === 0}>全部下载</Button>
|
|
</div>
|
|
<ul className={styles.list}>
|
|
{ids.map((id) => (
|
|
<CompactRow key={id} id={id} selected={false} onSelect={setSheetId} />
|
|
))}
|
|
</ul>
|
|
</main>
|
|
|
|
<Suspense fallback={null}>
|
|
{sheetId ? <PreviewSheet id={sheetId} onClose={closeSheet} readOnly onDelete={noop} /> : null}
|
|
</Suspense>
|
|
</div>
|
|
);
|
|
}
|