From 76184c0251342442b09400cd316f2e8d7c6822a4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 23 May 2019 18:16:49 +0300 Subject: [PATCH] feature(user-menu) add default user menu --- client/dom/dialog.js | 2 +- client/dom/index.js | 13 +- client/dom/io.js | 214 ++++++++++++++++++++ client/dom/rest.js | 232 +++------------------- client/modules/user-menu/default-menu.js | 38 ---- client/modules/user-menu/get-user-menu.js | 5 - client/modules/user-menu/index.js | 1 + common/try-to-promisify.js | 11 - madrun.js | 4 +- server/user-menu.js | 35 +++- static/user-menu.js | 42 ++++ 11 files changed, 325 insertions(+), 272 deletions(-) create mode 100644 client/dom/io.js delete mode 100644 client/modules/user-menu/default-menu.js delete mode 100644 common/try-to-promisify.js create mode 100644 static/user-menu.js diff --git a/client/dom/dialog.js b/client/dom/dialog.js index 13b36b2a..768ccfcc 100644 --- a/client/dom/dialog.js +++ b/client/dom/dialog.js @@ -17,7 +17,7 @@ module.exports.alert = (...a) => alert(title, ...a, { module.exports.prompt = (...a) => tryToCatch(prompt, title, ...a); module.exports.confirm = (...a) => tryToCatch(confirm, title, ...a); -module.exports.progress = (...a) => tryToCatch(progress, title, ...a); +module.exports.progress = (...a) => progress(title, ...a); module.exports.alert.noFiles = () => { return alert(title, 'No files selected!', { diff --git a/client/dom/index.js b/client/dom/index.js index 6c276866..2207c666 100644 --- a/client/dom/index.js +++ b/client/dom/index.js @@ -13,11 +13,10 @@ const Images = require('./images'); const load = require('./load'); const Files = require('./files'); const RESTful = require('./rest'); +const IO = require('./io'); const Storage = require('./storage'); const Dialog = require('./dialog'); -const read = callbackify(RESTful.read); - const currentFile = require('./current-file'); const DOMTree = require('./dom-tree'); @@ -27,10 +26,20 @@ 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; DOM.RESTful = RESTful; +DOM.IO = IO; DOM.Storage = Storage; DOM.Dialog = Dialog; diff --git a/client/dom/io.js b/client/dom/io.js new file mode 100644 index 00000000..24abe841 --- /dev/null +++ b/client/dom/io.js @@ -0,0 +1,214 @@ +'use strict'; + +/* global CloudCmd*/ + +const itype = require('itype/legacy'); +const {promisify} = require('es6-promisify'); + +const {FS} = require('../../common/cloudfunc'); + +const Images = require('./images'); +const load = require('./load'); + +const imgPosition = { + top: true, +}; + +module.exports._replaceHash = replaceHash; +function replaceHash(url) { + /* + * if we send ajax request - + * no need in hash so we escape # + */ + return url.replace(/#/g, '%23'); +} + +module.exports.delete = promisify((url, data, callback) => { + const isFunc = itype.function(data); + + if (!callback && isFunc) { + callback = data; + data = null; + } + + sendRequest({ + method : 'DELETE', + url : FS + url, + data, + callback, + imgPosition : { top: !!data }, + }); +}); + +module.exports.patch = promisify((url, data, callback) => { + const isFunc = itype.function(data); + + if (!callback && isFunc) { + callback = data; + data = null; + } + + sendRequest({ + method: 'PATCH', + url: FS + url, + data, + callback, + imgPosition, + }); +}); + +module.exports.write = promisify((url, data, callback) => { + const isFunc = itype.function(data); + + if (!callback && isFunc) { + callback = data; + data = null; + } + + sendRequest({ + method: 'PUT', + url: FS + url, + data, + callback, + imgPosition, + }); +}); + +module.exports.read = promisify((url, dataType, callback) => { + const notLog = !url.includes('?'); + const isFunc = itype.function(dataType); + + if (!callback && isFunc) { + callback = dataType; + dataType = 'text'; + } + + sendRequest({ + method: 'GET', + url: FS + url, + callback, + notLog, + dataType, + }); +}); + +module.exports.cp = promisify((data, callback) => { + sendRequest({ + method: 'PUT', + url: '/cp', + data, + callback, + imgPosition, + }); +}); + +module.exports.pack = promisify((data, callback) => { + sendRequest({ + method: 'PUT', + url: '/pack', + data, + callback, + }); +}); + +module.exports.extract = promisify((data, callback) => { + sendRequest({ + method : 'PUT', + url : '/extract', + data, + callback, + }); +}); + +module.exports.mv = promisify((data, callback) => { + sendRequest({ + method : 'PUT', + url : '/mv', + data, + callback, + imgPosition, + }); +}); + +module.exports.Config = { + read: promisify((callback) => { + sendRequest({ + method: 'GET', + url: '/config', + callback, + imgPosition, + notLog: true, + }); + }), + + write: promisify((data, callback) => { + sendRequest({ + method: 'PATCH', + url: '/config', + data, + callback, + imgPosition, + }); + }), +}; + +module.exports.Markdown = { + read: promisify((url, callback) => { + sendRequest({ + method: 'GET', + url: '/markdown' + url, + callback, + imgPosition, + notLog: true, + }); + }), + + render: promisify((data, callback) => { + sendRequest({ + method: 'PUT', + url: '/markdown', + data, + callback, + imgPosition, + notLog: true, + }); + }), +}; + +function sendRequest(params) { + const p = params; + const {prefixURL} = CloudCmd; + + p.url = prefixURL + p.url; + p.url = encodeURI(p.url); + + p.url = replaceHash(p.url); + + load.ajax({ + method : p.method, + url : p.url, + data : p.data, + dataType : p.dataType, + error : (jqXHR) => { + const response = jqXHR.responseText; + + const { + statusText, + status, + } = jqXHR; + + const text = status === 404 ? response : statusText; + + p.callback(Error(text)); + }, + success: (data) => { + Images.hide(); + + if (!p.notLog) + CloudCmd.log(data); + + p.callback(null, data); + }, + }); +} + diff --git a/client/dom/rest.js b/client/dom/rest.js index 9b36e27a..3a9d2f62 100644 --- a/client/dom/rest.js +++ b/client/dom/rest.js @@ -1,221 +1,43 @@ 'use strict'; -/* global CloudCmd, DOM */ +const tryToCatch = require('try-to-catch/legacy'); -const itype = require('itype/legacy'); -const promisify = require('../../common/try-to-promisify'); - -const {FS} = require('../../common/cloudfunc'); const {encode} = require('../../common/entity'); const Images = require('./images'); -const load = require('./load'); +const IO = require('./io'); +const Dialog = require('./dialog'); -const imgPosition = { - top: true, +const handleError = (promise) => async (...args) => { + const [e, data] = await tryToCatch(promise, ...args); + + if (!e) + return [e, data]; + + const encoded = encode(e.message); + + Images.show.error(encoded); + Dialog.alert(encoded); + + return [e, data]; }; -module.exports._replaceHash = replaceHash; -function replaceHash(url) { - /* - * if we send ajax request - - * no need in hash so we escape # - */ - return url.replace(/#/g, '%23'); -} - -module.exports.delete = promisify((url, data, callback) => { - const isFunc = itype.function(data); - - if (!callback && isFunc) { - callback = data; - data = null; - } - - sendRequest({ - method : 'DELETE', - url : FS + url, - data, - callback, - imgPosition : { top: !!data }, - }); -}); - -module.exports.patch = promisify((url, data, callback) => { - const isFunc = itype.function(data); - - if (!callback && isFunc) { - callback = data; - data = null; - } - - sendRequest({ - method: 'PATCH', - url: FS + url, - data, - callback, - imgPosition, - }); -}); - -module.exports.write = promisify((url, data, callback) => { - const isFunc = itype.function(data); - - if (!callback && isFunc) { - callback = data; - data = null; - } - - sendRequest({ - method: 'PUT', - url: FS + url, - data, - callback, - imgPosition, - }); -}); - -module.exports.read = promisify((url, dataType, callback) => { - const notLog = !url.includes('?'); - const isFunc = itype.function(dataType); - - if (!callback && isFunc) { - callback = dataType; - dataType = 'text'; - } - - sendRequest({ - method: 'GET', - url: FS + url, - callback, - notLog, - dataType, - }); -}); - -module.exports.cp = promisify((data, callback) => { - sendRequest({ - method: 'PUT', - url: '/cp', - data, - callback, - imgPosition, - }); -}); - -module.exports.pack = promisify((data, callback) => { - sendRequest({ - method: 'PUT', - url: '/pack', - data, - callback, - }); -}); - -module.exports.extract = promisify((data, callback) => { - sendRequest({ - method : 'PUT', - url : '/extract', - data, - callback, - }); -}); - -module.exports.mv = promisify((data, callback) => { - sendRequest({ - method : 'PUT', - url : '/mv', - data, - callback, - imgPosition, - }); -}); +module.exports.delete = handleError(IO.delete); +module.exports.patch = handleError(IO.patch); +module.exports.write = handleError(IO.write); +module.exports.read = handleError(IO.read); +module.exports.cp = handleError(IO.cp); +module.exports.pack = handleError(IO.pack); +module.exports.extract = handleError(IO.extract); +module.exports.mv = handleError(IO.mv); module.exports.Config = { - read: promisify((callback) => { - sendRequest({ - method: 'GET', - url: '/config', - callback, - imgPosition, - notLog: true, - }); - }), - - write: promisify((data, callback) => { - sendRequest({ - method: 'PATCH', - url: '/config', - data, - callback, - imgPosition, - }); - }), + read: handleError(IO.Config.read), + write: handleError(IO.Config.write), }; module.exports.Markdown = { - read: promisify((url, callback) => { - sendRequest({ - method: 'GET', - url: '/markdown' + url, - callback, - imgPosition, - notLog: true, - }); - }), - - render: promisify((data, callback) => { - sendRequest({ - method: 'PUT', - url: '/markdown', - data, - callback, - imgPosition, - notLog: true, - }); - }), + read: handleError(IO.Markdown.read), + render: handleError(IO.Markdown.render), }; -function sendRequest(params) { - const p = params; - const {prefixURL} = CloudCmd; - - p.url = prefixURL + p.url; - p.url = encodeURI(p.url); - - p.url = replaceHash(p.url); - - load.ajax({ - method : p.method, - url : p.url, - data : p.data, - dataType : p.dataType, - error : (jqXHR) => { - const response = jqXHR.responseText; - - const { - statusText, - status, - } = jqXHR; - - const text = status === 404 ? response : statusText; - const encoded = encode(text); - - Images.show.error(encoded); - - setTimeout(() => { - DOM.Dialog.alert(encoded); - }, 100); - - p.callback(Error(text)); - }, - success: (data) => { - Images.hide(); - - if (!p.notLog) - CloudCmd.log(data); - - p.callback(null, data); - }, - }); -} diff --git a/client/modules/user-menu/default-menu.js b/client/modules/user-menu/default-menu.js deleted file mode 100644 index fb75a834..00000000 --- a/client/modules/user-menu/default-menu.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -const data = `'use strict'; - -module.exports = { - 'F2 - Rename file': async ({DOM}) => { - await DOM.renameCurrent(); - }, -}; -`; - -module.exports = { - 'F2 - Rename file': async ({DOM}) => { - await DOM.renameCurrent(); - }, - - 'C - Create User Menu File': async ({DOM, CloudCmd}) => { - const { - RESTful, - CurrentInfo, - } = DOM; - - const {dirPath} = CurrentInfo; - const path = `${dirPath}.cloudcmd.menu.js`; - - const [e] = await RESTful.write(path, data); - - if (e) - return; - - await CloudCmd.refresh(); - DOM.setCurrentByName('.cloudcmd.menu.js'); - await CloudCmd.EditFile.show(); - }, -}; - -module.exports._data = data; - diff --git a/client/modules/user-menu/get-user-menu.js b/client/modules/user-menu/get-user-menu.js index 206161bb..2df17696 100644 --- a/client/modules/user-menu/get-user-menu.js +++ b/client/modules/user-menu/get-user-menu.js @@ -1,11 +1,6 @@ 'use strict'; -const defaultUserMenu = require('./default-menu.js'); - module.exports = (menuFn) => { - if (!menuFn) - return defaultUserMenu; - const module = {}; const fn = Function('module', menuFn); diff --git a/client/modules/user-menu/index.js b/client/modules/user-menu/index.js index 02d734e1..d76733b5 100644 --- a/client/modules/user-menu/index.js +++ b/client/modules/user-menu/index.js @@ -122,6 +122,7 @@ const runUserMenu = async (value, options, userMenu) => { const [e] = await tryToCatch(userMenu[value], { DOM, CloudCmd, + tryToCatch, }); if (e) diff --git a/common/try-to-promisify.js b/common/try-to-promisify.js deleted file mode 100644 index 0bb49b88..00000000 --- a/common/try-to-promisify.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -const {promisify} = require('es6-promisify'); -const wraptile = require('wraptile/legacy'); -const tryToCatch = require('try-to-catch/legacy'); - -module.exports = wraptile((fn, ...args) => { - const promise = promisify(fn); - return tryToCatch(promise, ...args); -}); - diff --git a/madrun.js b/madrun.js index 48af04d4..13f53612 100644 --- a/madrun.js +++ b/madrun.js @@ -31,7 +31,7 @@ module.exports = { 'lint': () => run(['putout', 'lint:*', 'spell']), 'lint:server': () => `eslint -c .eslintrc.server ${dirs} --ignore-pattern *.spec.js`, 'lint:test': () => `eslint --ignore-pattern '!.*' ${dirsTest}`, - 'lint:client': () => 'eslint --env browser client --ignore-pattern .cloudcmd.menu.js', + 'lint:client': () => 'eslint --env browser client static --ignore-pattern .cloudcmd.menu.js', 'lint:css': () => 'stylelint css/*.css', 'spell': () => 'yaspeller .', 'fix:lint': () => run(['putout', 'lint:*'], '--fix'), @@ -86,6 +86,6 @@ module.exports = { 'build:client': () => run('6to5:client'), 'build:client:dev': () => run('6to5:client:dev'), 'heroku-postbuild': () => run('6to5:client'), - 'putout': () => 'putout bin client server common test .cloudcmd.menu.js', + 'putout': () => 'putout bin client static server common test .cloudcmd.menu.js', }; diff --git a/server/user-menu.js b/server/user-menu.js index 82a02c87..7f81683c 100644 --- a/server/user-menu.js +++ b/server/user-menu.js @@ -2,6 +2,7 @@ const {homedir} = require('os'); const fs = require('fs'); + const {join} = require('path'); const {promisify} = require('util'); @@ -11,19 +12,32 @@ const findUp = require('find-up'); const readFile = promisify(fs.readFile); +const URL = '/api/v1/user-menu'; +const DEFAULT_MENU_PATH = join(__dirname, '../static/user-menu.js'); + module.exports = currify(async({menuName}, req, res, next) => { - if (req.url.indexOf('/api/v1/user-menu')) + if (req.url.indexOf(URL)) return next(); const {method} = req; if (method === 'GET') - return onGET(menuName, req.query, res); + return onGET({ + req, + res, + menuName, + }); next(); }); -async function onGET(menuName, {dir}, res) { +async function onGET({req, res, menuName}) { + const {dir} = req.query; + const url = req.url.replace(URL, ''); + + if (url === '/default') + return sendDefaultMenu(res); + const [errorFind, currentMenuPath] = await tryToCatch(findUp, [ menuName, ], {cwd: dir}); @@ -33,21 +47,26 @@ async function onGET(menuName, {dir}, res) { .status(404) .send(e.message); - if (errorFind && errorFind.code === 'ENOENT') - return res.send(''); - const homeMenuPath = join(homedir(), menuName); const menuPath = currentMenuPath || homeMenuPath; const [e, data] = await tryToCatch(readFile, menuPath, 'utf8'); if (!e) - return res.send(data); + return res + .type('js') + .send(data); if (e.code !== 'ENOENT') return res .status(404) .send(e.message); - return res.send(''); + sendDefaultMenu(res); +} + +function sendDefaultMenu(res) { + res.sendFile(DEFAULT_MENU_PATH, { + cacheControl: false, + }); } diff --git a/static/user-menu.js b/static/user-menu.js new file mode 100644 index 00000000..49e87c18 --- /dev/null +++ b/static/user-menu.js @@ -0,0 +1,42 @@ +'use strict'; + +module.exports = { + 'F2 - Rename file': async ({DOM}) => { + await DOM.renameCurrent(); + }, + + 'C - Create User Menu File': async ({DOM, CloudCmd}) => { + const {CurrentInfo} = DOM; + + const {dirPath} = CurrentInfo; + const path = `${dirPath}.cloudcmd.menu.js`; + const {prefix} = CloudCmd; + + const data = await readDefaultMenu({prefix}); + await createDefaultMenu({ + path, + data, + DOM, + CloudCmd, + }); + }, +}; + +async function createDefaultMenu({path, data, DOM, CloudCmd}) { + const {IO} = DOM; + + await IO.write(path, data); + await CloudCmd.refresh(); + + DOM.setCurrentByName('.cloudcmd.menu.js'); + + await CloudCmd.EditFile.show(); +} + +async function readDefaultMenu({prefix}) { + const res = await fetch(`${prefix}/api/v1/user-menu/default`); + const data = await res.text(); + + return data; +} +