@beauraines/rtm-cli 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.github/FUNDING.yml +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +143 -0
  4. package/build.js +86 -0
  5. package/config.json +40 -0
  6. package/package.json +58 -0
  7. package/screens/ls.png +0 -0
  8. package/screens/lsd.png +0 -0
  9. package/screens/lsp.png +0 -0
  10. package/screens/planner.png +0 -0
  11. package/src/cli.js +434 -0
  12. package/src/cmd/add.js +100 -0
  13. package/src/cmd/addList.js +92 -0
  14. package/src/cmd/addTags.js +85 -0
  15. package/src/cmd/archiveList.js +78 -0
  16. package/src/cmd/comp.js +82 -0
  17. package/src/cmd/decPri.js +82 -0
  18. package/src/cmd/due.js +82 -0
  19. package/src/cmd/edit.js +82 -0
  20. package/src/cmd/incPri.js +82 -0
  21. package/src/cmd/lists.js +60 -0
  22. package/src/cmd/login.js +27 -0
  23. package/src/cmd/logout.js +31 -0
  24. package/src/cmd/ls.js +181 -0
  25. package/src/cmd/lsd.js +193 -0
  26. package/src/cmd/lsp.js +171 -0
  27. package/src/cmd/move.js +83 -0
  28. package/src/cmd/notes.js +150 -0
  29. package/src/cmd/planner.js +510 -0
  30. package/src/cmd/postpone.js +82 -0
  31. package/src/cmd/pri.js +82 -0
  32. package/src/cmd/remove.js +82 -0
  33. package/src/cmd/removeList.js +79 -0
  34. package/src/cmd/removeTags.js +85 -0
  35. package/src/cmd/renameList.js +80 -0
  36. package/src/cmd/reset.js +35 -0
  37. package/src/cmd/setUrl.js +99 -0
  38. package/src/cmd/tags.js +101 -0
  39. package/src/cmd/uncomp.js +82 -0
  40. package/src/cmd/url.js +92 -0
  41. package/src/cmd/whoami.js +36 -0
  42. package/src/interactive.js +65 -0
  43. package/src/utils/config.js +236 -0
  44. package/src/utils/filter.js +43 -0
  45. package/src/utils/finish.js +21 -0
  46. package/src/utils/index.js +10 -0
  47. package/src/utils/log.js +163 -0
  48. package/src/utils/login.js +69 -0
  49. package/src/utils/printIndicator.js +43 -0
  50. package/src/utils/prompt.js +126 -0
  51. package/src/utils/sort.js +283 -0
@@ -0,0 +1,150 @@
1
+ 'use strict';
2
+
3
+ const log = require('../utils/log.js');
4
+ const config = require('../utils/config.js');
5
+ const { prompt } = require('../utils/prompt.js');
6
+ const finish = require('../utils/finish.js');
7
+ const filter = require('../utils/filter')
8
+ const { USER_CONFIG } = require('../utils/config.js');
9
+
10
+
11
+ let NOTES = [];
12
+
13
+
14
+ /**
15
+ * This command displays a task's note
16
+ * @param args index
17
+ * @param env
18
+ */
19
+ function action(args, env) {
20
+
21
+ // Reset Notes
22
+ NOTES = [];
23
+
24
+
25
+ // Prompt for task
26
+ if ( args.length < 1 ) {
27
+ prompt('Task:', _promptFinished);
28
+ }
29
+
30
+ // Use provided args
31
+ else {
32
+ for ( let i = 0; i < args[0].length; i++ ) {
33
+ _process(args[0][i], i+1, args[0].length);
34
+ }
35
+ }
36
+
37
+ }
38
+
39
+
40
+ /**
41
+ * Process the returned answers
42
+ * @private
43
+ */
44
+ function _promptFinished(answers) {
45
+ for ( let i = 0; i < answers.length; i++ ) {
46
+ let answer = answers[i];
47
+ _process(answer[0], i+1, answers.length);
48
+ }
49
+ }
50
+
51
+
52
+ /**
53
+ * Process the request
54
+ * @private
55
+ */
56
+ function _process(index, count=1, max=1) {
57
+
58
+ // Display info
59
+ log.spinner.start("Getting Task(s)...");
60
+
61
+ // Get User
62
+ config.user(function(user) {
63
+ index = parseInt(index.trim());
64
+
65
+ // Get Task
66
+ let filterString = filter("hasNotes:true")
67
+ user.tasks.getTask(index, filterString, function(err, task) {
68
+ if ( err ) {
69
+ if ( err.code === -3 ) {
70
+ log.spinner.warn("Task #" + index + " does not have any notes or is not found.");
71
+ }
72
+ else {
73
+ log.spinner.error("Could not get Task #" + index + " (" + err.msg + ")");
74
+ }
75
+ }
76
+
77
+ if ( task ) {
78
+ NOTES.push({
79
+ index: index,
80
+ name: task.name,
81
+ notes: task.notes
82
+ });
83
+ // console.log(task)
84
+ }
85
+
86
+ // Finish
87
+ _processFinished(count, max);
88
+
89
+ });
90
+
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Request Callback
96
+ * @private
97
+ */
98
+ function _processFinished(count, max) {
99
+ log.spinner.start("Getting Task [" + count + "/" + max + "]...");
100
+
101
+ // All tasks returned...
102
+ if ( count === max ) {
103
+ log.spinner.stop();
104
+
105
+ // Get Display Styles
106
+ let styles = config.get().styles;
107
+
108
+ // Print NOTES
109
+ for ( let i = 0; i < NOTES.length; i++ ) {
110
+ log.style(_pad(NOTES[i].index , NOTES.length), styles.index);
111
+ log.style(' ');
112
+ log.style(NOTES[i].name, true);
113
+ for (const note of NOTES[i].notes) {
114
+ log.style('Note: ',styles.due)
115
+ log.style(note.title ? note.title : '',true);
116
+ log('========');
117
+ log(note.body);
118
+ log();
119
+ }
120
+
121
+ }
122
+
123
+ return finish();
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Pad the Index number with leading 0s
129
+ * @param index Task Index Number
130
+ * @param maxIndex Max Task Index
131
+ * @returns {string}
132
+ * @private
133
+ */
134
+ function _pad(index, maxIndex) {
135
+ let max = maxIndex.toString().length;
136
+ let digits = index.toString().length;
137
+ let delta = max - digits;
138
+ for ( let i = 0; i < delta; i++ ) {
139
+ index = '0' + index;
140
+ }
141
+ return index;
142
+ }
143
+
144
+
145
+ module.exports = {
146
+ command: 'notes [indices...]',
147
+ options: [],
148
+ description: 'Display the associated Notes of a Task',
149
+ action: action
150
+ };
@@ -0,0 +1,510 @@
1
+ 'use strict';
2
+
3
+ const df = require('dateformat');
4
+ const Table = require('cli-table3');
5
+ const ws = require('window-size');
6
+ const chalk = require('chalk');
7
+ const parseFilter = require('../utils/filter.js');
8
+ const config = require('../utils/config.js');
9
+ const log = require('../utils/log.js');
10
+ const sort = require('../utils/sort.js');
11
+ const finish = require('../utils/finish.js');
12
+
13
+ // Possible Start Arguments
14
+ const START_ARGS = ['sun', 'mon', 'today'];
15
+ const DEFAULT_START = START_ARGS[0];
16
+ const DEFAULT_WIDTH = 80;
17
+
18
+
19
+ /**
20
+ * This command displays the tasks in a Weekly planner table. The week
21
+ * can start on either Sunday, Monday or the current day.
22
+ */
23
+ function action(args, env) {
24
+
25
+ // Set filter and start arguments
26
+ let filter = parseFilter(args.length > 0 ? args[0].join(' ') : '');
27
+ let start = (env.start === undefined ? DEFAULT_START : env.start).toLowerCase();
28
+ let fixedWidth = (env.width === undefined ? undefined : parseInt(env.width));
29
+
30
+ // Check start arg
31
+ if ( START_ARGS.indexOf(start) === -1 ) {
32
+ log.error("ERROR: start arg not valid: " + start);
33
+ log("Must be one of: " + START_ARGS);
34
+ return finish();
35
+ }
36
+
37
+ // Set start date
38
+ let startDate = new Date();
39
+ if ( start === 'sun' ) {
40
+ startDate.setDate(startDate.getDate() - startDate.getDay());
41
+ }
42
+ else if ( start === 'mon' ) {
43
+ startDate.setDate(startDate.getDate() - startDate.getDay() + 1);
44
+ }
45
+ startDate.setHours(0, 0, 0, 0);
46
+
47
+
48
+ // Get the User's Tasks
49
+ _getTasks(filter, function(tasks) {
50
+
51
+ // Display the Planner
52
+ _display(tasks, startDate, fixedWidth);
53
+
54
+ // Display the Overdue and No Due Tasks
55
+ _displayExtra(tasks, startDate, fixedWidth);
56
+
57
+ // Finish
58
+ finish();
59
+
60
+ });
61
+
62
+ }
63
+
64
+
65
+ /**
66
+ * Get the User's Tasks
67
+ * @param filter Filter String
68
+ * @param callback Callback function(tasks)
69
+ * @private
70
+ */
71
+ function _getTasks(filter, callback) {
72
+
73
+ // Get parsed filter
74
+ filter = parseFilter(filter);
75
+
76
+ // Start Spinner
77
+ log.spinner.start("Getting Tasks...");
78
+
79
+ // Get User
80
+ config.user(function(user) {
81
+
82
+ // Get User's Tasks
83
+ user.tasks.get(filter, function(err, tasks) {
84
+ if ( err ) {
85
+ log.spinner.error("Could not get tasks (" + err.msg + ")");
86
+ return finish();
87
+ }
88
+ log.spinner.stop();
89
+ return callback(tasks);
90
+ });
91
+
92
+ });
93
+
94
+ }
95
+
96
+
97
+ /**
98
+ * Display the Tasks in a Weekly Planner starting on the specified
99
+ * start date
100
+ * @param tasks User's Tasks
101
+ * @param start Start Date
102
+ * @param fixedWidth Fixed planner width, if provided
103
+ * @private
104
+ */
105
+ function _display(tasks, start, fixedWidth) {
106
+
107
+ // Header Titles
108
+ let headers = [];
109
+
110
+ // Width Information
111
+ let colMinWidths = []; // Array of each column's min width
112
+ let colMaxWidths = []; // Array of each column's max width
113
+
114
+ // Table Data
115
+ let data = [];
116
+
117
+
118
+ // Get Display Styles
119
+ let styles = config.get().styles;
120
+
121
+
122
+ // Parse each Date
123
+ for ( let i = 0; i < 7; i++ ) {
124
+
125
+ // Set current Date
126
+ let date = new Date(start);
127
+ date.setDate(date.getDate() + i);
128
+
129
+
130
+ // ==== DETERMINE HEADER ===== //
131
+
132
+ // Min Width for this column based on header text
133
+ let minWidth = 0;
134
+
135
+ // Header for TODAY
136
+ if ( _isToday(date) ) {
137
+ let text = "TODAY";
138
+ headers.push(
139
+ _style(text, styles.due + ".underline")
140
+ );
141
+ minWidth = _reset(text).length+2;
142
+ }
143
+
144
+ // Header for other days
145
+ else {
146
+ let text = df(date, config.get().plannerDateformat);
147
+ headers.push(
148
+ _style(text, styles.due)
149
+ );
150
+ minWidth = _reset(text).length+2;
151
+ }
152
+
153
+
154
+ // ==== DETERMINE COLUMN DATA ==== //
155
+
156
+ // Get Tasks DUE on Date
157
+ let dt = [];
158
+ for ( let j = 0; j < tasks.length; j++ ) {
159
+ let task = tasks[j];
160
+ if ( _sameDay(task.due, date) ) {
161
+ dt.push(task);
162
+ }
163
+ }
164
+
165
+ // Generate column data
166
+ let lines = _displayTasks(dt);
167
+
168
+ // Add data
169
+ data.push(
170
+ lines.join(
171
+ _style('\n', 'reset')
172
+ )
173
+ );
174
+
175
+
176
+ // ==== DETERMINE COLUMN MIN & MAX WIDTHS ==== //
177
+
178
+ // Determine max line width
179
+ let maxWidth = minWidth;
180
+ for ( let i = 0; i < lines.length; i++ ) {
181
+ let l = _reset(lines[i]).length + 2;
182
+ if ( l > maxWidth ) {
183
+ maxWidth = l;
184
+ }
185
+ }
186
+
187
+ // Set Col Widths
188
+ colMinWidths.push(minWidth);
189
+ colMaxWidths.push(maxWidth);
190
+
191
+ }
192
+
193
+
194
+ // ==== BUILD TABLE ==== //
195
+
196
+ let table = new Table({
197
+ colWidths: _calcWidths(colMinWidths, colMaxWidths, fixedWidth),
198
+ wordWrap: false,
199
+ head: headers,
200
+ style: {
201
+ head: []
202
+ }
203
+ });
204
+ table.push(data);
205
+
206
+ // Print Table
207
+ log(table.toString());
208
+
209
+ }
210
+
211
+
212
+ /**
213
+ * Display an extra table displaying the tasks due before the start
214
+ * of the planner and tasks with no due date
215
+ * @param tasks User Tasks
216
+ * @param start Planner Start Date
217
+ * @param fixedWidth Fixed planner width, if provided
218
+ * @private
219
+ */
220
+ function _displayExtra(tasks, start, fixedWidth) {
221
+
222
+ // Display Styles
223
+ let styles = config.get().styles;
224
+
225
+ // Get incomplete tasks that are due before the planner start and have no due date set
226
+ let overdue = [];
227
+ let nodue = [];
228
+ for ( let i = 0; i < tasks.length; i++ ) {
229
+ let task = tasks[i];
230
+ if ( task.due === undefined && !task.isCompleted ) {
231
+ nodue.push(task);
232
+ }
233
+ else if ( task.due < start && !task.isCompleted ) {
234
+ overdue.push(task);
235
+ }
236
+ }
237
+
238
+ // Set Headers of Extra Table
239
+ let headers = [];
240
+ if ( overdue.length > 0 ) {
241
+ headers.push(
242
+ _style("Overdue Tasks", styles.due)
243
+ );
244
+ }
245
+ if ( nodue.length > 0 ) {
246
+ headers.push(
247
+ _style("No Due Date", styles.due)
248
+ );
249
+ }
250
+
251
+ // Build Data
252
+ let data = [];
253
+
254
+ // Over Due Tasks
255
+ if ( overdue.length > 0 ) {
256
+ let overdueLines = _displayTasks(overdue);
257
+ data.push(
258
+ overdueLines.join(
259
+ _style('\n', 'reset')
260
+ )
261
+ );
262
+ }
263
+
264
+ // No Due Tasks
265
+ if ( nodue.length > 0 ) {
266
+ let nodueLines = _displayTasks(nodue);
267
+ data.push(
268
+ nodueLines.join(
269
+ _style('\n', 'reset')
270
+ )
271
+ );
272
+ }
273
+
274
+ // Set column widths
275
+ let colWidths = [];
276
+ if ( fixedWidth !== undefined ) {
277
+ for ( let i = 0; i < headers.length; i++ ) {
278
+ colWidths[i] = Math.floor((fixedWidth/headers.length)-(headers.length-1));
279
+ }
280
+ }
281
+
282
+ // Build Extra Table
283
+ let table = new Table({
284
+ head: headers,
285
+ style: { head: [] },
286
+ colWidths: colWidths
287
+ });
288
+ table.push(data);
289
+
290
+ // Print Extra Table
291
+ log(table.toString());
292
+
293
+ }
294
+
295
+
296
+ /**
297
+ * Generate the Column Data displaying the provided tasks
298
+ * @param tasks Tasks to Display
299
+ * @returns {Array} Array of lines to display
300
+ * @private
301
+ */
302
+ function _displayTasks(tasks) {
303
+
304
+ // Display Styles
305
+ let styles = config.get().styles;
306
+
307
+ // Sort by list, priority
308
+ tasks.sort(sort.tasks.ls);
309
+
310
+ // Lines to return
311
+ let lines = [];
312
+
313
+ // Output Tasks
314
+ let prevList = '';
315
+ for ( let j = 0; j < tasks.length; j++ ) {
316
+ let task = tasks[j];
317
+
318
+ // List Name
319
+ let list = task.list.name;
320
+ if ( prevList !== list ) {
321
+ lines.push(
322
+ _style(list + ":", styles.list)
323
+ );
324
+ prevList = list;
325
+ }
326
+
327
+ // Task Line
328
+ let line = '';
329
+
330
+ // Completed Task
331
+ if ( task.isCompleted ) {
332
+ line += _style('x', styles.completed);
333
+ line += _style(' ', 'reset');
334
+ line += _style(task.name, styles.completed);
335
+ }
336
+
337
+ // Incomplete Task
338
+ else {
339
+ let style = styles.priority[task.priority];
340
+ if ( task.priority > 0 ) {
341
+ line += _style(task.priority, style);
342
+ line += _style(' ', 'reset');
343
+ line += _style(task.name, style);
344
+ }
345
+ else {
346
+ line += _style(' ', 'reset');
347
+ line += _style(task.name, style);
348
+ }
349
+ }
350
+
351
+ // Reset Coloring
352
+ line += _style('', 'reset');
353
+
354
+ // Add Line
355
+ lines.push(line);
356
+ }
357
+
358
+ // Reset Coloring
359
+ lines.push(_style('', 'reset'));
360
+
361
+ return lines;
362
+ }
363
+
364
+
365
+ /**
366
+ * Calculate the optimum column widths given their desired min and max widths
367
+ * and the width of the console.
368
+ * @param colMinWidths array of min widths
369
+ * @param colMaxWidths array of max widths
370
+ * @param fixedWidth fixed planner display width
371
+ * @returns {Array} array of final widths
372
+ * @private
373
+ */
374
+ function _calcWidths(colMinWidths, colMaxWidths, fixedWidth) {
375
+
376
+ // Total Width Available
377
+ let width = DEFAULT_WIDTH;
378
+ if ( fixedWidth !== undefined ) {
379
+ width = fixedWidth;
380
+ }
381
+ else {
382
+ try {
383
+ width = ws.width;
384
+ }
385
+ catch (exception) {}
386
+ }
387
+ let widthTotal = width - 8;
388
+
389
+ // Requested Total Width
390
+ let widthRequested = 0;
391
+ for ( let i = 0; i < colMaxWidths.length; i++ ) {
392
+ widthRequested += colMaxWidths[i];
393
+ }
394
+
395
+ // Width to Reduce
396
+ let widthReduce = widthRequested > widthTotal ? widthRequested - widthTotal : 0;
397
+
398
+ // Columns with Overflow
399
+ let overflow = 0;
400
+ for ( let i = 0; i < colMinWidths.length; i++ ) {
401
+ if ( colMaxWidths[i] > colMinWidths[i] ) {
402
+ overflow++;
403
+ }
404
+ }
405
+
406
+ // Final Column Widths
407
+ let colWidths = [];
408
+
409
+ // Calculate each columns width
410
+ for ( let i = 0; i < colMinWidths.length; i++ ) {
411
+ let min = colMinWidths[i];
412
+ let max = colMaxWidths[i];
413
+
414
+ if ( widthReduce === 0 ) {
415
+ colWidths.push(max);
416
+ }
417
+ else if ( max > min ) {
418
+ let w = max - Math.ceil(widthReduce/overflow);
419
+ if ( w < min ) {
420
+ w = min;
421
+ }
422
+ colWidths.push(w);
423
+ }
424
+ else {
425
+ colWidths.push(min);
426
+ }
427
+ }
428
+
429
+ return colWidths;
430
+
431
+ }
432
+
433
+
434
+
435
+
436
+ // ==== HELPER FUNCTIONS ==== //
437
+
438
+
439
+ /**
440
+ * Check if the two Dates are the same day
441
+ * @param d1 Date 1
442
+ * @param d2 Date 2
443
+ * @returns {boolean}
444
+ * @private
445
+ */
446
+ function _sameDay(d1, d2) {
447
+ if ( d1 === undefined || d2 === undefined ) {
448
+ return false;
449
+ }
450
+ return d1.getFullYear() === d2.getFullYear() &&
451
+ d1.getMonth() === d2.getMonth() &&
452
+ d1.getDate() === d2.getDate();
453
+ }
454
+
455
+ /**
456
+ * Check if the specified Date is today
457
+ * @param date Date
458
+ * @returns {boolean}
459
+ * @private
460
+ */
461
+ function _isToday(date) {
462
+ return _sameDay(date, new Date());
463
+ }
464
+
465
+ /**
466
+ * Style the Text using the specfied Chalk style
467
+ * @param text Text to Style
468
+ * @param style Chalk Style (ex: yellow.underline)
469
+ * @returns {string} Styled String
470
+ * @private
471
+ */
472
+ function _style(text, style) {
473
+ if ( config.get().plain ) {
474
+ return text;
475
+ }
476
+
477
+ let parts = style.split('.');
478
+ let c = chalk;
479
+ for ( let i = 0; i < parts.length; i++ ) {
480
+ c = c[parts[i]];
481
+ }
482
+ return c(text);
483
+ }
484
+
485
+ /**
486
+ * Remove color and style info from the text string
487
+ * @param text Text String
488
+ * @returns {string}
489
+ * @private
490
+ */
491
+ function _reset(text) {
492
+ return text.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
493
+ }
494
+
495
+
496
+ module.exports = {
497
+ command: 'planner [filter...]',
498
+ options: [
499
+ {
500
+ option: "-s, --start <start>",
501
+ description: "Planner start day (sun, mon, or today)"
502
+ },
503
+ {
504
+ option: "-w, --width <cols>",
505
+ description: "Set fixed planner display width (in number of columns)"
506
+ }
507
+ ],
508
+ description: 'Display tasks in a weekly planner (--start: sun, mon, today)',
509
+ action: action
510
+ };