Files
filestash/client/pages/filespage.helper.js
2022-11-16 08:27:41 +11:00

293 lines
11 KiB
JavaScript

import { Files } from "../model/";
import { notify, upload, randomString } from "../helpers/";
import Path from "path";
import { Observable } from "rxjs/Observable";
import { t } from "../locales/";
export const sort = function(files, type) {
if (type === "name") {
return sortByName(files);
} else if (type === "date") {
return sortByDate(files);
} else {
return sortByType(files);
}
function _moveLoadingDownward(fileA, fileB) {
if (fileA.icon === "loading" && fileB.icon !== "loading") {
return +1;
} else if (fileA.icon !== "loading" && fileB.icon === "loading") {
return -1;
}
return 0;
};
function _moveFolderUpward(fileA, fileB) {
if (["directory", "link"].indexOf(fileA.type) === -1 &&
["directory", "link"].indexOf(fileB.type) !== -1) {
return +1;
} else if (["directory", "link"].indexOf(fileA.type) !== -1 &&
["directory", "link"].indexOf(fileB.type) === -1) {
return -1;
}
return 0;
};
function _moveHiddenFilesDownward(fileA, fileB) {
if (fileA.name[0] === "." && fileB.name[0] !== ".") return +1;
else if (fileA.name[0] !== "." && fileB.name[0] === ".") return -1;
return 0;
};
function sortByType(files) {
return files.sort((fileA, fileB) => {
let tmp = _moveLoadingDownward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveFolderUpward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveHiddenFilesDownward(fileA, fileB);
if (tmp !== 0) return tmp;
const aExt = Path.extname(fileA.name.toLowerCase());
const bExt = Path.extname(fileB.name.toLowerCase());
if (fileA.name.toLowerCase() === fileB.name.toLowerCase()) {
return fileA.name > fileB.name ? +1 : -1;
} else {
if (aExt !== bExt) return aExt > bExt ? +1 : -1;
else return fileA.name.toLowerCase() > fileB.name.toLowerCase() ? +1 : -1;
}
});
}
function sortByName(files) {
return files.sort((fileA, fileB) => {
let tmp = _moveLoadingDownward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveFolderUpward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveHiddenFilesDownward(fileA, fileB);
if (tmp !== 0) return tmp;
if (fileA.name.toLowerCase() === fileB.name.toLowerCase()) {
return fileA.name > fileB.name ? +1 : -1;
}
return fileA.name.toLowerCase() > fileB.name.toLowerCase() ? +1 : -1;
});
}
function sortByDate(files) {
return files.sort((fileA, fileB) => {
const tmp = _moveLoadingDownward(fileA, fileB);
if (tmp !== 0) return tmp;
if (fileB.time === fileA.time) {
return fileA.name > fileB.name ? +1 : -1;
}
return fileB.time - fileA.time;
});
}
};
export const onCreate = function(path, type, file) {
if (type === "file") {
return Files.touch(path, file)
.then(() => {
notify.send(
t("A file named '{{VALUE}}' was created", Path.basename(path)),
"success",
);
return Promise.resolve();
})
.catch((err) => {
notify.send(err, "error");
return Promise.reject(err);
});
} else if (type === "directory") {
return Files.mkdir(path)
.then(() => notify.send(
t("A folder named '{{VALUE}}' was created", Path.basename(path)),
"success",
))
.catch((err) => notify.send(err, "error"));
} else {
return Promise.reject({
message: t("internal error: can't create a {{VALUE}}", type.toString()),
code: "UNKNOWN_TYPE",
});
}
};
export const onRename = function(from, to, type) {
return Files.mv(from, to, type)
.then(() => notify.send(
t("The file '{{VALUE}}' was renamed", Path.basename(from)),
"success",
))
.catch((err) => notify.send(err, "error"));
};
export const onDelete = function(path, type) {
return Files.rm(path, type)
.then(() => notify.send(
t("The file {{VALUE}} was deleted", Path.basename(path)),
"success",
))
.catch((err) => notify.send(err, "error"));
};
export const onMultiDelete = function(arrOfPath) {
return Promise.all(arrOfPath.map((p) => Files.rm(p)))
.then(() => notify.send(t("All done!"), "success"))
.catch((err) => notify.send(err, "error"));
};
export const onMultiDownload = function(arr) {
return Files.zip(arr)
.catch((err) => notify.send(err, "error"));
};
export const onMultiRename = function(arrOfPath) {
return Promise.all(arrOfPath.map((p) => Files.mv(p[0], p[1])))
.then(() => notify.send(t("All done!"), "success"))
.catch((err) => notify.send(err, "error"));
};
/*
* The upload method has a few strategies:
* 1. user is coming from drag and drop + browser provides support to read entire folders
* 2. user is coming from drag and drop + browser DOES NOT provides support to read entire folders
* 3. user is coming from a upload form button as he doesn't have drag and drop with files
*/
export const onUpload = function(path, e) {
let extractFiles = null;
if (e.dataTransfer === undefined) { // case 3
extractFiles = extract_upload_crappy_hack_but_official_way(e.target);
} else {
if (e.dataTransfer.types && e.dataTransfer.types.length >= 0) {
if (e.dataTransfer.types[0] === "text/uri-list") {
return;
}
}
extractFiles = extract_upload_directory_the_way_that_works_but_non_official(
e.dataTransfer.items || [], [],
) // case 1
.then((files) => {
if (files.length === 0) { // case 2
return extract_upload_crappy_hack_but_official_way(e.dataTransfer);
}
return Promise.resolve(files);
});
}
extractFiles.then((files) => upload.add(path, files));
function extract_upload_directory_the_way_that_works_but_non_official(items, files = []) {
const traverseDirectory = (item, _files, parent_id) => {
const file = {
path: item.fullPath,
};
if (item.isFile) {
return new Promise((done, err) => {
file.type = "file";
item.file((_file, _err) => {
if (!_err) {
file.file = _file;
if (parent_id) file._prior = parent_id;
_files.push(file);
}
done(_files);
});
});
} else if (item.isDirectory) {
file.type = "directory";
file.path += "/";
file._id = randomString();
if (parent_id) file._prior = parent_id;
_files.push(file);
const reader = item.createReader();
const filereader = function(r) {
return new Promise((done) => {
r.readEntries(function(entries) {
Promise.all(entries.map((entry) => {
return traverseDirectory(entry, _files, file._id);
})).then((e) => {
if (entries.length > 0) {
return filereader(r).then(done);
}
return done(e);
});
});
});
};
return filereader(reader).then(() => {
return Promise.resolve(_files);
});
} else {
return Promise.resolve();
}
};
return Promise.all(
Array.prototype.slice.call(items).map((item) => {
if (typeof item.webkitGetAsEntry === "function") {
const entry = item.webkitGetAsEntry();
if(entry === null) return Promise.resolve([]);
return traverseDirectory(entry, files.slice(0));
}
}).filter((e) => e),
).then((res) => Promise.resolve([].concat.apply([], res)));
}
function extract_upload_crappy_hack_but_official_way(data) {
const _files = data.files;
return Promise.all(
Array.prototype.slice.call(_files).map((_file) => {
return detectType(_file)
.then(transform);
function detectType(_f) {
// the 4096 is an heuristic I've observed and taken from:
// https://stackoverflow.com/questions/25016442
// however the proposed answer is just wrong as it doesn't consider folder with
// name such as: test.png and as Stackoverflow favor consanguinity with their
// point system, I couldn't rectify the proposed answer. The following code is
// actually working as expected
if (_file.size % 4096 !== 0) {
return Promise.resolve("file");
}
return new Promise((done, err) => {
const reader = new window.FileReader();
reader.onload = function() {
done("file");
};
reader.onerror = function() {
done("directory");
};
reader.readAsText(_f);
});
}
function transform(_type) {
const file = {
type: _type,
path: _file.name,
};
if (file.type === "file") {
file.file = _file;
} else {
file.path += "/";
}
return Promise.resolve(file);
}
}),
);
}
};
export const onSearch = (keyword, path = "/") => {
return new Observable((obs) => {
Files.search(keyword, path)
.then((f) => obs.next(f))
.catch((err) => obs.error(err));
});
};