import { t as tr } from './i18n'; import { useEffect, useState, useRef } from 'react'; import { api } from './api'; import { useToast } from './Toast'; type Icon = { id: string; name: string; shared: boolean; source: string | null }; export const iconUrl = (id: string) => '/api/icons/' + encodeURIComponent(id) + '/image'; export function IconLibrary({ initialId, picker = false, onSelect, }: { initialId?: string | null; picker?: boolean; onSelect?: (id: string) => void; }) { const [selected, select] = useState(initialId || ''), [q, search] = useState(''), [page, setPage] = useState(1), [items, setItems] = useState([]), [total, setTotal] = useState(0), [loading, setLoading] = useState(false), [name, setName] = useState(''), [shared, setShared] = useState(false), [confirmed, setConfirmed] = useState(false), [busy, setBusy] = useState(false), [error, setError] = useState(''), [notice, setNotice] = useState(''), [refresh, setRefresh] = useState(0); const file = useRef(null); useToast(error, 'error'); useToast(notice, 'success'); useEffect(() => { let active = true; setLoading(true); const timer = setTimeout(() => { void api<{ items: Icon[]; total: number }>( '/icons?q=' + encodeURIComponent(q) + '&page=' + page, ) .then((v) => { if (active) { setItems(v.items); setTotal(v.total); } }) .catch((e) => { if (active) setError(e.message); }) .finally(() => { if (active) setLoading(false); }); }, 250); return () => { active = false; clearTimeout(timer); }; }, [q, page, refresh]); async function upload() { const image = file.current?.files?.[0]; setError(''); setNotice(''); if (!image) { setError(tr('请选择图片')); return; } if (!name.trim() || (shared && (!/\p{Script=Han}/u.test(name) || !confirmed))) { setError(tr('请填写名称;共享图标须含中文并确认公开')); return; } if (image.size > 2 * 1024 * 1024) { setError(tr('图片不能超过 2 MB')); return; } setBusy(true); try { const form = new FormData(); form.append('file', image); form.append('name', name); form.append('shared', String(shared)); if (shared) form.append('confirmed', 'true'); const res = await fetch('/api/icons/upload', { method: 'POST', body: form, credentials: 'same-origin', }); const v = await res.json(); if (!res.ok) throw Error(v.message || tr('上传失败')); if (picker) select(v.id); setNotice( shared ? tr('图标已发布,所有登录用户均可搜索和使用') : tr('图标已存入我的图标,仅自己可见'), ); setName(''); setConfirmed(false); if (file.current) file.current.value = ''; search(''); setPage(1); setRefresh((n) => n + 1); } catch (e) { setError((e as Error).message); } finally { setBusy(false); } } return (
{ if ( e.key === 'Enter' && e.target instanceof HTMLInputElement && e.target.type !== 'checkbox' ) e.preventDefault(); }} >

{picker ? tr('选择账户图标') : tr('图标库')}

{tr('搜索共享图标和我的图标,选择后可在多个账户复用。直接上传默认仅自己可见。')}

{picker && (
{selected ? ( <> {tr('当前账户图标')} {tr('已选择图标')} ) : ( {tr('使用默认账户图标')} )}
)} {loading ? (

{tr('正在加载图标…')}

) : ( <>
{items.map((i) => picker ? ( ) : (
{i.name} {i.shared ? tr('共享') : tr('仅自己')}
), )}
{!items.length &&

{tr('没有匹配图标,可以在下方上传。')}

}
{tr('共')} {total} {tr('个图标')}
)} {onSelect && ( )}
{tr('上传我的图标 / 导入共享图标')} {shared && ( )}
); }