fix (rewrite): edge cases around cache invalidation

This commit is contained in:
MickaelK 2024-07-17 08:40:29 +10:00
parent cefb67ca62
commit bd64529e4a
5 changed files with 45 additions and 38 deletions

View file

@ -1,6 +1,6 @@
import { createElement, onDestroy } from "../lib/skeleton/index.js";
import rxjs, { effect, onClick } from "../lib/rx.js";
import { fromHref, toHref } from "../lib/skeleton/router.js";
import { navigate, fromHref, toHref } from "../lib/skeleton/router.js";
import { qs, qsa } from "../lib/dom.js";
import { settingsGet, settingsSave } from "../lib/store.js";
import { loadCSS } from "../helpers/loader.js";
@ -73,7 +73,7 @@ export default async function ctrlSidebar(render) {
}
onDestroy(() => {
$page.classList.remove("search");
state.$cache = $files.firstElementChild.cloneNode(true);
state.$cache = $files.firstElementChild?.cloneNode(true);
state.scrollTop = $page.firstElementChild.scrollTop
});
const chunk = new pathChunk();
@ -84,11 +84,13 @@ export default async function ctrlSidebar(render) {
const path = chunk.toString(i);
try {
const $list = await createListOfFiles(path, arr[i+1], fullpath);
const $anchor = i === 0 ?
$tree :
qs($tree, `[data-path="${chunk.toString(i)}"]`);
const $anchor = i === 0 ? $tree : qs($tree, `[data-path="${chunk.toString(i)}"]`);
$anchor.appendChild($list);
} catch(err) { console.error("ERROR", i, err) }
} catch(err) { // cache isn't reliable, kill everything and refresh
$files.remove();
await cache().remove("/", false);
navigate(location.pathname + location.hash + location.search);
}
}
$files.replaceChildren($tree);
$page.firstElementChild.scrollTop = state.scrollTop;
@ -96,11 +98,13 @@ export default async function ctrlSidebar(render) {
// feature: smart refresh whenever something happen
let cleaners = [];
cleaners.push(hooks.ls.listen(async ({ path }) => {
const $list = await createListOfFiles(path);
try {
const $ul = qs($page, `[data-path="${path}"] ul`);
const $list = await createListOfFiles(path);
$ul.replaceWith($list);
} catch (err) {}
} catch (err) { // happens when the cache can't be rely on
$files.replaceChildren($list);
}
}));
cleaners.push(hooks.mutation.listen(async ({ op, path }) => {
if (["mv", "mkdir", "rm"].indexOf(op) === -1) return;
@ -113,11 +117,13 @@ export default async function ctrlSidebar(render) {
onDestroy(() => cleaners.map((fn) => fn()));
// feature: highlight current selection
const $active = qs($page, `[data-path="${chunk.toString()}"] a`);
$active.classList.add("active");
if (checkVisible($active) === false) {
$active.scrollIntoView({ behavior: "smooth" });
}
try {
const $active = qs($page, `[data-path="${chunk.toString()}"] a`);
$active.classList.add("active");
if (checkVisible($active) === false) {
$active.scrollIntoView({ behavior: "smooth" });
}
} catch(err) {}
// feature: quick search
effect(rxjs.fromEvent(qs($page, "h3 input"), "keydown").pipe(

View file

@ -19,13 +19,7 @@ export default function(opts) {
}
return res;
}),
rxjs.catchError((err) => {
if (err.status === 401) {
location.href = toHref("/login?next=" + location.pathname + location.hash + location.search);
return rxjs.EMPTY;
}
return rxjs.throwError(processError(err.xhr, err))
}),
rxjs.catchError((err) => rxjs.throwError(processError(err.xhr, err))),
);
}

View file

@ -12,7 +12,7 @@ export default function(render) {
effect(deleteSession().pipe(
rxjs.mergeMap(setup_config),
rxjs.tap(() => navigate(toHref("/"))),
rxjs.tap(() => window.CONFIG["logout"] ? location.href = CONFIG["logout"] : navigate(toHref("/"))),
rxjs.catchError(ctrlError(render)),
));
}

View file

@ -1,4 +1,4 @@
import ajax from "../../lib/ajax.js";
import { getSession } from "../../model/session.js";
class ICache {
constructor() {}
@ -173,30 +173,34 @@ class IndexDBCache extends ICache {
let cache = null;
export function clearCache(path) {
console.log("TODO: clear cache");
// cache.clear(path);
export async function clearCache(path) { // TODO: remove useless function
await cache.remove(path, false);
}
export async function init() {
const setup_cache = () => {
cache = new InMemoryCache();
if (!("indexedDB" in window)) return initCacheState();
if (!("indexedDB" in window)) return;
cache = new IndexDBCache();
return Promise.all([cache.db, initCacheState()]).catch((err) => {
return cache.db.catch((err) => {
if (err === "INDEXEDDB_NOT_SUPPORTED") {
// Firefox in private mode act like if it supports indexedDB but
// is throwing that string as an error if you try to use it ...
// so we fallback with our basic ram cache
cache = new DataFromMemory();
return initCacheState();
return;
}
throw err;
});
}
const setup_session = () => {
return Promise.resolve();
const setup_session = async () => {
if (!backendID) {
try {
const session = await getSession().toPromise();
backendID = session.backendID;
} catch (err) {}
}
};
return Promise.all([setup_cache(), setup_session()]);
@ -214,11 +218,3 @@ export function currentBackend() {
export function currentShare() {
return new window.URL(location.href).searchParams.get("share") || "";
}
async function initCacheState() {
if (!backendID) {
const { responseJSON } = await ajax({ url: "/api/session", responseType: "json" }).toPromise();
backendID = responseJSON.result.backendID;
}
return
}

View file

@ -1,8 +1,10 @@
import { toHref, navigate } from "../../lib/skeleton/router.js";
import rxjs from "../../lib/rx.js";
import ajax from "../../lib/ajax.js";
import { basename, forwardURLParams } from "../../lib/path.js";
import notification from "../../components/notification.js";
import assert from "../../lib/assert.js";
import { AjaxError } from "../../lib/error.js";
import t from "../../locales/index.js";
import { currentPath } from "./helper.js";
@ -28,6 +30,14 @@ const handleError = rxjs.catchError((err) => {
notification.error(err);
throw err;
});
const handleErrorRedirectLogin = rxjs.catchError((err) => {
if (err instanceof AjaxError && err.err().status === 401) {
navigate(toHref("/login?next=" + location.pathname + location.hash + location.search));
return rxjs.EMPTY;
}
throw err;
});
const trimDirectorySuffix = (name) => name.replace(new RegExp("/$"), "");
export const touch = (path) => ajax({
@ -75,6 +85,7 @@ export const ls = (path) => {
method: "GET",
responseType: "json",
}).pipe(
handleErrorRedirectLogin,
rxjs.map(({ responseJSON }) => ({
files: responseJSON.results,
permissions: responseJSON.permissions,