commit
0415ebb882
31 changed files with 400 additions and 88 deletions
|
|
@ -13,7 +13,7 @@ export class AuthGuard implements CanActivate {
|
|||
constructor(private accountService: AccountService, private toastr: ToastrService) {}
|
||||
|
||||
canActivate(): Observable<boolean> {
|
||||
// this automaticallys subs due to being router guard
|
||||
// TODO: on failure, can we bring them back to home to show login screen
|
||||
return this.accountService.currentUser$.pipe(
|
||||
map((user: User) => {
|
||||
if (user) {
|
||||
|
|
|
|||
17
src/app/_guards/library-access.guard.ts
Normal file
17
src/app/_guards/library-access.guard.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { MemberService } from '../_services/member.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LibraryAccessGuard implements CanActivate {
|
||||
|
||||
constructor(private memberService: MemberService) {}
|
||||
|
||||
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
const libraryId = parseInt(state.url.split('library/')[1], 10);
|
||||
return this.memberService.hasLibraryAccess(libraryId);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
export interface Volume {
|
||||
id: number;
|
||||
number: string;
|
||||
number: number;
|
||||
name: string;
|
||||
files: Array<string>;
|
||||
coverImage: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,20 @@ export class LibraryService {
|
|||
return this.httpClient.post(this.baseUrl + 'library/update-for', {username, selectedLibraries});
|
||||
}
|
||||
|
||||
scan(libraryId: number) {
|
||||
return this.httpClient.post(this.baseUrl + 'library/scan?libraryId=' + libraryId, {});
|
||||
}
|
||||
|
||||
create(model: {name: string, type: number, folders: string[]}) {
|
||||
return this.httpClient.post(this.baseUrl + 'library/create', model);
|
||||
}
|
||||
|
||||
delete(libraryId: number) {
|
||||
return this.httpClient.delete(this.baseUrl + 'library/delete?libraryId=' + libraryId, {});
|
||||
}
|
||||
|
||||
update(model: {name: string, folders: string[], id: number}) {
|
||||
return this.httpClient.post(this.baseUrl + 'library/update', model);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,4 +24,8 @@ export class MemberService {
|
|||
return this.httpClient.delete(this.baseUrl + 'users/delete-user?username=' + username);
|
||||
}
|
||||
|
||||
hasLibraryAccess(libraryId: number) {
|
||||
return this.httpClient.get<boolean>(this.baseUrl + 'users/has-library-access?libraryId=' + libraryId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<button *ngIf="routeStack.peek() !== undefined" (click)="goBack()" class="list-group-item list-group-item-action"><i class="fa fa-arrow-left mr-2"></i>Back</button>
|
||||
<button *ngFor="let folder of folders" class="list-group-item list-group-item-action" (click)="selectNode(folder)">
|
||||
<span>{{getStem(folder)}}</span>
|
||||
<button class="btn btn-primary pull-right" (click)="shareFolder(folder, $event)">Share</button>
|
||||
<button type="button" class="btn btn-primary pull-right" (click)="shareFolder(folder, $event)">Share</button>
|
||||
</button>
|
||||
<div class="text-center" *ngIf="folders.length === 0">
|
||||
There are no folders here
|
||||
|
|
|
|||
|
|
@ -84,7 +84,9 @@ export class DirectoryPickerComponent implements OnInit {
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.modal.close({success: true, folderPath: folderName});
|
||||
const fullPath = (this.routeStack.items.join('\\') + '\\' + folderName).replace('\\\\', '\\');
|
||||
|
||||
this.modal.close({success: true, folderPath: fullPath});
|
||||
}
|
||||
|
||||
close() {
|
||||
|
|
|
|||
|
|
@ -1 +1,37 @@
|
|||
<p>library-editor-modal works!</p>
|
||||
|
||||
<form [formGroup]="libraryForm">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{this.library !== undefined ? 'Edit' : 'New'}} Library</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="close()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="library-name">Name</label>
|
||||
<input id="library-name" class="form-control" formControlName="name" type="text">
|
||||
</div>
|
||||
|
||||
<div class="form-group" *ngIf="this.library === undefined">
|
||||
<label for="library-type">Type</label>
|
||||
<select class="form-control" id="library-type" formControlName="type">
|
||||
<option [value]="0">Manga</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<h4>Folders <button type="button" class="btn pull-right" (click)="openDirectoryPicker()"><i class="fa fa-plus"></i></button></h4>
|
||||
|
||||
<div class="list-group">
|
||||
<li class="list-group-item" *ngFor="let folder of selectedFolders; let i = index">
|
||||
{{folder}}
|
||||
<!-- TODO: Implement ability to remove folders added-->
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" (click)="reset()">Reset</button>
|
||||
<button type="button" class="btn btn-secondary" (click)="close()">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" (click)="submitLibrary()">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LibraryEditorModalComponent } from './library-editor-modal.component';
|
||||
|
||||
describe('LibraryEditorModalComponent', () => {
|
||||
let component: LibraryEditorModalComponent;
|
||||
let fixture: ComponentFixture<LibraryEditorModalComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LibraryEditorModalComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LibraryEditorModalComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,15 +1,77 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Library } from 'src/app/_models/library';
|
||||
import { LibraryService } from 'src/app/_services/library.service';
|
||||
import { DirectoryPickerComponent, DirectoryPickerResult } from '../directory-picker/directory-picker.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-library-editor-modal',
|
||||
templateUrl: './library-editor-modal.component.html',
|
||||
styleUrls: ['./library-editor-modal.component.scss']
|
||||
})
|
||||
export class LibraryEditorModalComponent implements OnInit {
|
||||
export class LibraryEditorModalComponent implements OnInit, OnChanges {
|
||||
|
||||
constructor() { }
|
||||
@Input() library: Library | undefined = undefined;
|
||||
|
||||
libraryForm: FormGroup = new FormGroup({
|
||||
name: new FormControl('', [Validators.required]),
|
||||
type: new FormControl(0, [Validators.required])
|
||||
});
|
||||
|
||||
selectedFolders: string[] = [];
|
||||
|
||||
constructor(private modalService: NgbModal, private libraryService: LibraryService, public modal: NgbActiveModal) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.setValues();
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
console.log('Library: ', this.library);
|
||||
}
|
||||
|
||||
submitLibrary() {
|
||||
const model = this.libraryForm.value;
|
||||
model.folders = this.selectedFolders;
|
||||
|
||||
if (this.library !== undefined) {
|
||||
model.id = this.library.id;
|
||||
this.libraryService.update(model).subscribe(() => {
|
||||
this.close(true);
|
||||
});
|
||||
} else {
|
||||
this.libraryService.create(model).subscribe(() => {
|
||||
this.close(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
close(returnVal= false) {
|
||||
this.modal.close(returnVal);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.setValues();
|
||||
}
|
||||
|
||||
setValues() {
|
||||
if (this.library !== undefined) {
|
||||
this.libraryForm.get('name')?.setValue(this.library.name);
|
||||
this.libraryForm.get('type')?.setValue(this.library.type);
|
||||
this.selectedFolders = this.library.folders;
|
||||
}
|
||||
}
|
||||
|
||||
openDirectoryPicker() {
|
||||
const modalRef = this.modalService.open(DirectoryPickerComponent);
|
||||
modalRef.closed.subscribe((closeResult: DirectoryPickerResult) => {
|
||||
if (closeResult.success) {
|
||||
this.selectedFolders.push(closeResult.folderPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { LibraryEditorModalComponent } from './_modals/library-editor-modal/libr
|
|||
import { SharedModule } from '../shared/shared.module';
|
||||
import { LibraryAccessModalComponent } from './_modals/library-access-modal/library-access-modal.component';
|
||||
import { DirectoryPickerComponent } from './_modals/directory-picker/directory-picker.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
|
||||
|
||||
|
|
@ -25,9 +25,10 @@ import { FormsModule } from '@angular/forms';
|
|||
imports: [
|
||||
CommonModule,
|
||||
AdminRoutingModule,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
NgbNavModule,
|
||||
SharedModule,
|
||||
FormsModule
|
||||
],
|
||||
providers: []
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
<h4>
|
||||
{{library.name | titlecase}}
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-danger mr-2 btn-sm"><i class="fa fa-trash" title="Delete {{library.name | titlecase}}"></i></button>
|
||||
<button class="btn btn-primary btn-sm" ><i class="fa fa-pencil" title="Edit {{library.name | titlecase}}"></i></button>
|
||||
<button class="btn btn-danger mr-2 btn-sm" (click)="deleteLibrary(library)"><i class="fa fa-trash" title="Delete {{library.name | titlecase}}"></i></button>
|
||||
<button class="btn btn-primary btn-sm" (click)="editLibrary(library)"><i class="fa fa-pencil" title="Edit {{library.name | titlecase}}"></i></button>
|
||||
</div>
|
||||
</h4>
|
||||
<!-- <div>Last Active: {{member.lastActive | date}}</div>
|
||||
|
|
@ -24,5 +24,3 @@
|
|||
<app-library-editor-modal></app-library-editor-modal>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" (click)="addFolder('')">Add Folder</button>
|
||||
|
|
@ -19,25 +19,42 @@ export class ManageLibraryComponent implements OnInit {
|
|||
|
||||
ngOnInit(): void {
|
||||
|
||||
this.getLibraries();
|
||||
|
||||
}
|
||||
|
||||
getLibraries() {
|
||||
this.libraryService.getLibraries().subscribe(libraries => {
|
||||
this.libraries = libraries;
|
||||
});
|
||||
}
|
||||
|
||||
editLibrary(library: Library) {
|
||||
const modalRef = this.modalService.open(LibraryEditorModalComponent);
|
||||
console.log('component instance: ', modalRef.componentInstance);
|
||||
modalRef.componentInstance.library = library;
|
||||
modalRef.closed.subscribe(refresh => {
|
||||
if (refresh) {
|
||||
this.getLibraries();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addLibrary() {
|
||||
const modalRef = this.modalService.open(LibraryEditorModalComponent);
|
||||
}
|
||||
|
||||
addFolder(library: string) {
|
||||
const modalRef = this.modalService.open(DirectoryPickerComponent);
|
||||
modalRef.closed.subscribe((closeResult: DirectoryPickerResult) => {
|
||||
console.log('Closed Result', closeResult);
|
||||
if (closeResult.success) {
|
||||
console.log('Add folder path to Library');
|
||||
modalRef.closed.subscribe(refresh => {
|
||||
if (refresh) {
|
||||
this.getLibraries();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteLibrary(library: Library) {
|
||||
if (confirm('Are you sure you want to delete this library? You cannot undo this action.')) {
|
||||
this.libraryService.delete(library.id).subscribe(() => {
|
||||
this.getLibraries();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import { HomeComponent } from './home/home.component';
|
|||
import { LibraryDetailComponent } from './library-detail/library-detail.component';
|
||||
import { LibraryComponent } from './library/library.component';
|
||||
import { SeriesDetailComponent } from './series-detail/series-detail.component';
|
||||
import { AuthGuard } from './_guards/auth.guard';
|
||||
import { LibraryAccessGuard } from './_guards/library-access.guard';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '', component: HomeComponent},
|
||||
|
|
@ -12,8 +15,15 @@ const routes: Routes = [
|
|||
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
|
||||
},
|
||||
{path: 'library', component: LibraryComponent},
|
||||
{path: 'library/:id', component: LibraryDetailComponent}, // NOTE: Should I put a guard up to prevent unauthorized access to libraries and series?
|
||||
{path: 'series/:id', component: SeriesDetailComponent},
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard, LibraryAccessGuard],
|
||||
children: [
|
||||
{path: 'library/:id', component: LibraryDetailComponent},
|
||||
{path: 'library/:id/series/:id', component: SeriesDetailComponent},
|
||||
]
|
||||
},
|
||||
{path: '**', component: HomeComponent, pathMatch: 'full'}
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<div class="container">
|
||||
<h2>Title (Manga/Recently Added)</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-2" *ngFor="let manga of series">
|
||||
<app-card-item [title]="manga.name" (clicked)="seriesClicked(manga)"></app-card-item>
|
||||
<div class="col-sm-auto" *ngFor="let manga of series">
|
||||
<app-series-card [data]="manga" [libraryId]="libraryId"></app-series-card>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="series.length === 0">
|
||||
<!-- Put a cricket here -->
|
||||
<!-- TODO: Put a loader here -->
|
||||
Nothing here....
|
||||
</ng-container>
|
||||
</div>
|
||||
|
|
@ -32,7 +32,7 @@ export class LibraryDetailComponent implements OnInit {
|
|||
}
|
||||
|
||||
seriesClicked(series: Series) {
|
||||
this.router.navigateByUrl('/series/' + series.id);
|
||||
this.router.navigate(['library', this.libraryId, 'series', series.id]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
<h2>Libraries</h2>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-4 col-lg-2" *ngFor="let library of libraries">
|
||||
<app-card-item [imageUrl]="library.coverImage" [title]="library.name" (clicked)="handleNavigation($event, library)" [actions]="actions" [entity]="library"></app-card-item>
|
||||
</div>
|
||||
<div class="col-sm-auto" *ngFor="let library of libraries">
|
||||
<app-library-card [data]="library"></app-library-card>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -17,29 +17,21 @@ export class LibraryComponent implements OnInit {
|
|||
|
||||
user: User | undefined;
|
||||
libraries: Library[] = [];
|
||||
actions: CardItemAction[] = [];
|
||||
|
||||
constructor(public accountService: AccountService, private libraryService: LibraryService, private router: Router) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
this.user = user;
|
||||
if (this.accountService.hasAdminRole(user)) {
|
||||
this.actions = [
|
||||
{title: 'Scan Library', callback: (data: Library) => {
|
||||
console.log('You tried to scan library: ' + data.name);
|
||||
}}
|
||||
];
|
||||
}
|
||||
this.libraryService.getLibrariesForMember(this.user.username).subscribe(libraries => {
|
||||
this.libraries = libraries;
|
||||
console.log('Libraries: ', this.libraries);
|
||||
});
|
||||
});
|
||||
if (this.libraries.length > 0) {
|
||||
// TODO: Remove this debug code
|
||||
console.warn('Warning, debug code is being used!');
|
||||
this.libraries[0].coverImage = '/assets/images/mock-cover.jpg';
|
||||
}
|
||||
|
||||
handleNavigation(event: any, library: Library) {
|
||||
this.router.navigateByUrl('/library/' + library.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<!-- TODO: Put SignalR notification button dropdown here. -->
|
||||
|
||||
<div ngbDropdown class=" nav-item dropdown" *ngIf="(accountService.currentUser$ | async) as user" dropdown>
|
||||
<button class="btn btn-outline-primary" ngbDropdownToggle>{{user.username | titlecase}}</button>
|
||||
<button class="btn btn-outline-secondary" ngbDropdownToggle>{{user.username | titlecase}}</button>
|
||||
<div ngbDropdownMenu >
|
||||
<button ngbDropdownItem routerLink="/admin/dashboard" *ngIf="user.roles.includes('Admin')">Server Settings</button>
|
||||
<button ngbDropdownItem (click)="logout()">Logout</button>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<div class="container" *ngIf="series !== undefined">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-4 col-mr-auto">
|
||||
<app-card-item [imageUrl]="series.coverImage === null ? 'assets/images/image-placeholder.jpg' : series.coverImage"></app-card-item>
|
||||
<button class="btn btn-primary">Read</button>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<div class="col-md-8">
|
||||
<h2>{{series.name | titlecase}}</h2>
|
||||
<div class="row">
|
||||
<ngb-rating></ngb-rating>
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
<h4 class="mt-3">Volumes</h4>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-2" *ngFor="let volume of volumes">
|
||||
<div class="col-sm-auto" *ngFor="let volume of volumes">
|
||||
<app-card-item [entity]="volume" [title]="'Volume ' + volume.number" (click)="openVolume(volume)"></app-card-item>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,24 @@
|
|||
<div class="card" style="width: 18rem;">
|
||||
<!-- TODO: I need an anchor around this so you can open in new page easily -->
|
||||
<img (click)="handleClick()" class="card-img-top" src="{{isNullOrEmpty(imageUrl) ? placeholderImage : imageUrl}}" alt="{{title}}">
|
||||
|
||||
<div class="card-body text-center" *ngIf="title.length > 0 || actions.length > 0">
|
||||
<h5 class="card-title" (click)="handleClick()">{{title}}</h5>
|
||||
|
||||
<span class="card-title" (click)="handleClick()">
|
||||
{{title}}
|
||||
</span>
|
||||
<div class="pull-right">
|
||||
<ng-container *ngIf="actions.length > 0">
|
||||
<div class="col">
|
||||
<div ngbDropdown class="d-inline-block">
|
||||
<div ngbDropdown container="body" class="d-inline-block">
|
||||
<button class="btn" id="actions-{{title}}" ngbDropdownToggle><i class="fa fa-ellipsis-v" aria-hidden="true"></i></button>
|
||||
<div ngbDropdownMenu attr.aria-labelledby="actions-{{title}}">
|
||||
<button ngbDropdownItem *ngFor="let action of actions" (click)="performAction($event, action)">{{action.title}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,7 +1,15 @@
|
|||
.card {
|
||||
margin: 5px;
|
||||
max-width: 130px;
|
||||
max-height: 195px;
|
||||
margin: 10px;
|
||||
// max-width: 130px;
|
||||
// max-height: 195px;
|
||||
max-width: 160px;
|
||||
max-height: 320px;
|
||||
|
||||
// 370 x 210 roughly
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
|
|
|
|||
|
|
@ -15,10 +15,11 @@ export class CardItemComponent implements OnInit {
|
|||
|
||||
@Input() imageUrl = '';
|
||||
@Input() title = '';
|
||||
@Input() actions: CardItemAction[] = []; // TODO: Create a factory that generates actions based on if admin, etc. for each card type.
|
||||
@Input() actions: CardItemAction[] = [];
|
||||
@Input() entity: any; // This is the entity we are representing. It will be returned if an action is executed.
|
||||
@Output() clicked = new EventEmitter<string>();
|
||||
|
||||
|
||||
placeholderImage = 'assets/images/image-placeholder.jpg';
|
||||
|
||||
constructor() { }
|
||||
|
|
|
|||
3
src/app/shared/library-card/library-card.component.html
Normal file
3
src/app/shared/library-card/library-card.component.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<ng-container *ngIf="data !== undefined">
|
||||
<app-card-item [title]="data.name" [actions]="actions" [imageUrl]="data.coverImage" [entity]="data" (clicked)="handleClick()"></app-card-item>
|
||||
</ng-container>
|
||||
0
src/app/shared/library-card/library-card.component.scss
Normal file
0
src/app/shared/library-card/library-card.component.scss
Normal file
76
src/app/shared/library-card/library-card.component.ts
Normal file
76
src/app/shared/library-card/library-card.component.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { Library } from 'src/app/_models/library';
|
||||
import { User } from 'src/app/_models/user';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { LibraryService } from 'src/app/_services/library.service';
|
||||
import { CardItemAction } from '../card-item/card-item.component';
|
||||
|
||||
// Represents a library type card. Uses a app-card-item internally
|
||||
@Component({
|
||||
selector: 'app-library-card',
|
||||
templateUrl: './library-card.component.html',
|
||||
styleUrls: ['./library-card.component.scss']
|
||||
})
|
||||
export class LibraryCardComponent implements OnInit, OnChanges {
|
||||
@Input() data: Library | undefined;
|
||||
@Output() clicked = new EventEmitter<Library>();
|
||||
|
||||
isAdmin = false;
|
||||
actions: CardItemAction[] = [];
|
||||
|
||||
constructor(private accountService: AccountService, private router: Router,
|
||||
private libraryService: LibraryService, private toastr: ToastrService) {
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
if (user) {
|
||||
this.isAdmin = this.accountService.hasAdminRole(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: any) {
|
||||
if (this.data) {
|
||||
this.generateActions();
|
||||
}
|
||||
}
|
||||
|
||||
generateActions() {
|
||||
this.actions = [
|
||||
{
|
||||
title: 'Mark as Read',
|
||||
callback: () => this.markAsRead
|
||||
},
|
||||
{
|
||||
title: 'Mark as Unread',
|
||||
callback: () => this.markAsUnread
|
||||
}
|
||||
];
|
||||
|
||||
if (this.isAdmin) {
|
||||
this.actions.push({title: 'Scan Library', callback: (data: Library) => {
|
||||
this.libraryService.scan(data?.id).subscribe((res: any) => {
|
||||
this.toastr.success('Scan started for ' + data.name);
|
||||
});
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
markAsUnread(library: any) {
|
||||
|
||||
}
|
||||
|
||||
markAsRead(library: any) {
|
||||
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
this.clicked.emit(this.data);
|
||||
this.router.navigate(['library', this.data?.id]);
|
||||
}
|
||||
|
||||
}
|
||||
3
src/app/shared/series-card/series-card.component.html
Normal file
3
src/app/shared/series-card/series-card.component.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<ng-container *ngIf="data !== undefined">
|
||||
<app-card-item [title]="data.name" [actions]="actions" [imageUrl]="data.coverImage" [entity]="data" (clicked)="handleClick()"></app-card-item>
|
||||
</ng-container>
|
||||
0
src/app/shared/series-card/series-card.component.scss
Normal file
0
src/app/shared/series-card/series-card.component.scss
Normal file
76
src/app/shared/series-card/series-card.component.ts
Normal file
76
src/app/shared/series-card/series-card.component.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { Series } from 'src/app/_models/series';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { SeriesService } from 'src/app/_services/series.service';
|
||||
import { CardItemAction } from '../card-item/card-item.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-series-card',
|
||||
templateUrl: './series-card.component.html',
|
||||
styleUrls: ['./series-card.component.scss']
|
||||
})
|
||||
export class SeriesCardComponent implements OnInit {
|
||||
@Input() data: Series | undefined;
|
||||
@Input() libraryId = 0;
|
||||
@Output() clicked = new EventEmitter<Series>();
|
||||
|
||||
isAdmin = false;
|
||||
actions: CardItemAction[] = [];
|
||||
|
||||
constructor(private accountService: AccountService, private router: Router,
|
||||
private seriesService: SeriesService, private toastr: ToastrService) {
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
if (user) {
|
||||
this.isAdmin = this.accountService.hasAdminRole(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: any) {
|
||||
if (this.data) {
|
||||
this.generateActions();
|
||||
}
|
||||
}
|
||||
|
||||
generateActions() {
|
||||
this.actions = [
|
||||
{
|
||||
title: 'Mark as Read',
|
||||
callback: () => this.markAsRead
|
||||
},
|
||||
{
|
||||
title: 'Mark as Unread',
|
||||
callback: () => this.markAsUnread
|
||||
}
|
||||
];
|
||||
|
||||
if (this.isAdmin) {
|
||||
// this.actions.push({title: 'Scan Library', callback: (data: Library) => {
|
||||
// this.libraryService.scan(this.libraryId).subscribe((res: any) => {
|
||||
// this.toastr.success('Scan started for ' + data.name);
|
||||
// });
|
||||
// }});
|
||||
}
|
||||
}
|
||||
|
||||
markAsUnread(series: any) {
|
||||
|
||||
}
|
||||
|
||||
markAsRead(series: any) {
|
||||
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
this.clicked.emit(this.data);
|
||||
this.router.navigate(['library', this.libraryId, 'series', this.data?.id]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,11 +4,18 @@ import { RegisterMemberComponent } from './register-member/register-member.compo
|
|||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { CardItemComponent } from './card-item/card-item.component';
|
||||
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { LibraryCardComponent } from './library-card/library-card.component';
|
||||
import { SeriesCardComponent } from './series-card/series-card.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [RegisterMemberComponent, CardItemComponent],
|
||||
declarations: [
|
||||
RegisterMemberComponent,
|
||||
CardItemComponent,
|
||||
LibraryCardComponent,
|
||||
SeriesCardComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
|
|
@ -16,7 +23,9 @@ import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
|
|||
],
|
||||
exports: [
|
||||
RegisterMemberComponent,
|
||||
CardItemComponent
|
||||
CardItemComponent,
|
||||
LibraryCardComponent,
|
||||
SeriesCardComponent
|
||||
]
|
||||
})
|
||||
export class SharedModule { }
|
||||
|
|
|
|||
BIN
src/assets/images/mock-cover.jpg
Normal file
BIN
src/assets/images/mock-cover.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Loading…
Add table
Add a link
Reference in a new issue