import { useEffect, useRef, useState } from 'react'; import { api, type SpaceEmoji } from '../../../api/client'; import { useExpressionStore } from '../../../stores/expressionStore'; import { useTransferStore } from '../../../stores/transferStore'; import { waitForTransferAttachment } from '../../../utils/waitForTransfer'; import { useT } from '../../../i18n'; interface ExpressionsPanelProps { spaceId: string; } /** Emoji e figurinha são carregados a cada mensagem: têm de ser leves. */ const MAX_BYTES = 512 * 1024; type Kind = 'emoji' | 'sticker'; export function ExpressionsPanel({ spaceId }: ExpressionsPanelProps) { const t = useT(); const emojis = useExpressionStore((s) => s.emojisBySpace.get(spaceId)) ?? []; const stickers = useExpressionStore((s) => s.stickersBySpace.get(spaceId)) ?? []; const setEmojis = useExpressionStore((s) => s.setEmojis); const setStickers = useExpressionStore((s) => s.setStickers); const [kind, setKind] = useState('emoji'); const [pending, setPending] = useState(null); const [name, setName] = useState(''); const [busy, setBusy] = useState(false); const [error, setError] = useState(''); const fileRef = useRef(null); useEffect(() => { let cancelled = false; Promise.all([api.expressions.emojis(spaceId), api.expressions.stickers(spaceId)]) .then(([e, s]) => { if (cancelled) return; setEmojis(spaceId, e.emojis); setStickers(spaceId, s.stickers); }) .catch(() => { /* lista vazia é o fallback honesto */ }); return () => { cancelled = true; }; }, [spaceId, setEmojis, setStickers]); const pickFile = (file: File) => { setError(''); if (file.size > MAX_BYTES) { setError(t('expressions.tooLarge')); return; } setPending(file); // Sugere o nome do arquivo já no formato aceito, para o caso comum não // exigir digitação nenhuma. const base = file.name.replace(/\.[^.]+$/, ''); setName(kind === 'emoji' ? base.toLowerCase().replace(/[^a-z0-9_]+/g, '_').slice(0, 32) : base.slice(0, 32)); }; const cancel = () => { setPending(null); setName(''); if (fileRef.current) fileRef.current.value = ''; }; const confirm = async () => { const file = pending; const trimmed = name.trim(); if (!file || !trimmed) return; setBusy(true); setError(''); try { const tid = await useTransferStore.getState().startUpload(file, { tray: false }); const { filename } = await waitForTransferAttachment(tid); if (kind === 'emoji') { const created = await api.expressions.addEmoji(spaceId, trimmed, filename); setEmojis(spaceId, [...emojis, created]); } else { const created = await api.expressions.addSticker(spaceId, trimmed, filename); setStickers(spaceId, [...stickers, created]); } cancel(); } catch (err) { // 409 é nome repetido — mensagem específica, porque a ação corretiva é // outra: mudar o nome, não trocar a imagem. const conflict = (err as { statusCode?: number } | undefined)?.statusCode === 409; setError(conflict ? t('expressions.nameTaken') : t('expressions.uploadFailed')); } finally { setBusy(false); } }; const remove = async (item: SpaceEmoji) => { const isEmoji = kind === 'emoji'; const previous = isEmoji ? emojis : stickers; const next = previous.filter((x) => x.id !== item.id); if (isEmoji) setEmojis(spaceId, next); else setStickers(spaceId, next); try { await (isEmoji ? api.expressions.removeEmoji(item.id) : api.expressions.removeSticker(item.id)); } catch { if (isEmoji) setEmojis(spaceId, previous); else setStickers(spaceId, previous); } }; const items = kind === 'emoji' ? emojis : stickers; return (

{t('expressions.title')}

{(['emoji', 'sticker'] as Kind[]).map((k) => ( ))}
{ const f = e.target.files?.[0]; if (f) pickFile(f); }} /> {error &&

{error}

} {pending && (
setName(e.target.value)} onKeyDown={(e) => { e.stopPropagation(); if (e.key === 'Enter' && name.trim()) void confirm(); if (e.key === 'Escape') cancel(); }} className="input-search w-full mb-1" /> {kind === 'emoji' && (

{t('expressions.nameHintEmoji')}

)}
)}
{items.length === 0 ? (

{kind === 'emoji' ? t('expressions.emptyEmojis') : t('expressions.emptyStickers')}

) : (
{items.map((item) => (
{item.name}
{kind === 'emoji' ? `:${item.name}:` : item.name}
))}
)}
); }