From 539a9d5c34539d987eab44dae226f20b44ec6a3c Mon Sep 17 00:00:00 2001 From: "Darryl L. Pierce" Date: Fri, 18 Dec 2020 16:50:17 -0500 Subject: [PATCH] Added saving the import settings on success [#563] * Directory and max. results are saved when comics are found. * Ingoring metadata and deleting blocked pages are saved on import. --- .../import-toolbar.component.ts | 57 +++++++------- .../effects/comic-import.effects.spec.ts | 42 +++++++++-- .../library/effects/comic-import.effects.ts | 35 +++++++-- .../src/app/library/library.constants.ts | 13 ++++ .../import-comics.component.spec.ts | 74 +++++++++---------- .../import-comics/import-comics.component.ts | 46 ++++++------ comixed-web/src/app/user/user.constants.ts | 9 --- 7 files changed, 169 insertions(+), 107 deletions(-) diff --git a/comixed-web/src/app/library/components/import-toolbar/import-toolbar.component.ts b/comixed-web/src/app/library/components/import-toolbar/import-toolbar.component.ts index f7e5e2cdd..8211dc221 100644 --- a/comixed-web/src/app/library/components/import-toolbar/import-toolbar.component.ts +++ b/comixed-web/src/app/library/components/import-toolbar/import-toolbar.component.ts @@ -16,29 +16,31 @@ * along with this program. If not, see */ -import { Component, OnInit } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; import { AbstractControl, FormBuilder, FormGroup, - Validators, + Validators } from '@angular/forms'; -import { LoggerService } from '@angular-ru/logger'; -import { Store } from '@ngrx/store'; -import { loadComicFiles } from '@app/library/actions/comic-import.actions'; -import { selectUser } from '@app/user/selectors/user.selectors'; -import { Subscription } from 'rxjs'; -import { getUserPreference } from '@app/user'; +import {LoggerService} from '@angular-ru/logger'; +import {Store} from '@ngrx/store'; +import {loadComicFiles} from '@app/library/actions/comic-import.actions'; +import {selectUser} from '@app/user/selectors/user.selectors'; +import {Subscription} from 'rxjs'; +import {getUserPreference} from '@app/user'; +import {filter} from 'rxjs/operators'; import { - USER_PREFERENCE_IMPORT_MAXIMUM, - USER_PREFERENCE_IMPORT_ROOT_DIRECTORY, -} from '@app/user/user.constants'; -import { filter } from 'rxjs/operators'; + DEFAULT_IMPORT_MAXIMUM_RESULTS, + DEFAULT_IMPORT_ROOT_DIRECTORY, + IMPORT_MAXIMUM_RESULTS_PREFERENCE, + IMPORT_ROOT_DIRECTORY_PREFERENCE +} from '@app/library/library.constants'; @Component({ selector: 'cx-import-toolbar', templateUrl: './import-toolbar.component.html', - styleUrls: ['./import-toolbar.component.scss'], + styleUrls: ['./import-toolbar.component.scss'] }) export class ImportToolbarComponent implements OnInit { loadFilesForm: FormGroup; @@ -46,11 +48,11 @@ export class ImportToolbarComponent implements OnInit { const; maximumOptions = [ - { label: 'load-comic-files.maximum.all-files', value: 0 }, - { label: 'load-comic-files.maximum.10-files', value: 10 }, - { label: 'load-comic-files.maximum.50-files', value: 50 }, - { label: 'load-comic-files.maximum.100-files', value: 100 }, - { label: 'load-comic-files.maximum.1000-files', value: 1000 }, + {label: 'load-comic-files.maximum.all-files', value: 0}, + {label: 'load-comic-files.maximum.10-files', value: 10}, + {label: 'load-comic-files.maximum.50-files', value: 50}, + {label: 'load-comic-files.maximum.100-files', value: 100}, + {label: 'load-comic-files.maximum.1000-files', value: 1000} ]; constructor( @@ -60,25 +62,25 @@ export class ImportToolbarComponent implements OnInit { ) { this.loadFilesForm = this.formBuilder.group({ directory: ['', Validators.required], - maximum: ['', Validators.required], + maximum: ['', Validators.required] }); this.userSubscription = this.store .select(selectUser) - .pipe(filter((user) => !!user)) - .subscribe((user) => { + .pipe(filter(user => !!user)) + .subscribe(user => { this.controls.directory.setValue( getUserPreference( user.preferences, - USER_PREFERENCE_IMPORT_ROOT_DIRECTORY, - '' + IMPORT_ROOT_DIRECTORY_PREFERENCE, + DEFAULT_IMPORT_ROOT_DIRECTORY ) ); this.controls.maximum.setValue( parseInt( getUserPreference( user.preferences, - USER_PREFERENCE_IMPORT_MAXIMUM, - '0' + IMPORT_MAXIMUM_RESULTS_PREFERENCE, + `${DEFAULT_IMPORT_MAXIMUM_RESULTS}` ), 10 ) @@ -86,7 +88,8 @@ export class ImportToolbarComponent implements OnInit { }); } - ngOnInit(): void {} + ngOnInit(): void { + } get controls(): { [p: string]: AbstractControl } { return this.loadFilesForm.controls; @@ -112,7 +115,7 @@ export class ImportToolbarComponent implements OnInit { this.store.dispatch( loadComicFiles({ directory: this.directory, - maximum: this.maximum, + maximum: this.maximum }) ); } diff --git a/comixed-web/src/app/library/effects/comic-import.effects.spec.ts b/comixed-web/src/app/library/effects/comic-import.effects.spec.ts index 412ce0540..2e9f2171a 100644 --- a/comixed-web/src/app/library/effects/comic-import.effects.spec.ts +++ b/comixed-web/src/app/library/effects/comic-import.effects.spec.ts @@ -43,6 +43,13 @@ import { import { hot } from 'jasmine-marbles'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { saveUserPreference } from '@app/user/actions/user.actions'; +import { + DELETE_BLOCKED_PAGES_PREFERENCE, + IGNORE_METADATA_PREFERENCE, + IMPORT_MAXIMUM_RESULTS_PREFERENCE, + IMPORT_ROOT_DIRECTORY_PREFERENCE +} from '@app/library/library.constants'; describe('ComicImportEffects', () => { const FILES = [COMIC_FILE_1, COMIC_FILE_2, COMIC_FILE_3, COMIC_FILE_4]; @@ -90,18 +97,28 @@ describe('ComicImportEffects', () => { }); describe('loading comic files', () => { + const MAXIMUM_RESULT = 100; + it('fires an action on success', () => { const serviceResponse = { files: FILES } as LoadComicFilesResponse; const action = loadComicFiles({ directory: ROOT_DIRECTORY, - maximum: 100 + maximum: MAXIMUM_RESULT + }); + const outcome1 = comicFilesLoaded({ files: FILES }); + const outcome2 = saveUserPreference({ + name: IMPORT_ROOT_DIRECTORY_PREFERENCE, + value: ROOT_DIRECTORY + }); + const outcome3 = saveUserPreference({ + name: IMPORT_MAXIMUM_RESULTS_PREFERENCE, + value: `${MAXIMUM_RESULT}` }); - const outcome = comicFilesLoaded({ files: FILES }); actions$ = hot('-a', { a: action }); comicImportService.loadComicFiles.and.returnValue(of(serviceResponse)); - const expected = hot('-b', { b: outcome }); + const expected = hot('-(bcd)', { b: outcome1, c: outcome2, d: outcome3 }); expect(effects.loadComicFiles$).toBeObservable(expected); expect(alertService.info).toHaveBeenCalledWith(jasmine.any(String)); }); @@ -141,19 +158,30 @@ describe('ComicImportEffects', () => { }); describe('sending comic files', () => { + const IGNORE_METADATA = Math.random() > 0.5; + const DELETE_BLOCKED_PAGES = Math.random() > 0.5; + it('fires an action on success', () => { const serviceResponse = new HttpResponse({ status: 200 }); const action = sendComicFiles({ files: FILES, - ignoreMetadata: false, - deleteBlockedPages: true + ignoreMetadata: IGNORE_METADATA, + deleteBlockedPages: DELETE_BLOCKED_PAGES + }); + const outcome1 = comicFilesSent(); + const outcome2 = saveUserPreference({ + name: IGNORE_METADATA_PREFERENCE, + value: `${IGNORE_METADATA}` + }); + const outcome3 = saveUserPreference({ + name: DELETE_BLOCKED_PAGES_PREFERENCE, + value: `${DELETE_BLOCKED_PAGES}` }); - const outcome = comicFilesSent(); actions$ = hot('-a', { a: action }); comicImportService.sendComicFiles.and.returnValue(of(serviceResponse)); - const expected = hot('-b', { b: outcome }); + const expected = hot('-(bcd)', { b: outcome1, c: outcome2, d: outcome3 }); expect(effects.sendComicFiles$).toBeObservable(expected); expect(alertService.info).toHaveBeenCalledWith(jasmine.any(String)); }); diff --git a/comixed-web/src/app/library/effects/comic-import.effects.ts b/comixed-web/src/app/library/effects/comic-import.effects.ts index 50fe9ac67..8ea1921f4 100644 --- a/comixed-web/src/app/library/effects/comic-import.effects.ts +++ b/comixed-web/src/app/library/effects/comic-import.effects.ts @@ -30,9 +30,16 @@ import { sendComicFiles, sendComicFilesFailed } from '@app/library/actions/comic-import.actions'; -import { catchError, map, switchMap, tap } from 'rxjs/operators'; +import { catchError, mergeMap, switchMap, tap } from 'rxjs/operators'; import { LoadComicFilesResponse } from '@app/library/models/net/load-comic-files-response'; import { of } from 'rxjs'; +import { saveUserPreference } from '@app/user/actions/user.actions'; +import { + DELETE_BLOCKED_PAGES_PREFERENCE, + IGNORE_METADATA_PREFERENCE, + IMPORT_MAXIMUM_RESULTS_PREFERENCE, + IMPORT_ROOT_DIRECTORY_PREFERENCE +} from '@app/library/library.constants'; @Injectable() export class ComicImportEffects { @@ -64,9 +71,17 @@ export class ComicImportEffects { ) ) ), - map((response: LoadComicFilesResponse) => - comicFilesLoaded({ files: response.files }) - ), + mergeMap((response: LoadComicFilesResponse) => [ + comicFilesLoaded({ files: response.files }), + saveUserPreference({ + name: IMPORT_ROOT_DIRECTORY_PREFERENCE, + value: action.directory + }), + saveUserPreference({ + name: IMPORT_MAXIMUM_RESULTS_PREFERENCE, + value: `${action.maximum}` + }) + ]), catchError(error => { this.logger.error('Service failure:', error); this.alertService.error( @@ -109,7 +124,17 @@ export class ComicImportEffects { ) ) ), - map(() => comicFilesSent()), + mergeMap(() => [ + comicFilesSent(), + saveUserPreference({ + name: IGNORE_METADATA_PREFERENCE, + value: `${action.ignoreMetadata}` + }), + saveUserPreference({ + name: DELETE_BLOCKED_PAGES_PREFERENCE, + value: `${action.deleteBlockedPages}` + }) + ]), catchError(error => { this.logger.error('Service failure:', error); this.alertService.error( diff --git a/comixed-web/src/app/library/library.constants.ts b/comixed-web/src/app/library/library.constants.ts index 3d437965d..a39e24c20 100644 --- a/comixed-web/src/app/library/library.constants.ts +++ b/comixed-web/src/app/library/library.constants.ts @@ -31,6 +31,19 @@ export const SEND_COMIC_FILES_URL = `${API_ROOT_URL}/files/import`; export const LOAD_COMIC_URL = `${API_ROOT_URL}/comics/\${id}`; +// import options +export const IMPORT_ROOT_DIRECTORY_PREFERENCE = + 'preference.import.root-directory'; +export const DEFAULT_IMPORT_ROOT_DIRECTORY = ''; +export const IMPORT_MAXIMUM_RESULTS_PREFERENCE = + 'preference.import.maximum-results'; +export const DEFAULT_IMPORT_MAXIMUM_RESULTS = 0; +export const IGNORE_METADATA_PREFERENCE = 'preference.import.ignore-metadata'; +export const IGNORE_METADATA_DEFAULT = `${false}`; +export const DELETE_BLOCKED_PAGES_PREFERENCE = + 'preference.import.delete-blocked-pages'; +export const DELETE_BLOCKED_PAGES_DEFAULT = `${false}`; + // display options export const PAGE_SIZE_PREFERENCE = 'preference.page-size'; export const DEFAULT_PAGE_SIZE = 400; diff --git a/comixed-web/src/app/library/pages/import-comics/import-comics.component.spec.ts b/comixed-web/src/app/library/pages/import-comics/import-comics.component.spec.ts index 670c28c51..b57b5bb71 100644 --- a/comixed-web/src/app/library/pages/import-comics/import-comics.component.spec.ts +++ b/comixed-web/src/app/library/pages/import-comics/import-comics.component.spec.ts @@ -16,51 +16,51 @@ * along with this program. If not, see */ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { ImportComicsComponent } from './import-comics.component'; -import { LoggerModule } from '@angular-ru/logger'; -import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { TranslateModule, TranslateService } from '@ngx-translate/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {ImportComicsComponent} from './import-comics.component'; +import {LoggerModule} from '@angular-ru/logger'; +import {MockStore, provideMockStore} from '@ngrx/store/testing'; +import {TranslateModule, TranslateService} from '@ngx-translate/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import { COMIC_IMPORT_FEATURE_KEY, initialState as initialComicImportState } from '@app/library/reducers/comic-import.reducer'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCheckboxModule } from '@angular/material/checkbox'; +import {MatDialogModule} from '@angular/material/dialog'; +import {MatButtonModule} from '@angular/material/button'; +import {MatCheckboxModule} from '@angular/material/checkbox'; import { initialState as initialUserState, USER_FEATURE_KEY } from '@app/user/reducers/user.reducer'; -import { setBusyState } from '@app/core/actions/busy.actions'; +import {setBusyState} from '@app/core/actions/busy.actions'; import { COMIC_FILE_1, COMIC_FILE_2, COMIC_FILE_3, COMIC_FILE_4 } from '@app/library/library.fixtures'; -import { ConfirmationService } from '@app/core'; -import { Confirmation } from '@app/core/models/confirmation'; -import { sendComicFiles } from '@app/library/actions/comic-import.actions'; -import { USER_ADMIN } from '@app/user/user.fixtures'; -import { User } from '@app/user/models/user'; +import {ConfirmationService} from '@app/core'; +import {Confirmation} from '@app/core/models/confirmation'; +import {sendComicFiles} from '@app/library/actions/comic-import.actions'; +import {USER_ADMIN} from '@app/user/user.fixtures'; +import {User} from '@app/user/models/user'; +import {MatIconModule} from '@angular/material/icon'; +import {ImportToolbarComponent} from '@app/library/components/import-toolbar/import-toolbar.component'; +import {ComicFileListComponent} from '@app/library/components/comic-file-list/comic-file-list.component'; +import {ComicFileDetailsComponent} from '@app/library/components/comic-file-details/comic-file-details.component'; +import {MatInputModule} from '@angular/material/input'; +import {MatSelectModule} from '@angular/material/select'; +import {MatTableModule} from '@angular/material/table'; +import {ComicFileCoverUrlPipe} from '@app/library/pipes/comic-file-cover-url.pipe'; +import {Title} from '@angular/platform-browser'; +import {MatCardModule} from '@angular/material/card'; +import {ComicPageComponent} from '@app/core/components/comic-page/comic-page.component'; +import {MatTooltipModule} from '@angular/material/tooltip'; import { - USER_PREFERENCE_DELETE_BLOCKED_PAGES, - USER_PREFERENCE_IGNORE_METADATA -} from '@app/user/user.constants'; -import { MatIconModule } from '@angular/material/icon'; -import { ImportToolbarComponent } from '@app/library/components/import-toolbar/import-toolbar.component'; -import { ComicFileListComponent } from '@app/library/components/comic-file-list/comic-file-list.component'; -import { ComicFileDetailsComponent } from '@app/library/components/comic-file-details/comic-file-details.component'; -import { MatInputModule } from '@angular/material/input'; -import { MatSelectModule } from '@angular/material/select'; -import { MatTableModule } from '@angular/material/table'; -import { ComicFileCoverUrlPipe } from '@app/library/pipes/comic-file-cover-url.pipe'; -import { Title } from '@angular/platform-browser'; -import { MatCardModule } from '@angular/material/card'; -import { ComicPageComponent } from '@app/core/components/comic-page/comic-page.component'; -import { MatTooltipModule } from '@angular/material/tooltip'; + DELETE_BLOCKED_PAGES_PREFERENCE, + IGNORE_METADATA_PREFERENCE +} from '@app/library/library.constants'; describe('ImportComicsComponent', () => { const initialState = { @@ -102,7 +102,7 @@ describe('ImportComicsComponent', () => { MatCardModule, MatTooltipModule ], - providers: [provideMockStore({ initialState }), ConfirmationService] + providers: [provideMockStore({initialState}), ConfirmationService] }).compileComponents(); fixture = TestBed.createComponent(ImportComicsComponent); @@ -139,11 +139,11 @@ describe('ImportComicsComponent', () => { ...USER_ADMIN, preferences: [ { - name: USER_PREFERENCE_IGNORE_METADATA, + name: IGNORE_METADATA_PREFERENCE, value: `${IGNORE_METADATA}` }, { - name: USER_PREFERENCE_DELETE_BLOCKED_PAGES, + name: DELETE_BLOCKED_PAGES_PREFERENCE, value: `${DELETE_BLOCKED_PAGES}` } ] @@ -185,7 +185,7 @@ describe('ImportComicsComponent', () => { it('fires an action', () => { expect(store.dispatch).toHaveBeenCalledWith( - setBusyState({ enabled: true }) + setBusyState({enabled: true}) ); }); }); @@ -208,7 +208,7 @@ describe('ImportComicsComponent', () => { it('fires an action', () => { expect(store.dispatch).toHaveBeenCalledWith( - setBusyState({ enabled: false }) + setBusyState({enabled: false}) ); }); }); @@ -233,7 +233,7 @@ describe('ImportComicsComponent', () => { it('fires an action', () => { expect(store.dispatch).toHaveBeenCalledWith( - setBusyState({ enabled: true }) + setBusyState({enabled: true}) ); }); }); @@ -256,7 +256,7 @@ describe('ImportComicsComponent', () => { it('fires an action', () => { expect(store.dispatch).toHaveBeenCalledWith( - setBusyState({ enabled: false }) + setBusyState({enabled: false}) ); }); }); diff --git a/comixed-web/src/app/library/pages/import-comics/import-comics.component.ts b/comixed-web/src/app/library/pages/import-comics/import-comics.component.ts index 34124238f..d924aa6fd 100644 --- a/comixed-web/src/app/library/pages/import-comics/import-comics.component.ts +++ b/comixed-web/src/app/library/pages/import-comics/import-comics.component.ts @@ -16,28 +16,30 @@ * along with this program. If not, see */ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Subscription } from 'rxjs'; -import { ComicFile } from '@app/library/models/comic-file'; -import { LoggerService } from '@angular-ru/logger'; -import { Store } from '@ngrx/store'; +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {Subscription} from 'rxjs'; +import {ComicFile} from '@app/library/models/comic-file'; +import {LoggerService} from '@angular-ru/logger'; +import {Store} from '@ngrx/store'; import { selectComicFiles, selectComicFileSelections, selectComicImportState } from '@app/library/selectors/comic-import.selectors'; -import { setBusyState } from '@app/core/actions/busy.actions'; -import { ConfirmationService } from '@app/core'; -import { TranslateService } from '@ngx-translate/core'; -import { sendComicFiles } from '@app/library/actions/comic-import.actions'; -import { selectUser } from '@app/user/selectors/user.selectors'; -import { filter } from 'rxjs/operators'; -import { getUserPreference } from '@app/user'; +import {setBusyState} from '@app/core/actions/busy.actions'; +import {ConfirmationService} from '@app/core'; +import {TranslateService} from '@ngx-translate/core'; +import {sendComicFiles} from '@app/library/actions/comic-import.actions'; +import {selectUser} from '@app/user/selectors/user.selectors'; +import {filter} from 'rxjs/operators'; +import {getUserPreference} from '@app/user'; +import {Title} from '@angular/platform-browser'; import { - USER_PREFERENCE_DELETE_BLOCKED_PAGES, - USER_PREFERENCE_IGNORE_METADATA -} from '@app/user/user.constants'; -import { Title } from '@angular/platform-browser'; + DELETE_BLOCKED_PAGES_DEFAULT, + DELETE_BLOCKED_PAGES_PREFERENCE, + IGNORE_METADATA_DEFAULT, + IGNORE_METADATA_PREFERENCE +} from '@app/library/library.constants'; @Component({ selector: 'cx-import-comics', @@ -76,14 +78,14 @@ export class ImportComicsComponent implements OnInit, OnDestroy { this.ignoreMetadata = getUserPreference( user.preferences, - USER_PREFERENCE_IGNORE_METADATA, - 'false' + IGNORE_METADATA_PREFERENCE, + IGNORE_METADATA_DEFAULT ) === 'true'; this.deleteBlockedPages = getUserPreference( user.preferences, - USER_PREFERENCE_DELETE_BLOCKED_PAGES, - 'false' + DELETE_BLOCKED_PAGES_PREFERENCE, + DELETE_BLOCKED_PAGES_DEFAULT ) === 'true'; }); this.filesSubscription = this.store @@ -103,7 +105,7 @@ export class ImportComicsComponent implements OnInit, OnDestroy { if (this.busy !== busy) { this.logger.debug('Setting busy state:', busy); this.busy = busy; - this.store.dispatch(setBusyState({ enabled: busy })); + this.store.dispatch(setBusyState({enabled: busy})); } this.importing = state.importing; }); @@ -135,7 +137,7 @@ export class ImportComicsComponent implements OnInit, OnDestroy { ), message: this.translateService.instant( 'import-comic-files.confirm-start-message', - { count: this.selectedFiles.length } + {count: this.selectedFiles.length} ), confirm: () => { this.logger.debug('Starting import'); diff --git a/comixed-web/src/app/user/user.constants.ts b/comixed-web/src/app/user/user.constants.ts index 2816826f9..3bdaf2a84 100644 --- a/comixed-web/src/app/user/user.constants.ts +++ b/comixed-web/src/app/user/user.constants.ts @@ -25,12 +25,3 @@ export const LOAD_CURRENT_USER_URL = `${API_ROOT_URL}/user`; export const LOGIN_USER_URL = `${API_ROOT_URL}/token/generate-token`; export const SAVE_USER_PREFERENCE_URL = `${API_ROOT_URL}/user/preferences/\${name}`; export const DELETE_USER_PREFERENCE_URL = `${API_ROOT_URL}/user/preferences/\${name}`; - -// user preferences -export const USER_PREFERENCE_IMPORT_ROOT_DIRECTORY = - 'comic-files.import.root-directory'; -export const USER_PREFERENCE_IMPORT_MAXIMUM = 'comic-files.import.maximum'; -export const USER_PREFERENCE_IGNORE_METADATA = - 'comic-files.import.ignore-metadata'; -export const USER_PREFERENCE_DELETE_BLOCKED_PAGES = - 'comic-files.import.delete-blocked-pages';