diff --git a/public/assets/boot/ctrl_boot_frontoffice.js b/public/assets/boot/ctrl_boot_frontoffice.js index 2f0214e5..3764a3a5 100644 --- a/public/assets/boot/ctrl_boot_frontoffice.js +++ b/public/assets/boot/ctrl_boot_frontoffice.js @@ -1,6 +1,5 @@ import rxjs, { ajax } from "../lib/rx.js"; // import { setup_cache } from "../helpers/cache.js"; -import { init as setup_translation } from "../locales/index.js"; import { init as setup_loader, loadJS } from "../helpers/loader.js"; import { report } from "../helpers/log.js"; @@ -50,6 +49,45 @@ function $error(msg) { document.body.appendChild($code); } +/// ///////////////////////////////////////// +// boot steps helpers +function setup_translation() { + let selectedLanguage = "en"; + switch (window.navigator.language) { + case "zh-TW": + selectedLanguage = "zh_tw"; + break; + default: + const userLanguage = window.navigator.language.split("-")[0] || "en"; + const idx = [ + "az", "be", "bg", "ca", "cs", "da", "de", "el", "es", "et", + "eu", "fi", "fr", "gl", "hr", "hu", "id", "is", "it", "ja", + "ka", "ko", "lt", "lv", "mn", "nb", "nl", "pl", "pt", "ro", + "ru", "sk", "sl", "sr", "sv", "th", "tr", "uk", "vi", "zh" + ].indexOf(window.navigator.language.split("-")[0] || ""); + if (idx !== -1) { + selectedLanguage = userLanguage; + } + } + + if (selectedLanguage === "en") { + return; + } + return ajax({ + url: "/assets/locales/" + selectedLanguage + ".json", + responseType: "json" + }).pipe( + rxjs.tap(({ responseHeaders, response }) => { + const contentType = responseHeaders["content-type"].trim(); + if (contentType !== "application/json") { + report("boot::translation", new Error(`wrong content type '${contentType}'`), "ctrl_boot_frontoffice.js"); + return; + } + window.LNG = response; + }) + ).toPromise(); +} + async function setup_xdg_open() { window.overrides = {}; return loadJS(import.meta.url, "/overrides/xdg-open.js"); @@ -62,7 +100,7 @@ async function setup_device() { if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { document.body.classList.add("dark-mode"); } - window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", function(e) { + window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", function(e) { e.matches ? document.body.classList.add("dark-mode") : document.body.classList.remove("dark-mode"); }); } diff --git a/public/assets/components/breadcrumb.js b/public/assets/components/breadcrumb.js index c7bdb0e8..e07a43f6 100644 --- a/public/assets/components/breadcrumb.js +++ b/public/assets/components/breadcrumb.js @@ -2,7 +2,7 @@ import { animate, slideYOut, slideYIn, opacityOut } from "../lib/animate.js"; import { loadCSS } from "../helpers/loader.js"; import { mv } from "../pages/filespage/model_files.js"; -import { extractPath } from "../pages/filespage/helper.js"; +import { extractPath, isDir } from "../pages/filespage/helper.js"; class ComponentBreadcrumb extends window.HTMLDivElement { constructor() { @@ -171,7 +171,8 @@ class ComponentBreadcrumb extends window.HTMLDivElement { let to = $path.getAttribute("data-path"); const [fromBasepath, fromName] = extractPath(from); - to += fromName + "/"; + to += fromName; + if (isDir(from)) to += "/"; await mv(from, to).toPromise(); }; }); diff --git a/public/assets/pages/adminpage/model_release.js b/public/assets/pages/adminpage/model_release.js index 938200e3..8bfeb091 100644 --- a/public/assets/pages/adminpage/model_release.js +++ b/public/assets/pages/adminpage/model_release.js @@ -3,7 +3,7 @@ import ajax from "../../lib/ajax.js"; const release$ = ajax({ url: "/about", - responseType: "text" + responseType: "text", }).pipe(rxjs.shareReplay(1)); export function get() { diff --git a/public/assets/pages/ctrl_error.js b/public/assets/pages/ctrl_error.js index d3c85ceb..dcc4f334 100644 --- a/public/assets/pages/ctrl_error.js +++ b/public/assets/pages/ctrl_error.js @@ -124,7 +124,7 @@ const css = ` } `; -function calculateBacklink(pathname) { +function calculateBacklink(pathname = "") { let url = "/"; const listPath = pathname.replace(new RegExp("/$"), "").split("/"); switch (listPath[1]) { diff --git a/public/assets/pages/ctrl_filespage.js b/public/assets/pages/ctrl_filespage.js index 2e5e11be..d6856a24 100644 --- a/public/assets/pages/ctrl_filespage.js +++ b/public/assets/pages/ctrl_filespage.js @@ -3,6 +3,7 @@ import rxjs, { effect } from "../lib/rx.js"; import { qs } from "../lib/dom.js"; import { loadCSS } from "../helpers/loader.js"; import WithShell, { init as initShell } from "../components/decorator_shell_filemanager.js"; +import { get as getConfig } from "./filespage/model_config.js"; import componentFilesystem, { init as initFilesystem } from "./filespage/ctrl_filesystem.js"; import componentSubmenu, { init as initSubmenu } from "./filespage/ctrl_submenu.js"; @@ -38,7 +39,7 @@ export default WithShell(function(render) { export function init() { return Promise.all([ loadCSS(import.meta.url, "./ctrl_filespage.css"), - initShell(), initFilesystem(), + initShell(), initFilesystem(), getConfig().toPromise(), initSubmenu(), initNewItem(), initUpload(), ]); } diff --git a/public/assets/pages/filespage/cache_transcient.js b/public/assets/pages/filespage/cache_transcient.js index 0a73ef88..01a9baa3 100644 --- a/public/assets/pages/filespage/cache_transcient.js +++ b/public/assets/pages/filespage/cache_transcient.js @@ -1,7 +1,7 @@ import { onDestroy } from "../../lib/skeleton/index.js"; import rxjs from "../../lib/rx.js"; import fscache from "./cache.js"; -import { extractPath } from "./helper.js"; +import { extractPath, isDir } from "./helper.js"; /* * The transcient cache is used to rerender the list of files in a particular location. That's used @@ -215,7 +215,6 @@ export function mv(ajax$, fromPath, toPath) { }); } const onSuccess = async () => { - console.log(fromPath, toPath) fscache().remove(fromPath, false); if (fromBasepath === toBasepath) { stateAdd(mutationFiles$, fromBasepath, { @@ -244,7 +243,21 @@ export function mv(ajax$, fromPath, toPath) { return file; }, }); - removeLoading(virtualFiles$, toBasepath, toName); + onDestroy(() => statePop(mutationFiles$, fromBasepath, fromName)); + statePop(virtualFiles$, toBasepath, toName); + await fscache().update(fromBasepath, ({ files, ...rest }) => ({ + files: files.filter((file) => file.name === fromName ? false : true), + ...rest, + })) + await fscache().update(toBasepath, ({ files, ...rest }) => ({ + files: files.concat([{ + name: fromName, + time: new Date().getTime(), + type, + }]), + ...rest, + })); + if (isDir(fromPath)) await fscache.remove(fromPath); } }; const onFailure = () => { diff --git a/public/assets/pages/filespage/ctrl_filesystem.js b/public/assets/pages/filespage/ctrl_filesystem.js index 47d19047..63f8871b 100644 --- a/public/assets/pages/filespage/ctrl_filesystem.js +++ b/public/assets/pages/filespage/ctrl_filesystem.js @@ -269,7 +269,7 @@ export default async function(render) { shift: false, files: [], }); if (elm1) addSelection({ - n: (files$.value || []).length, + n: (files$.value || []).length - 1, path: path + elm1.name + (elm1.type === "directory" ? "/" : ""), shift: true, files: (files$.value || []), }); diff --git a/public/assets/pages/filespage/ctrl_submenu.js b/public/assets/pages/filespage/ctrl_submenu.js index 1b1c1352..a8d92ee3 100644 --- a/public/assets/pages/filespage/ctrl_submenu.js +++ b/public/assets/pages/filespage/ctrl_submenu.js @@ -55,7 +55,7 @@ export default async function(render) { function componentLeft(render, { $scroll }) { effect(getSelection$().pipe( - rxjs.filter((selections) => selections.length === 0), + rxjs.filter(() => lengthSelection() === 0), rxjs.mergeMap(() => rxjs.merge(rxjs.fromEvent(window, "resize"), rxjs.of(null))), rxjs.mergeMap(() => getPermission()), rxjs.map(() => render(createFragment(` @@ -87,15 +87,15 @@ function componentLeft(render, { $scroll }) { onDestroy(() => setAction(null)); effect(getSelection$().pipe( - rxjs.filter((selections) => selections.length === 1), + rxjs.filter(() => lengthSelection() === 1), rxjs.map(() => render(createFragment(` - - + `); render($page); diff --git a/public/assets/pages/viewerpage/application_iframe.js b/public/assets/pages/viewerpage/application_iframe.js index ce7a2ecf..5148786b 100644 --- a/public/assets/pages/viewerpage/application_iframe.js +++ b/public/assets/pages/viewerpage/application_iframe.js @@ -3,13 +3,14 @@ import rxjs, { effect } from "../../lib/rx.js"; import { loadCSS } from "../../helpers/loader.js"; import assert from "../../lib/assert.js"; import ctrlError from "../ctrl_error.js"; +import notification from "../../components/notification.js"; -import { getDownloadUrl } from "./common.js"; +import { getCurrentPath } from "./common.js"; -export default function(render, opts = {}) { +export default function(render, { endpoint = "" }) { const $page = createElement(`
- +
`); render($page); @@ -19,10 +20,10 @@ export default function(render, opts = {}) { rxjs.tap((event) => { // TODO: notification switch (event.data.type) { case "notify::info": - // notify.send(t(event.data.message), "info"); + notify.info(t(event.data.message)); break; case "notify::error": - // notify.send(t(event.data.message), "error"); + notify.error(t(event.data.message)); break; } }), diff --git a/public/assets/pages/viewerpage/application_image.js b/public/assets/pages/viewerpage/application_image.js index 9d0018c8..e72474ce 100644 --- a/public/assets/pages/viewerpage/application_image.js +++ b/public/assets/pages/viewerpage/application_image.js @@ -22,7 +22,7 @@ export default function(render) {
-
+ `); diff --git a/public/tsconfig.json b/public/tsconfig.json index 47075a8b..ecd70f2f 100644 --- a/public/tsconfig.json +++ b/public/tsconfig.json @@ -3,9 +3,7 @@ "allowJs": true, "checkJs": true, "noEmit": true, - "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, "noImplicitReturns": true, "noImplicitThis": true, "noUnusedLocals": true, diff --git a/public/vite.config.js b/public/vite.config.js new file mode 100644 index 00000000..f6cc1d8f --- /dev/null +++ b/public/vite.config.js @@ -0,0 +1,18 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig(({ comand, mode }) => { + return { + plugins: [], + test: { + global: true, + environment: "jsdom", + setupFiles: ["./test/setup.js"], + }, + coverage: { + reporter: ["text", "html"], + exclude: [ + "./public/assets/lib/vendor/**" + ], + } + }; +});