fixed stickers and emojies creation packs
This commit is contained in:
parent
43894f2fc9
commit
139967399d
16 changed files with 771 additions and 22 deletions
|
|
@ -1,7 +1,8 @@
|
|||
import { ChevronLeft, ChevronRight, Loader2, X } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ChevronLeft, ChevronRight, Loader2, Plus, Trash2, X } from "lucide-react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { api, errorMessage } from "../api";
|
||||
import { ActionButton } from "../components/ActionButton";
|
||||
import { StickerDocumentPreview } from "../components/StickerDocumentPreview";
|
||||
import { Alert } from "../components/ui";
|
||||
import { useI18n } from "../i18n";
|
||||
|
|
@ -15,15 +16,14 @@ const PAGE_SIZE = 24;
|
|||
|
||||
export function StickerSetPreviewModal({ set, onClose }: { set: StickerSetRow; onClose: () => void }) {
|
||||
const { t } = useI18n();
|
||||
const noun = set.Kind === "emoji" ? "emoji" : "sticker";
|
||||
const [documentIDs, setDocumentIDs] = useState<string[] | null>(null);
|
||||
const [error, setError] = useState("");
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
const load = useCallback(() => {
|
||||
let cancelled = false;
|
||||
setDocumentIDs(null);
|
||||
setError("");
|
||||
setPage(1);
|
||||
api.stickerSetDocuments(set.ID).then((result) => {
|
||||
if (cancelled) return;
|
||||
setDocumentIDs(result.document_ids ?? []);
|
||||
|
|
@ -33,6 +33,12 @@ export function StickerSetPreviewModal({ set, onClose }: { set: StickerSetRow; o
|
|||
return () => { cancelled = true; };
|
||||
}, [set.ID]);
|
||||
|
||||
useEffect(() => {
|
||||
setDocumentIDs(null);
|
||||
setPage(1);
|
||||
return load();
|
||||
}, [load]);
|
||||
|
||||
const total = documentIDs?.length ?? 0;
|
||||
const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
|
||||
const currentPage = Math.min(page, totalPages);
|
||||
|
|
@ -52,6 +58,7 @@ export function StickerSetPreviewModal({ set, onClose }: { set: StickerSetRow; o
|
|||
<button className="icon-btn" type="button" onClick={onClose} aria-label={t("action.close")}><X size={15} /></button>
|
||||
</div>
|
||||
<div className="command-body">
|
||||
<AddStickerForm setID={set.ID} noun={noun} onAdded={load} />
|
||||
{error && <Alert>{error}</Alert>}
|
||||
{!error && documentIDs === null && (
|
||||
<div className="loading-line"><Loader2 className="spin" size={18} /> {t("common.loading")}</div>
|
||||
|
|
@ -61,7 +68,20 @@ export function StickerSetPreviewModal({ set, onClose }: { set: StickerSetRow; o
|
|||
)}
|
||||
{pageItems.length > 0 && (
|
||||
<div className="sticker-doc-grid" key={currentPage}>
|
||||
{pageItems.map((documentID) => <StickerDocumentPreview key={documentID} documentID={documentID} />)}
|
||||
{pageItems.map((documentID) => (
|
||||
<div className="sticker-doc-grid-cell" key={documentID}>
|
||||
<StickerDocumentPreview documentID={documentID} />
|
||||
<ActionButton
|
||||
compact
|
||||
tone="danger"
|
||||
label={t("stickers.removeSticker", { noun })}
|
||||
icon={<Trash2 size={12} />}
|
||||
path="/api/actions/remove-sticker-from-set"
|
||||
payload={() => ({ set_id: set.ID, document_id: documentID })}
|
||||
onDone={load}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{total > PAGE_SIZE && (
|
||||
|
|
@ -84,3 +104,62 @@ export function StickerSetPreviewModal({ set, onClose }: { set: StickerSetRow; o
|
|||
document.body
|
||||
);
|
||||
}
|
||||
|
||||
// Inline upload form embedded in the preview modal — file + emoji + a
|
||||
// mandatory audit reason, single confirmed call (no separate dry-run preview
|
||||
// step): unlike destructive actions, materializing one sticker document is
|
||||
// low-risk and reversible via the per-cell Remove button.
|
||||
function AddStickerForm({ setID, noun, onAdded }: { setID: string; noun: string; onAdded: () => void }) {
|
||||
const { t } = useI18n();
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [emoji, setEmoji] = useState("");
|
||||
const [reason, setReason] = useState("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
async function submit() {
|
||||
if (!file) {
|
||||
setError(t("stickers.fileRequired", { noun }));
|
||||
return;
|
||||
}
|
||||
if (!emoji.trim()) {
|
||||
setError(t("stickers.emojiRequired"));
|
||||
return;
|
||||
}
|
||||
if (!reason.trim()) {
|
||||
setError(t("action.reasonRequired"));
|
||||
return;
|
||||
}
|
||||
setBusy(true);
|
||||
setError("");
|
||||
try {
|
||||
const form = new FormData();
|
||||
form.set("metadata", JSON.stringify({ command_id: "", reason: reason.trim(), confirm: true, set_id: setID, emoji: emoji.trim() }));
|
||||
form.set("file", file, file.name);
|
||||
await api.addStickerToSet(form);
|
||||
setFile(null);
|
||||
setEmoji("");
|
||||
setReason("");
|
||||
onAdded();
|
||||
} catch (err) {
|
||||
setError(errorMessage(err));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sticker-add-form">
|
||||
<label className={`gift-file-picker compact ${file ? "has-file" : ""}`}>
|
||||
<input type="file" accept=".tgs,.json,.webp,application/json,application/x-tgsticker,image/webp" onChange={(event) => setFile(event.target.files?.[0] ?? null)} />
|
||||
<span className="gift-file-copy"><strong>{file ? file.name : t("stickers.filePrompt")}</strong></span>
|
||||
</label>
|
||||
<input className="small-input" value={emoji} onChange={(event) => setEmoji(event.target.value)} placeholder={t("stickers.emojiPlaceholder")} />
|
||||
<input className="small-input" value={reason} onChange={(event) => setReason(event.target.value)} placeholder={t("action.reasonPlaceholder")} />
|
||||
<button className="btn primary compact-btn" type="button" onClick={submit} disabled={busy}>
|
||||
{busy ? <Loader2 className="spin" size={14} /> : <Plus size={14} />} {t("stickers.addSticker", { noun })}
|
||||
</button>
|
||||
{error && <span className="sticker-add-form-error">{error}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue