From 5b9adeda065fbf5d85d92fac256ed1ac7859b2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ca=C5=82ka?= Date: Mon, 15 Apr 2024 23:54:44 +0200 Subject: [PATCH] refactor: use `Map` for generation state --- src/components/BulkGenerateModal.tsx | 141 ++++++++++++------- src/components/BulkGenerationStatus.tsx | 27 ++-- src/components/BulkGenerationStatusTable.tsx | 66 ++++----- src/hooks/useAttachments.ts | 12 +- src/media/media-upload.tsx | 2 +- src/types/index.ts | 21 +-- tsconfig.json | 1 + 7 files changed, 152 insertions(+), 118 deletions(-) diff --git a/src/components/BulkGenerateModal.tsx b/src/components/BulkGenerateModal.tsx index 8974151..a65b30f 100644 --- a/src/components/BulkGenerateModal.tsx +++ b/src/components/BulkGenerateModal.tsx @@ -8,75 +8,116 @@ import { ToggleControl, } from "@wordpress/components"; import { __, _n, _x, sprintf } from "@wordpress/i18n"; +import { decodeEntities } from "@wordpress/html-entities"; -import type { AttachmentWithGenerationStatus } from "../types"; +import type { AltGenerationMap } from "../types"; import BulkGenerationStatusTable from "./BulkGenerationStatusTable"; import generateAltText from "../utils/generateAltText"; import sleep from "../utils/sleep"; import useAttachments from "../hooks/useAttachments"; export default function BulkGenerateModal({ - attachment_ids, + attachmentIds, onClose, }: BulkGenerateModalProps) { const [overwriteExisting, setOverwriteExisting] = useState(false); const [customPrompt, setCustomPrompt] = useState(""); const [isGenerating, setIsGenerating] = useState(false); - const [attachments, setAttachments] = useState< - AttachmentWithGenerationStatus[] - >([]); - - const { attachmentData, hasAttachmentDataResolved } = - useAttachments(attachment_ids); + const { attachments, hasResolved } = useAttachments(attachmentIds); + const [altGenerationMap, setAltGenerationMap] = useState( + new Map(attachmentIds.map((id) => [id, { status: "", alt: "" }])), + ); useEffect(() => { - if (!hasAttachmentDataResolved) return; + if (!attachments.length) return; + + setAltGenerationMap((prevMap) => { + const newMap = new Map(prevMap); + + attachments.forEach((attachment) => { + const details = newMap.get(attachment.id); + if (!details) { + console.error( + "Generation details not found for attachment", + attachment, + ); + return; + } - setAttachments( - attachmentData.map((attachment) => ({ - ...attachment, - generation_status: "waiting", - })), - ); - }, [attachmentData, hasAttachmentDataResolved]); + details.alt = attachment.alt_text; + details.title = decodeEntities(attachment.title.rendered); + details.source_url = attachment.source_url; + + const thumbnail = + //@ts-ignore - missing WP types + attachment.media_details.sizes?.thumbnail ?? + attachment.media_details.sizes?.[0]; + + if (thumbnail) + details.thumbnail = { + width: thumbnail.width, + height: thumbnail.height, + source_url: thumbnail.source_url, + }; + + newMap.set(attachment.id, details); + }); + + return newMap; + }); + }, [attachments, hasResolved]); const handleStart = async () => { setIsGenerating(true); - for (let attachment of attachments) { - if (!overwriteExisting && attachment.alt_text.length) { - setAttachments((prev) => - prev.map((prevAttachment) => - prevAttachment.id === attachment.id - ? { ...prevAttachment, generation_status: "skipped" } - : prevAttachment, - ), - ); - continue; - } - - generateAltText(attachment.id, true) - .then((res) => { - setAttachments((prev) => { - const newAttachments = [...prev]; - const index = newAttachments.findIndex( - (prevAttachment) => prevAttachment.id === attachment.id, - ); - newAttachments[index] = { - ...newAttachments[index], - alt_text: res, - generation_status: "done", - }; - return newAttachments; - }); + setAltGenerationMap( + (prevMap) => + new Map( + Array.from(prevMap, ([id, details]) => { + details.status = "queued"; + + if (!overwriteExisting && details.alt.length) { + details.status = "skipped"; + } + + return [id, details]; + }), + ), + ); + + for (const [id, details] of altGenerationMap) { + if (details.status !== "queued") continue; + + setAltGenerationMap( + (prevMap) => + new Map(prevMap.set(id, { ...details, status: "generating" })), + ); + + generateAltText(id, true) + .then((alt) => { + setAltGenerationMap( + (prevMap) => + new Map(prevMap.set(id, { ...details, alt, status: "done" })), + ); }) .catch((error) => { - attachment.generation_status = "error"; + setAltGenerationMap( + (prevMap) => + new Map( + prevMap.set(id, { + ...details, + status: "error", + message: error.message, + }), + ), + ); console.error(error); }); // Wait for 1 second before processing the next image to avoid too many requests at once - // TODO: Use rate limiting info and implement a better solution + // TODO: + // Use rate limiting info and implement a better solution + // https://platform.openai.com/docs/guides/rate-limits/rate-limits-in-headers await sleep(1000); } @@ -131,8 +172,8 @@ export default function BulkGenerateModal({ /> @@ -141,10 +182,10 @@ export default function BulkGenerateModal({ _n( "%d image selected", "%d images selected", - attachment_ids.length, + attachmentIds.length, "alt-text-generator-gpt-vision", ), - attachment_ids.length, + attachmentIds.length, )}

@@ -156,7 +197,7 @@ export default function BulkGenerateModal({