Skip to content

Commit

Permalink
[Issue comixed#137] Add library actions to convert comics.
Browse files Browse the repository at this point in the history
  • Loading branch information
mcpierce committed Mar 18, 2020
1 parent c88575a commit f4a938c
Show file tree
Hide file tree
Showing 13 changed files with 363 additions and 28 deletions.
5 changes: 0 additions & 5 deletions comixed-frontend/src/app/app.constants.ts
Expand Up @@ -18,11 +18,6 @@

export const COMIXED_API_ROOT = '/api';

export const GET_COMICS_URL = `${COMIXED_API_ROOT}/library`;
export const GET_UPDATES_URL = `${COMIXED_API_ROOT}/comics/since/\${timestamp}`;
export const START_RESCAN_URL = `${COMIXED_API_ROOT}/comics/rescan`;
export const DELETE_MULTIPLE_COMICS_URL = `${COMIXED_API_ROOT}/comics/multiple/delete`;

export const GET_READING_LISTS_URL = `${COMIXED_API_ROOT}/lists`;
export const GET_READING_LIST_URL = `${COMIXED_API_ROOT}/lists/\${id}`;
export const CREATE_READING_LIST_URL = `${COMIXED_API_ROOT}/lists`;
Expand Down
34 changes: 32 additions & 2 deletions comixed-frontend/src/app/library/actions/library.actions.ts
Expand Up @@ -30,7 +30,10 @@ export enum LibraryActionTypes {
RescanFailedToStart = '[LIBRARY] Failed to start scanning library',
DeleteMultipleComics = '[LIBRARY] Delete multiple comics',
MultipleComicsDeleted = '[LIBRARY] Multiple comics deleted',
DeleteMultipleComicsFailed = '[LIBRARY] Failed to delete multiple comics'
DeleteMultipleComicsFailed = '[LIBRARY] Failed to delete multiple comics',
ConvertComics = '[LIBRARY] Convert comics to a new archive type',
ComicsConverting = '[LIBRARY] Comics converting to a new archive type',
ConvertComicsFailed = '[LIBRARY] Failed to convert comics'
}

export class LibraryReset implements Action {
Expand Down Expand Up @@ -110,6 +113,30 @@ export class LibraryDeleteMultipleComicsFailed implements Action {
constructor() {}
}

export class LibraryConvertComics implements Action {
readonly type = LibraryActionTypes.ConvertComics;

constructor(
public payload: {
comics: Comic[];
archiveType: string;
renamePages: boolean;
}
) {}
}

export class LibraryComicsConverting implements Action {
readonly type = LibraryActionTypes.ComicsConverting;

constructor() {}
}

export class LibraryConvertComicsFailed implements Action {
readonly type = LibraryActionTypes.ConvertComicsFailed;

constructor() {}
}

export type LibraryActions =
| LibraryReset
| LibraryGetUpdates
Expand All @@ -120,4 +147,7 @@ export type LibraryActions =
| LibraryStartRescanFailed
| LibraryDeleteMultipleComics
| LibraryMultipleComicsDeleted
| LibraryDeleteMultipleComicsFailed;
| LibraryDeleteMultipleComicsFailed
| LibraryConvertComics
| LibraryComicsConverting
| LibraryConvertComicsFailed;
45 changes: 45 additions & 0 deletions comixed-frontend/src/app/library/adaptors/library.adaptor.spec.ts
Expand Up @@ -44,6 +44,9 @@ import { LoggerTestingModule } from 'ngx-logger/testing';
import { MessageService } from 'primeng/api';
import * as LibraryActions from '../actions/library.actions';
import {
LibraryComicsConverting,
LibraryConvertComics,
LibraryConvertComicsFailed,
LibraryGetUpdates,
LibraryUpdatesReceived
} from '../actions/library.actions';
Expand All @@ -59,6 +62,8 @@ describe('LibraryAdaptor', () => {
const LAST_READ_DATES = [COMIC_1_LAST_READ_DATE];
const COMIC = COMIC_1;
const IDS = [7, 17, 65, 1, 29, 71];
const ARCHIVE_TYPE = 'CBZ';
const RENAME_PAGES = true;

let adaptor: LibraryAdaptor;
let store: Store<AppState>;
Expand Down Expand Up @@ -266,4 +271,44 @@ describe('LibraryAdaptor', () => {
expect(adaptor.getPreviousIssue(COMIC_3)).toEqual(COMIC_2);
});
});

describe('converting comics', () => {
beforeEach(() => {
adaptor.convertComics(COMICS, ARCHIVE_TYPE, RENAME_PAGES);
});

it('fires an action', () => {
expect(store.dispatch).toHaveBeenCalledWith(
new LibraryConvertComics({
comics: COMICS,
archiveType: ARCHIVE_TYPE,
renamePages: RENAME_PAGES
})
);
});

it('provides updates on conversion', () => {
adaptor.converting$.subscribe(response => expect(response).toBeTruthy());
});

describe('success', () => {
beforeEach(() => {
store.dispatch(new LibraryComicsConverting());
});

it('provides updates on conversion', () => {
adaptor.converting$.subscribe(response => expect(response).toBeFalsy());
});
});

describe('failure', () => {
beforeEach(() => {
store.dispatch(new LibraryConvertComicsFailed());
});

it('provides updates on conversion', () => {
adaptor.converting$.subscribe(response => expect(response).toBeFalsy());
});
});
});
});
25 changes: 25 additions & 0 deletions comixed-frontend/src/app/library/adaptors/library.adaptor.ts
Expand Up @@ -30,6 +30,7 @@ import { filter } from 'rxjs/operators';
import { extractField } from 'app/library/utility.functions';
import { LastReadDate } from 'app/library/models/last-read-date';
import {
LibraryConvertComics,
LibraryDeleteMultipleComics,
LibraryGetUpdates,
LibraryReset,
Expand Down Expand Up @@ -58,6 +59,7 @@ export class LibraryAdaptor {
private comicId = -1;
private _timeout = 60;
private _maximum = 100;
private _converting$ = new BehaviorSubject<boolean>(false);

constructor(
private store: Store<AppState>,
Expand Down Expand Up @@ -113,6 +115,9 @@ export class LibraryAdaptor {
) {
this._lastComicId$.next(state.lastComicId);
}
if (state.convertingComics !== this._converting$.getValue()) {
this._converting$.next(state.convertingComics);
}
});
}

Expand Down Expand Up @@ -216,4 +221,24 @@ export class LibraryAdaptor {

return index < comics.length ? comics[index + 1] : null;
}

convertComics(comics: Comic[], archiveType: string, renamePages: boolean) {
this.logger.debug(
'firing action to convert comics:',
comics,
archiveType,
renamePages
);
this.store.dispatch(
new LibraryConvertComics({
comics: comics,
archiveType: archiveType,
renamePages: renamePages
})
);
}

get converting$(): Observable<boolean> {
return this._converting$.asObservable();
}
}
82 changes: 72 additions & 10 deletions comixed-frontend/src/app/library/effects/library.effects.spec.ts
Expand Up @@ -16,12 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

import { HttpErrorResponse } from '@angular/common/http';
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { TranslateModule } from '@ngx-translate/core';
import { COMIC_1, COMIC_3, COMIC_5 } from 'app/comics/models/comic.fixtures';
import {
LibraryComicsConverting,
LibraryConvertComics,
LibraryConvertComicsFailed,
LibraryDeleteMultipleComics,
LibraryDeleteMultipleComicsFailed,
LibraryGetUpdates,
Expand Down Expand Up @@ -73,7 +76,8 @@ describe('LibraryEffects', () => {
startRescan: jasmine.createSpy('LibraryService.startRescan()'),
deleteMultipleComics: jasmine.createSpy(
'LibraryService.deleteMultipleComics()'
)
),
convertComics: jasmine.createSpy('LibraryService.convertComics()')
}
},
MessageService
Expand Down Expand Up @@ -119,7 +123,7 @@ describe('LibraryEffects', () => {
actions$ = hot('-a', { a: action });
libraryService.getUpdatesSince.and.returnValue(of(service_response));

const expected = cold('-b', { b: outcome });
const expected = hot('-b', { b: outcome });
expect(effects.getUpdates$).toBeObservable(expected);
});

Expand All @@ -139,7 +143,7 @@ describe('LibraryEffects', () => {
throwError(service_response)
);

const expected = cold('-b', { b: outcome });
const expected = hot('-b', { b: outcome });
expect(effects.getUpdates$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'error' })
Expand All @@ -159,7 +163,7 @@ describe('LibraryEffects', () => {
actions$ = hot('-a', { a: action });
libraryService.getUpdatesSince.and.throwError('expected');

const expected = cold('-(b|)', { b: outcome });
const expected = hot('-(b|)', { b: outcome });
expect(effects.getUpdates$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'error' })
Expand All @@ -176,7 +180,7 @@ describe('LibraryEffects', () => {
actions$ = hot('-a', { a: action });
libraryService.startRescan.and.returnValue(of(service_response));

const expected = cold('-b', { b: outcome });
const expected = hot('-b', { b: outcome });
expect(effects.startRescan$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'info' })
Expand Down Expand Up @@ -205,7 +209,7 @@ describe('LibraryEffects', () => {
actions$ = hot('-a', { a: action });
libraryService.startRescan.and.throwError('expected');

const expected = cold('-(b|)', { b: outcome });
const expected = hot('-(b|)', { b: outcome });
expect(effects.startRescan$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'error' })
Expand All @@ -226,7 +230,7 @@ describe('LibraryEffects', () => {
actions$ = hot('-a', { a: action });
libraryService.deleteMultipleComics.and.returnValue(of(service_response));

const expected = cold('-b', { b: outcome });
const expected = hot('-b', { b: outcome });
expect(effects.deleteMultipleComics$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'info' })
Expand All @@ -245,7 +249,7 @@ describe('LibraryEffects', () => {
throwError(service_response)
);

const expected = cold('-b', { b: outcome });
const expected = hot('-b', { b: outcome });
expect(effects.deleteMultipleComics$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'error' })
Expand All @@ -261,11 +265,69 @@ describe('LibraryEffects', () => {
actions$ = hot('-a', { a: action });
libraryService.deleteMultipleComics.and.throwError('expected');

const expected = cold('-(b|)', { b: outcome });
const expected = hot('-(b|)', { b: outcome });
expect(effects.deleteMultipleComics$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'error' })
);
});
});

describe('converting comics', () => {
it('fires an action on success', () => {
const serviceResponse = new HttpResponse({});
const action = new LibraryConvertComics({
comics: COMICS,
archiveType: 'CBZ',
renamePages: true
});
const outcome = new LibraryComicsConverting();

actions$ = hot('-a', { a: action });
libraryService.convertComics.and.returnValue(of(serviceResponse));

const expected = hot('-b', { b: outcome });
expect(effects.convertComics$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'info' })
);
});

it('fires an action on service failure', () => {
const serviceResponse = new HttpErrorResponse({});
const action = new LibraryConvertComics({
comics: COMICS,
archiveType: 'CBZ',
renamePages: true
});
const outcome = new LibraryConvertComicsFailed();

actions$ = hot('-a', { a: action });
libraryService.convertComics.and.returnValue(throwError(serviceResponse));

const expected = hot('-b', { b: outcome });
expect(effects.convertComics$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'error' })
);
});

it('fires an action on general failure', () => {
const action = new LibraryConvertComics({
comics: COMICS,
archiveType: 'CBZ',
renamePages: true
});
const outcome = new LibraryConvertComicsFailed();

actions$ = hot('-a', { a: action });
libraryService.convertComics.and.throwError('expected');

const expected = hot('-(b|)', { b: outcome });
expect(effects.convertComics$).toBeObservable(expected);
expect(messageService.add).toHaveBeenCalledWith(
objectContaining({ severity: 'error' })
);
});
});
});
46 changes: 46 additions & 0 deletions comixed-frontend/src/app/library/effects/library.effects.ts
Expand Up @@ -30,6 +30,9 @@ import { Observable, of } from 'rxjs';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import {
LibraryActionTypes,
LibraryComicsConverting,
LibraryConvertComics,
LibraryConvertComicsFailed,
LibraryDeleteMultipleComics,
LibraryDeleteMultipleComicsFailed,
LibraryGetUpdates,
Expand Down Expand Up @@ -200,4 +203,47 @@ export class LibraryEffects {
return of(new LibraryDeleteMultipleComicsFailed());
})
);

@Effect()
convertComics$: Observable<Action> = this.actions$.pipe(
ofType(LibraryActionTypes.ConvertComics),
map((action: LibraryConvertComics) => action.payload),
tap(action => this.logger.debug('effect: convert comics:', action)),
switchMap(action =>
this.libraryService
.convertComics(action.comics, action.archiveType, action.renamePages)
.pipe(
tap(response => this.logger.debug('received response:', response)),
tap(() =>
this.messageService.add({
severity: 'info',
detail: this.translateService.instant(
'library-effects.convert-comics.success.detail'
)
})
),
map(() => new LibraryComicsConverting()),
catchError(error => {
this.logger.error('service failure converting comics:', error);
this.messageService.add({
severity: 'error',
detail: this.translateService.instant(
'library-effects.convert-comics.error.details'
)
});
return of(new LibraryConvertComicsFailed());
})
)
),
catchError(error => {
this.logger.error('general failure converting comics:', error);
this.messageService.add({
severity: 'error',
detail: this.translateService.instant(
'library-effects.convert-comics.error.details'
)
});
return of(new LibraryConvertComicsFailed());
})
);
}

0 comments on commit f4a938c

Please sign in to comment.