import { defineStore } from "pinia"; import { ref, shallowRef, shallowReactive } from "vue"; import { log } from "@/log"; import { SSEManager, SSEMessageResource } from "@/SSEManager"; import { apiClient } from "@/client"; import { Communication, type CommunicationDTO, Contact, type ContactDTO, PublicReport, type PublicReportDTO, } from "@/type/api"; interface uriHaver { uri: string; } type jsonConverter = (arg: dto) => full; function createResourceStore( resource_name: string, api_base: string, from_json: jsonConverter, ) { const byURI = shallowReactive>(new Map()); const _resourceFetchAll = ref | null>(null); const _resourceFetchByURI = shallowRef | null>>( new Map(), ); // Subscription SSEManager.subscribe((msg: SSEMessageResource) => { if (msg.resource.startsWith(resource_name)) { if (msg.type == "created") { console.log("New resource", resource_name, msg.resource, msg.uri); fetchByURI(msg.uri); } else if (msg.type == "updated") { console.log("Updated resource", resource_name, msg.resource, msg.uri); fetchByURI(msg.uri); } } }); async function byID(id: string): Promise { const uri = uriFromID(id); return ensureURI(uri); } async function ensureURI(uri: string): Promise { // Check if we already have the data const existing = byURI.get(uri); if (existing) { return existing; } // Check if we're already fetching it let fetchPromise = _resourceFetchByURI.value.get(uri); if (fetchPromise) { return fetchPromise; } // Start fetching fetchPromise = fetchByURI(uri); _resourceFetchByURI.value.set(uri, fetchPromise); try { const result = await fetchPromise; return result; } finally { // Clean up the promise after it resolves _resourceFetchByURI.value.delete(uri); } } async function fetchAll(): Promise { /* const sessionStore = useSessionStore(); const session = await sessionStore.get(); const params = new URLSearchParams(); params.append("sort", "-created"); const url = `${session.urls.api.mailer}?${params}`; */ const url = `/api${api_base}`; const dtos = (await apiClient.JSONGet(url)) as dto[]; const resources = dtos.map((m: dto) => from_json(m)); //let new_all = new Map(); resources.forEach((r: full) => { byURI.set(r.uri, r); //new_all.set(r.uri, r); log.info("Set", resource_name, r); }); //byURI.value = new_all; return resources; } async function fetchByID(id: string): Promise { const uri = uriFromID(id); return fetchByURI(uri); } async function fetchByURI(uri: string): Promise { try { const response = await fetch(uri); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const body: dto = await response.json(); const report = from_json(body); //let new_all = new Map(byURI.value); //new_all.set(report.uri, report); byURI.set(report.uri, report); //byURI.value = new_all; return report; } catch (err) { console.error("Error loading users:", err); throw err; } } function hasAll(): boolean { return !!_resourceFetchAll.value; } function loadingAll(): boolean { return !!_resourceFetchAll.value; } function loadingURI(uri: string | undefined): boolean { if (uri === undefined) { return false; } return !!_resourceFetchByURI.value.get(uri); } function uriFromID(id: string): string { return `${api_base}/${id}`; } return { byID, byURI, ensureURI, fetchAll, fetchByID, fetchByURI, hasAll, loadingAll, loadingURI, }; } export const useStoreResource = defineStore("resource", () => { return { communication: createResourceStore( "sync:communication", "/communication", Communication.fromJSON, ), contact: createResourceStore( "sync:contact", "/contact", Contact.fromJSON, ), publicreport: createResourceStore( "sync:publicreport", "/publicreport", PublicReport.fromJSON, ), }; });