nidus-sync/ts/store/resource.ts
Eli Ribble b4ae9e5a95
Move communication workbench to use resource store
Because it's getting better all the time, including by adding the
ability to get new resources when they get created over SSE.
2026-05-20 23:49:59 +00:00

151 lines
3.6 KiB
TypeScript

import { defineStore } from "pinia";
import { ref, shallowRef } from "vue";
import { SSEManager, SSEMessageResource } from "@/SSEManager";
import { useSessionStore } from "@/store/session";
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 _resourceByURI = shallowRef<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") {
fetchByURI(msg.uri);
} else if (msg.type == "updated") {
fetchByURI(msg.uri);
}
}
});
async function byAll(): Promise<full[]> {
const cur = _resourceFetchAll.value;
if (cur) {
return cur;
}
return fetchAll();
}
async function byID(id: string): Promise<full> {
const uri = uriFromID(id);
return byURI(uri);
}
async function byURI(uri: string): Promise<full> {
let cur = _resourceFetchByURI.value.get(uri);
if (cur) {
return cur;
}
cur = fetchByURI(uri);
_resourceFetchByURI.value.set(uri, cur);
return cur;
}
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));
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 getAll(): full[] | null {
if (_resourceFetchAll) {
return Array.from(_resourceByURI.value.values());
}
return null;
}
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 {
byAll,
byID,
byURI,
fetchAll,
fetchByID,
fetchByURI,
getAll,
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,
),
};
});