Polish Round 1 (#2396)
This commit is contained in:
parent
cf2c43d390
commit
02b002d81a
197 changed files with 1233 additions and 1751 deletions
|
|
@ -22,7 +22,7 @@ export class AdminGuard implements CanActivate {
|
|||
}
|
||||
|
||||
this.toastr.error(this.translocoService.translate('toasts.unauthorized-1'));
|
||||
this.router.navigateByUrl('/libraries');
|
||||
this.router.navigateByUrl('/home');
|
||||
return false;
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ export interface Member {
|
|||
username: string;
|
||||
email: string;
|
||||
lastActive: string; // datetime
|
||||
lastActiveUtc: string; // datetime
|
||||
created: string; // datetime
|
||||
createdUtc: string; // datetime
|
||||
roles: string[];
|
||||
libraries: Library[];
|
||||
ageRestriction: AgeRestriction;
|
||||
isPending: boolean;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import { DayOfWeek } from 'src/app/_services/statistics.service';
|
||||
import {translate, TranslocoService} from "@ngneat/transloco";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'dayOfWeek',
|
||||
standalone: true
|
||||
name: 'dayOfWeek',
|
||||
standalone: true
|
||||
})
|
||||
export class DayOfWeekPipe implements PipeTransform {
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { FITTING_OPTION } from '../_models/reader-enums';
|
||||
import { FITTING_OPTION } from '../manga-reader/_models/reader-enums';
|
||||
|
||||
@Pipe({
|
||||
name: 'fittingIcon',
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { LayoutMode } from '../_models/layout-mode';
|
||||
import { LayoutMode } from '../manga-reader/_models/layout-mode';
|
||||
|
||||
@Pipe({
|
||||
name: 'layoutModeIcon',
|
||||
|
|
@ -18,8 +18,6 @@ export class ProviderImagePipe implements PipeTransform {
|
|||
case ScrobbleProvider.Kavita:
|
||||
return 'assets/images/logo-32.png';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { Routes } from '@angular/router';
|
||||
import { AdminGuard } from '../_guards/admin.guard';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { DashboardComponent } from '../admin/dashboard/dashboard.component';
|
||||
|
||||
const routes: Routes = [
|
||||
export const routes: Routes = [
|
||||
{path: '**', component: DashboardComponent, pathMatch: 'full', canActivate: [AdminGuard]},
|
||||
{
|
||||
path: '',
|
||||
|
|
@ -15,9 +14,3 @@ const routes: Routes = [
|
|||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AdminRoutingModule { }
|
||||
14
UI/Web/src/app/_routes/all-series-routing.module.ts
Normal file
14
UI/Web/src/app/_routes/all-series-routing.module.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { Routes } from "@angular/router";
|
||||
import { AuthGuard } from "../_guards/auth.guard";
|
||||
import { AllSeriesComponent } from "../all-series/_components/all-series/all-series.component";
|
||||
|
||||
|
||||
export const routes: Routes = [
|
||||
{path: '**', component: AllSeriesComponent, pathMatch: 'full', canActivate: [AuthGuard]},
|
||||
{
|
||||
path: '',
|
||||
component: AllSeriesComponent,
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard],
|
||||
}
|
||||
];
|
||||
16
UI/Web/src/app/_routes/announcements-routing.module.ts
Normal file
16
UI/Web/src/app/_routes/announcements-routing.module.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { Routes } from "@angular/router";
|
||||
import { AdminGuard } from "../_guards/admin.guard";
|
||||
import { AuthGuard } from "../_guards/auth.guard";
|
||||
import { AnnouncementsComponent } from "../announcements/_components/announcements/announcements.component";
|
||||
|
||||
export const routes: Routes = [
|
||||
{path: '**', component: AnnouncementsComponent, pathMatch: 'full', canActivate: [AuthGuard, AdminGuard]},
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard, AdminGuard],
|
||||
children: [
|
||||
{path: 'announcements', component: AnnouncementsComponent},
|
||||
]
|
||||
}
|
||||
];
|
||||
10
UI/Web/src/app/_routes/book-reader.router.module.ts
Normal file
10
UI/Web/src/app/_routes/book-reader.router.module.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { BookReaderComponent } from '../book-reader/_components/book-reader/book-reader.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: ':chapterId',
|
||||
component: BookReaderComponent,
|
||||
}
|
||||
];
|
||||
|
||||
15
UI/Web/src/app/_routes/bookmark-routing.module.ts
Normal file
15
UI/Web/src/app/_routes/bookmark-routing.module.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { Routes } from "@angular/router";
|
||||
import { AuthGuard } from "../_guards/auth.guard";
|
||||
import { BookmarksComponent } from "../bookmark/_components/bookmarks/bookmarks.component";
|
||||
|
||||
export const routes: Routes = [
|
||||
{path: '**', component: BookmarksComponent, pathMatch: 'full', canActivate: [AuthGuard]},
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{path: 'bookmarks', component: BookmarksComponent},
|
||||
]
|
||||
}
|
||||
];
|
||||
17
UI/Web/src/app/_routes/collections-routing.module.ts
Normal file
17
UI/Web/src/app/_routes/collections-routing.module.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { AuthGuard } from '../_guards/auth.guard';
|
||||
import { AllCollectionsComponent } from '../collections/_components/all-collections/all-collections.component';
|
||||
import { CollectionDetailComponent } from '../collections/_components/collection-detail/collection-detail.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{path: '', component: AllCollectionsComponent, pathMatch: 'full'},
|
||||
{path: ':id', component: CollectionDetailComponent},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
13
UI/Web/src/app/_routes/dashboard-routing.module.ts
Normal file
13
UI/Web/src/app/_routes/dashboard-routing.module.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { AuthGuard } from '../_guards/auth.guard';
|
||||
import { DashboardComponent } from '../dashboard/_components/dashboard.component';
|
||||
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard],
|
||||
component: DashboardComponent,
|
||||
}
|
||||
];
|
||||
20
UI/Web/src/app/_routes/library-detail-routing.module.ts
Normal file
20
UI/Web/src/app/_routes/library-detail-routing.module.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { AuthGuard } from '../_guards/auth.guard';
|
||||
import { LibraryAccessGuard } from '../_guards/library-access.guard';
|
||||
import { LibraryDetailComponent } from '../library-detail/library-detail.component';
|
||||
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: ':libraryId',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard, LibraryAccessGuard],
|
||||
component: LibraryDetailComponent
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard, LibraryAccessGuard],
|
||||
component: LibraryDetailComponent
|
||||
}
|
||||
];
|
||||
15
UI/Web/src/app/_routes/manga-reader.router.module.ts
Normal file
15
UI/Web/src/app/_routes/manga-reader.router.module.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { MangaReaderComponent } from '../manga-reader/_components/manga-reader/manga-reader.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: ':chapterId',
|
||||
component: MangaReaderComponent
|
||||
},
|
||||
{
|
||||
// This will allow the MangaReader to have a list to use for next/prev chapters rather than natural sort order
|
||||
path: ':chapterId/list/:listId',
|
||||
component: MangaReaderComponent
|
||||
}
|
||||
];
|
||||
|
||||
9
UI/Web/src/app/_routes/pdf-reader.router.module.ts
Normal file
9
UI/Web/src/app/_routes/pdf-reader.router.module.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { PdfReaderComponent } from '../pdf-reader/_components/pdf-reader/pdf-reader.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: ':chapterId',
|
||||
component: PdfReaderComponent,
|
||||
}
|
||||
];
|
||||
18
UI/Web/src/app/_routes/reading-list-routing.module.ts
Normal file
18
UI/Web/src/app/_routes/reading-list-routing.module.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { Routes } from "@angular/router";
|
||||
import { AuthGuard } from "../_guards/auth.guard";
|
||||
import { ReadingListDetailComponent } from "../reading-list/_components/reading-list-detail/reading-list-detail.component";
|
||||
import { ReadingListsComponent } from "../reading-list/_components/reading-lists/reading-lists.component";
|
||||
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{path: '', component: ReadingListsComponent, pathMatch: 'full'},
|
||||
{path: ':id', component: ReadingListDetailComponent, pathMatch: 'full'},
|
||||
]
|
||||
},
|
||||
{path: '**', component: ReadingListsComponent, pathMatch: 'full', canActivate: [AuthGuard]},
|
||||
];
|
||||
43
UI/Web/src/app/_routes/registration.router.module.ts
Normal file
43
UI/Web/src/app/_routes/registration.router.module.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { UserLoginComponent } from '../registration/user-login/user-login.component';
|
||||
import { ConfirmEmailChangeComponent } from '../registration/_components/confirm-email-change/confirm-email-change.component';
|
||||
import { ConfirmEmailComponent } from '../registration/_components/confirm-email/confirm-email.component';
|
||||
import { ConfirmMigrationEmailComponent } from '../registration/_components/confirm-migration-email/confirm-migration-email.component';
|
||||
import { ConfirmResetPasswordComponent } from '../registration/_components/confirm-reset-password/confirm-reset-password.component';
|
||||
import { RegisterComponent } from '../registration/_components/register/register.component';
|
||||
import { ResetPasswordComponent } from '../registration/_components/reset-password/reset-password.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: UserLoginComponent
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
component: UserLoginComponent
|
||||
},
|
||||
{
|
||||
path: 'confirm-email',
|
||||
component: ConfirmEmailComponent,
|
||||
},
|
||||
{
|
||||
path: 'confirm-migration-email',
|
||||
component: ConfirmMigrationEmailComponent,
|
||||
},
|
||||
{
|
||||
path: 'confirm-email-update',
|
||||
component: ConfirmEmailChangeComponent,
|
||||
},
|
||||
{
|
||||
path: 'register',
|
||||
component: RegisterComponent,
|
||||
},
|
||||
{
|
||||
path: 'reset-password',
|
||||
component: ResetPasswordComponent
|
||||
},
|
||||
{
|
||||
path: 'confirm-reset-password',
|
||||
component: ConfirmResetPasswordComponent
|
||||
}
|
||||
];
|
||||
15
UI/Web/src/app/_routes/user-settings-routing.module.ts
Normal file
15
UI/Web/src/app/_routes/user-settings-routing.module.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { AuthGuard } from '../_guards/auth.guard';
|
||||
import { UserPreferencesComponent } from '../user-settings/user-preferences/user-preferences.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{path: '**', component: UserPreferencesComponent, pathMatch: 'full'},
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{path: '', component: UserPreferencesComponent, pathMatch: 'full'},
|
||||
]
|
||||
}
|
||||
];
|
||||
15
UI/Web/src/app/_routes/want-to-read-routing.module.ts
Normal file
15
UI/Web/src/app/_routes/want-to-read-routing.module.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { AuthGuard } from '../_guards/auth.guard';
|
||||
import { WantToReadComponent } from '../want-to-read/_components/want-to-read/want-to-read.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{path: '**', component: WantToReadComponent, pathMatch: 'full'},
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{path: '', component: WantToReadComponent, pathMatch: 'full'},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
|
@ -95,10 +95,6 @@ export class ImageService {
|
|||
return `${this.baseUrl}image/cover-upload?filename=${encodeURIComponent(filename)}&apiKey=${this.encodedKey}`;
|
||||
}
|
||||
|
||||
updateErroredImage(event: any) {
|
||||
event.target.src = this.placeholderImage;
|
||||
}
|
||||
|
||||
updateErroredWebLinkImage(event: any) {
|
||||
event.target.src = this.errorWebLinkImage;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import { HttpClient } from '@angular/common/http';
|
|||
import {inject, Injectable} from '@angular/core';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { UserReadStatistics } from '../statistics/_models/user-read-statistics';
|
||||
import { PublicationStatusPipe } from '../pipe/publication-status.pipe';
|
||||
import { PublicationStatusPipe } from '../_pipes/publication-status.pipe';
|
||||
import { map } from 'rxjs';
|
||||
import { MangaFormatPipe } from '../pipe/manga-format.pipe';
|
||||
import { MangaFormatPipe } from '../_pipes/manga-format.pipe';
|
||||
import { FileExtensionBreakdown } from '../statistics/_models/file-breakdown';
|
||||
import { TopUserRead } from '../statistics/_models/top-reads';
|
||||
import { ReadHistoryEvent } from '../statistics/_models/read-history-event';
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
|
|||
import {ReactiveFormsModule} from "@angular/forms";
|
||||
import {UserReview} from "../review-card/user-review";
|
||||
import {SpoilerComponent} from "../spoiler/spoiler.component";
|
||||
import {SafeHtmlPipe} from "../../pipe/safe-html.pipe";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
|
||||
@Component({
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import {ReviewCardModalComponent} from "../review-card-modal/review-card-modal.c
|
|||
import {AccountService} from "../../_services/account.service";
|
||||
import {ReviewSeriesModalComponent} from "../review-series-modal/review-series-modal.component";
|
||||
import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
|
||||
import {DefaultValuePipe} from "../../pipe/default-value.pipe";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {ImageComponent} from "../../shared/image/image.component";
|
||||
import {ProviderImagePipe} from "../../pipe/provider-image.pipe";
|
||||
import {ProviderImagePipe} from "../../_pipes/provider-image.pipe";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
|
||||
@Component({
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import {ExternalSeriesDetail, SeriesStaff} from "../../_models/series-detail/ext
|
|||
import {SeriesService} from "../../_services/series.service";
|
||||
import {ImageComponent} from "../../shared/image/image.component";
|
||||
import {LoadingComponent} from "../../shared/loading/loading.component";
|
||||
import {SafeHtmlPipe} from "../../pipe/safe-html.pipe";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {A11yClickDirective} from "../../shared/a11y-click.directive";
|
||||
import {MetadataDetailComponent} from "../../series-detail/_components/metadata-detail/metadata-detail.component";
|
||||
import {PersonBadgeComponent} from "../../shared/person-badge/person-badge.component";
|
||||
import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component";
|
||||
import {ImageService} from "../../_services/image.service";
|
||||
import {PublicationStatusPipe} from "../../pipe/publication-status.pipe";
|
||||
import {PublicationStatusPipe} from "../../_pipes/publication-status.pipe";
|
||||
import {SeriesMetadata} from "../../_models/metadata/series-metadata";
|
||||
import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
|
||||
import {ActionService} from "../../_services/action.service";
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {SafeHtmlPipe} from "../../pipe/safe-html.pipe";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
|
||||
@Component({
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import {PaginatedResult, Pagination} from "../../_models/pagination";
|
|||
import {SortableHeader, SortEvent} from "../table/_directives/sortable-header.directive";
|
||||
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
|
||||
import {TranslocoModule} from "@ngneat/transloco";
|
||||
import {DefaultValuePipe} from "../../pipe/default-value.pipe";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {TranslocoLocaleModule} from "@ngneat/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../pipe/utc-to-local-time.pipe";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-scrobble-history',
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { FormGroup, FormControl, Validators, ReactiveFormsModule } from '@angula
|
|||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Member } from 'src/app/_models/auth/member';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { SentenceCasePipe } from '../../../pipe/sentence-case.pipe';
|
||||
import { SentenceCasePipe } from '../../../_pipes/sentence-case.pipe';
|
||||
import { NgIf } from '@angular/common';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AdminRoutingModule } from './admin-routing.module';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import {
|
||||
NgbAccordionModule,
|
||||
NgbCollapse,
|
||||
NgbDropdownModule,
|
||||
NgbNavModule,
|
||||
NgbTooltipModule,
|
||||
NgbTypeaheadModule
|
||||
} from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ManageLibraryComponent } from './manage-library/manage-library.component';
|
||||
import { ManageUsersComponent } from './manage-users/manage-users.component';
|
||||
import { LibraryAccessModalComponent } from './_modals/library-access-modal/library-access-modal.component';
|
||||
import { DirectoryPickerComponent } from './_modals/directory-picker/directory-picker.component';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { ResetPasswordModalComponent } from './_modals/reset-password-modal/reset-password-modal.component';
|
||||
import { ManageSettingsComponent } from './manage-settings/manage-settings.component';
|
||||
import { ManageSystemComponent } from './manage-system/manage-system.component';
|
||||
import { InviteUserComponent } from './invite-user/invite-user.component';
|
||||
import { RoleSelectorComponent } from './role-selector/role-selector.component';
|
||||
import { LibrarySelectorComponent } from './library-selector/library-selector.component';
|
||||
import { EditUserComponent } from './edit-user/edit-user.component';
|
||||
import { UserSettingsModule } from '../user-settings/user-settings.module';
|
||||
import { ManageMediaSettingsComponent } from './manage-media-settings/manage-media-settings.component';
|
||||
import { ManageEmailSettingsComponent } from './manage-email-settings/manage-email-settings.component';
|
||||
import { ManageTasksSettingsComponent } from './manage-tasks-settings/manage-tasks-settings.component';
|
||||
import { ManageLogsComponent } from './manage-logs/manage-logs.component';
|
||||
import { VirtualScrollerModule } from '@iharbeck/ngx-virtual-scroller';
|
||||
|
||||
import { ManageAlertsComponent } from './manage-alerts/manage-alerts.component';
|
||||
import {ManageScrobbleErrorsComponent} from "./manage-scrobble-errors/manage-scrobble-errors.component";
|
||||
import {DefaultValuePipe} from "../pipe/default-value.pipe";
|
||||
import {LibraryTypePipe} from "../pipe/library-type.pipe";
|
||||
import {TimeAgoPipe} from "../pipe/time-ago.pipe";
|
||||
import {SentenceCasePipe} from "../pipe/sentence-case.pipe";
|
||||
import {FilterPipe} from "../pipe/filter.pipe";
|
||||
import {TagBadgeComponent} from "../shared/tag-badge/tag-badge.component";
|
||||
import {LoadingComponent} from "../shared/loading/loading.component";
|
||||
import {
|
||||
SideNavCompanionBarComponent
|
||||
} from "../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
|
||||
import {RouterModule} from "@angular/router";
|
||||
import {LicenseComponent} from "./license/license.component";
|
||||
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
AdminRoutingModule,
|
||||
ReactiveFormsModule,
|
||||
RouterModule,
|
||||
FormsModule,
|
||||
NgbNavModule,
|
||||
NgbTooltipModule,
|
||||
NgbTypeaheadModule,
|
||||
NgbDropdownModule,
|
||||
NgbAccordionModule,
|
||||
UserSettingsModule,
|
||||
VirtualScrollerModule,
|
||||
ManageScrobbleErrorsComponent,
|
||||
DefaultValuePipe,
|
||||
LibraryTypePipe,
|
||||
TimeAgoPipe,
|
||||
SentenceCasePipe,
|
||||
FilterPipe,
|
||||
TagBadgeComponent,
|
||||
LoadingComponent,
|
||||
SideNavCompanionBarComponent,
|
||||
NgbCollapse,
|
||||
ManageUsersComponent,
|
||||
DashboardComponent,
|
||||
ManageLibraryComponent,
|
||||
LibraryAccessModalComponent,
|
||||
DirectoryPickerComponent,
|
||||
ResetPasswordModalComponent,
|
||||
ManageSettingsComponent,
|
||||
ManageSystemComponent,
|
||||
InviteUserComponent,
|
||||
RoleSelectorComponent,
|
||||
LibrarySelectorComponent,
|
||||
EditUserComponent,
|
||||
ManageMediaSettingsComponent,
|
||||
ManageEmailSettingsComponent,
|
||||
ManageTasksSettingsComponent,
|
||||
ManageLogsComponent,
|
||||
ManageAlertsComponent,
|
||||
LicenseComponent
|
||||
],
|
||||
providers: []
|
||||
})
|
||||
export class AdminModule { }
|
||||
|
|
@ -2,7 +2,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} f
|
|||
import {ActivatedRoute, RouterLink} from '@angular/router';
|
||||
import {Title} from '@angular/platform-browser';
|
||||
import {NavService} from '../../_services/nav.service';
|
||||
import {SentenceCasePipe} from '../../pipe/sentence-case.pipe';
|
||||
import {SentenceCasePipe} from '../../_pipes/sentence-case.pipe';
|
||||
import {LicenseComponent} from '../license/license.component';
|
||||
import {ManageTasksSettingsComponent} from '../manage-tasks-settings/manage-tasks-settings.component';
|
||||
import {ServerStatsComponent} from '../../statistics/_components/server-stats/server-stats.component';
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { AgeRestriction } from 'src/app/_models/metadata/age-restriction';
|
|||
import { Library } from 'src/app/_models/library';
|
||||
import { Member } from 'src/app/_models/auth/member';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { SentenceCasePipe } from '../../pipe/sentence-case.pipe';
|
||||
import { SentenceCasePipe } from '../../_pipes/sentence-case.pipe';
|
||||
import { RestrictionSelectorComponent } from '../../user-settings/restriction-selector/restriction-selector.component';
|
||||
import { LibrarySelectorComponent } from '../library-selector/library-selector.component';
|
||||
import { RoleSelectorComponent } from '../role-selector/role-selector.component';
|
||||
|
|
|
|||
|
|
@ -47,10 +47,10 @@
|
|||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" (click)="close()">
|
||||
{{t('cancel')}}
|
||||
<button type="button" class="btn btn-{{invited ? 'primary' : 'secondary'}}" (click)="close()">
|
||||
{{invited ? t('cancel') : t('close')}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" (click)="invite()" [disabled]="isSending || !inviteForm.valid || emailLink !== ''">
|
||||
<button *ngIf="!invited" type="button" class="btn btn-primary" (click)="invite()" [disabled]="isSending || !inviteForm.valid || emailLink !== ''">
|
||||
<span *ngIf="isSending" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span>{{isSending ? t('inviting') : t('invite')}}</span>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { LibrarySelectorComponent } from '../library-selector/library-selector.c
|
|||
import { RoleSelectorComponent } from '../role-selector/role-selector.component';
|
||||
import { NgIf } from '@angular/common';
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {SafeHtmlPipe} from "../../pipe/safe-html.pipe";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
|
||||
@Component({
|
||||
selector: 'app-invite-user',
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { ServerService } from 'src/app/_services/server.service';
|
|||
import { EVENTS, MessageHubService } from 'src/app/_services/message-hub.service';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import { FilterPipe } from '../../pipe/filter.pipe';
|
||||
import { FilterPipe } from '../../_pipes/filter.pipe';
|
||||
import { LoadingComponent } from '../../shared/loading/loading.component';
|
||||
import { NgIf, NgFor } from '@angular/common';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {ServerSettings} from '../_models/server-settings';
|
|||
import {NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {NgIf, NgTemplateOutlet} from '@angular/common';
|
||||
import {translate, TranslocoModule, TranslocoService} from "@ngneat/transloco";
|
||||
import {SafeHtmlPipe} from "../../pipe/safe-html.pipe";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {ServerService} from "../../_services/server.service";
|
||||
|
||||
@Component({
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ import { Library } from 'src/app/_models/library';
|
|||
import { LibraryService } from 'src/app/_services/library.service';
|
||||
import { EVENTS, Message, MessageHubService } from 'src/app/_services/message-hub.service';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import { SentenceCasePipe } from '../../pipe/sentence-case.pipe';
|
||||
import { TimeAgoPipe } from '../../pipe/time-ago.pipe';
|
||||
import { LibraryTypePipe } from '../../pipe/library-type.pipe';
|
||||
import { SentenceCasePipe } from '../../_pipes/sentence-case.pipe';
|
||||
import { TimeAgoPipe } from '../../_pipes/time-ago.pipe';
|
||||
import { LibraryTypePipe } from '../../_pipes/library-type.pipe';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { NgFor, NgIf } from '@angular/common';
|
||||
import {translate, TranslocoModule} from "@ngneat/transloco";
|
||||
import {DefaultDatePipe} from "../../pipe/default-date.pipe";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-library',
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ import {ScrobbleError} from "../../_models/scrobbling/scrobble-error";
|
|||
import {SeriesService} from "../../_services/series.service";
|
||||
import {EditSeriesModalComponent} from "../../cards/_modals/edit-series-modal/edit-series-modal.component";
|
||||
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {FilterPipe} from "../../pipe/filter.pipe";
|
||||
import {FilterPipe} from "../../_pipes/filter.pipe";
|
||||
import {LoadingComponent} from "../../shared/loading/loading.component";
|
||||
import {TranslocoModule} from "@ngneat/transloco";
|
||||
import {DefaultDatePipe} from "../../pipe/default-date.pipe";
|
||||
import {DefaultValuePipe} from "../../pipe/default-value.pipe";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {TranslocoLocaleModule} from "@ngneat/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../pipe/utc-to-local-time.pipe";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-scrobble-errors',
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ import {Job} from 'src/app/_models/job/job';
|
|||
import {UpdateNotificationModalComponent} from 'src/app/shared/update-notification/update-notification-modal.component';
|
||||
import {NgbModal, NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {DownloadService} from 'src/app/shared/_services/download.service';
|
||||
import {DefaultValuePipe} from '../../pipe/default-value.pipe';
|
||||
import {DefaultValuePipe} from '../../_pipes/default-value.pipe';
|
||||
import {AsyncPipe, DatePipe, NgFor, NgIf, NgTemplateOutlet, TitleCasePipe} from '@angular/common';
|
||||
import {TranslocoModule, TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoLocaleModule} from "@ngneat/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../pipe/utc-to-local-time.pipe";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
|
||||
interface AdhocTask {
|
||||
name: string;
|
||||
|
|
|
|||
|
|
@ -11,12 +11,7 @@
|
|||
<li *ngFor="let member of members; let idx = index;" class="list-group-item no-hover">
|
||||
<div>
|
||||
<h4>
|
||||
<span id="member-name--{{idx}}">{{member.username | titlecase}} </span>
|
||||
<span *ngIf="member.username === loggedInUsername">
|
||||
<i class="fas fa-star" aria-hidden="true"></i>
|
||||
<span class="visually-hidden">{{t('you-alt')}}</span>
|
||||
</span>
|
||||
<span class="badge bg-secondary text-dark" *ngIf="member.isPending">{{t('pending-title')}}</span>
|
||||
<span id="member-name--{{idx}}" [ngClass]="{'highlight': member.username === loggedInUsername}">{{member.username | titlecase}}</span>
|
||||
<div class="float-end" *ngIf="canEditMember(member)">
|
||||
<button class="btn btn-danger btn-sm me-2" (click)="deleteUser(member)"
|
||||
placement="top" [ngbTooltip]="t('delete-user-tooltip')" [attr.aria-label]="t('delete-user-alt', {user: member.username | titlecase})">
|
||||
|
|
@ -27,24 +22,37 @@
|
|||
<i class="fa fa-pen" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<button *ngIf="member.isPending" class="btn btn-secondary btn-sm me-2" (click)="resendEmail(member)"
|
||||
placement="top" [ngbTooltip]="t('resend-invite-tooltip')" [attr.aria-label]="t('resend-invite-alt', {user: member.username | titlecase})">{{t('resend')}}</button>
|
||||
<button *ngIf="member.isPending" class="btn btn-secondary btn-sm me-2" (click)="setup(member)"
|
||||
placement="top" [ngbTooltip]="t('setup-user-tooltip')" [attr.aria-label]="t('setup-user-alt', {user: member.username | titlecase})">Setup</button>
|
||||
<ng-container *ngIf="member.isPending">
|
||||
<button class="btn btn-secondary btn-sm me-2" (click)="resendEmail(member)"
|
||||
placement="top" [ngbTooltip]="t('resend-invite-tooltip')" [attr.aria-label]="t('resend-invite-alt', {user: member.username | titlecase})"><i class="fa-solid fa-share-from-square" aria-hidden="true"></i></button>
|
||||
<button class="btn btn-secondary btn-sm" (click)="setup(member)"
|
||||
placement="top" [ngbTooltip]="t('setup-user-tooltip')" [attr.aria-label]="t('setup-user-alt', {user: member.username | titlecase})"><i class="fa-solid fa-sliders" aria-hidden="true"></i></button>
|
||||
</ng-container>
|
||||
<button *ngIf="!member.isPending" class="btn btn-secondary btn-sm" (click)="updatePassword(member)"
|
||||
placement="top" [ngbTooltip]="t('change-password-tooltip')" [attr.aria-label]="t('change-password-alt', {user: member.username | titlecase})"><i class="fa fa-key" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
</h4>
|
||||
<div class="user-info">
|
||||
<div>{{t('last-active-title')}}
|
||||
<span>{{member.lastActive | date: 'short' | defaultDate}} <i class="presence fa fa-circle ms-1" [title]="t('online-now-tooltip')" aria-hidden="true" *ngIf="(messageHub.onlineUsers$ | async)?.includes(member.username)"></i></span>
|
||||
<span class="badge bg-secondary text-dark ms-1 pending-badge" *ngIf="member.isPending; else activeTime">{{t('pending-title')}}</span>
|
||||
<ng-template #activeTime>
|
||||
<span>{{member.lastActiveUtc | utcToLocalTime | defaultDate}} <i class="presence fa fa-circle ms-1" [title]="t('online-now-tooltip')" aria-hidden="true" *ngIf="(messageHub.onlineUsers$ | async)?.includes(member.username)"></i></span>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div *ngIf="!hasAdminRole(member) && member.libraries.length > 0">
|
||||
{{t('sharing-title')}}
|
||||
<app-tag-badge *ngFor="let lib of member.libraries" class="col-auto">{{lib.name}}</app-tag-badge>
|
||||
</div>
|
||||
<div *ngIf="!hasAdminRole(member)">{{t('sharing-title')}} {{formatLibraries(member)}}</div>
|
||||
<div class="row g-0">
|
||||
<div>
|
||||
{{t('roles-title')}} <span *ngIf="getRoles(member).length === 0; else showRoles">{{t('none')}}</span>
|
||||
<div *ngIf="getRoles(member) as roles">
|
||||
{{t('roles-title')}} <span *ngIf="roles.length === 0; else showRoles">{{null | defaultValue}}</span>
|
||||
<ng-template #showRoles>
|
||||
<app-tag-badge *ngFor="let role of getRoles(member)" class="col-auto">{{role}}</app-tag-badge>
|
||||
<ng-container *ngIf="hasAdminRole(member); else allRoles">
|
||||
<app-tag-badge class="col-auto">Admin</app-tag-badge>
|
||||
</ng-container>
|
||||
<ng-template #allRoles>
|
||||
<app-tag-badge *ngFor="let role of roles" class="col-auto">{{role}}</app-tag-badge>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,3 +6,11 @@
|
|||
.user-info > div {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.pending-badge {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,12 @@ import {EditUserComponent} from '../edit-user/edit-user.component';
|
|||
import {ServerService} from 'src/app/_services/server.service';
|
||||
import {Router} from '@angular/router';
|
||||
import {TagBadgeComponent} from '../../shared/tag-badge/tag-badge.component';
|
||||
import {AsyncPipe, DatePipe, NgFor, NgIf, TitleCasePipe} from '@angular/common';
|
||||
import {TranslocoModule, TranslocoService} from "@ngneat/transloco";
|
||||
import {DefaultDatePipe} from "../../pipe/default-date.pipe";
|
||||
import {AsyncPipe, DatePipe, NgClass, NgFor, NgIf, TitleCasePipe} from '@angular/common';
|
||||
import {translate, TranslocoModule, TranslocoService} from "@ngneat/transloco";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-users',
|
||||
|
|
@ -23,7 +26,7 @@ import {DefaultDatePipe} from "../../pipe/default-date.pipe";
|
|||
styleUrls: ['./manage-users.component.scss'],
|
||||
standalone: true,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [NgFor, NgIf, NgbTooltip, TagBadgeComponent, AsyncPipe, TitleCasePipe, DatePipe, TranslocoModule, DefaultDatePipe]
|
||||
imports: [NgFor, NgIf, NgbTooltip, TagBadgeComponent, AsyncPipe, TitleCasePipe, DatePipe, TranslocoModule, DefaultDatePipe, NgClass, DefaultValuePipe, ReadMoreComponent, UtcToLocalTimePipe]
|
||||
})
|
||||
export class ManageUsersComponent implements OnInit {
|
||||
|
||||
|
|
@ -31,24 +34,24 @@ export class ManageUsersComponent implements OnInit {
|
|||
loggedInUsername = '';
|
||||
loadingMembers = false;
|
||||
|
||||
translocoService = inject(TranslocoService);
|
||||
cdRef = inject(ChangeDetectorRef);
|
||||
private readonly translocoService = inject(TranslocoService);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly memberService = inject(MemberService);
|
||||
private readonly accountService = inject(AccountService);
|
||||
private readonly modalService = inject(NgbModal);
|
||||
private readonly toastr = inject(ToastrService);
|
||||
private readonly confirmService = inject(ConfirmService);
|
||||
public readonly messageHub = inject(MessageHubService);
|
||||
private readonly serverService = inject(ServerService);
|
||||
private readonly router = inject(Router);
|
||||
|
||||
constructor(private memberService: MemberService,
|
||||
private accountService: AccountService,
|
||||
private modalService: NgbModal,
|
||||
private toastr: ToastrService,
|
||||
private confirmService: ConfirmService,
|
||||
public messageHub: MessageHubService,
|
||||
private serverService: ServerService,
|
||||
private router: Router) {
|
||||
constructor() {
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe((user) => {
|
||||
if (user) {
|
||||
this.loggedInUsername = user.username;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
@ -136,7 +139,7 @@ export class ManageUsersComponent implements OnInit {
|
|||
|
||||
formatLibraries(member: Member) {
|
||||
if (member.libraries.length === 0) {
|
||||
return this.translocoService.translate('manage-users.none');
|
||||
return translate('manage-users.none');
|
||||
}
|
||||
|
||||
return member.libraries.map(item => item.name).join(', ');
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@ import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2";
|
|||
|
||||
|
||||
@Component({
|
||||
selector: 'app-all-series',
|
||||
templateUrl: './all-series.component.html',
|
||||
styleUrls: ['./all-series.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
selector: 'app-all-series',
|
||||
templateUrl: './all-series.component.html',
|
||||
styleUrls: ['./all-series.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [SideNavCompanionBarComponent, NgIf, BulkOperationsComponent, CardDetailLayoutComponent, SeriesCardComponent, DecimalPipe, TranslocoDirective]
|
||||
})
|
||||
export class AllSeriesComponent implements OnInit {
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
import { NgModule } from "@angular/core";
|
||||
import { Routes, RouterModule } from "@angular/router";
|
||||
import { AuthGuard } from "../_guards/auth.guard";
|
||||
import { AllSeriesComponent } from "./_components/all-series/all-series.component";
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '**', component: AllSeriesComponent, pathMatch: 'full', canActivate: [AuthGuard]},
|
||||
{
|
||||
path: '',
|
||||
component: AllSeriesComponent,
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard],
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AllSeriesRoutingModule { }
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AllSeriesRoutingModule } from './all-series-routing.module';
|
||||
import { AllSeriesComponent } from './_components/all-series/all-series.component';
|
||||
import {SeriesCardComponent} from "../cards/series-card/series-card.component";
|
||||
import {BulkOperationsComponent} from "../cards/bulk-operations/bulk-operations.component";
|
||||
import {CardDetailLayoutComponent} from "../cards/card-detail-layout/card-detail-layout.component";
|
||||
import {
|
||||
SideNavCompanionBarComponent
|
||||
} from "../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
AllSeriesRoutingModule,
|
||||
SeriesCardComponent,
|
||||
BulkOperationsComponent,
|
||||
CardDetailLayoutComponent,
|
||||
SideNavCompanionBarComponent,
|
||||
AllSeriesComponent,
|
||||
]
|
||||
})
|
||||
export class AllSeriesModule { }
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import { NgModule } from "@angular/core";
|
||||
import { Routes, RouterModule } from "@angular/router";
|
||||
import { AdminGuard } from "../_guards/admin.guard";
|
||||
import { AuthGuard } from "../_guards/auth.guard";
|
||||
import { AnnouncementsComponent } from "./_components/announcements/announcements.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '**', component: AnnouncementsComponent, pathMatch: 'full', canActivate: [AuthGuard, AdminGuard]},
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard, AdminGuard],
|
||||
children: [
|
||||
{path: 'announcments', component: AnnouncementsComponent},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AnnouncementsRoutingModule { }
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AnnouncementsComponent } from './_components/announcements/announcements.component';
|
||||
import { ChangelogComponent } from './_components/changelog/changelog.component';
|
||||
import { AnnouncementsRoutingModule } from './announcements-routing.module';
|
||||
import {ReadMoreComponent} from "../shared/read-more/read-more.component";
|
||||
import {LoadingComponent} from "../shared/loading/loading.component";
|
||||
import {
|
||||
SideNavCompanionBarComponent
|
||||
} from "../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
AnnouncementsRoutingModule,
|
||||
ReadMoreComponent,
|
||||
LoadingComponent,
|
||||
SideNavCompanionBarComponent,
|
||||
AnnouncementsComponent,
|
||||
ChangelogComponent
|
||||
]
|
||||
})
|
||||
export class AnnouncementsModule { }
|
||||
|
|
@ -8,50 +8,48 @@ const routes: Routes = [
|
|||
{
|
||||
path: 'admin',
|
||||
canActivate: [AdminGuard],
|
||||
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
|
||||
},
|
||||
{
|
||||
path: 'collections',
|
||||
canActivate: [AuthGuard],
|
||||
loadChildren: () => import('./collections/collections.module').then(m => m.CollectionsModule)
|
||||
loadChildren: () => import('./_routes/admin-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'preferences',
|
||||
canActivate: [AuthGuard],
|
||||
loadChildren: () => import('./user-settings/user-settings.module').then(m => m.UserSettingsModule)
|
||||
loadChildren: () => import('./_routes/user-settings-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'collections',
|
||||
loadChildren: () => import('./_routes/collections-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'lists',
|
||||
canActivate: [AuthGuard],
|
||||
loadChildren: () => import('./reading-list/reading-list.module').then(m => m.ReadingListModule)
|
||||
loadChildren: () => import('./_routes/reading-list-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'registration',
|
||||
loadChildren: () => import('../app/registration/registration.module').then(m => m.RegistrationModule)
|
||||
loadChildren: () => import('./_routes/registration.router.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
loadChildren: () => import('./_routes/registration.router.module').then(m => m.routes) // TODO: Refactor so we just use /registration/login going forward
|
||||
},
|
||||
{
|
||||
path: 'announcements',
|
||||
loadChildren: () => import('../app/announcements/announcements.module').then(m => m.AnnouncementsModule)
|
||||
loadChildren: () => import('./_routes/announcements-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'bookmarks',
|
||||
loadChildren: () => import('../app/bookmark/bookmark.module').then(m => m.BookmarkModule)
|
||||
loadChildren: () => import('./_routes/bookmark-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'all-series',
|
||||
loadChildren: () => import('../app/all-series/all-series.module').then(m => m.AllSeriesModule)
|
||||
},
|
||||
{
|
||||
path: 'libraries',
|
||||
loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule)
|
||||
},
|
||||
{
|
||||
path: 'libraries',
|
||||
loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule)
|
||||
loadChildren: () => import('./_routes/all-series-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'want-to-read',
|
||||
loadChildren: () => import('../app/want-to-read/want-to-read.module').then(m => m.WantToReadModule)
|
||||
loadChildren: () => import('./_routes/want-to-read-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'home',
|
||||
loadChildren: () => import('./_routes/dashboard-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: 'library',
|
||||
|
|
@ -61,30 +59,30 @@ const routes: Routes = [
|
|||
{
|
||||
path: ':libraryId',
|
||||
pathMatch: 'full',
|
||||
loadChildren: () => import('../app/library-detail/library-detail.module').then(m => m.LibraryDetailModule)
|
||||
loadChildren: () => import('./_routes/library-detail-routing.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: ':libraryId/series/:seriesId',
|
||||
pathMatch: 'full',
|
||||
loadChildren: () => import('../app/series-detail/series-detail.module').then(m => m.SeriesDetailModule)
|
||||
loadComponent: () => import('../app/series-detail/_components/series-detail/series-detail.component').then(c => c.SeriesDetailComponent)
|
||||
},
|
||||
{
|
||||
path: ':libraryId/series/:seriesId/manga',
|
||||
loadChildren: () => import('../app/manga-reader/manga-reader.module').then(m => m.MangaReaderModule)
|
||||
loadChildren: () => import('./_routes/manga-reader.router.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: ':libraryId/series/:seriesId/book',
|
||||
loadChildren: () => import('../app/book-reader/book-reader.module').then(m => m.BookReaderModule)
|
||||
loadChildren: () => import('./_routes/book-reader.router.module').then(m => m.routes)
|
||||
},
|
||||
{
|
||||
path: ':libraryId/series/:seriesId/pdf',
|
||||
loadChildren: () => import('../app/pdf-reader/pdf-reader.module').then(m => m.PdfReaderModule)
|
||||
loadChildren: () => import('./_routes/pdf-reader.router.module').then(m => m.routes)
|
||||
},
|
||||
]
|
||||
},
|
||||
{path: 'login', loadChildren: () => import('../app/registration/registration.module').then(m => m.RegistrationModule)},
|
||||
{path: '**', pathMatch: 'full', redirectTo: 'libraries'},
|
||||
{path: '**', pathMatch: 'prefix', redirectTo: 'libraries'},
|
||||
{path: '**', pathMatch: 'full', redirectTo: 'home'},
|
||||
{path: 'libraries', pathMatch: 'full', redirectTo: 'home'},
|
||||
{path: '**', pathMatch: 'prefix', redirectTo: 'home'},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import {
|
|||
ChangeDetectionStrategy, ChangeDetectorRef,
|
||||
Component,
|
||||
DestroyRef,
|
||||
ElementRef, EventEmitter,
|
||||
ElementRef, EventEmitter, HostListener,
|
||||
inject,
|
||||
Input,
|
||||
OnInit, Output,
|
||||
|
|
@ -15,6 +15,7 @@ import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/
|
|||
import {ReaderService} from "../../../_services/reader.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {KEY_CODES} from "../../../shared/_services/utility.service";
|
||||
|
||||
enum BookLineOverlayMode {
|
||||
None = 0,
|
||||
|
|
@ -52,6 +53,17 @@ export class BookLineOverlayComponent implements OnInit {
|
|||
get BookLineOverlayMode() { return BookLineOverlayMode; }
|
||||
constructor(private elementRef: ElementRef, private toastr: ToastrService) {}
|
||||
|
||||
@HostListener('window:keydown', ['$event'])
|
||||
handleKeyPress(event: KeyboardEvent) {
|
||||
if (event.key === KEY_CODES.ESC_KEY) {
|
||||
this.reset();
|
||||
this.cdRef.markForCheck();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
if (this.parent) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<div class="container-flex {{darkMode ? 'dark-mode' : ''}} reader-container {{ColumnLayout}} {{WritingStyleClass}}" tabindex="0" #reader>
|
||||
<div class="container-flex {{darkMode ? 'dark-mode' : ''}} reader-container {{ColumnLayout}} {{WritingStyleClass}}"
|
||||
tabindex="0" #reader (click)="handleContainerClick($event)" [ngClass]="{'pointer' : cursorIsPointer}">
|
||||
<ng-container *transloco="let t; read: 'book-reader'">
|
||||
<div class="fixed-top" #stickyTop>
|
||||
<a class="visually-hidden-focusable focus-visible" href="javascript:void(0);" (click)="moveFocus()">{{t('skip-header')}}</a>
|
||||
|
|
@ -98,7 +99,8 @@
|
|||
</app-drawer>
|
||||
</div>
|
||||
|
||||
<div #readingSection class="reading-section {{ColumnLayout}} {{WritingStyleClass}}" [ngStyle]="{'width': PageWidthForPagination}" [ngClass]="{'immersive' : immersiveMode || !actionBarVisible}" [@isLoading]="isLoading">
|
||||
<div #readingSection class="reading-section {{ColumnLayout}} {{WritingStyleClass}}" [ngStyle]="{'width': PageWidthForPagination}"
|
||||
[ngClass]="{'immersive' : immersiveMode || !actionBarVisible}" [@isLoading]="isLoading" (click)="handleReaderClick($event)">
|
||||
|
||||
<ng-container *ngIf="clickToPaginate">
|
||||
<div class="left {{clickOverlayClass('left')}} no-pointer-events no-observe"
|
||||
|
|
@ -111,9 +113,8 @@
|
|||
[ngStyle]="{height: PageHeightForPagination}"></div>
|
||||
</ng-container>
|
||||
<div #bookContainer class="book-container {{WritingStyleClass}}"
|
||||
[ngClass]="{'immersive' : immersiveMode, 'pointer' : cursorIsPointer}"
|
||||
(click)="handleReaderClick($event)"
|
||||
(mousedown)="mouseDown($event)">
|
||||
[ngClass]="{'immersive' : immersiveMode}"
|
||||
(mousedown)="mouseDown($event)" >
|
||||
|
||||
<div #readingHtml class="book-content {{ColumnLayout}} {{WritingStyleClass}}"
|
||||
[ngStyle]="{'max-height': ColumnHeight, 'max-width': VerticalBookContentWidth, 'width': VerticalBookContentWidth, 'column-width': ColumnWidth}"
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ $action-bar-height: 38px;
|
|||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
// background-color: purple !important;
|
||||
//background-color: purple !important;
|
||||
|
||||
&.column-layout-1 {
|
||||
height: calc((var(--vh, 1vh) * 100) - $action-bar-height);
|
||||
|
|
@ -198,10 +198,10 @@ $action-bar-height: 38px;
|
|||
// Fixes an issue where chrome will cut of margins, doesn't seem to affect other browsers
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.book-content {
|
||||
|
|
@ -325,6 +325,10 @@ $action-bar-height: 38px;
|
|||
}
|
||||
|
||||
$pagination-color: transparent;
|
||||
$pagination-opacity: 0;
|
||||
|
||||
//$pagination-color: red;
|
||||
//$pagination-opacity: 0.7;
|
||||
|
||||
|
||||
.right {
|
||||
|
|
@ -356,9 +360,9 @@ $pagination-color: transparent;
|
|||
width: 18%;
|
||||
z-index: 3;
|
||||
background: $pagination-color;
|
||||
opacity: $pagination-opacity;
|
||||
border-color: transparent;
|
||||
border: none !important;
|
||||
opacity: 0;
|
||||
outline: none;
|
||||
|
||||
&.immersive {
|
||||
|
|
@ -376,11 +380,12 @@ $pagination-color: transparent;
|
|||
top: $action-bar-height;
|
||||
width: 20vw;
|
||||
background: $pagination-color;
|
||||
opacity: $pagination-opacity;
|
||||
border-color: transparent;
|
||||
border: none !important;
|
||||
z-index: 3;
|
||||
opacity: 0;
|
||||
outline: none;
|
||||
height: 100vw;
|
||||
|
||||
&.immersive {
|
||||
top: 0px;
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
handleScrollEvent() {
|
||||
// Highlight the current chapter we are on
|
||||
if (Object.keys(this.pageAnchors).length !== 0) {
|
||||
// get the height of the document so we can capture markers that are halfway on the document viewport
|
||||
// get the height of the document, so we can capture markers that are halfway on the document viewport
|
||||
const verticalOffset = this.reader.nativeElement?.scrollTop || (this.scrollService.scrollPosition + (this.document.body.offsetHeight / 2));
|
||||
|
||||
const alreadyReached = Object.values(this.pageAnchors).filter((i: number) => i <= verticalOffset);
|
||||
|
|
@ -576,7 +576,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
const chapterId = this.route.snapshot.paramMap.get('chapterId');
|
||||
|
||||
if (libraryId === null || seriesId === null || chapterId === null) {
|
||||
this.router.navigateByUrl('/libraries');
|
||||
this.router.navigateByUrl('/home');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -618,7 +618,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.cdRef.markForCheck();
|
||||
|
||||
|
||||
|
||||
this.bookService.getBookInfo(this.chapterId).subscribe(info => {
|
||||
if (this.readingListMode && info.seriesFormat !== MangaFormat.EPUB) {
|
||||
// Redirect to the manga reader.
|
||||
|
|
@ -711,6 +710,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
} else if (event.key === KEY_CODES.LEFT_ARROW) {
|
||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
||||
} else if (event.key === KEY_CODES.ESC_KEY) {
|
||||
const isHighlighting = window.getSelection()?.toString() != '';
|
||||
if (isHighlighting) return;
|
||||
this.closeReader();
|
||||
} else if (event.key === KEY_CODES.SPACE) {
|
||||
this.toggleDrawer();
|
||||
|
|
@ -1590,12 +1591,14 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
private isCursorOverLeftPaginationArea(event: MouseEvent): boolean {
|
||||
const leftPaginationAreaEnd = window.innerWidth * 0.2;
|
||||
//console.log('user clicked on ', event.clientX, ' and left pagination ends on ', leftPaginationAreaEnd);
|
||||
return event.clientX <= leftPaginationAreaEnd;
|
||||
}
|
||||
|
||||
private isCursorOverRightPaginationArea(event: MouseEvent): boolean {
|
||||
const rightPaginationAreaStart = event.clientX >= window.innerWidth * 0.8;
|
||||
return rightPaginationAreaStart;
|
||||
const rightPaginationAreaStart = window.innerWidth * 0.8;
|
||||
//console.log('user clicked on ', event.clientX, ' and right pagination starts at ', rightPaginationAreaStart);
|
||||
return event.clientX >= rightPaginationAreaStart;
|
||||
}
|
||||
|
||||
private isCursorOverPaginationArea(event: MouseEvent): boolean {
|
||||
|
|
@ -1611,25 +1614,39 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
// Responsibile for handling pagination only
|
||||
handleContainerClick(event: MouseEvent) {
|
||||
|
||||
//if (event.target)
|
||||
console.log('target: ', event.target);
|
||||
if (['action-bar'].some(className => (event.target as Element).classList.contains(className))) {
|
||||
console.log('exiting early')
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isCursorOverLeftPaginationArea(event)) {
|
||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
||||
} else if (this.isCursorOverRightPaginationArea(event)) {
|
||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)
|
||||
} else {
|
||||
this.toggleMenu(event);
|
||||
}
|
||||
}
|
||||
|
||||
handleReaderClick(event: MouseEvent) {
|
||||
if (!this.clickToPaginate) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.toggleMenu(event);
|
||||
return;
|
||||
}
|
||||
|
||||
const isHighlighting = window.getSelection()?.toString() != '';
|
||||
|
||||
if (isHighlighting) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isCursorOverLeftPaginationArea(event)) {
|
||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
||||
} else if (this.isCursorOverRightPaginationArea(event)) {
|
||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)
|
||||
} else {
|
||||
this.toggleMenu(event);
|
||||
}
|
||||
}
|
||||
|
||||
toggleMenu(event: MouseEvent) {
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
@Pipe({
|
||||
name: 'safeStyle',
|
||||
standalone: true
|
||||
})
|
||||
export class SafeStylePipe implements PipeTransform {
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
|
||||
transform(value: string): unknown {
|
||||
return this.sanitizer.bypassSecurityTrustStyle(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { BookReaderComponent } from './_components/book-reader/book-reader.component';
|
||||
import { BookReaderRoutingModule } from './book-reader.router.module';
|
||||
import { SafeStylePipe } from './_pipes/safe-style.pipe';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { NgbAccordionModule, NgbNavModule, NgbProgressbarModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ReaderSettingsComponent } from './_components/reader-settings/reader-settings.component';
|
||||
import { TableOfContentsComponent } from './_components/table-of-contents/table-of-contents.component';
|
||||
import {DrawerComponent} from "../shared/drawer/drawer.component";
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
BookReaderRoutingModule,
|
||||
ReactiveFormsModule,
|
||||
NgbProgressbarModule,
|
||||
NgbTooltipModule,
|
||||
NgbTooltipModule,
|
||||
NgbAccordionModule,
|
||||
NgbNavModule,
|
||||
DrawerComponent,
|
||||
BookReaderComponent, SafeStylePipe, TableOfContentsComponent, ReaderSettingsComponent,
|
||||
], exports: [
|
||||
BookReaderComponent,
|
||||
SafeStylePipe
|
||||
]
|
||||
})
|
||||
export class BookReaderModule { }
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { BookReaderComponent } from './_components/book-reader/book-reader.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: ':chapterId',
|
||||
component: BookReaderComponent,
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes), ],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class BookReaderRoutingModule { }
|
||||
|
|
@ -66,7 +66,7 @@ export class BookmarksComponent implements OnInit {
|
|||
|
||||
private readonly translocoService = inject(TranslocoService);
|
||||
|
||||
constructor(private readerService: ReaderService, private seriesService: SeriesService,
|
||||
constructor(private readerService: ReaderService,
|
||||
private downloadService: DownloadService, private toastr: ToastrService,
|
||||
private confirmService: ConfirmService, public bulkSelectionService: BulkSelectionService,
|
||||
public imageService: ImageService, private actionFactoryService: ActionFactoryService,
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
import { NgModule } from "@angular/core";
|
||||
import { Routes, RouterModule } from "@angular/router";
|
||||
import { AuthGuard } from "../_guards/auth.guard";
|
||||
import { BookmarksComponent } from "./_components/bookmarks/bookmarks.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '**', component: BookmarksComponent, pathMatch: 'full', canActivate: [AuthGuard]},
|
||||
{
|
||||
path: '',
|
||||
runGuardsAndResolvers: 'always',
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{path: 'bookmarks', component: BookmarksComponent},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class BookmarkRoutingModule { }
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { BookmarkRoutingModule } from './bookmark-routing.module';
|
||||
import { BookmarksComponent } from './_components/bookmarks/bookmarks.component';
|
||||
import {BulkOperationsComponent} from "../cards/bulk-operations/bulk-operations.component";
|
||||
import {CardDetailLayoutComponent} from "../cards/card-detail-layout/card-detail-layout.component";
|
||||
import {
|
||||
SideNavCompanionBarComponent
|
||||
} from "../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
|
||||
import {CardItemComponent} from "../cards/card-item/card-item.component";
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
BookmarkRoutingModule,
|
||||
BulkOperationsComponent,
|
||||
CardDetailLayoutComponent,
|
||||
SideNavCompanionBarComponent,
|
||||
CardItemComponent,
|
||||
BookmarksComponent
|
||||
]
|
||||
})
|
||||
export class BookmarkModule { }
|
||||
|
|
@ -17,7 +17,7 @@ import { CollectionTag } from 'src/app/_models/collection-tag';
|
|||
import { ReadingList } from 'src/app/_models/reading-list';
|
||||
import { CollectionTagService } from 'src/app/_services/collection-tag.service';
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {FilterPipe} from "../../../pipe/filter.pipe";
|
||||
import {FilterPipe} from "../../../_pipes/filter.pipe";
|
||||
import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco";
|
||||
|
||||
@Component({
|
||||
|
|
|
|||
|
|
@ -42,18 +42,18 @@ import {CommonModule} from "@angular/common";
|
|||
import {TypeaheadComponent} from "../../../typeahead/_components/typeahead.component";
|
||||
import {CoverImageChooserComponent} from "../../cover-image-chooser/cover-image-chooser.component";
|
||||
import {EditSeriesRelationComponent} from "../../edit-series-relation/edit-series-relation.component";
|
||||
import {SentenceCasePipe} from "../../../pipe/sentence-case.pipe";
|
||||
import {MangaFormatPipe} from "../../../pipe/manga-format.pipe";
|
||||
import {DefaultDatePipe} from "../../../pipe/default-date.pipe";
|
||||
import {TimeAgoPipe} from "../../../pipe/time-ago.pipe";
|
||||
import {SentenceCasePipe} from "../../../_pipes/sentence-case.pipe";
|
||||
import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe";
|
||||
import {DefaultDatePipe} from "../../../_pipes/default-date.pipe";
|
||||
import {TimeAgoPipe} from "../../../_pipes/time-ago.pipe";
|
||||
import {TagBadgeComponent} from "../../../shared/tag-badge/tag-badge.component";
|
||||
import {PublicationStatusPipe} from "../../../pipe/publication-status.pipe";
|
||||
import {BytesPipe} from "../../../pipe/bytes.pipe";
|
||||
import {PublicationStatusPipe} from "../../../_pipes/publication-status.pipe";
|
||||
import {BytesPipe} from "../../../_pipes/bytes.pipe";
|
||||
import {ImageComponent} from "../../../shared/image/image.component";
|
||||
import {DefaultValuePipe} from "../../../pipe/default-value.pipe";
|
||||
import {DefaultValuePipe} from "../../../_pipes/default-value.pipe";
|
||||
import {TranslocoModule} from "@ngneat/transloco";
|
||||
import {TranslocoDatePipe} from "@ngneat/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../../pipe/utc-to-local-time.pipe";
|
||||
import {UtcToLocalTimePipe} from "../../../_pipes/utc-to-local-time.pipe";
|
||||
|
||||
enum TabID {
|
||||
General = 0,
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
|
|||
import {EntityInfoCardsComponent} from "../entity-info-cards/entity-info-cards.component";
|
||||
import {CoverImageChooserComponent} from "../cover-image-chooser/cover-image-chooser.component";
|
||||
import {ChapterMetadataDetailComponent} from "../chapter-metadata-detail/chapter-metadata-detail.component";
|
||||
import {DefaultDatePipe} from "../../pipe/default-date.pipe";
|
||||
import {BytesPipe} from "../../pipe/bytes.pipe";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
import {BytesPipe} from "../../_pipes/bytes.pipe";
|
||||
import {BadgeExpanderComponent} from "../../shared/badge-expander/badge-expander.component";
|
||||
import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component";
|
||||
import {PersonBadgeComponent} from "../../shared/person-badge/person-badge.component";
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@
|
|||
</span>
|
||||
<span *ngIf="header !== undefined && header.length > 0">
|
||||
{{header}}
|
||||
<span class="badge bg-primary rounded-pill" [attr.aria-label]="t('total-items', {count: pagination.totalItems})" *ngIf="pagination !== undefined">{{pagination.totalItems}}</span>
|
||||
<span class="badge bg-primary rounded-pill"
|
||||
[attr.aria-label]="t('total-items', {count: pagination.totalItems})"
|
||||
*ngIf="pagination !== undefined">{{pagination.totalItems}}</span>
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-metadata-filter [filterSettings]="filterSettings" [filterOpen]="filterOpen" (applyFilter)="applyMetadataFilter($event)"></app-metadata-filter>
|
||||
<div class="viewport-container" [ngClass]="{'empty': items.length === 0 && !isLoading}">
|
||||
<div class="content-container">
|
||||
|
|
@ -20,9 +23,13 @@
|
|||
<p *ngIf="items.length === 0 && !isLoading">
|
||||
<ng-container [ngTemplateOutlet]="noDataTemplate"></ng-container>
|
||||
</p>
|
||||
<virtual-scroller [ngClass]="{'empty': items.length === 0 && !isLoading}" #scroll [items]="items" [bufferAmount]="1" [parentScroll]="parentScroll">
|
||||
|
||||
<virtual-scroller [ngClass]="{'empty': items.length === 0 && !isLoading}" #scroll [items]="items" [bufferAmount]="bufferAmount" [parentScroll]="parentScroll" >
|
||||
<div class="grid row g-0" #container>
|
||||
<div class="card col-auto mt-2 mb-2" (click)="tryToSaveJumpKey(item)" *ngFor="let item of scroll.viewPortItems; trackBy:trackByIdentity; index as i" id="jumpbar-index--{{i}}" [attr.jumpbar-index]="i">
|
||||
<div class="card col-auto mt-2 mb-2"
|
||||
(click)="tryToSaveJumpKey(item)"
|
||||
*ngFor="let item of scroll.viewPortItems; trackBy:trackByIdentity; index as i" id="jumpbar-index--{{i}}"
|
||||
[attr.jumpbar-index]="i">
|
||||
<ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item, idx: scroll.viewPortInfo.startIndexWithBuffer + i }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -32,8 +39,9 @@
|
|||
|
||||
<ng-container *ngIf="jumpBarKeysToRender.length >= 4 && items.length > 0 && scroll.viewPortInfo.maxScrollPosition > 0" [ngTemplateOutlet]="jumpBar" [ngTemplateOutletContext]="{ id: 'jumpbar' }"></ng-container>
|
||||
</div>
|
||||
|
||||
<ng-template #cardTemplate>
|
||||
<virtual-scroller #scroll [items]="items" [bufferAmount]="1">
|
||||
<virtual-scroller #scroll [items]="items" [bufferAmount]="bufferAmount">
|
||||
<div class="grid row g-0" #container>
|
||||
<div class="card col-auto mt-2 mb-2" (click)="tryToSaveJumpKey(item)" *ngFor="let item of scroll.viewPortItems; trackBy:trackByIdentity; index as i" id="jumpbar-index--{{i}}" [attr.jumpbar-index]="i">
|
||||
<ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item, idx: i }"></ng-container>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
.card-container {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
//overflow-y: auto;
|
||||
}
|
||||
|
||||
.grid {
|
||||
|
|
@ -93,11 +92,7 @@
|
|||
|
||||
.virtual-scroller, virtual-scroller {
|
||||
width: 100%;
|
||||
//height: calc(100vh - 160px); // 64 is a random number, 523 for me.
|
||||
height: calc(var(--vh) * 100 - 173px);
|
||||
//height: calc(100vh - 160px);
|
||||
//background-color: red;
|
||||
//max-height: calc(var(--vh)*100 - 170px);
|
||||
}
|
||||
|
||||
virtual-scroller.empty {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import {SeriesFilterV2} from "../../_models/metadata/v2/series-filter-v2";
|
|||
imports: [CommonModule, LoadingComponent, VirtualScrollerModule, CardActionablesComponent, NgbTooltip, MetadataFilterComponent, TranslocoDirective],
|
||||
templateUrl: './card-detail-layout.component.html',
|
||||
styleUrls: ['./card-detail-layout.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CardDetailLayoutComponent implements OnInit, OnChanges {
|
||||
|
||||
|
|
@ -95,6 +95,7 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
|
|||
|
||||
updateApplied: number = 0;
|
||||
hasResumedJumpKey: boolean = false;
|
||||
bufferAmount: number = 1;
|
||||
|
||||
|
||||
get Breakpoint() {
|
||||
|
|
|
|||
|
|
@ -34,16 +34,16 @@ import {ImageComponent} from "../../shared/image/image.component";
|
|||
import {NgbProgressbar, NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {DownloadIndicatorComponent} from "../download-indicator/download-indicator.component";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {MangaFormatPipe} from "../../pipe/manga-format.pipe";
|
||||
import {MangaFormatIconPipe} from "../../pipe/manga-format-icon.pipe";
|
||||
import {SentenceCasePipe} from "../../pipe/sentence-case.pipe";
|
||||
import {MangaFormatPipe} from "../../_pipes/manga-format.pipe";
|
||||
import {MangaFormatIconPipe} from "../../_pipes/manga-format-icon.pipe";
|
||||
import {SentenceCasePipe} from "../../_pipes/sentence-case.pipe";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {RouterLink} from "@angular/router";
|
||||
import {translate, TranslocoModule, TranslocoService} from "@ngneat/transloco";
|
||||
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
|
||||
import {NextExpectedChapter} from "../../_models/series-detail/next-expected-chapter";
|
||||
import {UtcToLocalTimePipe} from "../../pipe/utc-to-local-time.pipe";
|
||||
import {TimeAgoPipe} from "../../pipe/time-ago.pipe";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
import {TimeAgoPipe} from "../../_pipes/time-ago.pipe";
|
||||
|
||||
@Component({
|
||||
selector: 'app-card-item',
|
||||
|
|
@ -229,7 +229,6 @@ export class CardItemComponent implements OnInit {
|
|||
|
||||
if (nextDate.expectedDate) {
|
||||
const utcPipe = new UtcToLocalTimePipe();
|
||||
//const timeUntilPipe = new TimeAgoPipe(this.cdRef, this.ngZone, this.translocoService);
|
||||
this.title = utcPipe.transform(nextDate.expectedDate);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
|||
import {TypeaheadComponent} from "../../typeahead/_components/typeahead.component";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {TranslocoModule} from "@ngneat/transloco";
|
||||
import {RelationshipPipe} from "../../pipe/relationship.pipe";
|
||||
import {RelationshipPipe} from "../../_pipes/relationship.pipe";
|
||||
|
||||
interface RelationControl {
|
||||
series: {id: number, name: string} | undefined; // Will add type as well
|
||||
|
|
|
|||
|
|
@ -18,17 +18,17 @@ import { SeriesService } from 'src/app/_services/series.service';
|
|||
import { ImageService } from 'src/app/_services/image.service';
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {IconAndTitleComponent} from "../../shared/icon-and-title/icon-and-title.component";
|
||||
import {SafeHtmlPipe} from "../../pipe/safe-html.pipe";
|
||||
import {DefaultDatePipe} from "../../pipe/default-date.pipe";
|
||||
import {BytesPipe} from "../../pipe/bytes.pipe";
|
||||
import {CompactNumberPipe} from "../../pipe/compact-number.pipe";
|
||||
import {AgeRatingPipe} from "../../pipe/age-rating.pipe";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
import {BytesPipe} from "../../_pipes/bytes.pipe";
|
||||
import {CompactNumberPipe} from "../../_pipes/compact-number.pipe";
|
||||
import {AgeRatingPipe} from "../../_pipes/age-rating.pipe";
|
||||
import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {MetadataDetailComponent} from "../../series-detail/_components/metadata-detail/metadata-detail.component";
|
||||
import {TranslocoModule} from "@ngneat/transloco";
|
||||
import {TranslocoLocaleModule} from "@ngneat/transloco-locale";
|
||||
import {FilterField} from "../../_models/metadata/v2/filter-field";
|
||||
import {UtcToLocalTimePipe} from "../../pipe/utc-to-local-time.pipe";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
|
||||
@Component({
|
||||
selector: 'app-entity-info-cards',
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {EditSeriesModalComponent} from '../_modals/edit-series-modal/edit-series
|
|||
import {RelationKind} from 'src/app/_models/series-detail/relation-kind';
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {CardItemComponent} from "../card-item/card-item.component";
|
||||
import {RelationshipPipe} from "../../pipe/relationship.pipe";
|
||||
import {RelationshipPipe} from "../../_pipes/relationship.pipe";
|
||||
import {Device} from "../../_models/device/device";
|
||||
import {translate, TranslocoService} from "@ngneat/transloco";
|
||||
import {SeriesPreviewDrawerComponent} from "../../_single-module/series-preview-drawer/series-preview-drawer.component";
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
|||
import {ScrobblingService} from "../../_services/scrobbling.service";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {IconAndTitleComponent} from "../../shared/icon-and-title/icon-and-title.component";
|
||||
import {AgeRatingPipe} from "../../pipe/age-rating.pipe";
|
||||
import {DefaultValuePipe} from "../../pipe/default-value.pipe";
|
||||
import {LanguageNamePipe} from "../../pipe/language-name.pipe";
|
||||
import {PublicationStatusPipe} from "../../pipe/publication-status.pipe";
|
||||
import {MangaFormatPipe} from "../../pipe/manga-format.pipe";
|
||||
import {TimeAgoPipe} from "../../pipe/time-ago.pipe";
|
||||
import {CompactNumberPipe} from "../../pipe/compact-number.pipe";
|
||||
import {MangaFormatIconPipe} from "../../pipe/manga-format-icon.pipe";
|
||||
import {AgeRatingPipe} from "../../_pipes/age-rating.pipe";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {LanguageNamePipe} from "../../_pipes/language-name.pipe";
|
||||
import {PublicationStatusPipe} from "../../_pipes/publication-status.pipe";
|
||||
import {MangaFormatPipe} from "../../_pipes/manga-format.pipe";
|
||||
import {TimeAgoPipe} from "../../_pipes/time-ago.pipe";
|
||||
import {CompactNumberPipe} from "../../_pipes/compact-number.pipe";
|
||||
import {MangaFormatIconPipe} from "../../_pipes/manga-format-icon.pipe";
|
||||
import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ import {ToastrService} from "ngx-toastr";
|
|||
|
||||
|
||||
@Component({
|
||||
selector: 'app-all-collections',
|
||||
templateUrl: './all-collections.component.html',
|
||||
styleUrls: ['./all-collections.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
selector: 'app-all-collections',
|
||||
templateUrl: './all-collections.component.html',
|
||||
styleUrls: ['./all-collections.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [SideNavCompanionBarComponent, CardDetailLayoutComponent, CardItemComponent, NgIf, AsyncPipe, DecimalPipe, TranslocoDirective]
|
||||
})
|
||||
export class AllCollectionsComponent implements OnInit {
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@ import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison";
|
|||
import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2";
|
||||
|
||||
@Component({
|
||||
selector: 'app-collection-detail',
|
||||
templateUrl: './collection-detail.component.html',
|
||||
styleUrls: ['./collection-detail.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
selector: 'app-collection-detail',
|
||||
templateUrl: './collection-detail.component.html',
|
||||
styleUrls: ['./collection-detail.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [NgIf, SideNavCompanionBarComponent, CardActionablesComponent, NgStyle, ImageComponent, ReadMoreComponent, BulkOperationsComponent, CardDetailLayoutComponent, SeriesCardComponent, TranslocoDirective]
|
||||
})
|
||||
export class CollectionDetailComponent implements OnInit, AfterContentChecked {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue