import log from "electron-log/renderer"; import getDatabase from "../app/local_storage/rxdb"; import { APP_SETTINGS } from "../app/local_storage/schemas"; import { cacheDataUrl } from "./cacheSlice"; export const IconTag = "icon"; // property name in json result that that should be cached as attachment let dispatch; export const setBackendDispatch = (storeDispatch) => { dispatch = storeDispatch; }; const toBlobUrl = async (url) => { let blob; const response = await fetch(url); if (response.ok) blob = await response.blob(); return blob; }; const blobToBase64 = async (blob) => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = (err) => reject(err); reader.readAsDataURL(blob); }); }; const putUrlAsAttachment = async (url, db, name) => { const blob = await toBlobUrl(url); if (blob && document) { const dataURL = await blobToBase64(blob); const result = await db.attachments.upsert({ name: name.substring(0, 40), // maxLength in schema path: url.pathname, type: blob.type, base64data: dataURL, }); return result; } return null; }; // Parse attachments from response and extract all attachments with IconTag into {name: path, ...} const parseAttachments = (object, settingPropName) => { const result = {}; const recursiveSearch = (o, name) => { if (o) { Object.entries(o).forEach(([k, v]) => { if (v && typeof v === "object") recursiveSearch(v, v.name ?? v.title); if (v && k === IconTag) result[`${settingPropName}_${name}_${IconTag}`] = v; }); } }; recursiveSearch(object); return result; }; const cacheAttachments = async (object, db, settingPropName) => { const attachments = parseAttachments(object, settingPropName); const cachedAttachments = await db.attachments.find().exec(); Object.entries(attachments).forEach(async ([name, path]) => { // in memory previous data check to prevent refetching const cachedAttachment = cachedAttachments.find( (a) => a.get("name", false) === name ); if (cachedAttachment) { const previousPath = cachedAttachment.get("path"); if (path === previousPath) { return; } // stop if attachment has not changed } // continue and refetch/cache attachment const url = new URL(path, process.env.BACKEND_URL); const attachment = await putUrlAsAttachment(url, db, name); // caching to rxdb as document if (attachment) { const dataURL = attachment.get("base64data"); dispatch(cacheDataUrl({ name, dataURL })); // caching to store } }); }; export const clearWorkplace = () => getDatabase() .then((db) => db.settings.findOne(APP_SETTINGS).exec()) .then((document) => { document?.remove(); return true; }); export const cache = (settingPropName, response, storeAction) => getDatabase() .then((db) => Promise.all([ Promise.resolve(db), db.settings.findOne(APP_SETTINGS).exec(), ]) ) .then(async (value) => { const [db, setting] = value; // caching to rxdb if (setting) { await setting.incrementalModify((oldData) => { oldData[settingPropName] = response; return oldData; }); } else { await db.settings.upsert({ _id: APP_SETTINGS, [settingPropName]: response, }); } await cacheAttachments(response, db, settingPropName); dispatch(storeAction(response)); // caching to store return true; }) .catch((e) => { log.error( `Caching error in ${[settingPropName]}: ${JSON.stringify(e).substring( 0, 300 )}...` ); });