From 070eadbd8f360892635fbff3a824a54ad9254364 Mon Sep 17 00:00:00 2001 From: "Darryl L. Pierce" Date: Tue, 30 Jun 2020 08:36:49 -0400 Subject: [PATCH] Changed consolidating to moving the library [#21] --- .../app/library/actions/library.actions.ts | 34 +++++----- .../library/adaptors/library.adaptor.spec.ts | 31 +++++---- .../app/library/adaptors/library.adaptor.ts | 14 ++++- .../library/effects/library.effects.spec.ts | 33 +++++++--- .../app/library/effects/library.effects.ts | 63 ++++++++++--------- .../src/app/library/library.constants.ts | 2 +- .../models/net/consolidate-library-request.ts | 2 + .../library/reducers/library.reducer.spec.ts | 27 ++++---- .../app/library/reducers/library.reducer.ts | 15 ++--- .../library/services/library.service.spec.ts | 9 ++- .../app/library/services/library.service.ts | 17 +++-- .../app/user/models/preferences.constants.ts | 5 +- 12 files changed, 146 insertions(+), 106 deletions(-) diff --git a/comixed-frontend/src/app/library/actions/library.actions.ts b/comixed-frontend/src/app/library/actions/library.actions.ts index a38d97025..61b1808d0 100644 --- a/comixed-frontend/src/app/library/actions/library.actions.ts +++ b/comixed-frontend/src/app/library/actions/library.actions.ts @@ -35,9 +35,9 @@ export enum LibraryActionTypes { ConvertComics = '[LIBRARY] Convert comics to a new archive type', ComicsConverting = '[LIBRARY] Comics converting to a new archive type', ConvertComicsFailed = '[LIBRARY] Failed to convert comics', - Consolidate = '[LIBRARY] Consolidate the library', - Consolidated = '[LIBRARY] Library is consolidated', - ConsolidateFailed = '[LIBRARY] Failed to consolidate library', + MoveComics = '[LIBRARY] Move the library', + ComicsMoved = '[LIBRARY] Library was moved', + MoveComicsFailed = '[LIBRARY] Moving the library failed', ClearImageCache = '[LIBRARY] Clear the image cache', ImageCacheCleared = '[LIBRARY] Image cache cleared', ClearImageCacheFailed = '[LIBRARY] Failed to clear the image cache' @@ -145,20 +145,26 @@ export class LibraryConvertComicsFailed implements Action { constructor() {} } -export class LibraryConsolidate implements Action { - readonly type = LibraryActionTypes.Consolidate; +export class LibraryMoveComics implements Action { + readonly type = LibraryActionTypes.MoveComics; - constructor(public payload: { deletePhysicalFiles: boolean }) {} + constructor( + public payload: { + deletePhysicalFiles: boolean; + directory: string; + renamingRule: string; + } + ) {} } -export class LibraryConsolidated implements Action { - readonly type = LibraryActionTypes.Consolidated; +export class LibraryComicsMoved implements Action { + readonly type = LibraryActionTypes.ComicsMoved; - constructor(public payload: { deletedComics: Comic[] }) {} + constructor() {} } -export class LibraryConsolidateFailed implements Action { - readonly type = LibraryActionTypes.ConsolidateFailed; +export class LibraryMoveComicsFailed implements Action { + readonly type = LibraryActionTypes.MoveComicsFailed; constructor() {} } @@ -195,9 +201,9 @@ export type LibraryActions = | LibraryConvertComics | LibraryComicsConverting | LibraryConvertComicsFailed - | LibraryConsolidate - | LibraryConsolidated - | LibraryConsolidateFailed + | LibraryMoveComics + | LibraryComicsMoved + | LibraryMoveComicsFailed | LibraryClearImageCache | LibraryImageCacheCleared | LibraryClearImageCacheFailed; diff --git a/comixed-frontend/src/app/library/adaptors/library.adaptor.spec.ts b/comixed-frontend/src/app/library/adaptors/library.adaptor.spec.ts index a5eaa94d0..4be2af320 100644 --- a/comixed-frontend/src/app/library/adaptors/library.adaptor.spec.ts +++ b/comixed-frontend/src/app/library/adaptors/library.adaptor.spec.ts @@ -47,13 +47,13 @@ import { LibraryClearImageCache, LibraryClearImageCacheFailed, LibraryComicsConverting, - LibraryConsolidate, - LibraryConsolidated, - LibraryConsolidateFailed, + LibraryComicsMoved, LibraryConvertComics, LibraryConvertComicsFailed, LibraryGetUpdates, LibraryImageCacheCleared, + LibraryMoveComics, + LibraryMoveComicsFailed, LibraryUpdatesReceived } from '../actions/library.actions'; import { LibraryAdaptor } from './library.adaptor'; @@ -76,6 +76,9 @@ describe('LibraryAdaptor', () => { const ARCHIVE_TYPE = 'CBZ'; const RENAME_PAGES = true; const READING_LISTS = [READING_LIST_1, READING_LIST_2]; + const DIRECTORY = '/Users/comixedreader/Documents/comics'; + const RENAMING_RULE = + '$PUBLISHER/$SERIES/$VOLUME/$SERIES v$VOLUME #$ISSUE [$COVERDATE]'; let adaptor: LibraryAdaptor; let store: Store; @@ -312,12 +315,16 @@ describe('LibraryAdaptor', () => { describe('consolidating the library', () => { beforeEach(() => { - adaptor.consolidate(true); + adaptor.consolidate(true, DIRECTORY, RENAMING_RULE); }); it('fires an action', () => { expect(store.dispatch).toHaveBeenCalledWith( - new LibraryConsolidate({ deletePhysicalFiles: true }) + new LibraryMoveComics({ + deletePhysicalFiles: true, + directory: DIRECTORY, + renamingRule: RENAMING_RULE + }) ); }); @@ -343,9 +350,7 @@ describe('LibraryAdaptor', () => { readingLists: [] }) ); - store.dispatch( - new LibraryConsolidated({ deletedComics: DELETED_COMICS }) - ); + store.dispatch(new LibraryComicsMoved()); }); it('provides updates on consolidating', () => { @@ -353,19 +358,11 @@ describe('LibraryAdaptor', () => { expect(response).toBeFalsy() ); }); - - it('provides updates on comics', () => { - DELETED_COMICS.forEach(comic => { - adaptor.comic$.subscribe(comics => - expect(comics).not.toContain(comic) - ); - }); - }); }); describe('failure', () => { beforeEach(() => { - store.dispatch(new LibraryConsolidateFailed()); + store.dispatch(new LibraryMoveComicsFailed()); }); it('provides updates on consolidating', () => { diff --git a/comixed-frontend/src/app/library/adaptors/library.adaptor.ts b/comixed-frontend/src/app/library/adaptors/library.adaptor.ts index b415dd992..45305834d 100644 --- a/comixed-frontend/src/app/library/adaptors/library.adaptor.ts +++ b/comixed-frontend/src/app/library/adaptors/library.adaptor.ts @@ -31,10 +31,10 @@ import { extractField } from 'app/library/library.functions'; import { LastReadDate } from 'app/library/models/last-read-date'; import { LibraryClearImageCache, - LibraryConsolidate, LibraryConvertComics, LibraryDeleteMultipleComics, LibraryGetUpdates, + LibraryMoveComics, LibraryReset, LibraryStartRescan } from 'app/library/actions/library.actions'; @@ -300,12 +300,20 @@ export class LibraryAdaptor { return this._converting$.asObservable(); } - consolidate(deletePhysicalFiles: boolean): void { + consolidate( + deletePhysicalFiles: boolean, + targetDirectory: string, + renamingRule: string + ): void { this.logger.debug( `firing action to consolidate library: deletePhysicalFiles=${deletePhysicalFiles}` ); this.store.dispatch( - new LibraryConsolidate({ deletePhysicalFiles: deletePhysicalFiles }) + new LibraryMoveComics({ + deletePhysicalFiles: deletePhysicalFiles, + directory: targetDirectory, + renamingRule: renamingRule + }) ); } diff --git a/comixed-frontend/src/app/library/effects/library.effects.spec.ts b/comixed-frontend/src/app/library/effects/library.effects.spec.ts index 655f07ddf..ad7019062 100644 --- a/comixed-frontend/src/app/library/effects/library.effects.spec.ts +++ b/comixed-frontend/src/app/library/effects/library.effects.spec.ts @@ -25,9 +25,7 @@ import { LibraryClearImageCache, LibraryClearImageCacheFailed, LibraryComicsConverting, - LibraryConsolidate, - LibraryConsolidated, - LibraryConsolidateFailed, + LibraryComicsMoved, LibraryConvertComics, LibraryConvertComicsFailed, LibraryDeleteMultipleComics, @@ -35,6 +33,8 @@ import { LibraryGetUpdates, LibraryGetUpdatesFailed, LibraryImageCacheCleared, + LibraryMoveComics, + LibraryMoveComicsFailed, LibraryMultipleComicsDeleted, LibraryRescanStarted, LibraryStartRescan, @@ -61,6 +61,9 @@ describe('LibraryEffects', () => { const LAST_READ_DATES = [COMIC_1_LAST_READ_DATE]; const COUNT = 25; const ASCENDING = false; + const DIRECTORY = '/Users/comixedreader/Documents/comics'; + const RENAMING_RULE = + '$PUBLISHER/$SERIES/$VOLUME/$SERIES v$VOLUME #$ISSUE [$COVERDATE]'; let actions$: Observable; let effects: LibraryEffects; @@ -347,8 +350,12 @@ describe('LibraryEffects', () => { describe('consolidating the library', () => { it('fires an action on success', () => { const serviceResponse = COMICS; - const action = new LibraryConsolidate({ deletePhysicalFiles: true }); - const outcome = new LibraryConsolidated({ deletedComics: COMICS }); + const action = new LibraryMoveComics({ + deletePhysicalFiles: true, + directory: DIRECTORY, + renamingRule: RENAMING_RULE + }); + const outcome = new LibraryComicsMoved(); actions$ = hot('-a', { a: action }); libraryService.consolidate.and.returnValue(of(serviceResponse)); @@ -362,8 +369,12 @@ describe('LibraryEffects', () => { it('fires an action on service failure', () => { const serviceResponse = new HttpErrorResponse({}); - const action = new LibraryConsolidate({ deletePhysicalFiles: true }); - const outcome = new LibraryConsolidateFailed(); + const action = new LibraryMoveComics({ + deletePhysicalFiles: true, + directory: DIRECTORY, + renamingRule: RENAMING_RULE + }); + const outcome = new LibraryMoveComicsFailed(); actions$ = hot('-a', { a: action }); libraryService.consolidate.and.returnValue(throwError(serviceResponse)); @@ -376,8 +387,12 @@ describe('LibraryEffects', () => { }); it('fires an action on general failure', () => { - const action = new LibraryConsolidate({ deletePhysicalFiles: true }); - const outcome = new LibraryConsolidateFailed(); + const action = new LibraryMoveComics({ + deletePhysicalFiles: true, + directory: DIRECTORY, + renamingRule: RENAMING_RULE + }); + const outcome = new LibraryMoveComicsFailed(); actions$ = hot('-a', { a: action }); libraryService.consolidate.and.throwError('expected'); diff --git a/comixed-frontend/src/app/library/effects/library.effects.ts b/comixed-frontend/src/app/library/effects/library.effects.ts index 1ed2e54ac..b4bee79af 100644 --- a/comixed-frontend/src/app/library/effects/library.effects.ts +++ b/comixed-frontend/src/app/library/effects/library.effects.ts @@ -32,9 +32,7 @@ import { LibraryActionTypes, LibraryClearImageCacheFailed, LibraryComicsConverting, - LibraryConsolidate, - LibraryConsolidated, - LibraryConsolidateFailed, + LibraryComicsMoved, LibraryConvertComics, LibraryConvertComicsFailed, LibraryDeleteMultipleComics, @@ -42,6 +40,8 @@ import { LibraryGetUpdates, LibraryGetUpdatesFailed, LibraryImageCacheCleared, + LibraryMoveComics, + LibraryMoveComicsFailed, LibraryMultipleComicsDeleted, LibraryRescanStarted, LibraryStartRescanFailed, @@ -257,35 +257,38 @@ export class LibraryEffects { @Effect() consolidate$: Observable = this.actions$.pipe( - ofType(LibraryActionTypes.Consolidate), + ofType(LibraryActionTypes.MoveComics), tap(action => this.logger.debug('effect: consolidate library:', action)), - map((action: LibraryConsolidate) => action.payload), + map((action: LibraryMoveComics) => action.payload), switchMap(action => - this.libraryService.consolidate(action.deletePhysicalFiles).pipe( - tap(response => this.logger.debug('received response:', response)), - tap(() => - this.messageService.add({ - severity: 'info', - detail: this.translateService.instant( - 'library-effects.consolidate.success.detail' - ) + this.libraryService + .consolidate( + action.deletePhysicalFiles, + action.directory, + action.renamingRule + ) + .pipe( + tap(response => this.logger.debug('received response:', response)), + tap(() => + this.messageService.add({ + severity: 'info', + detail: this.translateService.instant( + 'library-effects.consolidate.success.detail' + ) + }) + ), + map((response: Comic[]) => new LibraryComicsMoved()), + catchError(error => { + this.logger.error('service failure conslidating library:', error); + this.messageService.add({ + severity: 'error', + detail: this.translateService.instant( + 'library-effects.consolidate.error.detail' + ) + }); + return of(new LibraryMoveComicsFailed()); }) - ), - map( - (response: Comic[]) => - new LibraryConsolidated({ deletedComics: response }) - ), - catchError(error => { - this.logger.error('service failure conslidating library:', error); - this.messageService.add({ - severity: 'error', - detail: this.translateService.instant( - 'library-effects.consolidate.error.detail' - ) - }); - return of(new LibraryConsolidateFailed()); - }) - ) + ) ), catchError(error => { this.logger.error('general failure conslidating library:', error); @@ -295,7 +298,7 @@ export class LibraryEffects { 'general-message.error.general-service-failure' ) }); - return of(new LibraryConsolidateFailed()); + return of(new LibraryMoveComicsFailed()); }) ); diff --git a/comixed-frontend/src/app/library/library.constants.ts b/comixed-frontend/src/app/library/library.constants.ts index 65839a95f..44567d429 100644 --- a/comixed-frontend/src/app/library/library.constants.ts +++ b/comixed-frontend/src/app/library/library.constants.ts @@ -28,7 +28,7 @@ export const SET_DELETED_STATE_URL = `${API_ROOT_URL}/pages/hashes/deleted`; export const START_RESCAN_URL = `${COMIXED_API_ROOT}/comics/rescan`; export const DELETE_MULTIPLE_COMICS_URL = `${COMIXED_API_ROOT}/comics/multiple/delete`; export const CONVERT_COMICS_URL = `${COMIXED_API_ROOT}/library/convert`; -export const CONSOLIDATE_LIBRARY_URL = `${COMIXED_API_ROOT}/library/consolidate`; +export const CONSOLIDATE_LIBRARY_URL = `${COMIXED_API_ROOT}/library/move`; export const CLEAR_IMAGE_CACHE_URL = `${COMIXED_API_ROOT}/library/cache/images`; export const GET_COLLECTION_ENTRIES_URL = `${API_ROOT_URL}/collections/\${type}`; diff --git a/comixed-frontend/src/app/library/models/net/consolidate-library-request.ts b/comixed-frontend/src/app/library/models/net/consolidate-library-request.ts index d314eab73..9542c4e8f 100644 --- a/comixed-frontend/src/app/library/models/net/consolidate-library-request.ts +++ b/comixed-frontend/src/app/library/models/net/consolidate-library-request.ts @@ -18,4 +18,6 @@ export interface ConsolidateLibraryRequest { deletePhysicalFiles: boolean; + targetDirectory: string; + renamingRule: string; } diff --git a/comixed-frontend/src/app/library/reducers/library.reducer.spec.ts b/comixed-frontend/src/app/library/reducers/library.reducer.spec.ts index 315de0e36..c291bedfb 100644 --- a/comixed-frontend/src/app/library/reducers/library.reducer.spec.ts +++ b/comixed-frontend/src/app/library/reducers/library.reducer.spec.ts @@ -27,9 +27,7 @@ import { LibraryClearImageCache, LibraryClearImageCacheFailed, LibraryComicsConverting, - LibraryConsolidate, - LibraryConsolidated, - LibraryConsolidateFailed, + LibraryComicsMoved, LibraryConvertComics, LibraryConvertComicsFailed, LibraryDeleteMultipleComics, @@ -37,6 +35,8 @@ import { LibraryGetUpdates, LibraryGetUpdatesFailed, LibraryImageCacheCleared, + LibraryMoveComics, + LibraryMoveComicsFailed, LibraryMultipleComicsDeleted, LibraryRescanStarted, LibraryReset, @@ -63,6 +63,9 @@ describe('Library Reducer', () => { const ASCENDING = true; const COMIC_COUNT = 2372; const LATEST_UPDATED_DATE = new Date(); + const DIRECTORY = '/Users/comixedreader/Documents/comics'; + const RENAMING_RULE = + '$PUBLISHER/$SERIES/$VOLUME/$SERIES v$VOLUME #$ISSUE [$COVERDATE]'; let state: LibraryState; @@ -368,7 +371,11 @@ describe('Library Reducer', () => { beforeEach(() => { state = reducer( { ...state, consolidating: false }, - new LibraryConsolidate({ deletePhysicalFiles: true }) + new LibraryMoveComics({ + deletePhysicalFiles: true, + directory: DIRECTORY, + renamingRule: RENAMING_RULE + }) ); }); @@ -378,8 +385,6 @@ describe('Library Reducer', () => { }); describe('when the library is consolidated', () => { - const DELETED_COMICS = [COMICS[2]]; - beforeEach(() => { state = reducer( { @@ -387,26 +392,20 @@ describe('Library Reducer', () => { consolidating: true, comics: COMICS }, - new LibraryConsolidated({ deletedComics: DELETED_COMICS }) + new LibraryComicsMoved() ); }); it('clears the consolidating library flag', () => { expect(state.consolidating).toBeFalsy(); }); - - it('removes the deleted comics from the state', () => { - DELETED_COMICS.forEach(comic => - expect(state.comics).not.toContain(comic) - ); - }); }); describe('when consolidation fails', () => { beforeEach(() => { state = reducer( { ...state, consolidating: true }, - new LibraryConsolidateFailed() + new LibraryMoveComicsFailed() ); }); diff --git a/comixed-frontend/src/app/library/reducers/library.reducer.ts b/comixed-frontend/src/app/library/reducers/library.reducer.ts index 4289f4c24..5a2f43d4e 100644 --- a/comixed-frontend/src/app/library/reducers/library.reducer.ts +++ b/comixed-frontend/src/app/library/reducers/library.reducer.ts @@ -17,11 +17,7 @@ */ import { LibraryActions, LibraryActionTypes } from '../actions/library.actions'; -import { - deleteComics, - mergeComics, - mergeReadingLists -} from 'app/library/library.functions'; +import { mergeComics, mergeReadingLists } from 'app/library/library.functions'; import { Comic } from 'app/comics'; import { LastReadDate } from 'app/library/models/last-read-date'; import { ReadingList } from 'app/comics/models/reading-list'; @@ -129,15 +125,14 @@ export function reducer( case LibraryActionTypes.ConvertComicsFailed: return { ...state, convertingComics: false }; - case LibraryActionTypes.Consolidate: + case LibraryActionTypes.MoveComics: return { ...state, consolidating: true }; - case LibraryActionTypes.Consolidated: { - const comics = deleteComics(state.comics, action.payload.deletedComics); - return { ...state, consolidating: false, comics: comics }; + case LibraryActionTypes.ComicsMoved: { + return { ...state, consolidating: false }; } - case LibraryActionTypes.ConsolidateFailed: + case LibraryActionTypes.MoveComicsFailed: return { ...state, consolidating: false }; case LibraryActionTypes.ClearImageCache: diff --git a/comixed-frontend/src/app/library/services/library.service.spec.ts b/comixed-frontend/src/app/library/services/library.service.spec.ts index 22848a828..19e6e530d 100644 --- a/comixed-frontend/src/app/library/services/library.service.spec.ts +++ b/comixed-frontend/src/app/library/services/library.service.spec.ts @@ -58,6 +58,9 @@ describe('LibraryService', () => { const LAST_READ_DATES = [COMIC_1_LAST_READ_DATE]; const COMIC_COUNT = 2372; const LATEST_UPDATED_DATE = new Date(); + const DIRECTORY = '/Users/comixedreader/Documents/comics'; + const RENAMING_RULE = + '$PUBLISHER/$SERIES/$VOLUME/$SERIES v$VOLUME #$ISSUE [$COVERDATE]'; let service: LibraryService; let httpMock: HttpTestingController; @@ -158,13 +161,15 @@ describe('LibraryService', () => { it('can consolidate the library', () => { service - .consolidate(true) + .consolidate(true, DIRECTORY, RENAMING_RULE) .subscribe(response => expect(response).toEqual(COMICS)); const req = httpMock.expectOne(interpolate(CONSOLIDATE_LIBRARY_URL)); expect(req.request.method).toEqual('POST'); expect(req.request.body).toEqual({ - deletePhysicalFiles: true + deletePhysicalFiles: true, + targetDirectory: DIRECTORY, + renamingRule: RENAMING_RULE } as ConsolidateLibraryRequest); req.flush(COMICS); }); diff --git a/comixed-frontend/src/app/library/services/library.service.ts b/comixed-frontend/src/app/library/services/library.service.ts index 2540483e1..cc78c44b0 100644 --- a/comixed-frontend/src/app/library/services/library.service.ts +++ b/comixed-frontend/src/app/library/services/library.service.ts @@ -90,12 +90,19 @@ export class LibraryService { } as ConvertComicsRequest); } - consolidate(deletePhysicalFiles: boolean): Observable { - this.logger.debug( - `[POST] http request consolidate library: deletePhysicalFiles=${deletePhysicalFiles}` - ); + consolidate( + deletePhysicalFiles: boolean, + targetDirectory: string, + renamingRule: string + ): Observable { + this.logger.debug('[POST] http request consolidate library:'); + this.logger.debug(` deletePhysicalFiles=${deletePhysicalFiles}`); + this.logger.debug(` targetDirectory=${targetDirectory}`); + this.logger.debug(` renamingRule=${renamingRule}`); return this.http.post(interpolate(CONSOLIDATE_LIBRARY_URL), { - deletePhysicalFiles: deletePhysicalFiles + deletePhysicalFiles: deletePhysicalFiles, + targetDirectory: targetDirectory, + renamingRule: renamingRule } as ConsolidateLibraryRequest); } diff --git a/comixed-frontend/src/app/user/models/preferences.constants.ts b/comixed-frontend/src/app/user/models/preferences.constants.ts index 7142458c2..ae051eae5 100644 --- a/comixed-frontend/src/app/user/models/preferences.constants.ts +++ b/comixed-frontend/src/app/user/models/preferences.constants.ts @@ -20,8 +20,11 @@ export const LIBRARY_SORT = 'library.sort-by'; export const LIBRARY_ROWS = 'library.rows'; export const LIBRARY_COVER_SIZE = 'library.cover-size'; export const LIBRARY_CURRENT_TAB = 'library.current-tab'; -export const CONSOLIDATE_DELETE_PHYSICAL_FILES = +export const MOVE_COMICS_DELETE_PHYSICAL_FILE = 'library.consolidate.delete-physical-file'; +export const MOVE_COMICS_TARGET_DIRECTORY = + 'library.consolidate.target-directory'; +export const MOVE_COMICS_RENAMING_RULE = 'library.consolidate.renaming-rule'; export const IMPORT_SORT = 'import.sort-by'; export const IMPORT_ROWS = 'import.rows';