import React from 'react'; import { Files } from '../model/'; import { notify, upload } 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 sortByType(files){ return files.sort((fileA, fileB) => { if(fileA.icon === 'loading' && fileB.icon !== 'loading'){return +1;} else if(fileA.icon !== 'loading' && fileB.icon === 'loading'){return -1;} else{ 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; }else{ if(fileA.name[0] === "." && fileB.name[0] !== ".") return +1; else if(fileA.name[0] !== "." && fileB.name[0] === ".") return -1; else{ const aExt = Path.extname(fileA.name.toLowerCase()), 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) => { if(fileA.icon === 'loading' && fileB.icon !== 'loading'){return +1;} else if(fileA.icon !== 'loading' && fileB.icon === 'loading'){return -1;} else{ if(fileA.name[0] === "." && fileB.name[0] !== ".") return +1; else if(fileA.name[0] !== "." && fileB.name[0] === ".") return -1; else{ 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) => { if(fileA.icon === 'loading' && fileB.icon !== 'loading'){return +1;} else if(fileA.icon !== 'loading' && fileB.icon === 'loading'){return -1;} else{ 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 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)); // adapted from: https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript function _rand_id(){ function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } return s4() + s4() + s4() + s4(); } function extract_upload_directory_the_way_that_works_but_non_official(items, files = []){ const traverseDirectory = (item, _files, parent_id) => { let 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 = _rand_id(); if(parent_id) file._prior = parent_id; _files.push(file); let 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'){ return traverseDirectory(item.webkitGetAsEntry(), 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/how-to-distinguish-if-a-file-or-folder-is-being-dragged-prior-to-it-being-droppe // 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) => { let reader = new window.FileReader(); reader.onload = function() { done('file'); }; reader.onerror = function() { done('directory'); }; reader.readAsText(_f); }); } function transform(_type){ let 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)); }); };