chore (rewrite): get config at page init

This commit is contained in:
MickaelK
2024-07-02 19:28:30 +10:00
parent 5099422836
commit e11a039c7c
13 changed files with 161 additions and 82 deletions

View File

@ -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";
}

View File

@ -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) {

View File

@ -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();

View File

@ -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;
}

View File

@ -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(),
]);
}

View File

@ -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)),
));

View File

@ -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(`<div class="component_page_viewerpage"></div>`);
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),

View File

@ -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;

View File

@ -89,13 +89,15 @@ function componentFilezone(render, { workers$ }) {
const MAX_WORKERS = 4;
// TODO: calculate total
function componentUploadQueue(render, { workers$ }) {
const $page = createElement(`
<div class="component_upload hidden">
<h2 class="no-select">${t("Current Upload")}
<div class="count_block">
<span class="completed">24</span>
<span class="grandTotal">24</span>
<span class="completed">0</span>
<span class="grandTotal">0</span>
</div>
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MS45NzYgNTEuOTc2Ij4KICA8cGF0aCBzdHlsZT0iZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eTowLjUzMzMzMjg1O3N0cm9rZS13aWR0aDoxLjQ1NjgxMTE5IiBkPSJtIDQxLjAwNTMxLDQwLjg0NDA2MiBjIC0xLjEzNzc2OCwxLjEzNzc2NSAtMi45ODIwODgsMS4xMzc3NjUgLTQuMTE5ODYxLDAgTCAyNi4wNjg2MjgsMzAuMDI3MjM0IDE0LjczNzU1MSw0MS4zNTgzMSBjIC0xLjEzNzc3MSwxLjEzNzc3MSAtMi45ODIwOTMsMS4xMzc3NzEgLTQuMTE5ODYxLDAgLTEuMTM3NzcyMiwtMS4xMzc3NjggLTEuMTM3NzcyMiwtMi45ODIwODggMCwtNC4xMTk4NjEgTCAyMS45NDg3NjYsMjUuOTA3MzcyIDExLjEzMTkzOCwxNS4wOTA1NTEgYyAtMS4xMzc3NjQ3LC0xLjEzNzc3MSAtMS4xMzc3NjQ3LC0yLjk4MzU1MyAwLC00LjExOTg2MSAxLjEzNzc3NCwtMS4xMzc3NzIxIDIuOTgyMDk4LC0xLjEzNzc3MjEgNC4xMTk4NjUsMCBMIDI2LjA2ODYyOCwyMS43ODc1MTIgMzYuMzY5NzM5LDExLjQ4NjM5OSBjIDEuMTM3NzY4LC0xLjEzNzc2OCAyLjk4MjA5MywtMS4xMzc3NjggNC4xMTk4NjIsMCAxLjEzNzc2NywxLjEzNzc2OSAxLjEzNzc2NywyLjk4MjA5NCAwLDQuMTE5ODYyIEwgMzAuMTg4NDg5LDI1LjkwNzM3MiA0MS4wMDUzMSwzNi43MjQxOTcgYyAxLjEzNzc3MSwxLjEzNzc2NyAxLjEzNzc3MSwyLjk4MjA5MSAwLDQuMTE5ODY1IHoiIC8+Cjwvc3ZnPgo=" alt="close">
</h2>
@ -112,13 +114,28 @@ function componentUploadQueue(render, { workers$ }) {
<div class="file_control no-select"></div>
</div>
`);
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<tasks.length; i++) {
const $task = $file.cloneNode(true);
@ -230,6 +248,7 @@ function componentUploadQueue(render, { workers$ }) {
} catch(err) {
updateDOMWithStatus($task, { exec, status: "error", nworker });
}
updateTotal.incrementCompleted(1);
task.done = true;
if (tasks.length === 0 // no remaining tasks

View File

@ -1,8 +0,0 @@
import rxjs from "../../lib/rx.js";
import { get as getConfig } from "../../model/config.js";
const config$ = getConfig().pipe(rxjs.shareReplay(1));
export function get() {
return config$;
}

View File

@ -1,23 +1,16 @@
import { onDestroy } from "../../lib/skeleton/index.js";
import rxjs, { effect, preventDefault } from "../../lib/rx.js";
import { settingsGet, settingsSave } from "../../lib/store.js";
import { get as getConfig } from "./model_config.js";
const state$ = new rxjs.BehaviorSubject(null);
const state$ = new rxjs.BehaviorSubject(settingsGet({
view: window.CONFIG.default_view || "grid",
show_hidden: window.CONFIG.display_hidden || false,
sort: window.CONFIG.default_sort || "type",
order: null,
search: "",
}, "filespage"));
getConfig().subscribe((config) => {
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 };

View File

@ -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<window.CONFIG.thumbnailer.length; i++) {
set.add(window.CONFIG.thumbnailer[i]);
}
return set;
})(),
};
getConfig().subscribe((config) => {
TYPES.MIME = config.mime;
for (let i=0; i<config.thumbnailer.length; i++) {
TYPES.THUMBNAILER.add(config.thumbnailer[i]);
}
});
const $tmpl = createElement(`
<a href="__TEMPLATE__" class="component_thing no-select" draggable="false" data-link>

View File

@ -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(