import { openDB, type IDBPDatabase } from "idb"; import type { Readable } from "svelte/motion"; import { readable, writable } from "svelte/store"; // ##################### // ### export const intervalInSeconds = 15 * 60; // ### // ##################### export interface IGalleryImage { id: IDBValidKey; when: Date; dataUrl: string; } export interface IFetchGalleryImagesArgs { constraint?: IDBKeyRange; direction?: IDBCursorDirection; take?: number; skip?: number; } export interface IFetchGalleryImagesPage { count: number; images: Array; } export interface IFetchGalleryImagesQuery { result: Readable; refresh() : Promise; setArgs(args: IFetchGalleryImagesArgs): void; } export interface IGalleryDb { storeImage (image: Omit) : Promise; selectImages (index: string): IFetchGalleryImagesQuery; } const storeName = "images"; export async function initGallery() { const updates = Array(); const db = await openDB("image-diary", 1, { upgrade(db) { const images = db.createObjectStore(storeName, { keyPath: "id", autoIncrement: true }); images.createIndex("ix_when", ["when"]); }, }); const fnStoreImage = async (image: IGalleryImage) => { const tx = db.transaction(storeName, "readwrite"); const id = await tx.objectStore(storeName).add(image); await tx.done; for (const update of updates) { update(); } return id; } const fnSelectImages = (index: string): IFetchGalleryImagesQuery => { var skip = 0; var take = 5; var direction: IDBCursorDirection = 'next'; var constraint: IDBKeyRange | null = null; const setters = new Array<(v: IFetchGalleryImagesPage) => void>(); const fnRefresh = async () => { if (setters.length == 0) { return; } // console.log('refresh', 'skip', skip, 'take', take, 'direction', direction, 'constraint', constraint); const tx = db.transaction(storeName, "readonly"); const store = tx.objectStore(storeName); const index = store.index("ix_when"); var shouldSkip = skip; var shouldTake = take; var result = new Array(); const count = await index.count(); for await (const cursor of index.iterate(constraint, direction)) { if (shouldSkip > 0) { cursor.advance(skip); shouldSkip = 0; continue; } if (shouldTake > 0) { result.push(cursor.value); shouldTake--; continue; } break; } for (const set of setters) { set({ count: count, images: result }); } await tx.done; }; return { result: readable({count: 0, images: []}, (set) => { setters.push(set); updates.push(fnRefresh); return () => { setters.splice(setters.indexOf(set), 1); updates.splice(updates.indexOf(fnRefresh), 1); }; }), refresh: fnRefresh, setArgs: (args) => { constraint = args.constraint ?? null; direction = args.direction ?? 'next'; skip = args.skip ?? 0; take = args.take ?? 5; }, } } return { storeImage: fnStoreImage, selectImages: fnSelectImages, }; }