nidus-sync/ts/store/resource.ts
Eli Ribble cecb9ef0f0
Some checks failed
/ golint (push) Failing after 10s
Fix reactive nature of generic resource store
These changes are meant to make it possible for new events that come in
to immediately render in the components that depend on them.
2026-05-21 23:12:37 +00:00

162 lines
4.1 KiB
TypeScript

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<dto, full> = (arg: dto) => full;
function createResourceStore<dto, full extends uriHaver>(
resource_name: string,
api_base: string,
from_json: jsonConverter<dto, full>,
) {
const byURI = shallowReactive<Map<string, full>>(new Map());
const _resourceFetchAll = ref<Promise<full[]> | null>(null);
const _resourceFetchByURI = shallowRef<Map<string, Promise<full> | 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<full> {
const uri = uriFromID(id);
return ensureURI(uri);
}
async function ensureURI(uri: string): Promise<full> {
// 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<full[]> {
/*
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<string, full>();
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<full> {
const uri = uriFromID(id);
return fetchByURI(uri);
}
async function fetchByURI(uri: string): Promise<full> {
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<string, full>(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<CommunicationDTO, Communication>(
"sync:communication",
"/communication",
Communication.fromJSON,
),
contact: createResourceStore<ContactDTO, Contact>(
"sync:contact",
"/contact",
Contact.fromJSON,
),
publicreport: createResourceStore<PublicReportDTO, PublicReport>(
"sync:publicreport",
"/publicreport",
PublicReport.fromJSON,
),
};
});