From 8bc14a8be894425f2aeddd5ace5db6fee09cda17 Mon Sep 17 00:00:00 2001 From: Tzahi12345 Date: Tue, 10 Jan 2023 00:09:15 -0500 Subject: [PATCH] Missing options are now auto-added to tasks Added onlyNumber directive for number-only inputs --- backend/tasks.js | 43 ++++++++--- src/app/app.module.ts | 4 +- .../task-settings.component.html | 8 ++- .../directives/only-number.directive.spec.ts | 8 +++ src/app/directives/only-number.directive.ts | 72 +++++++++++++++++++ 5 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 src/app/directives/only-number.directive.spec.ts create mode 100644 src/app/directives/only-number.directive.ts diff --git a/backend/tasks.js b/backend/tasks.js index 4cd9795..eeb83ae 100644 --- a/backend/tasks.js +++ b/backend/tasks.js @@ -1,6 +1,7 @@ const db_api = require('./db'); const notifications_api = require('./notifications'); const youtubedl_api = require('./youtube-dl'); +const subscriptions_api = require('./subscriptions'); const fs = require('fs-extra'); const logger = require('./logger'); @@ -43,6 +44,17 @@ const TASKS = { } } +const defaultOptions = { + all: { + auto_confirm: false + }, + delete_old_files: { + blacklist_files: false, + blacklist_subscription_files: false, + threshold_days: '' + } +} + function scheduleJob(task_key, schedule) { // schedule has to be converted from our format to one node-schedule can consume let converted_schedule = null; @@ -64,7 +76,7 @@ function scheduleJob(task_key, schedule) { logger.verbose(`Skipping running task ${task_state['key']} as it is already in progress.`); return; } - + // remove schedule if it's a one-time task if (task_state['schedule']['type'] !== 'recurring') await db_api.updateRecord('tasks', {key: task_key}, {schedule: null}); // we're just "running" the task, any confirmation should be user-initiated @@ -84,9 +96,10 @@ exports.setupTasks = async () => { const tasks_keys = Object.keys(TASKS); for (let i = 0; i < tasks_keys.length; i++) { const task_key = tasks_keys[i]; + const mergedDefaultOptions = Object.assign(defaultOptions['all'], defaultOptions[task_key] || {}); const task_in_db = await db_api.getRecord('tasks', {key: task_key}); if (!task_in_db) { - // insert task metadata into table if missing + // insert task metadata into table if missing, eventually move title to UI await db_api.insertRecordIntoTable('tasks', { key: task_key, title: TASKS[task_key]['title'], @@ -97,9 +110,17 @@ exports.setupTasks = async () => { data: null, error: null, schedule: null, - options: {} + options: Object.assign(defaultOptions['all'], defaultOptions[task_key] || {}) }); } else { + // verify all options exist in task + for (const key of Object.keys(mergedDefaultOptions)) { + if (!(task_in_db.options && task_in_db.options.hasOwnProperty(key))) { + const option_key = `options.${key}` + await db_api.updateRecord('tasks', {key: task_key}, {[option_key]: mergedDefaultOptions[key]}); + } + } + // reset task if necessary await db_api.updateRecord('tasks', {key: task_key}, {running: false, confirming: false}); @@ -220,16 +241,18 @@ async function checkForAutoDeleteFiles() { return null; } const delete_older_than_timestamp = Date.now() - task_obj['options']['threshold_days']*86400*1000; - const uids = (await db_api.getRecords('files', {registered: {$lt: delete_older_than_timestamp}})).map(file => file.uid); - return {uids: uids}; + const files = (await db_api.getRecords('files', {registered: {$lt: delete_older_than_timestamp}})) + const files_to_remove = files.map(file => {return {uid: file.uid, sub_id: file.sub_id}}); + return {files_to_remove: files_to_remove}; } async function autoDeleteFiles(data) { - if (data['uids']) { - logger.info(`Removing ${data['uids'].length} old files!`); - for (let i = 0; i < data['uids'].length; i++) { - const file_to_remove = data['uids'][i]; - await db_api.deleteFile(file_to_remove); + const task_obj = await db_api.getRecord('tasks', {key: 'delete_old_files'}); + if (data['files_to_remove']) { + logger.info(`Removing ${data['files_to_remove'].length} old files!`); + for (let i = 0; i < data['files_to_remove'].length; i++) { + const file_to_remove = data['files_to_remove'][i]; + await db_api.deleteFile(file_to_remove['uid'], task_obj['options']['blacklist_files'] || (file_to_remove['sub_id'] && file_to_remove['blacklist_subscription_files'])); } } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8eb8642..e60e0fb 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -93,6 +93,7 @@ import { NotificationsListComponent } from './components/notifications-list/noti import { TaskSettingsComponent } from './components/task-settings/task-settings.component'; import { GenerateRssUrlComponent } from './dialogs/generate-rss-url/generate-rss-url.component'; import { SortPropertyComponent } from './components/sort-property/sort-property.component'; +import { OnlyNumberDirective } from './directives/only-number.directive'; registerLocaleData(es, 'es'); @@ -143,7 +144,8 @@ registerLocaleData(es, 'es'); NotificationsListComponent, TaskSettingsComponent, GenerateRssUrlComponent, - SortPropertyComponent + SortPropertyComponent, + OnlyNumberDirective ], imports: [ CommonModule, diff --git a/src/app/components/task-settings/task-settings.component.html b/src/app/components/task-settings/task-settings.component.html index 6639bbf..9894c7e 100644 --- a/src/app/components/task-settings/task-settings.component.html +++ b/src/app/components/task-settings/task-settings.component.html @@ -4,9 +4,15 @@
Delete files older than - + days +
+ Blacklist all files +
+
+ Blacklist deleted subscription files +
diff --git a/src/app/directives/only-number.directive.spec.ts b/src/app/directives/only-number.directive.spec.ts new file mode 100644 index 0000000..36714b1 --- /dev/null +++ b/src/app/directives/only-number.directive.spec.ts @@ -0,0 +1,8 @@ +import { OnlyNumberDirective } from './only-number.directive'; + +describe('OnlyNumberDirective', () => { + it('should create an instance', () => { + const directive = new OnlyNumberDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/directives/only-number.directive.ts b/src/app/directives/only-number.directive.ts new file mode 100644 index 0000000..1fb4355 --- /dev/null +++ b/src/app/directives/only-number.directive.ts @@ -0,0 +1,72 @@ +// https://stackoverflow.com/a/58535434/8088021 + +import { Directive, ElementRef, HostListener } from '@angular/core'; + +@Directive({ + selector: '[onlyNumber]' +}) +export class OnlyNumberDirective { + + private navigationKeys = [ + 'Backspace', + 'Delete', + 'Tab', + 'Escape', + 'Enter', + 'Home', + 'End', + 'ArrowLeft', + 'ArrowRight', + 'Clear', + 'Copy', + 'Paste' + ]; + inputElement: HTMLElement; + constructor(public el: ElementRef) { + this.inputElement = el.nativeElement; + } + + @HostListener('keydown', ['$event']) + onKeyDown(e: KeyboardEvent) { + if ( + this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc. + (e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A + (e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C + (e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V + (e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X + (e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac) + (e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac) + (e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac) + (e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac) + ) { + // let it happen, don't do anything + return; + } + // Ensure that it is a number and stop the keypress + const key = Number(e.key) + if ( + (e.shiftKey || (isNaN(key) && !(e.key === '.'))) + ) { + e.preventDefault(); + } + } + + @HostListener('paste', ['$event']) + onPaste(event: ClipboardEvent) { + event.preventDefault(); + const pastedInput: string = event.clipboardData + .getData('text/plain') + .replace(/\D/g, ''); // get a digit-only string + document.execCommand('insertText', false, pastedInput); + } + + @HostListener('drop', ['$event']) + onDrop(event: DragEvent) { + event.preventDefault(); + const textData = event.dataTransfer.getData('text').replace(/\D/g, ''); + this.inputElement.focus(); + document.execCommand('insertText', false, textData); + } + + +} \ No newline at end of file