160 lines
4.8 KiB
TypeScript
160 lines
4.8 KiB
TypeScript
import { createEffect, onCleanup } from "solid-js";
|
|
import { createStore } from "solid-js/store";
|
|
import { RecordModel, RecordSubscription } from "pocketbase";
|
|
import * as pbf from "@nedpals/pbf";
|
|
import { z } from "zod";
|
|
import { nanoid } from "nanoid";
|
|
|
|
import { usePB } from "./context.js";
|
|
|
|
interface IUseRecordOptions {
|
|
realtime?: boolean;
|
|
validator?: z.AnyZodObject;
|
|
expand?: string[];
|
|
}
|
|
|
|
export function useRecord<T>(collection: string, id: () => string, options?: IUseRecordOptions) {
|
|
const pb = usePB();
|
|
const requestKey = nanoid();
|
|
|
|
const [record, setRecord] = createStore<{
|
|
loading: boolean,
|
|
record: T & RecordModel | null,
|
|
error: Error | null,
|
|
}>({
|
|
loading: true,
|
|
error: null,
|
|
record: null,
|
|
});
|
|
|
|
const v = options?.validator ?? ({ parse: (i) => id });
|
|
|
|
createEffect(() => {
|
|
const cid = id();
|
|
|
|
const setRecordChecked: typeof setRecord = (...args) => {
|
|
if (cid !== id()) return;
|
|
(setRecord as any)(...args);
|
|
}
|
|
|
|
setRecordChecked("loading", () => true)
|
|
setRecordChecked("error", () => null)
|
|
pb.collection(collection).getOne(cid, {
|
|
expand: options?.expand ? options.expand.join(",") : undefined,
|
|
requestKey,
|
|
}).then((value) => {
|
|
setRecordChecked("record", () => v.parse(value) as T & RecordModel)
|
|
}).catch((error) => {
|
|
setRecordChecked("error", () => error as Error)
|
|
}).finally(() => {
|
|
setRecordChecked("loading", () => false)
|
|
});
|
|
|
|
const sub = pb.realtime.subscribe(collection + "/" + cid, (value) => {
|
|
//TODO: Check what actions exist and if some needs special handling
|
|
setRecordChecked("record", () => v.parse(value.record) as T & RecordModel)
|
|
}, {
|
|
query: {
|
|
expand: options?.expand ? options.expand.join(",") : undefined,
|
|
requestKey: requestKey + "-rt",
|
|
}
|
|
});
|
|
|
|
sub.catch(err => { console.error("Error in realtime subscription", err) });
|
|
|
|
onCleanup(() => {
|
|
pb.cancelRequest(requestKey);
|
|
pb.cancelRequest(requestKey + "-rt");
|
|
sub.then(s => s());
|
|
});
|
|
})
|
|
|
|
return record;
|
|
}
|
|
|
|
interface IUseRecordsOptions {
|
|
realtime?: boolean;
|
|
validator?: z.AnyZodObject;
|
|
filter?: () => pbf.MaybeFilter<pbf.Filter> | undefined,
|
|
expand?: string[];
|
|
}
|
|
|
|
export function useRecords<T>(collection: string, options: IUseRecordsOptions = { expand: [] }) {
|
|
const pb = usePB();
|
|
const requestKey = nanoid();
|
|
|
|
const fstr = () => options.filter && options.filter() ? pbf.stringify(options.filter()) : undefined;
|
|
const v = options.validator ?? ({ parse: (i) => i });
|
|
|
|
const [records, setRecords] = createStore<{
|
|
loading: boolean,
|
|
records: (T & RecordModel)[],
|
|
error: Error | null,
|
|
}>({
|
|
records: [],
|
|
loading: true,
|
|
error: null,
|
|
});
|
|
|
|
createEffect(() => {
|
|
const cf = fstr();
|
|
|
|
const setRecordChecked: typeof setRecords = (...args) => {
|
|
if (cf !== fstr()) return;
|
|
(setRecords as any)(...args);
|
|
}
|
|
|
|
setRecordChecked("loading", () => true)
|
|
setRecordChecked("error", () => null)
|
|
|
|
pb.collection(collection).getFullList({
|
|
requestKey,
|
|
filter: cf,
|
|
expand: options?.expand ? options.expand.join(",") : undefined,
|
|
}).then((values) => {
|
|
setRecordChecked("records", () => values.map(value => v.parse(value) as T & RecordModel))
|
|
}).catch((error) => {
|
|
setRecordChecked("error", () => error as Error)
|
|
}).finally(() => {
|
|
setRecordChecked("loading", () => false)
|
|
});
|
|
|
|
const sub = pb.realtime.subscribe(collection, (event: RecordSubscription<T & RecordModel>) => {
|
|
console.log("Event", event);
|
|
const rec = v.parse(event.record) as T & RecordModel;
|
|
switch (event.action) {
|
|
case "create":
|
|
setRecordChecked("records", (records) => [...records, rec]);
|
|
break;
|
|
|
|
case "update":
|
|
setRecordChecked("records", (records) => records.map(r => r.id === rec.id ? rec : r));
|
|
break;
|
|
|
|
case "delete":
|
|
setRecordChecked("records", (records) => records.filter(r => r.id !== rec.id));
|
|
break;
|
|
|
|
default:
|
|
console.log("Unknown action", event.action);
|
|
}
|
|
}, {
|
|
requestKey: requestKey + "-rt",
|
|
query: {
|
|
filter: cf,
|
|
expand: options?.expand ? options.expand.join(",") : undefined,
|
|
}
|
|
});
|
|
|
|
sub.then(unsub => { console.log("Subscribed to realtime changes!"); });
|
|
sub.catch(err => { console.error("Error in realtime subscription", err) });
|
|
|
|
onCleanup(() => {
|
|
pb.cancelRequest(requestKey);
|
|
pb.cancelRequest(requestKey + "-rt");
|
|
sub.then(s => s());
|
|
});
|
|
})
|
|
|
|
return records;
|
|
} |