diff --git a/public/assets/components/breadcrumb.js b/public/assets/components/breadcrumb.js
index e07a43f6..c68760d0 100644
--- a/public/assets/components/breadcrumb.js
+++ b/public/assets/components/breadcrumb.js
@@ -1,8 +1,14 @@
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, isDir } from "../pages/filespage/helper.js";
+import { extractPath, isDir, isNativeFileUpload } from "../pages/filespage/helper.js";
+import { mv as mv$ } from "../pages/filespage/model_files.js";
+import { mv as mvVL, withVirtualLayer } from "../pages/filespage/model_virtual_layer.js";
+
+const mv = (from, to) => withVirtualLayer(
+ mv$(from, to),
+ mvVL(from, to),
+);
class ComponentBreadcrumb extends window.HTMLDivElement {
constructor() {
@@ -158,13 +164,6 @@ class ComponentBreadcrumb extends window.HTMLDivElement {
setupDragDropTarget() {
this.querySelectorAll("a.label").forEach(($folder) => {
const $path = $folder.closest(".component_path-element");
- $folder.ondragover = (e) => {
- e.preventDefault();
- $path.classList.add("highlight");
- };
- $folder.ondragleave = () => {
- $path.classList.remove("highlight");
- };
$folder.ondrop = async (e) => {
$path.classList.remove("highlight");
const from = e.dataTransfer.getData("path");
@@ -175,6 +174,14 @@ class ComponentBreadcrumb extends window.HTMLDivElement {
if (isDir(from)) to += "/";
await mv(from, to).toPromise();
};
+ $folder.ondragover = (e) => {
+ if (isNativeFileUpload(e)) return;
+ e.preventDefault();
+ $path.classList.add("highlight");
+ };
+ $folder.ondragleave = () => {
+ $path.classList.remove("highlight");
+ };
});
}
diff --git a/public/assets/pages/filespage/ctrl_upload.js b/public/assets/pages/filespage/ctrl_upload.js
index c8fa1b7d..36790816 100644
--- a/public/assets/pages/filespage/ctrl_upload.js
+++ b/public/assets/pages/filespage/ctrl_upload.js
@@ -5,7 +5,7 @@ import { loadCSS } from "../../helpers/loader.js";
import { qs } from "../../lib/dom.js";
import { AjaxError } from "../../lib/error.js";
import assert from "../../lib/assert.js";
-import { currentPath } from "./helper.js";
+import { currentPath, isNativeFileUpload } from "./helper.js";
import { mkdir, save } from "./model_virtual_layer.js";
import t from "../../locales/index.js";
@@ -55,22 +55,17 @@ function componentUploadFAB(render, { workers$ }) {
}
function componentFilezone(render, { workers$ }) {
- const $target = document.body.querySelector(`[data-bind="filemanager-children"]`);
+ const selector = `[data-bind="filemanager-children"]`;
+ const $target = document.body.querySelector(selector);
+
$target.ondragenter = (e) => {
- e.preventDefault();
- e.stopPropagation();
+ if (!isNativeFileUpload(e)) return;
$target.classList.add("dropzone");
- e.dataTransfer.setData("type", "fileupload");
- };
- $target.ondragover = (e) => {
- e.preventDefault();
- };
- $target.ondragleave = () => {
- // console.log("DRAGLEAVE");
};
$target.ondrop = async (e) => {
+ if (!isNativeFileUpload(e)) return;
+ $target.classList.remove("dropzone");
e.preventDefault();
- e.stopPropagation();
const loadID = setTimeout(() => render(createElement("
LOADING
")), 2000);
if (e.dataTransfer.items instanceof window.DataTransferItemList) {
workers$.next(await processItems(e.dataTransfer.items));
@@ -79,10 +74,17 @@ function componentFilezone(render, { workers$ }) {
} else {
assert.fail("NOT_IMPLEMENTED - unknown entry type in ctrl_upload.js", entry);
}
- $target.classList.remove("dropzone");
clearTimeout(loadID);
render(createFragment(""));
};
+ $target.ondragleave = (e) => {
+ if (!isNativeFileUpload(e)) return;
+ if (!(e.relatedTarget === null || // eg: drag outside the window
+ !e.relatedTarget.closest(selector) // eg: drag on the breadcrumb, ...
+ )) return;
+ $target.classList.remove("dropzone");
+ };
+ $target.ondragover = (e) => e.preventDefault();
}
const MAX_WORKERS = 4;
@@ -113,13 +115,12 @@ function componentUploadQueue(render, { workers$ }) {
// feature1: close the queue
onClick(qs($page, `img[alt="close"]`)).pipe(
- rxjs.mergeMap(() => animate($page, { time: 200, keyframes: slideYOut(50) })),
+ // rxjs.mergeMap(() => animate($page, { time: 200, keyframes: slideYOut(50) })),
rxjs.tap(() => $page.classList.add("hidden")),
).subscribe();
// feature2: setup the task queue in the dom
workers$.subscribe(({ tasks }) => {
- console.log("TASKS SETUP DOM", tasks);
if (tasks.length === 0) return;
$page.classList.remove("hidden");
const $fragment = document.createDocumentFragment();
@@ -216,7 +217,6 @@ function componentUploadQueue(render, { workers$ }) {
};
const noFailureAllowed = (fn) => fn().catch(() => noFailureAllowed(fn));
workers$.subscribe(async ({ tasks: newTasks }) => {
- console.log("TASKS PROCESS", newTasks);
tasks = tasks.concat(newTasks); // add new tasks to the pool
while(true) {
const nworker = reservations.indexOf(false);
diff --git a/public/assets/pages/filespage/helper.js b/public/assets/pages/filespage/helper.js
index b162629c..347885fa 100644
--- a/public/assets/pages/filespage/helper.js
+++ b/public/assets/pages/filespage/helper.js
@@ -111,3 +111,5 @@ function _moveHiddenFilesDownward(fileA, fileB) {
if (!aIsHidden && bIsHidden) return -1;
return 0;
}
+
+export const isNativeFileUpload = (e) => JSON.stringify(e.dataTransfer.types) === '["Files"]';
diff --git a/public/assets/pages/filespage/model_virtual_layer.js b/public/assets/pages/filespage/model_virtual_layer.js
index 69219cc0..693b80fa 100644
--- a/public/assets/pages/filespage/model_virtual_layer.js
+++ b/public/assets/pages/filespage/model_virtual_layer.js
@@ -63,7 +63,7 @@ export function touch(path) {
async afterSuccess() {
removeLoading(virtualFiles$, basepath, filename);
onDestroy(() => statePop(virtualFiles$, basepath, filename));
- await fscache().update(basepath, ({ files, ...rest }) => ({
+ await fscache().update(basepath, ({ files = [], ...rest }) => ({
files: files.concat([file]),
...rest,
}));
@@ -100,7 +100,7 @@ export function mkdir(path) {
async afterSuccess() {
removeLoading(virtualFiles$, basepath, dirname);
onDestroy(() => statePop(virtualFiles$, basepath, dirname));
- await fscache().update(basepath, ({ files, ...rest }) => ({
+ await fscache().update(basepath, ({ files = [], ...rest }) => ({
files: files.concat([file]),
...rest,
}));
@@ -137,7 +137,7 @@ export function save(path, size) {
async afterSuccess() {
removeLoading(virtualFiles$, basepath, filename);
onDestroy(() => statePop(virtualFiles$, basepath, filename));
- await fscache().update(basepath, ({ files, ...rest }) => ({
+ await fscache().update(basepath, ({ files = [], ...rest }) => ({
files: files.concat([file]),
...rest,
}));
@@ -190,7 +190,7 @@ export function rm(...paths) {
});
onDestroy(() => statePop(mutationFiles$, basepath, basepath));
await Promise.all(paths.map((path) => fscache().remove(path, false)));
- await fscache().update(basepath, ({ files, ...rest }) => ({
+ await fscache().update(basepath, ({ files = [], ...rest }) => ({
files: files.filter(({ name }) => {
for (let i=0;i {
+ await fscache().update(fromBasepath, ({ files = [], ...rest }) => {
return {
files: files.map((file) => {
if (file.name === fromName) {
@@ -300,11 +300,11 @@ export function mv(fromPath, toPath) {
});
onDestroy(() => statePop(mutationFiles$, fromBasepath, fromName));
statePop(virtualFiles$, toBasepath, toName);
- await fscache().update(fromBasepath, ({ files, ...rest }) => ({
+ await fscache().update(fromBasepath, ({ files = [], ...rest }) => ({
files: files.filter((file) => file.name === fromName ? false : true),
...rest,
}))
- await fscache().update(toBasepath, ({ files, ...rest }) => ({
+ await fscache().update(toBasepath, ({ files = [], ...rest }) => ({
files: files.concat([{
name: fromName,
time: new Date().getTime(),
diff --git a/public/assets/pages/filespage/thing.js b/public/assets/pages/filespage/thing.js
index 456091c7..0466351c 100644
--- a/public/assets/pages/filespage/thing.js
+++ b/public/assets/pages/filespage/thing.js
@@ -3,12 +3,19 @@ import { qs } from "../../lib/dom.js";
import { animate, opacityIn } from "../../lib/animate.js";
import assert from "../../lib/assert.js";
-import { extractPath, isDir } from "./helper.js";
-import { mv } from "./model_files.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";
+import { mv as mv$ } from "./model_files.js";
+import { mv as mvVL, withVirtualLayer } from "./model_virtual_layer.js";
+
+const mv = (from, to) => withVirtualLayer(
+ mv$(from, to),
+ mvVL(from, to),
+);
+
const IMAGE = {
FILE: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiPgogIDxwYXRoIHN0eWxlPSJjb2xvcjojMDAwMDAwO3RleHQtaW5kZW50OjA7dGV4dC10cmFuc2Zvcm06bm9uZTtmaWxsOiM4YzhjOGM7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlLXdpZHRoOjAuOTg0ODEwNDEiIGQ9Im0gMiwxMy4wODI0MTIgMC4wMTk0NjIsMS40OTIzNDcgYyA1ZS02LDAuMjIyMTQ1IDAuMjA1NTkwMiwwLjQyNDI2MiAwLjQzMTE1MDIsMC40MjQyNzIgTCAxMy41ODk2MTIsMTUgQyAxMy44MTUxNzMsMTQuOTk5OTk1IDEzLjk5OTk5LDE0Ljc5Nzg3NCAxNCwxNC41NzU3MjkgdiAtMS40OTMzMTcgYyAtNC4xNzE4NjkyLDAuNjYyMDIzIC03LjY1MTY5MjgsMC4zOTg2OTYgLTEyLDAgeiIgLz4KICA8cGF0aCBzdHlsZT0iY29sb3I6IzAwMDAwMDt0ZXh0LWluZGVudDowO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlzcGxheTppbmxpbmU7ZmlsbDojYWFhYWFhO3N0cm9rZS13aWR0aDowLjk4NDA4MTI3IiBkPSJNIDIuMzUwMSwxLjAwMTMzMTIgQyAyLjE1MjU5LDEuMDM4MzI0NyAxLjk5NjU5LDEuMjI3MjcyMyAyLjAwMDA5LDEuNDI0OTM1NiBWIDE0LjEzMzQ1NyBjIDVlLTYsMC4yMjE4MTYgMC4yMDUyMywwLjQyMzYzNCAwLjQzMDc5LDAuNDIzNjQ0IGwgMTEuMTM5LC0xLjAxZS00IGMgMC4yMjU1NiwtNmUtNiAwLjQzMDExLC0wLjIwMDc1OCAwLjQzMDEyLC0wLjQyMjU3NCBsIDYuN2UtNCwtOS44MjI2NDI2IGMgLTIuNDg0MDQ2LC0xLjM1NTAwNiAtMi40MzUyMzQsLTIuMDMxMjI1NCAtMy41MDAxLC0zLjMwOTcwNyAtMC4wNDMsLTAuMDE1ODgyIDAuMDQ2LDAuMDAxNzQgMCwwIEwgMi40MzA2NywxLjAwMTEwOCBDIDIuNDAzODMsMC45OTg1OSAyLjM3Njc0LDAuOTk4NTkgMi4zNDk5LDEuMDAxMTA4IFoiIC8+CiAgPHBhdGggc3R5bGU9ImRpc3BsYXk6aW5saW5lO2ZpbGw6IzhjOGM4YztmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzllNzU3NTtzdHJva2Utd2lkdGg6MDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIiBkPSJtIDEwLjUwMDU3LDEuMDAyMDc2NCBjIDAsMy4yNzY4MDI4IC0wLjAwNTIsMy4xNzM5MTYxIDAuMzYyOTIxLDMuMjY5ODIwMiAwLjI4MDEwOSwwLjA3Mjk4NCAzLjEzNzE4LDAuMDM5ODg3IDMuMTM3MTgsMC4wMzk4ODcgLTEuMTIwMDY3LC0xLjA1NTY2OTIgLTIuMzMzNCwtMi4yMDY0NzEzIC0zLjUwMDEsLTMuMzA5NzA3NCB6IiAvPgo8L3N2Zz4K",
FOLDER: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiPgogIDxnIHRyYW5zZm9ybT0ibWF0cml4KDAuODY2NjY0MzEsMCwwLDAuODY2NjcsLTE3Mi4wNDU3OCwtODY0LjMyNzU5KSIgc3R5bGU9ImZpbGw6Izc1YmJkOTtmaWxsLW9wYWNpdHk6MC45NDExNzY0NztmaWxsLXJ1bGU6ZXZlbm9kZCI+CiAgICA8cGF0aCBzdHlsZT0iZmlsbDojNzViYmQ5O2ZpbGwtb3BhY2l0eTowLjk0MTE3NjQ3O2ZpbGwtcnVsZTpldmVub2RkIiBkPSJtIDIwMC4yLDk5OS43MiBjIC0wLjI4OTEzLDAgLTAuNTMxMjUsMC4yNDIxIC0wLjUzMTI1LDAuNTMxMiB2IDEyLjc4NCBjIDAsMC4yOTg1IDAuMjMyNjQsMC41MzEyIDAuNTMxMjUsMC41MzEyIGggMTUuMDkxIGMgMC4yOTg2LDAgMC41MzEyNCwtMC4yMzI3IDAuNTMxMjQsLTAuNTMxMiBsIDRlLTQsLTEwLjQ3NCBjIDAsLTAuMjg4OSAtMC4yNDIxMSwtMC41MzM4IC0wLjUzMTI0LC0wLjUzMzggbCAtNy41NDU3LDVlLTQgLTIuMzA3NiwtMi4zMDc4MyB6IiAvPgogIDwvZz4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjg2NjY3LDAsMCwwLjg2NjY3LC0xNzIuMDQ2OTIsLTg2NC43ODM0KSIgc3R5bGU9ImZpbGw6IzlhZDFlZDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZCI+CiAgICA8cGF0aCBzdHlsZT0iZmlsbDojOWFkMWVkO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkIiBkPSJtIDIwMC4yLDk5OS43MiBjIC0wLjI4OTEzLDAgLTAuNTMxMjUsMC4yNDIxIC0wLjUzMTI1LDAuNTMxMiB2IDEyLjc4NCBjIDAsMC4yOTg1IDAuMjMyNjQsMC41MzEyIDAuNTMxMjUsMC41MzEyIGggMTUuMDkxIGMgMC4yOTg2LDAgMC41MzEyNCwtMC4yMzI3IDAuNTMxMjQsLTAuNTMxMiBsIDRlLTQsLTEwLjQ3NCBjIDAsLTAuMjg4OSAtMC4yNDIxMSwtMC41MzM4IC0wLjUzMTI0LC0wLjUzMzggbCAtNy41NDU3LDVlLTQgLTIuMzA3NiwtMi4zMDc4MyB6IiAvPgogIDwvZz4KPC9zdmc+Cg==",
@@ -153,14 +160,6 @@ export function createThing({
e.dataTransfer.setData("path", path);
e.dataTransfer.setDragImage($thing, e.offsetX, -10);
};
- $thing.ondragover = (e) => {
- if ($thing.getAttribute("data-droptarget") !== "true") return;
- e.preventDefault();
- $thing.classList.add("hover");
- };
- $thing.ondragleave = () => {
- $thing.classList.remove("hover");
- };
$thing.ondrop = async (e) => {
$thing.classList.remove("hover");
const from = e.dataTransfer.getData("path");
@@ -173,6 +172,15 @@ export function createThing({
}
await mv(from, to).toPromise();
};
+ $thing.ondragover = (e) => {
+ if(isNativeFileUpload(e)) return;
+ else if ($thing.getAttribute("data-droptarget") !== "true") return;
+ e.preventDefault();
+ $thing.classList.add("hover");
+ };
+ $thing.ondragleave = () => {
+ $thing.classList.remove("hover");
+ };
return $thing;
}