mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-15 12:55:41 +01:00
chore (rewrite): persistence of settings and modal handling
This commit is contained in:
parent
b7e7e78920
commit
d0e856fe90
7 changed files with 92 additions and 53 deletions
|
|
@ -3,7 +3,8 @@ export function settingsGet(initialValues, prefix = "") {
|
|||
let currentSettings = {};
|
||||
Object.keys(initialValues).forEach((key) => {
|
||||
const settingsKey = prefix ? `${prefix}_${key}` : key;
|
||||
currentSettings[key] = raw[settingsKey];
|
||||
if (settingsKey in raw) currentSettings[key] = raw[settingsKey];
|
||||
else currentSettings[key] = initialValues[key];
|
||||
});
|
||||
return currentSettings;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,32 +134,31 @@ function componentLeft(render, { $scroll }) {
|
|||
onClick(qs($page, `[data-action="tag"]`)).pipe(rxjs.tap(() => {
|
||||
componentTag(createModal(modalOpt));
|
||||
})),
|
||||
onClick(qs($page, `[data-action="rename"]`)).pipe(
|
||||
rxjs.mergeMap(() => componentRename(
|
||||
onClick(qs($page, `[data-action="rename"]`)).pipe(rxjs.mergeMap(() => {
|
||||
const path = expandSelection()[0].path;
|
||||
return rxjs.from(componentRename(
|
||||
createModal(modalOpt),
|
||||
basename(expandSelection()[0].path.replace(new RegExp("/$"), "")),
|
||||
)),
|
||||
rxjs.mergeMap((val) => { // TODO: migrate to transcient impl
|
||||
const path = expandSelection()[0].path;
|
||||
basename(path.replace(new RegExp("/$"), "")),
|
||||
)).pipe(rxjs.mergeMap((val) => {
|
||||
const [basepath, filename] = extractPath(path);
|
||||
clearSelection();
|
||||
clearCache(path);
|
||||
clearCache(basepath + val);
|
||||
return mv(path, basepath + val);
|
||||
}),
|
||||
),
|
||||
onClick(qs($page, `[data-action="delete"]`)).pipe(
|
||||
rxjs.mergeMap(() => componentDelete(
|
||||
}));
|
||||
})),
|
||||
onClick(qs($page, `[data-action="delete"]`)).pipe(rxjs.mergeMap(() => {
|
||||
const path = expandSelection()[0].path;
|
||||
return rxjs.from(componentDelete(
|
||||
createModal(modalOpt),
|
||||
basename(expandSelection()[0].path.replace(new RegExp("/$"), "")).substr(0, 15),
|
||||
)),
|
||||
rxjs.mergeMap((val) => { // TODO: migrate to transcient impl
|
||||
basename(path.replace(new RegExp("/$"), "")).substr(0, 15),
|
||||
)).pipe(rxjs.mergeMap(() =>{
|
||||
const selection = expandSelection()[0].path;
|
||||
clearSelection();
|
||||
clearCache(selection);
|
||||
clearCache(path);
|
||||
return rm(selection);
|
||||
}),
|
||||
),
|
||||
}));
|
||||
})),
|
||||
)),
|
||||
));
|
||||
|
||||
|
|
@ -174,14 +173,16 @@ function componentLeft(render, { $scroll }) {
|
|||
</button>
|
||||
`))),
|
||||
rxjs.mergeMap(($page) => rxjs.merge(
|
||||
onClick(qs($page, `[data-action="delete"]`)).pipe(
|
||||
rxjs.mergeMap(() => componentDelete(createModal(modalOpt), "remove")),
|
||||
rxjs.mergeMap((val) => {
|
||||
const selections = expandSelection().map(({ path }) => path);
|
||||
onClick(qs($page, `[data-action="delete"]`)).pipe(rxjs.mergeMap(() => {
|
||||
const paths = expandSelection().map(({ path }) => path);
|
||||
return rxjs.from(componentDelete(
|
||||
createModal(modalOpt),
|
||||
"remove",
|
||||
)).pipe(rxjs.mergeMap((val) => {
|
||||
clearSelection();
|
||||
return rm(...selections);
|
||||
}),
|
||||
),
|
||||
return rm(...paths);
|
||||
}));
|
||||
})),
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
|
@ -203,9 +204,20 @@ function componentRight(render) {
|
|||
rxjs.share(),
|
||||
);
|
||||
|
||||
const defaultLayout = (view) => {
|
||||
switch (view) {
|
||||
case "grid": return `<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.LIST_VIEW}" alt="grid" />`;
|
||||
case "list": return `<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.GRID_VIEW}" alt="list" />`;
|
||||
default: throw new Error("NOT_IMPLEMENTED");
|
||||
}
|
||||
};
|
||||
const defaultSort = () => {
|
||||
return `<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.SORT}" alt="sort" />`;
|
||||
};
|
||||
effect(getSelection$().pipe(
|
||||
rxjs.filter((selections) => selections.length === 0),
|
||||
rxjs.map(() => render(createFragment(`
|
||||
rxjs.mergeMap(() => getState$().pipe(rxjs.first())),
|
||||
rxjs.map(({ view, sort }) => render(createFragment(`
|
||||
<form style="display: inline-block;" onsubmit="event.preventDefault()">
|
||||
<input class="hidden" placeholder="${t("search")}" name="q" style="
|
||||
background: transparent;
|
||||
|
|
@ -218,10 +230,10 @@ function componentRight(render) {
|
|||
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.MAGNIFYING_GLASS}" alt="search" />
|
||||
</button>
|
||||
<button data-action="view" title="${t("Layout")}">
|
||||
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.LIST_VIEW}" alt="list" />
|
||||
${defaultLayout(view)}
|
||||
</button>
|
||||
<button data-action="sort" title="${t("Sort")}">
|
||||
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.SORT}" alt="sort" />
|
||||
${defaultSort(sort)}
|
||||
</button>
|
||||
<div class="component_dropdown view sort" data-target="sort">
|
||||
<div class="dropdown_container">
|
||||
|
|
@ -245,13 +257,13 @@ function componentRight(render) {
|
|||
onClick(qs($page, `[data-action="view"]`)).pipe(rxjs.tap(($button) => {
|
||||
const $img = $button.querySelector("img");
|
||||
if ($img.getAttribute("alt") === "list") {
|
||||
setState("view", "list");
|
||||
$img.setAttribute("alt", "grid");
|
||||
$img.setAttribute("src", "data:image/svg+xml;base64," + ICONS.GRID_VIEW);
|
||||
} else {
|
||||
setState("view", "grid");
|
||||
$img.setAttribute("alt", "list");
|
||||
$img.setAttribute("alt", "grid");
|
||||
$img.setAttribute("src", "data:image/svg+xml;base64," + ICONS.LIST_VIEW);
|
||||
} else {
|
||||
setState("view", "list");
|
||||
$img.setAttribute("alt", "list");
|
||||
$img.setAttribute("src", "data:image/svg+xml;base64," + ICONS.GRID_VIEW);
|
||||
}
|
||||
})),
|
||||
// feature: sort button
|
||||
|
|
|
|||
|
|
@ -19,14 +19,13 @@ export default function(render, removeLabel) {
|
|||
const $input = qs($modal, "input");
|
||||
const pressOK = render($modal, (id) => {
|
||||
if (id !== MODAL_RIGHT_BUTTON) {
|
||||
ret.complete();
|
||||
return ret.toPromise();
|
||||
return;
|
||||
}
|
||||
else if (!isValid()) {
|
||||
qs($modal, ".modal-error-message").textContent = t("Doesn't match");
|
||||
return ret.toPromise();
|
||||
}
|
||||
ret.next(true);
|
||||
ret.next();
|
||||
ret.complete();
|
||||
return ret.toPromise();
|
||||
}).bind(this, MODAL_RIGHT_BUTTON);
|
||||
|
|
|
|||
|
|
@ -17,13 +17,12 @@ export default function(render, filename) {
|
|||
`);
|
||||
const ret = new rxjs.Subject();
|
||||
const $input = qs($modal, "input");
|
||||
const pressOK = render($modal, (id) => {
|
||||
const pressOK = render($modal, function (id) {
|
||||
const value = $input.value.trim();
|
||||
if (id !== MODAL_RIGHT_BUTTON) {
|
||||
ret.complete();
|
||||
return ret.toPromise();
|
||||
return;
|
||||
} else if (!value || value === filename) {
|
||||
qs($modal, ".modal-error-message").textContent = "Not Valid";
|
||||
qs($modal, ".modal-error-message").textContent = t("Not Valid");
|
||||
return ret.toPromise();
|
||||
}
|
||||
ret.next(value);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default function(render) {
|
|||
MODAL SHARE
|
||||
</div>
|
||||
`);
|
||||
render($modal, ({ id }) => {
|
||||
render($modal, (id) => {
|
||||
if (id !== 1) return;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import rxjs from "../../lib/rx.js";
|
||||
import ajax from "../../lib/ajax.js";
|
||||
import { basename } from "../../lib/path.js";
|
||||
import notification from "../../components/notification.js";
|
||||
import t from "../../locales/index.js";
|
||||
|
||||
import { currentPath } from "./helper.js";
|
||||
import { setPermissions } from "./model_acl.js";
|
||||
|
|
@ -20,34 +22,48 @@ import { ls as middlewareLs } from "./model_virtual_layer.js";
|
|||
* 3. the new file is being persisted in the screen if the API call is a success
|
||||
*/
|
||||
|
||||
const withNotification = rxjs.catchError((err) => {
|
||||
const handleSuccess = (text) => rxjs.tap(() => notification.info(text));
|
||||
const handleError = rxjs.catchError((err) => {
|
||||
notification.error(err);
|
||||
throw err;
|
||||
});
|
||||
const trimDirectorySuffix = (name) => name.replace(new RegExp("/$"), "");
|
||||
|
||||
export const touch = (path) => ajax({
|
||||
url: `api/files/touch?path=${encodeURIComponent(path)}`,
|
||||
method: "POST",
|
||||
responseType: "json",
|
||||
}).pipe(withNotification);
|
||||
}).pipe(
|
||||
handleSuccess(t("A file named '{{VALUE}}' was created", basename(path))),
|
||||
handleError,
|
||||
);
|
||||
|
||||
export const mkdir = (path) => ajax({
|
||||
url: `api/files/mkdir?path=${encodeURIComponent(path)}`,
|
||||
method: "POST",
|
||||
responseType: "json",
|
||||
}).pipe(withNotification);
|
||||
}).pipe(
|
||||
handleSuccess(t("A folder named '{{VALUE}}' was created", basename(trimDirectorySuffix(path)))),
|
||||
handleError,
|
||||
);
|
||||
|
||||
export const rm = (...paths) => rxjs.forkJoin(paths.map((path) => ajax({
|
||||
url: `api/files/rm?path=${encodeURIComponent(path)}`,
|
||||
method: "POST",
|
||||
responseType: "json",
|
||||
}).pipe(withNotification)));
|
||||
}))).pipe(
|
||||
handleSuccess(paths.length > 1 ? t("All Done!") : t("The file '{{VALUE}}' was deleted", basename(trimDirectorySuffix(paths[0])))),
|
||||
handleError,
|
||||
);
|
||||
|
||||
export const mv = (from, to) => ajax({
|
||||
url: `api/files/mv?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`,
|
||||
method: "POST",
|
||||
responseType: "json",
|
||||
}).pipe(withNotification);
|
||||
}).pipe(
|
||||
handleSuccess(t("The file '{{VALUE}}' was renamed", basename(trimDirectorySuffix(from)))),
|
||||
handleError,
|
||||
);
|
||||
|
||||
export const save = (path) => rxjs.of(null).pipe(rxjs.delay(1000));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,23 @@
|
|||
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({
|
||||
view: "grid",
|
||||
sort: "type",
|
||||
show_hidden: false,
|
||||
order: null,
|
||||
search: "",
|
||||
const state$ = new rxjs.BehaviorSubject(null);
|
||||
|
||||
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();
|
||||
export const getState$ = () => state$.asObservable().pipe(
|
||||
rxjs.filter((state) => state !== null),
|
||||
);
|
||||
|
||||
export const setState = (...args) => {
|
||||
const obj = { ...state$.value };
|
||||
|
|
@ -22,7 +29,12 @@ export const setState = (...args) => {
|
|||
}
|
||||
if (!hasChange) return
|
||||
state$.next(obj);
|
||||
settingsSave(state$.value, "filespage");
|
||||
settingsSave({
|
||||
view: state$.value.view,
|
||||
show_hidden: state$.value.show_hidden,
|
||||
sort: state$.value.sort,
|
||||
order: state$.value.order,
|
||||
}, "filespage");
|
||||
}
|
||||
|
||||
effect(rxjs.fromEvent(window, "keydown").pipe(
|
||||
|
|
|
|||
Loading…
Reference in a new issue