mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-10-31 18:16:00 +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));
 | |
|     });
 | |
| };
 | 
