diff --git a/public/assets/boot/ctrl_boot_frontoffice.js b/public/assets/boot/ctrl_boot_frontoffice.js index e3c4780a..24cb9d5a 100644 --- a/public/assets/boot/ctrl_boot_frontoffice.js +++ b/public/assets/boot/ctrl_boot_frontoffice.js @@ -3,27 +3,27 @@ import { toHref } from "../lib/skeleton/router.js"; // import { setup_cache } from "../helpers/cache.js"; import { init as setup_loader, loadJS } from "../helpers/loader.js"; import { init as setup_translation } from "../locales/index.js"; +import { init as setup_config } from "../model/config.js"; +import { init as setup_chromecast } from "../model/chromecast.js"; import { report } from "../helpers/log.js"; export default async function main() { try { - let config = {}; - // await Config.refresh() - await Promise.all([ // procedure with no outside dependencies + setup_config(), setup_translation(), setup_xdg_open(), // setup_cache(), // TODO: dependency on session setup_device(), - // setup_sw(), // TODO + setup_sw(), setup_blue_death_screen(), setup_loader(), setup_history(), ]); await Promise.all([ // procedure with dependency on config - // setup_chromecast() // TODO - setup_base(config), + setup_chromecast(), + setup_title(), ]); window.dispatchEvent(new window.Event("pagechange")); @@ -72,30 +72,23 @@ async function setup_device() { }); } -async function setup_base(config) { - // TODO: base as config in admin - const $meta = document.createElement("base"); - $meta.setAttribute("href", location.origin); - document.head.appendChild($meta); +async function setup_sw() { + if (!("serviceWorker" in window.navigator)) return; + + if (window.navigator.userAgent.indexOf("Mozilla/") !== -1 && + window.navigator.userAgent.indexOf("Firefox/") !== -1 && + window.navigator.userAgent.indexOf("Gecko/") !== -1) { + // Firefox was acting weird with service worker so we disabled it + // see: https://github.com/mickael-kerjean/filestash/issues/255 + return; + } + try { + // await window.navigator.serviceWorker.register("/sw_cache.js"); + } catch (err) { + report("ServiceWorker registration failed", err); + } } -// async function setup_sw() { -// if (!("serviceWorker" in window.navigator)) return; - -// if (window.navigator.userAgent.indexOf("Mozilla/") !== -1 && -// window.navigator.userAgent.indexOf("Firefox/") !== -1 && -// window.navigator.userAgent.indexOf("Gecko/") !== -1) { -// // Firefox was acting weird with service worker so we disabled it -// // see: https://github.com/mickael-kerjean/filestash/issues/255 -// return; -// } -// try { -// await window.navigator.serviceWorker.register("/sw_cache.js"); -// } catch (err) { -// report("ServiceWorker registration failed", err); -// } -// } - async function setup_blue_death_screen() { window.onerror = function(msg, url, lineNo, colNo, error) { report(msg, error, url, lineNo, colNo); @@ -117,3 +110,7 @@ async function setup_blue_death_screen() { async function setup_history() { window.history.replaceState({}, ""); } + +async function setup_title() { + document.title = CONFIG.name || "Filestash"; +} diff --git a/public/assets/components/breadcrumb.js b/public/assets/components/breadcrumb.js index bf00705b..053896a1 100644 --- a/public/assets/components/breadcrumb.js +++ b/public/assets/components/breadcrumb.js @@ -72,7 +72,7 @@ class ComponentBreadcrumb extends window.HTMLElement { // STEP2: setup the actual content this.querySelector(`[data-bind="path"]`).innerHTML = pathChunks.map((chunk, idx) => { - const label = idx === 0 ? "Filestash" : chunk; + const label = idx === 0 ? (window.CONFIG.name || "Filestash") : chunk; const link = pathChunks.slice(0, idx + 1).join("/") + "/"; const limitSize = (word, highlight = false) => { if (highlight === true && word.length > 30) { diff --git a/public/assets/model/chromecast.js b/public/assets/model/chromecast.js new file mode 100644 index 00000000..46ffd32c --- /dev/null +++ b/public/assets/model/chromecast.js @@ -0,0 +1,81 @@ +export async function init() { + if (!CONFIG.enable_chromecast) { + return Promise.resolve(); + } else if (!("chrome" in window)) { + return Promise.resolve(); + } else if (location.hostname === "localhost" || location.hostname === "127.0.0.1") { + return Promise.resolve(); + } + // return Chromecast.init(); +} + +// import { Session } from "./session"; +// import { currentShare, objectGet } from "../helpers/"; + +// class ChromecastManager { +// init() { +// return new Promise((done) => { +// const script = document.createElement("script"); +// script.src = "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"; +// script.onerror = () => done() +// window["__onGCastApiAvailable"] = function(isAvailable) { +// if (isAvailable) cast.framework.CastContext.getInstance().setOptions({ +// receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID, +// autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED, +// }); +// done(); +// }; +// document.head.appendChild(script) +// }); +// } + +// origin() { +// return location.origin; +// }; + +// createLink(apiPath) { +// const shareID = currentShare(); +// if (shareID) { +// const target = new URL(this.origin() + apiPath); +// target.searchParams.append("share", shareID); +// return target.toString(); +// } +// const target = new URL(this.origin() + apiPath) +// return target.toString(); +// } + +// createRequest(mediaInfo) { +// let prior = Promise.resolve(); +// if (!Session.authorization) prior = Session.currentUser(); +// return prior.then(() => { +// if (!Session.authorization) throw new Error("Invalid account"); +// // TODO: it would be much much nicer to set the authorization from an HTTP header +// // but this would require to create a custom web receiver app, setup accounts on +// // google, etc,... Until that happens, we're setting the authorization within the +// // url. Once we have that app, the authorisation will come from a customData field +// // of a chrome.cast.media.LoadRequest +// const target = new URL(mediaInfo.contentId); +// target.searchParams.append("authorization", Session.authorization); +// mediaInfo.contentId = target.toString(); +// return new chrome.cast.media.LoadRequest(mediaInfo); +// }); +// } + +// context() { +// if (!objectGet(window.chrome, ["cast", "isAvailable"])) { +// return; +// } +// return cast.framework.CastContext.getInstance(); +// } +// session() { +// const context = this.context(); +// if (!context) return; +// return context.getCurrentSession(); +// } +// media() { +// const session = this.session(); +// if (!session) return; +// return session.getMediaSession(); +// } +// } +// export const Chromecast = new ChromecastManager(); diff --git a/public/assets/model/config.js b/public/assets/model/config.js index fcfcd046..5adee060 100644 --- a/public/assets/model/config.js +++ b/public/assets/model/config.js @@ -9,6 +9,7 @@ const config$ = ajax({ rxjs.map(({ responseJSON }) => responseJSON.result), ); -export function get() { - return config$; +export async function init() { + let config = await config$.toPromise(); + window.CONFIG = config; } diff --git a/public/assets/pages/ctrl_filespage.js b/public/assets/pages/ctrl_filespage.js index 86b297be..a2a3d548 100644 --- a/public/assets/pages/ctrl_filespage.js +++ b/public/assets/pages/ctrl_filespage.js @@ -9,7 +9,6 @@ import componentFilesystem, { init as initFilesystem } from "./filespage/ctrl_fi import componentSubmenu, { init as initSubmenu } from "./filespage/ctrl_submenu.js"; import componentNewItem, { init as initNewItem } from "./filespage/ctrl_newitem.js"; import componentUpload, { init as initUpload } from "./filespage/ctrl_upload.js"; -import { get as getConfig } from "./filespage/model_config.js"; import "../components/breadcrumb.js"; @@ -44,7 +43,7 @@ export default WithShell(function(render) { export function init() { return Promise.all([ loadCSS(import.meta.url, "ctrl_filespage.css"), - initShell(), initFilesystem(), getConfig().toPromise(), + initShell(), initFilesystem(), initSubmenu(), initNewItem(), initUpload(), ]); } diff --git a/public/assets/pages/ctrl_logout.js b/public/assets/pages/ctrl_logout.js index 2a699881..e23610b1 100644 --- a/public/assets/pages/ctrl_logout.js +++ b/public/assets/pages/ctrl_logout.js @@ -5,11 +5,13 @@ import rxjs, { effect } from "../lib/rx.js"; import { deleteSession } from "../model/session.js"; import ctrlError from "./ctrl_error.js"; import $loader from "../components/loader.js"; +import { init as setup_config } from "../model/config.js"; export default function(render) { render($loader); effect(deleteSession().pipe( + rxjs.mergeMap(setup_config), rxjs.tap(() => navigate(toHref("/"))), rxjs.catchError(ctrlError(render)), )); diff --git a/public/assets/pages/ctrl_viewerpage.js b/public/assets/pages/ctrl_viewerpage.js index 89975d37..94ad5255 100644 --- a/public/assets/pages/ctrl_viewerpage.js +++ b/public/assets/pages/ctrl_viewerpage.js @@ -4,7 +4,6 @@ import { ApplicationError } from "../lib/error.js"; import { basename } from "../lib/path.js"; import { loadCSS } from "../helpers/loader.js"; import WithShell, { init as initShell } from "../components/decorator_shell_filemanager.js"; -import { get as getConfig } from "../model/config.js"; import ctrlError from "./ctrl_error.js"; import { opener } from "./viewerpage/mimetype.js"; @@ -12,11 +11,6 @@ import { getCurrentPath } from "./viewerpage/common.js"; import "../components/breadcrumb.js"; -const mime$ = getConfig().pipe( - rxjs.map((config) => config.mime), - rxjs.shareReplay(), -); - function loadModule(appName) { switch (appName) { case "editor": @@ -48,7 +42,7 @@ export default WithShell(async function(render) { const $page = createElement(`
`); render($page); - effect(mime$.pipe( + effect(rxjs.of(CONFIG.mime || {}).pipe( rxjs.map((mimes) => opener(basename(getCurrentPath()), mimes)), rxjs.mergeMap(([opener, options]) => rxjs.from(loadModule(opener)).pipe( rxjs.map((module) => module.default(createRender($page), options)), @@ -61,7 +55,7 @@ export async function init() { return Promise.all([ loadCSS(import.meta.url, "./ctrl_viewerpage.css"), initShell(), - mime$.pipe( + rxjs.of(CONFIG.mime || {}).pipe( rxjs.map((mimes) => opener(basename(getCurrentPath()), mimes)), rxjs.mergeMap(([opener]) => loadModule(opener)), rxjs.mergeMap((module) => typeof module.init === "function"? module.init() : rxjs.EMPTY), diff --git a/public/assets/pages/filespage/ctrl_upload.css b/public/assets/pages/filespage/ctrl_upload.css index 292aa4c7..1c077027 100644 --- a/public/assets/pages/filespage/ctrl_upload.css +++ b/public/assets/pages/filespage/ctrl_upload.css @@ -13,6 +13,10 @@ border-top-left-radius: 15px; border-top-right-radius: 15px; } +.dark-mode .component_upload { + color: var(--bg-color); +} + .component_upload h2 { padding: 20px 20px 5px 20px; margin: 0 0 5px 0; @@ -28,13 +32,13 @@ } .component_upload h2 .count_block span.grandTotal { font-size: 0.8em; - color: var(--emphasis-secondary); + opacity: 0.8; } .component_upload h2 .count_block span.grandTotal:before { content: "/"; } .component_upload h2 .count_block span.completed { - color: var(--emphasis-secondary); + opacity: 0.8; } .component_upload h2 .component_icon { cursor: pointer; @@ -66,7 +70,7 @@ color: var(--error); } .component_upload .stats_content .todo_color { - color: var(--light); + opacity: 0.7; } .component_upload .stats_content .file_row { display: flex; diff --git a/public/assets/pages/filespage/ctrl_upload.js b/public/assets/pages/filespage/ctrl_upload.js index 82f71a7a..265152fb 100644 --- a/public/assets/pages/filespage/ctrl_upload.js +++ b/public/assets/pages/filespage/ctrl_upload.js @@ -89,13 +89,15 @@ function componentFilezone(render, { workers$ }) { const MAX_WORKERS = 4; +// TODO: calculate total + function componentUploadQueue(render, { workers$ }) { const $page = createElement(` `); + const updateTotal = { + reset: () => { + qs($page, ".grandTotal").innerText = 0; + qs($page, ".completed").innerText = 0; + }, + addToTotal: (n) => { + const $total = qs($page, ".grandTotal"); + $total.innerText = parseInt($total.innerText) + n; + }, + incrementCompleted: () => { + const $completed = qs($page, ".completed"); + $completed.innerText = parseInt($completed.innerText) + 1; + }, + }; // feature1: close the queue onClick(qs($page, `img[alt="close"]`)).pipe( rxjs.tap(async (cancel) => { const cleanup = await animate($page, { time: 200, keyframes: slideYOut(50) }); - qs($page, ".stats_content").innerHTML = ""; + $content.innerHTML = ""; $page.classList.add("hidden"); + updateTotal.reset(); cleanup(); }), ).subscribe(); @@ -126,6 +143,7 @@ function componentUploadQueue(render, { workers$ }) { // feature2: setup the task queue in the dom workers$.subscribe(({ tasks }) => { if (tasks.length === 0) return; + updateTotal.addToTotal(tasks.length); const $fragment = document.createDocumentFragment(); for (let i = 0; i { - state$.next(settingsGet({ - view: config.default_view || "grid", - show_hidden: config.display_hidden || false, - sort: config.default_sort || "type", - order: null, - search: "", - }, "filespage")); -}); - -export const getState$ = () => state$.asObservable().pipe( - rxjs.filter((state) => state !== null), -); +export const getState$ = () => state$.asObservable(); export const setState = (...args) => { const obj = { ...state$.value }; diff --git a/public/assets/pages/filespage/thing.js b/public/assets/pages/filespage/thing.js index db32e7e4..88db6999 100644 --- a/public/assets/pages/filespage/thing.js +++ b/public/assets/pages/filespage/thing.js @@ -5,7 +5,6 @@ import { animate, opacityIn } from "../../lib/animate.js"; import assert from "../../lib/assert.js"; import { extractPath, isDir, isNativeFileUpload } from "./helper.js"; -import { get as getConfig } from "./model_config.js"; import { files$ } from "./ctrl_filesystem.js"; import { addSelection, isSelected, clearSelection } from "./state_selection.js"; @@ -26,15 +25,15 @@ const IMAGE = { let TYPES = { - MIME: {}, - THUMBNAILER: new Set(), + MIME: window.CONFIG.mime, + THUMBNAILER: (function() { + const set = new Set(); + for (let i=0; i { - TYPES.MIME = config.mime; - for (let i=0; i diff --git a/public/assets/pages/viewerpage/application_editor.js b/public/assets/pages/viewerpage/application_editor.js index 7cc6332e..3633f228 100644 --- a/public/assets/pages/viewerpage/application_editor.js +++ b/public/assets/pages/viewerpage/application_editor.js @@ -7,7 +7,6 @@ import { createModal, MODAL_RIGHT_BUTTON } from "../../components/modal.js"; import { loadCSS, loadJS } from "../../helpers/loader.js"; import ajax from "../../lib/ajax.js"; import { extname } from "../../lib/path.js"; -import { get as getConfig } from "../../model/config.js"; import t from "../../locales/index.js"; import ctrlError from "../ctrl_error.js"; @@ -38,7 +37,6 @@ export default async function(render) { render($page); renderMenubar($dom.menubar(), buttonDownload(getFilename(), getDownloadUrl())); - const getConfig$ = getConfig().pipe(rxjs.shareReplay(1)); const content$ = new rxjs.ReplaySubject(1); // feature1: setup the dom @@ -59,7 +57,7 @@ export default async function(render) { } return rxjs.of(content); }), - rxjs.mergeMap((content) => getConfig$.pipe( + rxjs.mergeMap((content) => rxjs.of(window.CONFIG).pipe( rxjs.mergeMap((config) => rxjs.from(loadKeybinding(config.editor)).pipe(rxjs.mapTo(config))), rxjs.map((config) => [content, config]), rxjs.mergeMap((arr) => rxjs.from(loadMode(extname(getFilename()))).pipe(