Polish Round 1 (#2396)

This commit is contained in:
Joe Milazzo 2023-11-04 12:29:10 -05:00 committed by GitHub
parent cf2c43d390
commit 02b002d81a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
197 changed files with 1233 additions and 1751 deletions

View file

@ -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;
})
);

View file

@ -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;
}
}

View file

@ -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 {

View file

@ -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',

View file

@ -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',

View file

@ -18,8 +18,6 @@ export class ProviderImagePipe implements PipeTransform {
case ScrobbleProvider.Kavita:
return 'assets/images/logo-32.png';
}
return '';
}
}

View file

@ -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 { }

View 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],
}
];

View 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},
]
}
];

View 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,
}
];

View 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},
]
}
];

View 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},
]
}
];

View 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,
}
];

View 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
}
];

View 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
}
];

View 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,
}
];

View 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]},
];

View 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
}
];

View 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'},
]
}
];

View 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'},
]
}
];

View file

@ -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;
}

View file

@ -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';

View file

@ -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({

View file

@ -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({

View file

@ -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";

View file

@ -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({

View file

@ -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',

View file

@ -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";

View file

@ -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 { }

View file

@ -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';

View file

@ -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';

View file

@ -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>

View file

@ -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',

View file

@ -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";

View file

@ -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({

View file

@ -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',

View file

@ -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',

View file

@ -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;

View file

@ -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>

View file

@ -6,3 +6,11 @@
.user-info > div {
margin-top: 3px;
}
.highlight {
color: var(--primary-color);
}
.pending-badge {
font-size: 15px;
}

View file

@ -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(', ');

View file

@ -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 {

View file

@ -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 { }

View file

@ -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 { }

View file

@ -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 { }

View file

@ -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 { }

View file

@ -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({

View file

@ -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) {

View file

@ -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}"

View file

@ -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;

View file

@ -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) {

View file

@ -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);
}
}

View file

@ -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 { }

View file

@ -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 { }

View file

@ -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,

View file

@ -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 { }

View file

@ -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 { }

View file

@ -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({

View file

@ -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,

View file

@ -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";

View file

@ -8,11 +8,14 @@
</span>
<span *ngIf="header !== undefined && header.length > 0">
{{header}}&nbsp;
<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>

View file

@ -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 {

View file

@ -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() {

View file

@ -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);
}

View file

@ -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

View file

@ -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',

View file

@ -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";

View file

@ -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";

View file

@ -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 {

View file

@ -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