Skip to content

Commit

Permalink
Added clicking on comic covers to toggle their selection [#584]
Browse files Browse the repository at this point in the history
  • Loading branch information
mcpierce committed Jan 16, 2021
1 parent a05fe77 commit 8f616b1
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 3 deletions.
Expand Up @@ -15,6 +15,7 @@
[style.width]="'100%'"
[style.height]="'auto'"
[alt]="title"
(click)="onCoverClicked()"
/>
</div>
<div *ngIf="!!description" [innerHTML]="description"></div>
Expand Down
Expand Up @@ -25,8 +25,10 @@ import {
DISPLAY_FEATURE_KEY,
initialState as initialDisplayState
} from '@app/library/reducers/display.reducer';
import { COMIC_3 } from '@app/library/library.fixtures';

describe('ComicDetailCardComponent', () => {
const COMIC = COMIC_3;
const initialState = { [DISPLAY_FEATURE_KEY]: initialDisplayState };

let component: ComicDetailCardComponent;
Expand Down Expand Up @@ -66,4 +68,36 @@ describe('ComicDetailCardComponent', () => {
expect(component.imageWidth).toEqual(`${PAGE_SIZE}px`);
});
});

describe('when the image is clicked', () => {
beforeEach(() => {
spyOn(component.selectionChanged, 'emit');
});

describe('not displaying a comic', () => {
beforeEach(() => {
component.comic = null;
component.onCoverClicked();
});

it('does nothing', () => {
expect(component.selectionChanged.emit).not.toHaveBeenCalled();
});
});

describe('displaying a comic', () => {
beforeEach(() => {
component.selected = Math.random() > 0.5;
component.comic = COMIC;
component.onCoverClicked();
});

it('emits an event', () => {
expect(component.selectionChanged.emit).toHaveBeenCalledWith({
comic: COMIC,
selected: !component.selected
});
});
});
});
});
Expand Up @@ -16,19 +16,29 @@
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import {
Component,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output
} from '@angular/core';
import { Subscription } from 'rxjs';
import { Store } from '@ngrx/store';
import { selectDisplayState } from '@app/library/selectors/display.selectors';
import { filter } from 'rxjs/operators';
import { LoggerService } from '@angular-ru/logger';
import { ComicSelectEvent } from '@app/core';
import { Comic } from '@app/library';

@Component({
selector: 'cx-comic-detail-card',
templateUrl: './comic-detail-card.component.html',
styleUrls: ['./comic-detail-card.component.scss']
})
export class ComicDetailCardComponent implements OnInit, OnDestroy {
@Input() comic: Comic;
@Input() title: string;
@Input() imageUrl: string;
@Input() description: string;
Expand All @@ -39,6 +49,8 @@ export class ComicDetailCardComponent implements OnInit, OnDestroy {
@Input() blurred = false;
@Input() selected = false;

@Output() selectionChanged = new EventEmitter<ComicSelectEvent>();

displayOptionSubscription: Subscription;

constructor(private logger: LoggerService, private store: Store<any>) {
Expand All @@ -55,4 +67,15 @@ export class ComicDetailCardComponent implements OnInit, OnDestroy {
ngOnDestroy(): void {
this.displayOptionSubscription.unsubscribe();
}

onCoverClicked(): void {
// only respond to the click if the details are for a comic
if (!!this.comic) {
this.logger.trace('Comic cover clicked');
this.selectionChanged.emit({
comic: this.comic,
selected: !this.selected
});
}
}
}
Expand Up @@ -42,9 +42,9 @@ export class ComicPageComponent {
return this.pageSize === -1 ? 'auto' : `${this.pageSize}px`;
}

/** Invoked when the cover is clicked. */
/** Invoked when the page is clicked. */
onClick(): void {
this.logger.trace('Cover clicked:', this.source, this.selected);
this.logger.trace('Page clicked:', this.source, this.selected);
this.pageClicked.emit({
source: this.source,
selected: !this.selected
Expand Down
1 change: 1 addition & 0 deletions comixed-web/src/app/core/index.ts
Expand Up @@ -27,6 +27,7 @@ import {

export * from '@app/core/core.constants';
export * from '@app/core/core.functions';
export * from '@app/core/models/event/comic-select-event';
export { SortableListItem } from '@app/core/models/ui/sortable-list-item';
export { TokenService } from '@app/core/services/token.service';
export { AlertService } from '@app/core/services/alert.service';
Expand Down
27 changes: 27 additions & 0 deletions comixed-web/src/app/core/models/event/comic-select-event.ts
@@ -0,0 +1,27 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2021, The ComiXed Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

import { Comic } from '../../../library';

/**
* Fired when a comic selection event occurs.
*/
export interface ComicSelectEvent {
comic: Comic;
selected: boolean;
}
Expand Up @@ -28,12 +28,14 @@
<div id="cx-all-comics" class="cx-comic-detail-card-container">
<cx-comic-detail-card
*ngFor="let comic of comics"
[comic]="comic"
[title]="comic | comicTitle"
[imageUrl]="comic | comicCoverUrl"
[detailLink]="['/library', 'comics', comic.id]"
[blurred]="!comic.fileDetails"
[busy]="!comic.fileDetails"
[selected]="isSelected(comic)"
(selectionChanged)="onSelectionChanged($event.comic, $event.selected)"
></cx-comic-detail-card>
</div>
</div>
Expand Down
Expand Up @@ -40,9 +40,15 @@ import {
import { MatBadgeModule } from '@angular/material/badge';
import { saveUserPreference } from '@app/user/actions/user.actions';
import { PAGINATION_PREFERENCE } from '@app/library/library.constants';
import { COMIC_1, COMIC_2 } from '@app/library/library.fixtures';
import {
deselectComics,
selectComics
} from '@app/library/actions/library.actions';

describe('ComicCoversComponent', () => {
const PAGINATION = 25;
const COMIC = COMIC_2;
const initialState = {
[DISPLAY_FEATURE_KEY]: initialDisplayState,
[LIBRARY_FEATURE_KEY]: initialLibraryState
Expand Down Expand Up @@ -95,4 +101,44 @@ describe('ComicCoversComponent', () => {
);
});
});

describe('checking if a comic is selected', () => {
beforeEach(() => {
component.selected = [COMIC_1];
});

it('returns true for selected comics', () => {
expect(component.isSelected(COMIC_1)).toBeTrue();
});

it('returns false for unselected comics', () => {
expect(component.isSelected(COMIC_2)).toBeFalse();
});
});

describe('when a comic select event is received', () => {
describe('selecting a comic', () => {
beforeEach(() => {
component.onSelectionChanged(COMIC, true);
});

it('fires an action', () => {
expect(store.dispatch).toHaveBeenCalledWith(
selectComics({ comics: [COMIC] })
);
});
});

describe('deselecting a comic', () => {
beforeEach(() => {
component.onSelectionChanged(COMIC, false);
});

it('fires an action', () => {
expect(store.dispatch).toHaveBeenCalledWith(
deselectComics({ comics: [COMIC] })
);
});
});
});
});
Expand Up @@ -38,6 +38,10 @@ import {
import { selectDisplayState } from '@app/library/selectors/display.selectors';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import {
deselectComics,
selectComics
} from '@app/library/actions/library.actions';

@Component({
selector: 'cx-comic-covers',
Expand Down Expand Up @@ -113,6 +117,16 @@ export class ComicCoversComponent implements OnInit, OnDestroy, AfterViewInit {
return this.selected.includes(comic);
}

onSelectionChanged(comic: Comic, selected: boolean): void {
if (selected) {
this.logger.debug('Marking comic as selected:', comic);
this.store.dispatch(selectComics({ comics: [comic] }));
} else {
this.logger.debug('Unmarking comic as selected:', comic);
this.store.dispatch(deselectComics({ comics: [comic] }));
}
}

private loadTranslations(): void {
this.logger.debug('Loading translations');
this.paginator._intl.itemsPerPageLabel = this.translateService.instant(
Expand Down

0 comments on commit 8f616b1

Please sign in to comment.