nidus-sync/ts/store/resource.ts
Eli Ribble ffb981e40b
Add generic resource store, start adding context cards
These cards are meant to be generic and can show an unlimited amount of
related context about a communication.
2026-05-15 17:10:03 +00:00

110 lines
2.8 KiB
TypeScript

import { defineStore } from "pinia";
import { shallowRef } from "vue";
import { SSEManager, SSEMessageResource } from "@/SSEManager";
import { useSessionStore } from "@/store/session";
import { apiClient } from "@/client";
import {
Communication,
type CommunicationDTO,
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 _resourceByURI = shallowRef<Map<string, full>>(new Map());
const _resourceFetchByURI = shallowRef<Map<string, Promise<full> | null>>(
new Map(),
);
// Subscription
SSEManager.subscribe((msg: SSEMessageResource) => {
if (msg.resource.startsWith(resource_name) && msg.type == "updated") {
fetchByURI(msg.uri);
}
});
async function byID(id: string): Promise<full> {
const uri = uriFromID(id);
const cur = _resourceFetchByURI.value.get(uri);
if (cur) {
return cur;
}
return fetchByID(id);
}
async function byURI(uri: string): Promise<full> {
const cur = _resourceFetchByURI.value.get(uri);
if (cur) {
return cur;
}
return fetchByURI(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 dtos = (await apiClient.JSONGet(url)) as dto[];
const resources = dtos.map((m: dto) => from_json(m));
resources.forEach((r: full) => {
_resourceByURI.value.set(r.uri, r);
});
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);
_resourceByURI.value.set(report.uri, report);
return report;
} catch (err) {
console.error("Error loading users:", err);
throw err;
}
}
function loadingURI(uri: string): boolean {
return !!_resourceFetchByURI.value.get(uri);
}
function uriFromID(id: string): string {
return `${api_base}/${id}`;
}
return {
byID,
byURI,
fetchAll,
loadingURI,
};
}
export const useStoreResource = defineStore("resource", () => {
return {
communication: createResourceStore<CommunicationDTO, Communication>(
"sync:communication",
"/communication",
Communication.fromJSON,
),
publicreport: createResourceStore<PublicReportDTO, PublicReport>(
"sync:publicreport",
"/publicreport",
PublicReport.fromJSON,
),
};
});