mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-11-02 03:54:59 +08:00
293 lines
11 KiB
JavaScript
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));
|
|
});
|
|
};
|