import { createStore } from "solid-js/store"; import { type InferOutput, literal, pipe, safeParse, strictObject, string, union, uuid, } from "valibot"; import { base } from "../network"; import { createContext, createEffect, onCleanup } from "solid-js"; import { useSearchImageContext } from "../contexts/SearchImageContext"; const processingImagesValidator = strictObject({ ImageID: pipe(string(), uuid()), ImageName: string(), Status: union([ literal("not-started"), literal("in-progress"), literal("complete"), ]), }); type NotificationState = { ProcessingImages: Record< string, InferOutput | undefined >; }; export const Notifications = (onCompleteImage: () => void) => { const [state, setState] = createStore({ ProcessingImages: {}, }); const { processingImages } = useSearchImageContext(); const access = localStorage.getItem("access"); if (access == null) { throw new Error("Access token not defined"); } const dataEventListener = (e: MessageEvent) => { if (typeof e.data !== "string") { console.error("Error type is not string"); return; } let jsonData: object = {}; try { jsonData = JSON.parse(e.data); } catch (e) { console.error(e); return; } const processingImage = safeParse(processingImagesValidator, jsonData); if (!processingImage.success) { console.error("Processing image could not be parsed.", e.data); return; } console.log("SSE: ", processingImage); const { ImageID, Status } = processingImage.output; if (Status === "complete") { setState("ProcessingImages", ImageID, undefined); onCompleteImage(); } else { setState("ProcessingImages", ImageID, processingImage.output); } }; const upsertImageProcessing = ( images: NotificationState["ProcessingImages"], ) => { setState("ProcessingImages", (currentImages) => ({ ...currentImages, ...images, })); }; createEffect(() => { const images = processingImages(); if (images == null) { return; } upsertImageProcessing( Object.fromEntries( images.map((i) => [ i.ImageID, { ImageID: i.ImageID, ImageName: i.Image.ImageName, Status: i.Status, }, ]), ), ); }); const events = new EventSource(`${base}/notifications?token=${access}`); events.addEventListener("data", dataEventListener); events.onerror = (e) => { console.error(e); }; onCleanup(() => { events.removeEventListener("data", dataEventListener); events.close(); }); return { state, }; }; export const NotificationsContext = createContext>();