diff --git a/client/client.js b/client/client.js index dacecc3b..f411cd39 100644 --- a/client/client.js +++ b/client/client.js @@ -19,18 +19,17 @@ const {unregisterSW} = require('./sw/register'); const jonny = require('jonny/legacy'); const currify = require('currify/legacy'); -const bind = (f, ...a) => () => f(...a); -const noop = () => {}; const noJS = (a) => a.replace(/.js$/, ''); +const loadCSS = promisify(load.css); +const loadJS = promisify(load.js); + const { apiURL, formatMsg, buildFromJSON, } = require('../common/cloudfunc'); -const callbackify = require('../common/callbackify'); - const loadModule = require('./load-module'); inherits(CloudCmdProto, Emitify); @@ -137,19 +136,7 @@ function CloudCmdProto(DOM) { * выполняет весь функционал по * инициализации */ - this.init = (prefix, config) => { - const func = bind(exec.series, [ - initModules, - baseInit, - loadStyle, - exec.with(CloudCmd.route, location.hash), - ], noop); - - const funcBefore = (callback) => { - const src = prefix + CloudCmd.DIRCLIENT_MODULES + 'polyfill.js'; - load.js(src, callback); - }; - + this.init = async (prefix, config) => { CloudCmd.prefix = prefix; CloudCmd.prefixURL = `${prefix}${apiURL}`; CloudCmd.prefixSocket = config.prefixSocket; @@ -171,14 +158,21 @@ function CloudCmdProto(DOM) { if (config.oneFilePanel) CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity; - exec.if(document.body.scrollIntoViewIfNeeded, func, funcBefore); + if (!document.body.scrollIntoViewIfNeeded) + await loadJS(prefix + CloudCmd.DIRCLIENT_MODULES + 'polyfill.js'); + + await initModules(); + await baseInit(); + await loadStyle(); + + CloudCmd.route(location.hash); }; - function loadStyle(callback) { + async function loadStyle() { const {prefix} = CloudCmd; const name = prefix + '/dist/cloudcmd.common.css'; - load.css(name, callback); + await loadCSS(name); } this.route = (path) => { @@ -203,12 +197,12 @@ function CloudCmdProto(DOM) { CloudCmd.execFromModule(module, 'show'); }; - this.logOut = () => { + this.logOut = async () => { const url = CloudCmd.prefix + '/logout'; const error = () => document.location.reload(); const {prefix} = CloudCmd; - DOM.Storage.clear(); + await DOM.Storage.clear(); unregisterSW(prefix); DOM.load.ajax({ url, @@ -216,7 +210,7 @@ function CloudCmdProto(DOM) { }); }; - const initModules = callbackify(async () => { + const initModules = async () => { exec.if(CloudCmd.Key, () => { Key = new CloudCmd.Key(); CloudCmd.Key = Key; @@ -253,9 +247,9 @@ function CloudCmdProto(DOM) { for (const module of modules.local) { load(null, module, doBefore[module]); } - }); + }; - function baseInit(callback) { + async function baseInit() { const files = DOM.getFiles(); CloudCmd.on('current-file', DOM.updateCurrentInfo); @@ -279,15 +273,12 @@ function CloudCmdProto(DOM) { Listeners.initKeysPanel(); if (!CloudCmd.config('dirStorage')) - return callback(); + return; - Storage.get(dirPath, (error, data) => { - if (!data) { - data = getJSONfromFileTable(); - Storage.set(dirPath, data); - } - callback(); - }); + const data = await Storage.get(dirPath); + + if (!data) + await Storage.set(dirPath, getJSONfromFileTable()); } function getPanels() { diff --git a/client/dom/buffer.js b/client/dom/buffer.js index 6d7f05e8..d99429e5 100644 --- a/client/dom/buffer.js +++ b/client/dom/buffer.js @@ -3,8 +3,8 @@ /* global CloudCmd */ const jonny = require('jonny/legacy'); -const exec = require('execon'); +const tryToPromiseAll = require('../../common/try-to-promise-all'); const Storage = require('./storage'); const DOM = require('./'); @@ -58,7 +58,19 @@ function BufferProto() { showMessage('Buffer disabled in config!'); } - function copy() { + async function readBuffer() { + const [e, result] = await tryToPromiseAll([ + Storage.get(COPY), + Storage.get(CUT), + ]); + + return [ + e, + ...result, + ]; + } + + async function copy() { const names = getNames(); const from = Info.dirPath; @@ -67,14 +79,14 @@ function BufferProto() { if (!names.length) return; - Storage.remove(CUT) - .set(COPY, { - from, - names, - }); + await Storage.remove(CUT); + await Storage.set(COPY, { + from, + names, + }); } - function cut() { + async function cut() { const names = getNames(); const from = Info.dirPath; @@ -85,11 +97,10 @@ function BufferProto() { addCutClass(); - Storage - .set(CUT, { - from, - names, - }); + await Storage.set(CUT, { + from, + names, + }); } function clear() { @@ -99,33 +110,28 @@ function BufferProto() { rmCutClass(); } - function paste() { - const copy = Storage.get.bind(Storage, COPY); - const cut = Storage.get.bind(Storage, CUT); + async function paste() { + const [error, cp, ct] = await readBuffer(); - exec.parallel([copy, cut], (error, cp, ct) => { - const opStr = cp ? 'copy' : 'move'; - const opData = cp || ct; - const {Operation} = CloudCmd; - const msg = 'Path is same!'; - const path = Info.dirPath; - - if (!error && !cp && !ct) - error = 'Buffer is empty!'; - - if (error) - return showMessage(error); - - const data = jonny.parse(opData); - data.to = path; - - if (data.from === path) - return showMessage(msg); - - Operation.show(opStr, data); - clear(); - }); + if (error || !cp && !ct) + return showMessage(error || 'Buffer is empty!'); + + const opStr = cp ? 'copy' : 'move'; + const opData = cp || ct; + const {Operation} = CloudCmd; + const msg = 'Path is same!'; + const path = Info.dirPath; + + const data = jonny.parse(opData); + data.to = path; + + if (data.from === path) + return showMessage(msg); + + Operation.show(opStr, data); + clear(); } return Buffer; } + diff --git a/client/dom/current-file.js b/client/dom/current-file.js index b319db57..568297a0 100644 --- a/client/dom/current-file.js +++ b/client/dom/current-file.js @@ -168,7 +168,6 @@ function unsetCurrentFile(currentFile) { */ module.exports.setCurrentFile = (currentFile, options) => { const o = options; - const CENTER = true; const currentFileWas = DOM.getCurrentFile(); if (!currentFile) @@ -203,6 +202,7 @@ module.exports.setCurrentFile = (currentFile, options) => { } /* scrolling to current file */ + const CENTER = true; DOM.scrollIntoViewIfNeeded(currentFile, CENTER); CloudCmd.emit('current-file', currentFile); @@ -212,6 +212,11 @@ module.exports.setCurrentFile = (currentFile, options) => { return DOM; }; +this.setCurrentByName = (name) => { + const current = DOM.getCurrentByName(name); + return DOM.setCurrentFile(current); +}; + /* * set current file by position * diff --git a/client/dom/index.js b/client/dom/index.js index ab54010a..73f49317 100644 --- a/client/dom/index.js +++ b/client/dom/index.js @@ -5,6 +5,7 @@ const itype = require('itype/legacy'); const exec = require('execon'); const jonny = require('jonny/legacy'); +const tryToPromiseAll = require('../../common/try-to-promise-all'); const Util = require('../../common/util'); const callbackify = require('../../common/callbackify'); @@ -26,15 +27,6 @@ const DOM = { ...new CmdProto(), }; -const read = callbackify(async (...args) => { - const [e, data] = await RESTful.read(...args); - - if (e) - throw e; - - return data; -}); - DOM.Images = Images; DOM.load = load; DOM.Files = Files; @@ -262,12 +254,13 @@ function CmdProto() { * @callback * @currentFile */ - this.loadCurrentHash = (callback, currentFile) => { + this.loadCurrentHash = async (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const query = '?hash'; const link = DOM.getCurrentPath(current); - read(link + query, callback); + const [, data] = await RESTful.read(link + query); + return data; }; /** @@ -275,12 +268,13 @@ function CmdProto() { * @callback * @currentFile */ - this.loadCurrentTime = (callback, currentFile) => { + this.loadCurrentTime = async (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const query = '?time'; const link = DOM.getCurrentPath(current); - read(link + query, callback); + const [, data] = await RESTful.read(link + query); + return data; }; /** @@ -314,55 +308,57 @@ function CmdProto() { return owner.textContent; }; + const mixArgs = (f) => (a, b) => f(b, a); + /** * unified way to get current file content * * @param callback * @param currentFile */ - this.getCurrentData = (callback, currentFile) => { - let hash; + this.getCurrentData = callbackify(mixArgs(async (callback, currentFile) => { const {Dialog} = DOM; const Info = DOM.CurrentInfo; const current = currentFile || DOM.getCurrentFile(); const path = DOM.getCurrentPath(current); const isDir = DOM.isCurrentIsDir(current); - const func = (error, data) => { - const ONE_MEGABYTE = 1024 * 1024 * 1024; - - if (!error) { - if (itype.object(data)) - data = jonny.stringify(data); - - const {length} = data; - - if (hash && length < ONE_MEGABYTE) - DOM.saveDataToStorage(path, data, hash); - } - - callback(error, data); - }; - if (Info.name === '..') { Dialog.alert.noFiles(); return callback(Error('No files selected!')); } - if (isDir) - return read(path, func); + if (isDir) { + const [e, data] = await RESTful.read(path); + + if (e) + throw e; + + return data; + } - DOM.checkStorageHash(path, (error, equal, hashNew) => { - if (error) - return callback(error); - - if (equal) - return DOM.getDataFromStorage(path, callback); - - hash = hashNew; - read(path, func); - }); - }; + const [hashNew, hash] = await DOM.checkStorageHash(path); + + if (hash === hashNew) + return await DOM.getDataFromStorage(path); + + let [e, data] = await RESTful.read(path); + + if (e) + return; + + const ONE_MEGABYTE = 1024 * 1024 * 1024; + + if (itype.object(data)) + data = jonny.stringify(data); + + const {length} = data; + + if (hash && length < ONE_MEGABYTE) + await DOM.saveDataToStorage(path, data, hashNew); + + return data; + })); /** * unified way to get RefreshButton @@ -374,11 +370,6 @@ function CmdProto() { return refresh; }; - this.setCurrentByName = (name) => { - const current = DOM.getCurrentByName(name); - return DOM.setCurrentFile(current); - }; - /** * select current file * @param currentFile @@ -530,28 +521,21 @@ function CmdProto() { /** * check storage hash */ - this.checkStorageHash = (name, callback) => { - const {parallel} = exec; + this.checkStorageHash = async (name) => { const nameHash = name + '-hash'; - const getStoreHash = exec.with(Storage.get, nameHash); if (typeof name !== 'string') throw Error('name should be a string!'); - if (typeof callback !== 'function') - throw Error('callback should be a function!'); + const [error, loadHash, storeHash] = await tryToPromiseAll([ + DOM.loadCurrentHash(), + Storage.get(nameHash), + ]); - parallel([DOM.loadCurrentHash, getStoreHash], (error, loadHash, storeHash) => { - let equal; - const isContain = /error/.test(loadHash); - - if (isContain) - error = loadHash; - else if (loadHash === storeHash) - equal = true; - - callback(error, equal, loadHash); - }); + if (error) + throw error; + + return [loadHash, storeHash]; }; /** @@ -562,25 +546,21 @@ function CmdProto() { * @param hash * @param callback */ - this.saveDataToStorage = function(name, data, hash, callback) { + this.saveDataToStorage = async (name, data, hash) => { const isDir = DOM.isCurrentIsDir(); + + if (isDir) + return; + + hash = hash || await DOM.loadCurrentHash(); + const nameHash = name + '-hash'; const nameData = name + '-data'; - if (isDir) - return exec(callback); + await Storage.set(nameHash, hash); + await Storage.set(nameData, data); - exec.if(hash, () => { - Storage.set(nameHash, hash); - Storage.set(nameData, data); - - exec(callback, hash); - }, (callback) => { - DOM.loadCurrentHash((error, loadHash) => { - hash = loadHash; - callback(); - }); - }); + return hash; }; /** @@ -590,7 +570,7 @@ function CmdProto() { * @param data * @param callback */ - this.getDataFromStorage = (name, callback) => { + this.getDataFromStorage = callbackify(async (name, callback) => { const nameHash = name + '-hash'; const nameData = name + '-data'; const isDir = DOM.isCurrentIsDir(); @@ -598,11 +578,13 @@ function CmdProto() { if (isDir) return exec(callback); - exec.parallel([ - exec.with(Storage.get, nameData), - exec.with(Storage.get, nameHash), - ], callback); - }; + const result = Promise.all([ + await Storage.get(nameData), + await Storage.get(nameHash), + ]); + + return result; + }); this.getFM = () => { return DOM.getPanel().parentElement; diff --git a/client/dom/storage.js b/client/dom/storage.js index c1a5fbd4..628bc9bd 100644 --- a/client/dom/storage.js +++ b/client/dom/storage.js @@ -1,20 +1,22 @@ 'use strict'; -const itype = require('itype/legacy'); -const jonny = require('jonny/legacy'); -const exec = require('execon'); -const tryCatch = require('try-catch'); -const setItem = localStorage.setItem.bind(localStorage); - -/** remove element */ -module.exports.remove = (item, callback) => { - localStorage.removeItem(item); - exec(callback, null); - - return module.exports; +module.exports.set = async (name, data) => { + localStorage.setItem(name, data); }; -module.exports.removeMatch = (string, callback) => { +module.exports.get = async (name) => { + return localStorage.getItem(name); +}; + +module.exports.clear = async () => { + localStorage.clear(); +}; + +module.exports.remove = async(item) => { + localStorage.removeItem(item); +}; + +module.exports.removeMatch = (string) => { const reg = RegExp('^' + string + '.*$'); const test = (a) => reg.test(a); const remove = (a) => localStorage.removeItem(a); @@ -22,46 +24,5 @@ module.exports.removeMatch = (string, callback) => { Object.keys(localStorage) .filter(test) .forEach(remove); - - exec(callback); - - return module.exports; -}; - -/** если доступен localStorage и - * в нём есть нужная нам директория - - * записываем данные в него - */ -module.exports.set = (name, data, callback) => { - let str; - let error; - - if (itype.object(data)) - str = jonny.stringify(data); - - if (name) - [error] = tryCatch(setItem, name, str || data); - - exec(callback, error); - - return module.exports; -}; - -/** Если доступен Storage принимаем из него данные*/ -module.exports.get = (name, callback) => { - const ret = localStorage.getItem(name); - - exec(callback, null, ret); - - return module.exports; -}; - -/** функция чистит весь кэш для всех каталогов*/ -module.exports.clear = (callback) => { - localStorage.clear(); - - exec(callback); - - return module.exports; }; diff --git a/client/key/index.js b/client/key/index.js index e35ad9de..906ac963 100644 --- a/client/key/index.js +++ b/client/key/index.js @@ -6,7 +6,6 @@ const Info = DOM.CurrentInfo; const exec = require('execon'); const clipboard = require('@cloudcmd/clipboard'); -const wraptile = require('wraptile/legacy'); const Events = require('../dom/events'); const Buffer = require('../dom/buffer'); @@ -56,7 +55,7 @@ function KeyProto() { return fromCharCode(event.keyIdentifier); } - function listener(event) { + async function listener(event) { const {keyCode} = event; const alt = event.altKey; @@ -85,7 +84,7 @@ function KeyProto() { return setCurrentByChar(char, Chars); Chars([]); - switchKey(event); + await switchKey(event); if (keyCode >= KEY.F1 && keyCode <= KEY.F10) return; @@ -115,7 +114,7 @@ function KeyProto() { return char; } - function switchKey(event) { + async function switchKey(event) { let i; let isSelected; let prev; @@ -483,7 +482,8 @@ function KeyProto() { case Key.D: if (ctrlMeta) { CloudCmd.log('clearing storage...'); - DOM.Storage.clear(wraptile(CloudCmd.log, 'storage cleared')); + await DOM.Storage.clear(); + CloudCmd.log('storage cleared'); event.preventDefault(); } break; diff --git a/client/modules/operation/index.js b/client/modules/operation/index.js index 82a0e75d..97aab7f8 100644 --- a/client/modules/operation/index.js +++ b/client/modules/operation/index.js @@ -412,26 +412,26 @@ async function _processFiles(options, data) { names, }; - operation(files, () => { - DOM.Storage.remove(from, () => { - const { - panel, - panelPassive, - } = Info; - - const setCurrent = () => { - const currentName = name || data.names[0]; - DOM.setCurrentByName(currentName); - }; - - if (!Info.isOnePanel) - CloudCmd.refresh({ - panel: panelPassive, - noCurrent: true, - }); - - CloudCmd.refresh({panel}, setCurrent); - }); + operation(files, async () => { + await DOM.Storage.remove(from); + + const { + panel, + panelPassive, + } = Info; + + const setCurrent = () => { + const currentName = name || data.names[0]; + DOM.setCurrentByName(currentName); + }; + + if (!Info.isOnePanel) + CloudCmd.refresh({ + panel: panelPassive, + noCurrent: true, + }); + + CloudCmd.refresh({panel}, setCurrent); }); } } diff --git a/common/try-to-promise-all.js b/common/try-to-promise-all.js new file mode 100644 index 00000000..80000fa2 --- /dev/null +++ b/common/try-to-promise-all.js @@ -0,0 +1,14 @@ +'use strict'; + +const tryToCatch = require('try-to-catch/legacy'); +const all = Promise.all.bind(Promise); + +module.exports = async (a) => { + const [e, result = []] = await tryToCatch(all, a); + + return [ + e, + ...result, + ]; +}; + diff --git a/common/try-to-promise-all.spec.js b/common/try-to-promise-all.spec.js new file mode 100644 index 00000000..628bc931 --- /dev/null +++ b/common/try-to-promise-all.spec.js @@ -0,0 +1,29 @@ +'use strict'; + +const test = require('supertape'); +const tryToPromiseAll = require('./try-to-promise-all'); + +const resolve = Promise.resolve.bind(Promise); +const reject = Promise.reject.bind(Promise); + +test('try-to-promise-all', async (t) => { + const [, ...result] = await tryToPromiseAll([ + resolve('a'), + resolve('b'), + ]); + + const expected = ['a', 'b']; + + t.deepEqual(result, expected); + t.end(); +}); + +test('try-to-promise-all: error', async (t) => { + const [e] = await tryToPromiseAll([ + reject('a'), + ]); + + t.equal(e, 'a'); + t.end(); +}); +