diff options
-rwxr-xr-x | _notes_cli.js | 167 | ||||
-rw-r--r-- | notes.js | 173 | ||||
-rwxr-xr-x | notes_cli.js | 341 |
3 files changed, 341 insertions, 340 deletions
diff --git a/_notes_cli.js b/_notes_cli.js deleted file mode 100755 index 904f493..0000000 --- a/_notes_cli.js +++ /dev/null @@ -1,167 +0,0 @@ -const fs = require('fs') -const { homedir } = require('os') - -let conf - -const https = require('https') -const readline = require("readline") -// const spawn = require('child_process').spawn - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout -}) - -const cl = (m, c) => console.log(c ? `\x1b[${c}m${m}\x1b[0m` : m) -const cls = () => console.clear() - -const configPath = `${homedir}/.local/share/notes_cli` -console.log(configPath) - -const setConf = (c, callback) => { - fs.writeFile(configPath, JSON.stringify(c), function(err) { - if(err) { - cl(err, 32) - cl('Error writting configuration file', 32) - return null - } - - conf = c - return callback() - }) -} - -const getConf = () => { - fs.readFile(configPath, function(err, f){ - if (err) { - cl('Configuration file not found', 33) - return login() - } - - try { - const r = JSON.parse(f.toString()) - - console.log(r) - - if (!r.session || !r.userId || !r.email || !r.list) { - cl('Error parsing configuration', 31) - process.exit(1) - } - - conf = r - return console.log('here handle config') - - // - - } catch (e) { - cl('Error reading configuration', 31) - process.exit(1) - } - }) -} - -const post = (path, data, callback) => { - const dataString = JSON.stringify(data) - const options = { - hostname: 'apps.pruss.it', - port: 443, - path, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Content-Length': dataString.length - }, - timeout: 1000 - }; - - const req = https.request(options, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - return callback(JSON.parse(data), res.headers, res.statusCode); - }); - - }).on("error", () => { - callback(null) - }); - - req.write(dataString); - req.end(); -} - -const login = () => { - cl('\nLogin to My Apps\n', 32) - cl('+-------------------------------------------------------+\n'+ - '|Account can be created through apps.pruss.it website.|\n'+ - '|To quit type "q" and press enter. |\n'+ - '+-------------------------------------------------------+\n', 32) - 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() - cl('\nNot a valid email address, try again.', 31) - rl.pause() - return login() - } - - rl.stdoutMuted = true - - rl.question('Password: ', p => { - if (p === 'q') { - cl('\nExiting My Apps', 31) - rl.close() - process.exit(1) - } - - rl.pause() - - post( - '/api/login', - {"email": e, "password": p}, - (o, h, s) => { - if (!o || s !== 201) { - cl('Could not log in, try again', 31) - return login() - } - - const {_id, email, isVerified, noteList} = o - const session = h['set-cookie'] - - if (!_id || !email || !noteList) { - cl('Could not log in', 31) - process.exit(1) - } else if (!isVerified) { - cl('User not verified.\nPlease first verify the user using apps.pruss.it', 32) - return 1 - } - - const c = {userId: _id, email, list: noteList, session} - - setConf(c, () => { - cl('Successfully saved configuration', 32) - cl('saved config: ', conf) - }) - }) - }) - - rl._writeToOutput = function _writeToOutput(stringToWrite) { - if (rl.stdoutMuted) - rl.output.write("*") - else - rl.output.write(stringToWrite) - }; - - rl.history = rl.history.slice(1) - }) -} - -cls() -getConf() diff --git a/notes.js b/notes.js deleted file mode 100644 index 5aa7c2f..0000000 --- a/notes.js +++ /dev/null @@ -1,173 +0,0 @@ -const rl = require('readline'); - -const list_mock = [ - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', - '13', - '14', - '15', - '16', - '17', - '18', - '19', - '20', - '21', - '22', - '23', - '24', - '25', - '26', - '27', - '28', - '29', - '30', - '31', - '32', - '33', - '34', - '35', - '36', - '37', - '38', - '39', - '40', - '41', - '42', - '43', - '44', - '45', - '46', - '47', - '48', - '49', - '50' -] - -let active = 0; scroll = 0; draw = { t: 'p', v: ['Loading Notes App...'] }, appTitle = '', menu = '' -const pr = t => process.stdout.write(t) -const cls = () => console.clear() -const cursor = { - hide: () => pr('\u001B[?25l'), - show: () => pr('\u001B[?25h') -} - -// =========== DRAW APP ============= - -const drawApp = () => { - const lines = process.stdout.rows - const columns = process.stdout.columns - // process.stdout.write('\x1Bc') - cls() - for (var i = 0; i < lines - 1; i++) { - const dist = t => [Math.floor((columns - t) / 2), Math.ceil((columns - t) / 2) - 2] - switch(i){ - case 0: - pr('╔'+'═'.repeat(columns - 2)+'╗') - break - case 1: - pr('║'+' '.repeat(dist(appTitle.length)[0])+appTitle+' '.repeat(dist(appTitle.length)[1])+'║') - break - case 2: - pr('╠'+'═'.repeat(columns - 2) +'╣') - break - case lines - 3: - pr('╟'+'─'.repeat(columns - 2) +'╢') - break - case lines - 2: - pr('║'+' '+menu.substring(0,columns-4)+' '.repeat(columns-menu.length-3)+'║') - 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])+'║') - } 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])+'║') - } 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])+'║') - } else if (i < (Math.floor((lines - draw.v.length) / 2) - 1) || (i > (Math.floor((lines + draw.v.length) / 2)))) { - pr('║'+' '.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])+'║') - } - } else if (draw.t === 'l') { - const l = draw.v[i-3+scroll] && draw.v[i-3+scroll].substring(0,columns-4) - - pr(l - ? ('║'+(active===i-3+scroll?'\x1b[7m ':' ')+l+' '.repeat(columns-l.length-3)+'\x1b[0m║') - : ('║'+' '.repeat(columns - 2)+'║') - ) - } - } - } - - pr('╚'+'═'.repeat(columns - 2)+'╝') -} - -// ============ GET KEY ================= - -const getKey = () => { - rl.emitKeypressEvents(process.stdin); - process.stdin.setRawMode(true); - process.stdin.on('keypress', (_, key) => { - const lines = process.stdout.rows - switch(key.name) { - case 'up': - case 'k': - active > 0 && active-- - active < scroll && scroll-- - drawApp() - break - case 'down': - case 'j': - if (draw.t === 'l') { - active < draw.v.length - 1 && active++ - active - scroll > lines - 7 && scroll++ - drawApp() - } - break - case 'return': - case 'o': - // draw = { t: 'p', v: ['popup test'] } - // draw = { t: 'p', v: ['popup test', 'aaaaaaa'] } - draw = { t: 'p', v: ['popup test', '', `This will display note nr ${active + 1}.`] } - drawApp() - // process.stdout.write(draw.v[active]); - break - case 'q': - cursor.show() - console.clear() - pr('Bye!\n\n') - process.exit() - default: - } - }) -} - -// ============ EXECUTION ================ - -cls() -cursor.hide() -process.stdout.on('resize', drawApp); - -appTitle = "Notes App" -menu = '[Q]uit [↓/j] Down [↑/k] Up [Enter/o] Open' -draw = { t: 'l', v: list_mock } - -drawApp() -getKey() - - diff --git a/notes_cli.js b/notes_cli.js new file mode 100755 index 0000000..be427d8 --- /dev/null +++ b/notes_cli.js @@ -0,0 +1,341 @@ +const fs = require('fs') +const { homedir } = require('os') + +let conf + +const https = require('https') +const readline = require("readline") +// const spawn = require('child_process').spawn + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}) + +const cl = (m, c) => console.log(c ? `\x1b[${c}m${m}\x1b[0m` : m) +const cls = () => console.clear() + +const configPath = `${homedir}/.local/share/notes_cli` + +// app view +let active = 0; scroll = 0; draw = { t: 'p', v: () => ['Loading NOTES CLI...'] }, headers = () => '' +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/, '') + +/* Views */ +const formatNote = note => { + const columns = process.stdout.columns + return columns > 72 + ? `${fixedStr(note.title, columns - 45)} ${formatDate(note.created_at)} ${formatDate(note.updated_at)}` + : fixedStr(note.title, columns - note.title.length-2) +} + +const showList = (notes, status) => { + if (status !== 200) { + cls() + return login('Session error') + } + headers = () => { + const columns = process.stdout.columns + return columns > 72 + ? `${fixedStr(' Title', columns - 44)} Created at${' '.repeat(10)} Updated at ${' '.repeat(9)}` + : ` Title ${' '.repeat(columns - 9)}` + } + cursor.hide() + process.stdout.on('resize', drawApp); + draw = { t: 'l', v: () => notes.map(formatNote) } + + drawApp() + getKey() +} + + +// =========== DRAW APP ============= + +const drawApp = () => { + const lines = process.stdout.rows + const columns = process.stdout.columns + // process.stdout.write('\x1Bc') + cls() + for (var i = 0; i < lines - 1; i++) { + 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)+'╗') + 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])+'║') + break + case 2: + pr('╠'+'═'.repeat(columns - 2) +'╣') + break + case 3: + const h = headers() + pr('║'+(h || ' '.repeat(columns-2))+'║') + break + case 4: + pr('╟'+'─'.repeat(columns - 2) +'╢') + break + case lines - 3: + pr('╠'+'═'.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)+'║') + 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])+'║') + } 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])+'║') + } 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])+'║') + } else if (i < (Math.floor((lines - draw.v().length) / 2) - 1) || (i > (Math.floor((lines + draw.v().length) / 2)))) { + pr('║'+' '.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])+'║') + } + } else if (draw.t === 'l') { + const l = draw.v()[i-5+scroll] && draw.v()[i-5+scroll].substring(0,columns-4) + + pr(l + ? ('║'+(active===i-5+scroll?'\x1b[7m ':' ')+l+' '.repeat(columns-l.length-3)+'\x1b[0m║') + : ('║'+' '.repeat(columns - 2)+'║') + ) + } + } + } + + pr('╚'+'═'.repeat(columns - 2)+'╝') +} + +// ============ GET KEY ================= + +const getKey = () => { + readline.emitKeypressEvents(process.stdin); + process.stdin.setRawMode(true); + process.stdin.on('keypress', (_, key) => { + const lines = process.stdout.rows + switch(key.name) { + case 'up': + case 'k': + active > 0 && active-- + active < scroll && scroll-- + drawApp() + break + case 'down': + case 'j': + if (draw.t === 'l') { + active < draw.v().length - 1 && active++ + active - scroll > lines - 7 && scroll++ + drawApp() + } + break + case 'return': + case 'o': + draw = { t: 'p', v: () => ['popup test', '', `This will display note nr ${active + 1}.`] } + drawApp() + // process.stdout.write(draw.v[active]); + // openNote(active) + break + case 'q': + cursor.show() + console.clear() + pr('Bye!\n\n') + process.exit() + default: + } + }) +} + + +/* Configuration */ + +const setConf = (c, callback) => { + fs.writeFile(configPath, JSON.stringify(c), function(err) { + if(err) { + cl(err, 32) + cl('Error writting configuration file', 32) + return null + } + + conf = c + return callback() + }) +} + +const getConf = () => { + fs.readFile(configPath, function(err, f){ + if (err) { + return login() + } + + try { + const r = JSON.parse(f.toString()) + + if (!r.session || !r.userId || !r.email || !r.list) { + return login('Error parsing configuration') + } + + fetchList(r) + } catch (e) { + return login('Error reading configuration') + } + }) +} + +/* Login */ + +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.') + } + + rl.stdoutMuted = true + + rl.question(' Password: ', p => { + if (p === 'q') { + cl('\nExiting My Apps', 31) + rl.close() + process.exit(1) + } + + 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') + } + + const {_id, email, isVerified, noteList} = o + const session = h['set-cookie'] + + if (!_id || !email || !noteList) { + cl('Could not log in', 31) + process.exit(1) + } else if (!isVerified) { + cl('User not verified.\nPlease first verify the user using apps.pruss.it', 32) + return 1 + } + + const c = {userId: _id, email, list: noteList, session} + + setConf(c, () => { + cl('Successfully saved configuration', 32) + cl('saved config: ', conf) + }) + }) + }) + + rl._writeToOutput = function _writeToOutput(stringToWrite) { + if (rl.stdoutMuted) + rl.output.write("*") + else + rl.output.write(stringToWrite) + }; + + rl.history = rl.history.slice(1) + }) +} + +/* API */ + +const fetchList = config => { + conf = config + drawApp() + get('/api/notes', showList, conf.session) +} + +/* HTTPS METHODS */ + +const get = (path, callback, cookie = '') => { + const options = { + hostname: 'apps.pruss.it', + port: 443, + path, + method: 'GET', + headers: { + 'Cookie': cookie, + }, + }; + + const req = https.request(options, res => { + let data = ''; + + res.on('data', chunk => { + data += chunk + }); + + res.on('end', () => callback(JSON.parse(data), res.statusCode, res.headers)); + }); + + req.on('error', () => { + callback(null) + }); + + req.end(); +} + +const post = (path, data, callback, cookie = '') => { + const dataString = JSON.stringify(data) + const options = { + hostname: 'apps.pruss.it', + port: 443, + path, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': dataString.length, + 'Cookie': cookie, + }, + timeout: 1000 + }; + + const req = https.request(options, (res) => { + let data = ''; + + res.on('data', chunk => { + data += chunk; + }); + + res.on('end', () => callback(JSON.parse(data), res.statusCode, res.headers)); + }).on("error", () => { + callback(null) + }); + + req.write(dataString); + req.end(); +} + +cls() +getConf() |