diff options
-rwxr-xr-x | notes_cli.js | 198 |
1 files changed, 101 insertions, 97 deletions
diff --git a/notes_cli.js b/notes_cli.js index 46acde1..dceccd2 100755 --- a/notes_cli.js +++ b/notes_cli.js @@ -2,22 +2,44 @@ const fs = require('fs') const { homedir } = require('os') const https = require('https') const readline = require("readline") -const spawn = require('child_process').spawn const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) -let conf, notesList = [], active = 0; scroll = 0; draw = { t: 'p', v: () => ['Loading NOTES CLI...'] } +rl._writeToOutput = function _writeToOutput(stringToWrite) { + if (rl.stdoutMuted) + rl.output.write("\x1B[2K\x1B[200D Password: "+('*'.repeat(rl.line.length))) + else + rl.output.write(stringToWrite); +}; + +const cursor = { + hide: () => process.stdout.write('\u001B[?25l'), + show: () => process.stdout.write('\u001B[?25h') +} + +cursor.hide() + +process.on('exit',() => { + rl.close() + cursor.show() + cls() + cl('Bye!\n\n') +}) + +const exitWithError = (e) => { + cls() + draw = { t: 'p', v: () => e.map(m => `\x1b[31m${m}\x1b[0m`)} + drawApp() + setTimeout(() => process.exit(1), 2000) +} + +let conf, notesList = [], active = 0; scroll = 0; draw = { t: 'p', v: () => ['Loading NOTES CLI...'] }; menu = 'Press [h] for help' const cl = (m, c) => console.log(c ? `\x1b[${c}m${m}\x1b[0m` : m) const cls = () => console.clear() const filesPath = `${homedir}/.local/share/notes_cli` -const pr = t => process.stdout.write(t) -const cursor = { - hide: () => pr('\u001B[?25l'), - show: () => pr('\u001B[?25h') -} const fixedStr = (s, l) => s.length < l ? s.padEnd(l) : s.substring(0, l) const formatDate = d => d.replace('T',' ').replace(/\..*Z/, '') @@ -33,25 +55,21 @@ const formatNote = note => { const showList = (notes, status) => { if (status !== 200) { cls() - return login('Session error') + return getEmail('Session error') } notesList = notes - cursor.hide() drawApp() process.stdout.on('resize', drawApp); draw = { t: 'l', v: () => notes.map(formatNote) } drawApp() - getKey() } const saveTmpFile = (note, { content }) => { const file = `${filesPath}/${note._id}.tmp` fs.writeFile(file, JSON.stringify(content), function(err) { if(err) { - cl(err, 32) - cl('Error creating tmp file', 32) - return null + exitWithError(['Error creating tmp file']) } return editTmpFile(note) @@ -62,7 +80,7 @@ const readTmpFile = (note) => { const file =`${filesPath}/${note._id}.tmp` fs.readFile(file, function(err, f){ if (err) { - return login() + return getEmail() } try { @@ -71,20 +89,18 @@ const readTmpFile = (note) => { fs.unlink(file, () => {}) if (!content) { - return login('Error saving note') + return getEmail('Error saving note') } putNote(note, content) } catch (e) { - return login('Session error') + return getEmail('Session error') } }) } const noteSaved = () => { drawApp() - cursor.hide() - rl.resume() fetchList() } @@ -92,10 +108,12 @@ const editTmpFile = (note) => { const file = `${filesPath}/${note._id}.tmp` const child_process = require('child_process') const editor = process.env.EDITOR || 'vi'; - rl.pause() cursor.show() const child = child_process.spawn(editor, [file], { stdio: 'inherit' }); + rl.pause() child.on('exit', function (e) { + rl.resume() + cursor.hide() if (e === 0) { draw = { t: 'p', v: () => ['Saving changes...'] } readTmpFile(note) @@ -103,8 +121,6 @@ const editTmpFile = (note) => { draw = { t: 'p', v: () => ['Changes not saved'] } fs.unlink(file, () => {}) drawApp() - cursor.hide() - rl.resume() fetchList() } }); @@ -112,7 +128,7 @@ const editTmpFile = (note) => { // =========== DRAW APP ============= -const drawApp = () => { +const drawApp = (lastLine = true) => { const lines = process.stdout.rows const columns = process.stdout.columns const headers = columns > 72 @@ -124,48 +140,47 @@ const drawApp = () => { const dist = t => columns > t + 2 ? [Math.floor((columns - t) / 2), Math.ceil((columns - t) / 2) - 2] : [0, 0] switch(i){ case 0: - pr('╔'+'═'.repeat(columns - 2)+'╗') + cl('╔'+'═'.repeat(columns - 2)+'╗') break case 1: - pr('║'+' '.repeat(dist(conf.email.length+12)[0])+`NOTES CLI (${conf.email})`.substring(0, columns-2)+' '.repeat(dist(conf.email.length+12)[1])+'║') + conf?.email + ? cl('║'+' '.repeat(dist(conf.email.length+12)[0])+`NOTES CLI (${conf.email})`.substring(0, columns-2)+' '.repeat(dist(conf.email.length+12)[1])+'║') + : cl('║'+' '.repeat(dist(9)[0])+`NOTES CLI`.substring(0, columns-2)+' '.repeat(dist(9)[1])+'║') break case 2: - pr('╠'+'═'.repeat(columns - 2) +'╣') + cl('╠'+'═'.repeat(columns - 2) +'╣') break case 3: - pr('║'+(headers || ' '.repeat(columns-2))+'║') + cl('║'+(headers || ' '.repeat(columns-2))+'║') break case 4: - pr('╟'+'─'.repeat(columns - 2) +'╢') - break - case lines - 3: - pr('╠'+'═'.repeat(columns - 2) +'╣') + cl('╟'+'─'.repeat(columns - 2) +'╢') break case lines - 2: - const menu = '[Q]uit [↓/j] Down [↑/k] Up [Enter/o] Open' - pr('║'+' '+menu.substring(0,columns-4)+' '.repeat(columns-menu.length-3 > 0 ? columns-menu.length-3 : 1)+'║') + cl('╚'+'═'.repeat(columns - 2) +'╝') break default: const max = Math.max(...(draw.v().map(el => el.length))) if (draw.t === 'p') { if (i === (Math.floor((lines - draw.v().length) / 2) - 2)){ - pr('║'+' '.repeat(dist(max+4)[0])+'┌'+'─'.repeat(max+2)+'┐'+' '.repeat(dist(max+4)[1])+'║') + cl('║'+' '.repeat(dist(max+4)[0])+'┌'+'─'.repeat(max+2)+'┐'+' '.repeat(dist(max+4)[1])+'║') } else if (i === (Math.floor((lines + draw.v().length) / 2) + 1)) { - pr('║'+' '.repeat(dist(max+4)[0])+'└'+'─'.repeat(max+2)+'┘'+' '.repeat(dist(max+4)[1])+'║') + cl('║'+' '.repeat(dist(max+4)[0])+'└'+'─'.repeat(max+2)+'┘'+' '.repeat(dist(max+4)[1])+'║') } else if (i === (Math.floor((lines - draw.v().length) / 2) - 1) || i === (Math.floor((lines + draw.v().length) / 2))) { - pr('║'+' '.repeat(dist(max+4)[0])+'│'+' '.repeat(max+2)+'│'+' '.repeat(dist(max+4)[1])+'║') + cl('║'+' '.repeat(dist(max+4)[0])+'│'+' '.repeat(max+2)+'│'+' '.repeat(dist(max+4)[1])+'║') } else if (i < (Math.floor((lines - draw.v().length) / 2) - 1) || (i > (Math.floor((lines + draw.v().length) / 2)))) { - pr('║'+' '.repeat(columns - 2)+'║') + cl('║'+' '.repeat(columns - 2)+'║') } else { const n = i - (Math.floor((lines - draw.v().length) / 2)) const t = draw.v()[n] - const s = [Math.floor((max+2-t.length) / 2), Math.ceil((max+2-t.length) / 2)] - pr('║'+' '.repeat(dist(max+4)[0])+'│'+' '.repeat(s[0])+t+' '.repeat(s[1])+'│'+' '.repeat(dist(max+4)[1])+'║') + const tLength = t.indexOf('\x1b') > -1 ? t.length - 9 : t.length + const s = [Math.floor((max+2-tLength) / 2), Math.ceil((max+2-tLength) / 2)] + cl('║'+' '.repeat(dist(max+4)[0])+'│'+' '.repeat(s[0])+t+' '.repeat(s[1])+'│'+' '.repeat(dist(max+4)[1])+'║') } } else if (draw.t === 'l') { const l = draw.v()[i-5+scroll] && draw.v()[i-5+scroll].substring(0,columns-4) - pr(l + cl(l ? ('║'+(active===i-5+scroll?'\x1b[7m ':' ')+l+' '.repeat(columns-l.length-3)+'\x1b[0m║') : ('║'+' '.repeat(columns - 2)+'║') ) @@ -173,7 +188,11 @@ const drawApp = () => { } } - pr('╚'+'═'.repeat(columns - 2)+'╝') + if (lastLine) { + const activeNr = `${active+1}` + const notesNr = `${notesList.length}` + process.stdout.write(' '+menu+' '.repeat(columns-5-menu.length-activeNr.length-notesNr.length)+`[${activeNr}/${notesNr}]`) + } } // ============ GET KEY ================= @@ -203,9 +222,6 @@ const getKey = () => { fetchNote(notesList[active]) break case 'q': - cursor.show() - cls() - pr('Bye!\n\n') process.exit() default: } @@ -214,111 +230,99 @@ const getKey = () => { /* Session */ -const setSession = (c, callback) => { +const setSession = (session) => { if (!fs.existsSync(filesPath)){ fs.mkdirSync(filesPath, { recursive: true }); } - fs.writeFile(`${filesPath}/session`, JSON.stringify(c), function(err) { + fs.writeFile(`${filesPath}/session`, JSON.stringify(session), function(err) { if(err) { - cl(err, 32) - cl('Error writting session file', 32) - return null + exitWithError(['Error writting session file']) } - conf = c - return callback() + rl.resume() + getSession() }) } const getSession = () => { fs.readFile(`${filesPath}/session`, function(err, f){ if (err) { - return login() + return getEmail() } try { conf = JSON.parse(f.toString()) if (!conf.session || !conf.userId || !conf.email || !conf.list) { - return login('Session error') + return getEmail('Session error') } + getKey() fetchList() } catch (e) { - return login('Session error') + return getEmail('Session error') } }) } /* Login */ +const loginMsg = (text, err) => ['Login to apps.pruss.it', '', `\x1b[31m${err}\x1b[0m`, '', ...text] -const login = (error = '') => { - cl('\n Login to apps.pruss.it\n\n') - cl(' ┌'+'─'.repeat(50)+'┐\n'+' │'+' '.repeat(50)+'│\n'+' │ Account can be created on '+ - 'apps.pruss.it website. │\n'+' │ To quit type "q" and press enter.'+' '.repeat(16)+'│\n'+ - ' │'+' '.repeat(50)+'│\n'+' └'+'─'.repeat(50)+'┘\n', 32) - cl(' '+error+'\n', 31) - rl.resume() - rl.question(' Email: ', e => { - if (e === 'q') { - cl('\nExiting My Apps', 31) - rl.close() - process.exit(1) - } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)) { - cls() - rl.pause() - return login('Not a valid email address, try again.') - } - +const getPass = (e) => { + cls() + draw = { t: 'p', v: () => loginMsg(['Please type password for', `your apps.pruss.it account`, 'or press [q] to quit'], '')} + drawApp(false) rl.stdoutMuted = true - - rl.question(' Password: ', p => { + rl.write() + rl.question('', p => { + rl.pause() + rl.stdoutMuted = false + drawApp(false) if (p === 'q') { - cl('\nExiting My Apps', 31) - rl.close() - process.exit(1) + process.exit() } - rl.pause() - post( '/api/login', {"email": e, "password": p}, (o, s, h) => { if (!o || s !== 201) { - rl.pause() - cls() - rl.stdoutMuted = false - return login('Could not log in, try again') + return getEmail('Could not log in, try again') } const {_id, email, isVerified, noteList} = o const session = h['set-cookie'] if (!_id || !email || !noteList) { - cl('Could not log in', 31) - process.exit(1) + exitWithError(['Could not log in']) } else if (!isVerified) { - cl('User not verified.\nPlease first verify the user using apps.pruss.it', 32) - return 1 + exitWithError(['User not verified.', 'Please first verify the user using apps.pruss.it']) } - const c = {userId: _id, email, list: noteList, session} - - rl.resume() - setSession(c, getSession) + setSession({userId: _id, email, list: noteList, session}) }) }) - rl._writeToOutput = function _writeToOutput(stringToWrite) { - if (rl.stdoutMuted) - rl.output.write("*") - else - rl.output.write(stringToWrite) - }; - rl.history = rl.history.slice(1) +} + +const getEmail = (error = '') => { + cls() + draw = { t: 'p', v: () => loginMsg(['Please input email for your','apps.pruss.it account or' , 'press [q] to quit'], error)} + drawApp(false) + cursor.show() + rl.question(' Email: ', e => { + cursor.hide() + rl.pause() + if (e === 'q') { + process.exit() + } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)) { + cls() + return getEmail('Not a valid email address, try again.') + } + + getPass(e) }) } |