aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xnotes_cli.js198
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)
})
}