mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 08:22:24 +01:00
173 lines
6.3 KiB
JavaScript
173 lines
6.3 KiB
JavaScript
import { createElement, createRender, onDestroy } from "../../lib/skeleton/index.js";
|
|
import rxjs, { effect, stateMutation, applyMutation, preventDefault } from "../../lib/rx.js";
|
|
import { qs } from "../../lib/dom.js";
|
|
import { ApplicationError } from "../../lib/error.js";
|
|
import { transition, animate, zoomIn, slideXOut, slideXIn } from "../../lib/animate.js";
|
|
import bcrypt from "../../lib/vendor/bcrypt.js";
|
|
import { CSS } from "../../helpers/loader.js";
|
|
import modal from "../../components/modal.js";
|
|
import { get as getConfig } from "../../model/config.js";
|
|
|
|
import { get as getAdminConfig, save as saveConfig } from "./model_config.js";
|
|
import ctrlError from "../ctrl_error.js";
|
|
import WithShell from "./decorator_sidemenu.js";
|
|
import { cssHideMenu } from "./animate.js";
|
|
import { formObjToJSON$ } from "./helper_form.js";
|
|
import { getDeps } from "./model_setup.js";
|
|
|
|
import "../../components/icon.js";
|
|
|
|
const stepper$ = new rxjs.BehaviorSubject(1);
|
|
|
|
export default async function(render) {
|
|
const $page = createElement(`
|
|
<div class="component_setup">
|
|
<div data-bind="multistep-form"></div>
|
|
<style>${await CSS(import.meta.url, "ctrl_setup.css")}</style>
|
|
</div>
|
|
`);
|
|
render($page);
|
|
|
|
effect(stepper$.pipe(
|
|
rxjs.map((step) => {
|
|
if (step === 1) return WithShell(componentStep1);
|
|
else if (step === 2) return WithShell(componentStep2);
|
|
throw new ApplicationError("INTERNAL_ERROR", "Assumption failed");
|
|
}),
|
|
rxjs.tap((ctrl) => ctrl(createRender(qs($page, `[data-bind="multistep-form"]`)))),
|
|
rxjs.catchError((err) => ctrlError(err)(render)),
|
|
));
|
|
};
|
|
|
|
function componentStep1(render) {
|
|
const $page = createElement(`
|
|
<div id="step1">
|
|
<h4>Admin Password</h4>
|
|
<div>
|
|
<p>Create your instance admin password: </p>
|
|
<form>
|
|
<div class="input_group">
|
|
<input type="password" name="password" placeholder="Password" class="component_input" autocomplete autofocus>
|
|
<button class="transparent">
|
|
<component-icon name="arrow_right"></component-icon>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<style>${cssHideMenu}</style>
|
|
</div>
|
|
`);
|
|
render(transition($page, {
|
|
timeEnter: 250,
|
|
enter: zoomIn(1.2),
|
|
timeLeave: 0
|
|
}));
|
|
|
|
// feature: form handling
|
|
effect(rxjs.fromEvent(qs($page, "form"), "submit").pipe(
|
|
preventDefault(),
|
|
rxjs.mapTo(["name", "loading"]), applyMutation(qs($page, "component-icon"), "setAttribute"),
|
|
rxjs.map(() => qs($page, "input").value),
|
|
rxjs.combineLatestWith(getAdminConfig().pipe(rxjs.first())),
|
|
rxjs.map(([pwd, config]) => {
|
|
config["auth"]["admin"]["value"] = bcrypt.hashSync(pwd);
|
|
return config;
|
|
}),
|
|
reshapeConfigBeforeSave,
|
|
saveConfig(),
|
|
rxjs.tap(() => animate($page, { time: 200, keyframes: slideXOut(-30) })),
|
|
rxjs.delay(200),
|
|
rxjs.tap(() => stepper$.next(2))
|
|
));
|
|
}
|
|
|
|
const reshapeConfigBeforeSave = rxjs.pipe(
|
|
formObjToJSON$(),
|
|
rxjs.combineLatestWith(getConfig().pipe(rxjs.first())),
|
|
rxjs.map(([config, publicConfig]) => {
|
|
config["connections"] = publicConfig["connections"];
|
|
return config;
|
|
}),
|
|
)
|
|
|
|
function componentStep2(render) {
|
|
const deps = [];
|
|
const $page = createElement(`
|
|
<div id="step2">
|
|
<h4>
|
|
<component-icon name="arrow_left" data-bind="previous"></component-icon>
|
|
Summary
|
|
</h4>
|
|
<div data-bind="dependencies"></div>
|
|
<style>${cssHideMenu}</style>
|
|
</div>
|
|
`);
|
|
render($page);
|
|
|
|
// feature: show state of dependencies
|
|
effect(getDeps().pipe(
|
|
rxjs.mergeMap((deps) => deps),
|
|
rxjs.map(({ name_success, name_failure, pass, severe, message }) => ({
|
|
className: (severe ? "severe" : "") + " " +(pass ? "yes" : "no"),
|
|
label: pass ? name_success : name_failure,
|
|
extraLabel: pass ? "" : ": " + message,
|
|
})),
|
|
rxjs.map(({ label, className, extraLabel }) => createElement(`
|
|
<div class="component_dependency_installed ${className}">
|
|
<span>${label}</span>${extraLabel}
|
|
</div>
|
|
`)),
|
|
applyMutation(qs($page, `[data-bind="dependencies"]`), "appendChild"),
|
|
))
|
|
|
|
// feature: navigate previous step
|
|
effect(rxjs.fromEvent(qs($page, `[data-bind="previous"]`), "click").pipe(
|
|
rxjs.tap(() => stepper$.next(1))
|
|
));
|
|
|
|
// feature: reveal animation
|
|
effect(rxjs.of(null).pipe(
|
|
rxjs.tap(() => animate(qs($page, "h4"), { time: 200, keyframes: slideXIn(30) })),
|
|
rxjs.delay(200),
|
|
rxjs.mapTo([]), applyMutation(qs($page, "style"), "remove")
|
|
));
|
|
|
|
// feature: telemetry popup
|
|
const $modal = createElement(`
|
|
<div>
|
|
<p style="text-align: justify;">
|
|
Help making this software better by sending crash reports and anonymous usage statistics
|
|
</p>
|
|
<form style="font-size: 0.9em; margin-top: 10px;">
|
|
<label>
|
|
<div class="component_checkbox">
|
|
<input type="checkbox">
|
|
<span class="indicator"></span>
|
|
</div>
|
|
I accept but the data is not to be share with any third party
|
|
</label>
|
|
</form>
|
|
</div>
|
|
`);
|
|
effect(getAdminConfig().pipe(
|
|
reshapeConfigBeforeSave,
|
|
rxjs.delay(300),
|
|
rxjs.filter((config) => config["log"]["telemetry"] !== true),
|
|
rxjs.mergeMap((config) => new Promise((next) => {
|
|
modal.open($modal, {
|
|
withButtonsRight: "OK",
|
|
onQuit: () => next(config),
|
|
});
|
|
qs($modal, `[type="checkbox"]`).oninput = (e) => {
|
|
if (!e.target.checked) return;
|
|
qs(document, "component-modal > div").click();
|
|
}
|
|
})),
|
|
rxjs.filter(() => qs($modal, `[type="checkbox"]`).checked),
|
|
rxjs.map((config) => {
|
|
config["log"]["telemetry"] = true;
|
|
return config;
|
|
}),
|
|
saveConfig(),
|
|
));
|
|
}
|