From 8e85ba069c39b8047650ca30e1d6a805a7992a7f Mon Sep 17 00:00:00 2001 From: Joe Milazzo Date: Wed, 18 Oct 2023 17:52:54 -0500 Subject: [PATCH] Release Polishing (#2325) Co-authored-by: Robbie Davis --- API/API.csproj | 1 - API/Controllers/AccountController.cs | 1 + README.md | 6 +-- SECURITY.md | 2 +- UI/Web/src/app/_services/action.service.ts | 45 +++++++++---------- .../series-preview-drawer.component.scss | 4 ++ UI/Web/src/app/app.component.ts | 4 +- .../bulk-add-to-collection.component.ts | 7 ++- .../edit-collection-tags.component.ts | 7 ++- .../card-detail-drawer.component.ts | 9 ++-- .../cards/list-item/list-item.component.ts | 5 +-- .../series-card/series-card.component.ts | 5 +-- .../library-detail.component.ts | 5 +-- .../manga-reader/manga-reader.component.ts | 21 ++++----- .../pdf-reader/pdf-reader.component.ts | 6 +-- .../draggable-ordered-list.component.html | 11 +---- .../draggable-ordered-list.component.ts | 4 +- .../reading-list-detail.component.html | 2 +- .../reading-list-detail.component.ts | 16 ++++--- .../customize-dashboard-modal.component.html | 1 + .../customize-dashboard-modal.component.scss | 8 ++++ ...customize-dashboard-streams.component.html | 2 +- .../customize-sidenav-streams.component.html | 1 - .../manage-external-sources.component.html | 2 +- .../user-preferences.component.ts | 2 +- UI/Web/src/assets/langs/en.json | 3 +- UI/Web/src/main.ts | 2 +- openapi.json | 2 +- 28 files changed, 88 insertions(+), 96 deletions(-) diff --git a/API/API.csproj b/API/API.csproj index 3786e7e62..a10557938 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -54,7 +54,6 @@ - diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index ee5bbafcf..f0ff0ed6f 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -268,6 +268,7 @@ public class AccountController : BaseApiController dto.RefreshToken = await _tokenService.CreateRefreshToken(user); dto.KavitaVersion = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion)) .Value; + dto.Preferences = _mapper.Map(user.UserPreferences); return Ok(dto); } diff --git a/README.md b/README.md index af047f83f..f6d2a437e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ your reading collection with your friends and family! - Ability to manage users with rich Role-based management for age restrictions, abilities within the app, etc - Rich web readers supporting webtoon, continuous reading mode (continue without leaving the reader), virtual pages (epub), etc - Full Localization Support -- Ability to customize your dashboard and side nav with smart filters +- Ability to customize your dashboard and side nav with smart filters, custom order and visibility toggles. ## Support @@ -110,10 +110,6 @@ Thank you to [Weblate](https://hosted.weblate.org/engage/kavita/) who hosts our Translation status -## Huntr -We would like to extend a big thank you to [Huntr](https://huntr.dev/repos/kareadita/kavita) who has worked with Kavita in reporting security vulnerabilities. If you are interested in -being paid to help secure Kavita, please give them a try. - ## PikaPods If you are looking to try your hand at self-hosting but lack the machine, [PikaPods](https://www.pikapods.com/pods?run=kavita) is a great service that allows you to easily spin up a server. 20% of app revenues are contributed back to Kavita via OpenCollective. diff --git a/SECURITY.md b/SECURITY.md index 56f010fb3..6b50ba3d1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,4 +7,4 @@ Security is maintained on latest stable version only. ## Reporting a Vulnerability -Please reach out to majora2007 via our Discord or you can (and should) report your vulnerability via [Huntr](https://huntr.dev/repos/kareadita/kavita). +Please reach out to majora2007 via our Discord or you can (and should) report your vulnerability via Github Security Disclosure. diff --git a/UI/Web/src/app/_services/action.service.ts b/UI/Web/src/app/_services/action.service.ts index a061729c7..1a6f4082b 100644 --- a/UI/Web/src/app/_services/action.service.ts +++ b/UI/Web/src/app/_services/action.service.ts @@ -43,8 +43,7 @@ export class ActionService implements OnDestroy { constructor(private libraryService: LibraryService, private seriesService: SeriesService, private readerService: ReaderService, private toastr: ToastrService, private modalService: NgbModal, - private confirmService: ConfirmService, private memberService: MemberService, private deviceService: DeviceService, - private translocoService: TranslocoService) { } + private confirmService: ConfirmService, private memberService: MemberService, private deviceService: DeviceService) { } ngOnDestroy() { this.onDestroy.next(); @@ -66,7 +65,7 @@ export class ActionService implements OnDestroy { const force = false; // await this.promptIfForce(); this.libraryService.scan(library.id, force).pipe(take(1)).subscribe((res: any) => { - this.toastr.info(this.translocoService.translate('toasts.scan-queued', {name: library.name})); + this.toastr.info(translate('toasts.scan-queued', {name: library.name})); if (callback) { callback(library); } @@ -95,7 +94,7 @@ export class ActionService implements OnDestroy { const forceUpdate = true; //await this.promptIfForce(); this.libraryService.refreshMetadata(library?.id, forceUpdate).pipe(take(1)).subscribe((res: any) => { - this.toastr.info(this.translocoService.translate('toasts.scan-queued', {name: library.name})); + this.toastr.info(translate('toasts.scan-queued', {name: library.name})); if (callback) { callback(library); } @@ -129,7 +128,7 @@ export class ActionService implements OnDestroy { } this.libraryService.analyze(library?.id).pipe(take(1)).subscribe((res: any) => { - this.toastr.info(this.translocoService.translate('toasts.library-file-analysis-queued', {name: library.name})); + this.toastr.info(translate('toasts.library-file-analysis-queued', {name: library.name})); if (callback) { callback(library); } @@ -144,7 +143,7 @@ export class ActionService implements OnDestroy { markSeriesAsRead(series: Series, callback?: SeriesActionCallback) { this.seriesService.markRead(series.id).pipe(take(1)).subscribe(res => { series.pagesRead = series.pages; - this.toastr.success(this.translocoService.translate('toasts.entity-read', {name: series.name})); + this.toastr.success(translate('toasts.entity-read', {name: series.name})); if (callback) { callback(series); } @@ -159,7 +158,7 @@ export class ActionService implements OnDestroy { markSeriesAsUnread(series: Series, callback?: SeriesActionCallback) { this.seriesService.markUnread(series.id).pipe(take(1)).subscribe(res => { series.pagesRead = 0; - this.toastr.success(this.translocoService.translate('toasts.entity-unread', {name: series.name})); + this.toastr.success(translate('toasts.entity-unread', {name: series.name})); if (callback) { callback(series); } @@ -173,7 +172,7 @@ export class ActionService implements OnDestroy { */ async scanSeries(series: Series, callback?: SeriesActionCallback) { this.seriesService.scan(series.libraryId, series.id).pipe(take(1)).subscribe((res: any) => { - this.toastr.info(this.translocoService.translate('toasts.scan-queued', {name: series.name})); + this.toastr.info(translate('toasts.scan-queued', {name: series.name})); if (callback) { callback(series); } @@ -187,7 +186,7 @@ export class ActionService implements OnDestroy { */ analyzeFilesForSeries(series: Series, callback?: SeriesActionCallback) { this.seriesService.analyzeFiles(series.libraryId, series.id).pipe(take(1)).subscribe((res: any) => { - this.toastr.info(this.translocoService.translate('toasts.scan-queued', {name: series.name})); + this.toastr.info(translate('toasts.scan-queued', {name: series.name})); if (callback) { callback(series); } @@ -208,7 +207,7 @@ export class ActionService implements OnDestroy { } this.seriesService.refreshMetadata(series).pipe(take(1)).subscribe((res: any) => { - this.toastr.info(this.translocoService.translate('toasts.refresh-covers-queued', {name: series.name})); + this.toastr.info(translate('toasts.refresh-covers-queued', {name: series.name})); if (callback) { callback(series); } @@ -225,7 +224,7 @@ export class ActionService implements OnDestroy { this.readerService.markVolumeRead(seriesId, volume.id).pipe(take(1)).subscribe(() => { volume.pagesRead = volume.pages; volume.chapters?.forEach(c => c.pagesRead = c.pages); - this.toastr.success(this.translocoService.translate('toasts.mark-read')); + this.toastr.success(translate('toasts.mark-read')); if (callback) { callback(volume); @@ -243,7 +242,7 @@ export class ActionService implements OnDestroy { this.readerService.markVolumeUnread(seriesId, volume.id).subscribe(() => { volume.pagesRead = 0; volume.chapters?.forEach(c => c.pagesRead = 0); - this.toastr.success(this.translocoService.translate('toasts.mark-unread')); + this.toastr.success(translate('toasts.mark-unread')); if (callback) { callback(volume); } @@ -259,7 +258,7 @@ export class ActionService implements OnDestroy { markChapterAsRead(libraryId: number, seriesId: number, chapter: Chapter, callback?: ChapterActionCallback) { this.readerService.saveProgress(libraryId, seriesId, chapter.volumeId, chapter.id, chapter.pages).pipe(take(1)).subscribe(results => { chapter.pagesRead = chapter.pages; - this.toastr.success(this.translocoService.translate('toasts.mark-read')); + this.toastr.success(translate('toasts.mark-read')); if (callback) { callback(chapter); } @@ -275,7 +274,7 @@ export class ActionService implements OnDestroy { markChapterAsUnread(libraryId: number, seriesId: number, chapter: Chapter, callback?: ChapterActionCallback) { this.readerService.saveProgress(libraryId, seriesId, chapter.volumeId, chapter.id, 0).pipe(take(1)).subscribe(results => { chapter.pagesRead = 0; - this.toastr.success(this.translocoService.translate('toasts.mark-unread')); + this.toastr.success(translate('toasts.mark-unread')); if (callback) { callback(chapter); } @@ -296,7 +295,7 @@ export class ActionService implements OnDestroy { volume.chapters?.forEach(c => c.pagesRead = c.pages); }); chapters?.forEach(c => c.pagesRead = c.pages); - this.toastr.success(this.translocoService.translate('toasts.mark-read')); + this.toastr.success(translate('toasts.mark-read')); if (callback) { callback(); @@ -317,7 +316,7 @@ export class ActionService implements OnDestroy { volume.chapters?.forEach(c => c.pagesRead = 0); }); chapters?.forEach(c => c.pagesRead = 0); - this.toastr.success(this.translocoService.translate('toasts.mark-unread')); + this.toastr.success(translate('toasts.mark-unread')); if (callback) { callback(); @@ -335,7 +334,7 @@ export class ActionService implements OnDestroy { series.forEach(s => { s.pagesRead = s.pages; }); - this.toastr.success(this.translocoService.translate('toasts.mark-read')); + this.toastr.success(translate('toasts.mark-read')); if (callback) { callback(); @@ -353,7 +352,7 @@ export class ActionService implements OnDestroy { series.forEach(s => { s.pagesRead = s.pages; }); - this.toastr.success(this.translocoService.translate('toasts.mark-unread')); + this.toastr.success(translate('toasts.mark-unread')); if (callback) { callback(); @@ -396,7 +395,7 @@ export class ActionService implements OnDestroy { removeMultipleSeriesFromWantToReadList(seriesIds: Array, callback?: VoidActionCallback) { this.memberService.removeSeriesToWantToRead(seriesIds).subscribe(() => { - this.toastr.success(this.translocoService.translate('toasts.series-removed-want-to-read')); + this.toastr.success(translate('toasts.series-removed-want-to-read')); if (callback) { callback(); } @@ -547,7 +546,7 @@ export class ActionService implements OnDestroy { return; } this.seriesService.deleteMultipleSeries(seriesIds.map(s => s.id)).pipe(take(1)).subscribe(() => { - this.toastr.success(this.translocoService.translate('toasts.series-deleted')); + this.toastr.success(translate('toasts.series-deleted')); if (callback) { callback(true); @@ -565,7 +564,7 @@ export class ActionService implements OnDestroy { this.seriesService.delete(series.id).subscribe((res: boolean) => { if (callback) { - this.toastr.success(this.translocoService.translate('toasts.series-deleted')); + this.toastr.success(translate('toasts.series-deleted')); callback(res); } }); @@ -573,7 +572,7 @@ export class ActionService implements OnDestroy { sendToDevice(chapterIds: Array, device: Device, callback?: VoidActionCallback) { this.deviceService.sendTo(chapterIds, device.id).subscribe(() => { - this.toastr.success(this.translocoService.translate('toasts.file-send-to', {name: device.name})); + this.toastr.success(translate('toasts.file-send-to', {name: device.name})); if (callback) { callback(); } @@ -582,7 +581,7 @@ export class ActionService implements OnDestroy { sendSeriesToDevice(seriesId: number, device: Device, callback?: VoidActionCallback) { this.deviceService.sendSeriesTo(seriesId, device.id).subscribe(() => { - this.toastr.success(this.translocoService.translate('toasts.file-send-to', {name: device.name})); + this.toastr.success(translate('toasts.file-send-to', {name: device.name})); if (callback) { callback(); } diff --git a/UI/Web/src/app/_single-module/series-preview-drawer/series-preview-drawer.component.scss b/UI/Web/src/app/_single-module/series-preview-drawer/series-preview-drawer.component.scss index f13699570..c34531887 100644 --- a/UI/Web/src/app/_single-module/series-preview-drawer/series-preview-drawer.component.scss +++ b/UI/Web/src/app/_single-module/series-preview-drawer/series-preview-drawer.component.scss @@ -12,3 +12,7 @@ .muted { font-size: 14px; } + +a.read-more-link { + white-space: nowrap; +} \ No newline at end of file diff --git a/UI/Web/src/app/app.component.ts b/UI/Web/src/app/app.component.ts index c87d1773f..356866bad 100644 --- a/UI/Web/src/app/app.component.ts +++ b/UI/Web/src/app/app.component.ts @@ -1,6 +1,6 @@ import {Component, DestroyRef, HostListener, inject, Inject, OnInit} from '@angular/core'; import { NavigationStart, Router, RouterOutlet } from '@angular/router'; -import {map, pluck, shareReplay, take} from 'rxjs/operators'; +import {map, shareReplay, take} from 'rxjs/operators'; import { AccountService } from './_services/account.service'; import { LibraryService } from './_services/library.service'; import { NavService } from './_services/nav.service'; @@ -12,7 +12,6 @@ import {ThemeService} from "./_services/theme.service"; import { SideNavComponent } from './sidenav/_components/side-nav/side-nav.component'; import {NavHeaderComponent} from "./nav/_components/nav-header/nav-header.component"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import {translate, TranslocoService} from "@ngneat/transloco"; @Component({ selector: 'app-root', @@ -26,7 +25,6 @@ export class AppComponent implements OnInit { transitionState$!: Observable; destroyRef = inject(DestroyRef); - translocoService = inject(TranslocoService); constructor(private accountService: AccountService, public navService: NavService, private libraryService: LibraryService, diff --git a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts index 25a591f85..410fcffba 100644 --- a/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts +++ b/UI/Web/src/app/cards/_modals/bulk-add-to-collection/bulk-add-to-collection.component.ts @@ -18,7 +18,7 @@ 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 {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; @Component({ selector: 'app-bulk-add-to-collection', @@ -46,7 +46,6 @@ export class BulkAddToCollectionComponent implements OnInit, AfterViewInit { collectionTitleTrackby = (index: number, item: CollectionTag) => `${item.title}`; - translocoService = inject(TranslocoService); @ViewChild('title') inputElem!: ElementRef; @@ -83,7 +82,7 @@ export class BulkAddToCollectionComponent implements OnInit, AfterViewInit { create() { const tagName = this.listForm.value.title; this.collectionService.addByMultiple(0, this.seriesIds, tagName).subscribe(() => { - this.toastr.success(this.translocoService.translate('toasts.series-added-to-collection', {collectionName: tagName})); + this.toastr.success(translate('toasts.series-added-to-collection', {collectionName: tagName})); this.modal.close(); }); } @@ -92,7 +91,7 @@ export class BulkAddToCollectionComponent implements OnInit, AfterViewInit { if (this.seriesIds.length === 0) return; this.collectionService.addByMultiple(tag.id, this.seriesIds, '').subscribe(() => { - this.toastr.success(this.translocoService.translate('toasts.series-added-to-collection', {collectionName: tag.title})); + this.toastr.success(translate('toasts.series-added-to-collection', {collectionName: tag.title})); this.modal.close(); }); diff --git a/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.ts b/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.ts index 583a09658..443e4bda3 100644 --- a/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.ts +++ b/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.ts @@ -33,7 +33,7 @@ import { UploadService } from 'src/app/_services/upload.service'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {CommonModule} from "@angular/common"; import {CoverImageChooserComponent} from "../../cover-image-chooser/cover-image-chooser.component"; -import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; enum TabID { @@ -65,7 +65,6 @@ export class EditCollectionTagsComponent implements OnInit { imageUrls: Array = []; selectedCover: string = ''; private readonly destroyRef = inject(DestroyRef); - translocoService = inject(TranslocoService); get hasSomeSelected() { return this.selections != null && this.selections.hasSomeSelected(); @@ -172,7 +171,7 @@ export class EditCollectionTagsComponent implements OnInit { tag.id = this.tag.id; if (unselectedIds.length == this.series.length && - !await this.confirmService.confirm(this.translocoService.translate('toasts.no-series-collection-warning'))) { + !await this.confirmService.confirm(translate('toasts.no-series-collection-warning'))) { return; } @@ -187,7 +186,7 @@ export class EditCollectionTagsComponent implements OnInit { forkJoin(apis).subscribe(() => { this.modal.close({success: true, coverImageUpdated: selectedIndex > 0}); - this.toastr.success(this.translocoService.translate('toasts.collection-updated')); + this.toastr.success(translate('toasts.collection-updated')); }); } diff --git a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts index 6910421db..204c7bf98 100644 --- a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts +++ b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts @@ -48,7 +48,7 @@ import {BytesPipe} from "../../pipe/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"; -import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; enum TabID { @@ -73,7 +73,6 @@ export class CardDetailDrawerComponent implements OnInit { @Input() libraryId: number = 0; @Input({required: true}) data!: Volume | Chapter; private readonly destroyRef = inject(DestroyRef); - private readonly translocoService = inject(TranslocoService); /** @@ -209,7 +208,7 @@ export class CardDetailDrawerComponent implements OnInit { resetCoverImage() { this.uploadService.resetChapterCoverLock(this.chapter.id).subscribe(() => { - this.toastr.info(this.translocoService.translate('toasts.regen-cover')); + this.toastr.info(translate('toasts.regen-cover')); }); } @@ -262,7 +261,7 @@ export class CardDetailDrawerComponent implements OnInit { readChapter(chapter: Chapter, incognito: boolean = false) { if (chapter.pages === 0) { - this.toastr.error(this.translocoService.translate('toasts.no-pages')); + this.toastr.error(translate('toasts.no-pages')); return; } @@ -273,7 +272,7 @@ export class CardDetailDrawerComponent implements OnInit { download(chapter: Chapter) { if (this.downloadInProgress) { - this.toastr.info(this.translocoService.translate('toasts.download-in-progress')); + this.toastr.info(translate('toasts.download-in-progress')); return; } diff --git a/UI/Web/src/app/cards/list-item/list-item.component.ts b/UI/Web/src/app/cards/list-item/list-item.component.ts index cf1f848ed..3bafbaf6c 100644 --- a/UI/Web/src/app/cards/list-item/list-item.component.ts +++ b/UI/Web/src/app/cards/list-item/list-item.component.ts @@ -25,7 +25,7 @@ import {ImageComponent} from "../../shared/image/image.component"; import {DownloadIndicatorComponent} from "../download-indicator/download-indicator.component"; import {EntityInfoCardsComponent} from "../entity-info-cards/entity-info-cards.component"; import {NgbProgressbar, NgbTooltip} from "@ng-bootstrap/ng-bootstrap"; -import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@ngneat/transloco"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; @Component({ @@ -90,7 +90,6 @@ export class ListItemComponent implements OnInit { @Output() read: EventEmitter = new EventEmitter(); private readonly destroyRef = inject(DestroyRef); - private readonly translocoService = inject(TranslocoService); actionInProgress: boolean = false; summary: string = ''; @@ -136,7 +135,7 @@ export class ListItemComponent implements OnInit { performAction(action: ActionItem) { if (action.action == Action.Download) { if (this.downloadInProgress) { - this.toastr.info(this.translocoService.translate('toasts.download-in-progress')); + this.toastr.info(translate('toasts.download-in-progress')); return; } diff --git a/UI/Web/src/app/cards/series-card/series-card.component.ts b/UI/Web/src/app/cards/series-card/series-card.component.ts index 4d364770a..5a1d5215b 100644 --- a/UI/Web/src/app/cards/series-card/series-card.component.ts +++ b/UI/Web/src/app/cards/series-card/series-card.component.ts @@ -22,7 +22,7 @@ import {CommonModule} from "@angular/common"; import {CardItemComponent} from "../card-item/card-item.component"; import {RelationshipPipe} from "../../pipe/relationship.pipe"; import {Device} from "../../_models/device/device"; -import {TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoService} from "@ngneat/transloco"; import {SeriesPreviewDrawerComponent} from "../../_single-module/series-preview-drawer/series-preview-drawer.component"; function deepClone(obj: any): any { @@ -96,7 +96,6 @@ export class SeriesCardComponent implements OnInit, OnChanges { actions: ActionItem[] = []; imageUrl: string = ''; - private readonly translocoService = inject(TranslocoService); private readonly offcanvasService = inject(NgbOffcanvas); constructor(private router: Router, private cdRef: ChangeDetectorRef, @@ -206,7 +205,7 @@ export class SeriesCardComponent implements OnInit, OnChanges { async scanLibrary(series: Series) { this.seriesService.scan(series.libraryId, series.id).subscribe((res: any) => { - this.toastr.success(this.translocoService.translate('toasts.scan-queued', {name: series.name})); + this.toastr.success(translate('toasts.scan-queued', {name: series.name})); }); } diff --git a/UI/Web/src/app/library-detail/library-detail.component.ts b/UI/Web/src/app/library-detail/library-detail.component.ts index 977b6c3cc..eb7148726 100644 --- a/UI/Web/src/app/library-detail/library-detail.component.ts +++ b/UI/Web/src/app/library-detail/library-detail.component.ts @@ -38,7 +38,7 @@ import {NgbNav, NgbNavContent, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavOut import { SideNavCompanionBarComponent } from '../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component'; -import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {TranslocoDirective} from "@ngneat/transloco"; import {SeriesFilterV2} from "../_models/metadata/v2/series-filter-v2"; import {MetadataService} from "../_services/metadata.service"; import {FilterComparison} from "../_models/metadata/v2/filter-comparison"; @@ -68,11 +68,8 @@ export class LibraryDetailComponent implements OnInit { filterActive: boolean = false; filterActiveCheck!: SeriesFilterV2; refresh: EventEmitter = new EventEmitter(); - jumpKeys: Array = []; - translocoService = inject(TranslocoService); - tabs: Array<{title: string, fragment: string, icon: string}> = [ {title: 'library-tab', fragment: '', icon: 'fa-landmark'}, {title: 'recommended-tab', fragment: 'recommended', icon: 'fa-award'}, diff --git a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts index c7d0c3513..fb3d5bc5e 100644 --- a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts +++ b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts @@ -121,7 +121,10 @@ enum KeyDirection { ]) ], standalone: true, - imports: [NgStyle, NgIf, LoadingComponent, SwipeDirective, CanvasRendererComponent, SingleRendererComponent, DoubleRendererComponent, DoubleReverseRendererComponent, DoubleNoCoverRendererComponent, InfiniteScrollerComponent, NgxSliderModule, ReactiveFormsModule, NgFor, NgSwitch, NgSwitchCase, FittingIconPipe, ReaderModeIconPipe, FullscreenIconPipe, TranslocoDirective] + imports: [NgStyle, NgIf, LoadingComponent, SwipeDirective, CanvasRendererComponent, SingleRendererComponent, + DoubleRendererComponent, DoubleReverseRendererComponent, DoubleNoCoverRendererComponent, InfiniteScrollerComponent, + NgxSliderModule, ReactiveFormsModule, NgFor, NgSwitch, NgSwitchCase, FittingIconPipe, ReaderModeIconPipe, + FullscreenIconPipe, TranslocoDirective] }) export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { @@ -382,8 +385,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { private pageNumSubject: Subject<{pageNum: number, maxPages: number}> = new ReplaySubject(); pageNum$: Observable<{pageNum: number, maxPages: number}> = this.pageNumSubject.asObservable(); - private readonly translocoService = inject(TranslocoService); - getPageUrl = (pageNum: number, chapterId: number = this.chapterId) => { if (this.bookmarkMode) return this.readerService.getBookmarkPageUrl(this.seriesId, this.user.apiKey, pageNum); return this.readerService.getPageUrl(chapterId, pageNum); @@ -591,7 +592,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.memberService.hasReadingProgress(this.libraryId).pipe(take(1)).subscribe(progress => { if (!progress) { this.toggleMenu(); - this.toastr.info(this.translocoService.translate('manga-reader.first-time-reading-manga')); + this.toastr.info(translate('manga-reader.first-time-reading-manga')); } }); }); @@ -723,7 +724,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.generalSettingsForm.get('layoutMode')?.setValue(LayoutMode.Single); this.generalSettingsForm.get('layoutMode')?.disable(); - this.toastr.info(this.translocoService.translate('manga-reader.layout-mode-switched')); + this.toastr.info(translate('manga-reader.layout-mode-switched')); this.cdRef.markForCheck(); } @@ -1223,7 +1224,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { loadNextChapter() { if (this.nextPageDisabled || this.nextChapterDisabled || this.bookmarkMode) { - this.toastr.info(this.translocoService.translate('manga-reader.no-next-chapter')); + this.toastr.info(translate('manga-reader.no-next-chapter')); this.isLoading = false; this.cdRef.markForCheck(); return; @@ -1241,7 +1242,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { loadPrevChapter() { if (this.prevPageDisabled || this.prevChapterDisabled || this.bookmarkMode) { - this.toastr.info(this.translocoService.translate('manga-reader.no-prev-chapter')); + this.toastr.info(translate('manga-reader.no-prev-chapter')); this.isLoading = false; this.cdRef.markForCheck(); return; @@ -1608,7 +1609,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.incognitoMode = false; const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId); window.history.replaceState({}, '', newRoute); - this.toastr.info(this.translocoService.translate('toasts.incognito-off')); + this.toastr.info(translate('toasts.incognito-off')); if (!this.bookmarkMode) { this.readerService.saveProgress(this.libraryId, this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */}); } @@ -1624,7 +1625,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { {key: '↓', description: 'next-page'}, {key: 'G', description: 'go-to'}, {key: 'B', description: 'bookmark'}, - {key: this.translocoService.translate('shortcuts-modal.double-click'), description: 'bookmark'}, + {key: translate('shortcuts-modal.double-click'), description: 'bookmark'}, {key: 'ESC', description: 'close-reader'}, {key: 'SPACE', description: 'toggle-menu'}, ]; @@ -1646,7 +1647,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { data.pageSplitOption = parseInt(modelSettings.pageSplitOption, 10); this.accountService.updatePreferences(data).subscribe(updatedPrefs => { - this.toastr.success(this.translocoService.translate('manga-reader.user-preferences-updated')); + this.toastr.success(translate('manga-reader.user-preferences-updated')); if (this.user) { this.user.preferences = updatedPrefs; this.cdRef.markForCheck(); diff --git a/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.ts b/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.ts index b9396ea5d..4b9983497 100644 --- a/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.ts +++ b/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.ts @@ -21,7 +21,7 @@ import { SeriesService } from 'src/app/_services/series.service'; import { ThemeService } from 'src/app/_services/theme.service'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { NgIf, NgStyle, AsyncPipe } from '@angular/common'; -import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; +import {translate, TranslocoDirective} from "@ngneat/transloco"; @Component({ selector: 'app-pdf-reader', @@ -87,8 +87,6 @@ export class PdfReaderComponent implements OnInit, OnDestroy { */ bookMode: PageViewModeType = 'multiple'; - private readonly translocoService = inject(TranslocoService); - constructor(private route: ActivatedRoute, private router: Router, public accountService: AccountService, private seriesService: SeriesService, public readerService: ReaderService, private navService: NavService, private toastr: ToastrService, @@ -178,7 +176,7 @@ export class PdfReaderComponent implements OnInit, OnDestroy { this.incognitoMode = false; const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId); window.history.replaceState({}, '', newRoute); - this.toastr.info(this.translocoService.translate('toasts.incognito-off')); + this.toastr.info(translate('toasts.incognito-off')); this.saveProgress(); this.cdRef.markForCheck(); } diff --git a/UI/Web/src/app/reading-list/_components/draggable-ordered-list/draggable-ordered-list.component.html b/UI/Web/src/app/reading-list/_components/draggable-ordered-list/draggable-ordered-list.component.html index 0eff1661a..9d4a823aa 100644 --- a/UI/Web/src/app/reading-list/_components/draggable-ordered-list/draggable-ordered-list.component.html +++ b/UI/Web/src/app/reading-list/_components/draggable-ordered-list/draggable-ordered-list.component.html @@ -2,15 +2,6 @@
- - - - - - - - -
@@ -46,7 +37,7 @@ diff --git a/UI/Web/src/app/reading-list/_components/draggable-ordered-list/draggable-ordered-list.component.ts b/UI/Web/src/app/reading-list/_components/draggable-ordered-list/draggable-ordered-list.component.ts index 1c5f3fb60..10b1b9d94 100644 --- a/UI/Web/src/app/reading-list/_components/draggable-ordered-list/draggable-ordered-list.component.ts +++ b/UI/Web/src/app/reading-list/_components/draggable-ordered-list/draggable-ordered-list.component.ts @@ -66,8 +66,10 @@ export class DraggableOrderedListComponent { * When enabled, draggability is disabled and a checkbox renders instead of order box or drag handle */ @Input() bulkMode: boolean = false; - @Input({required: true}) itemHeight: number = 60; @Input() trackByIdentity: TrackByFunction = (index: number, item: any) => `${item.id}_${item.order}_${item.title}`; + /** + * After an item is re-ordered, you MUST reload from backend the new data. This is because accessibility mode will use item.order which needs to be in sync. + */ @Output() orderUpdated: EventEmitter = new EventEmitter(); @Output() itemRemove: EventEmitter = new EventEmitter(); @ContentChild('draggableItem') itemTemplate!: TemplateRef; diff --git a/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.html b/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.html index 6c244be10..52af401e7 100644 --- a/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.html +++ b/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.html @@ -127,7 +127,7 @@ + [showRemoveButton]="false"> diff --git a/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.ts b/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.ts index 3df8049de..d63674a8b 100644 --- a/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.ts +++ b/UI/Web/src/app/reading-list/_components/reading-list-detail/reading-list-detail.component.ts @@ -46,7 +46,11 @@ import {Title} from "@angular/platform-browser"; styleUrls: ['./reading-list-detail.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [SideNavCompanionBarComponent, NgIf, CardActionablesComponent, ImageComponent, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem, ReadMoreComponent, BadgeExpanderComponent, PersonBadgeComponent, A11yClickDirective, LoadingComponent, DraggableOrderedListComponent, ReadingListItemComponent, NgClass, AsyncPipe, DecimalPipe, DatePipe, TranslocoDirective, MetadataDetailComponent] + imports: [SideNavCompanionBarComponent, NgIf, CardActionablesComponent, ImageComponent, NgbDropdown, + NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem, ReadMoreComponent, BadgeExpanderComponent, + PersonBadgeComponent, A11yClickDirective, LoadingComponent, DraggableOrderedListComponent, + ReadingListItemComponent, NgClass, AsyncPipe, DecimalPipe, DatePipe, TranslocoDirective, + MetadataDetailComponent] }) export class ReadingListDetailComponent implements OnInit { items: Array = []; @@ -56,11 +60,7 @@ export class ReadingListDetailComponent implements OnInit { isAdmin: boolean = false; isLoading: boolean = false; accessibilityMode: boolean = false; - - // Downloading hasDownloadingRole: boolean = false; - downloadInProgress: boolean = false; - readingListSummary: string = ''; libraryTypes: {[key: number]: LibraryType} = {}; @@ -131,7 +131,7 @@ export class ReadingListDetailComponent implements OnInit { this.cdRef.markForCheck(); this.readingListService.getListItems(this.listId).subscribe(items => { - this.items = items; + this.items = [...items]; this.isLoading = false; this.cdRef.markForCheck(); }); @@ -178,7 +178,9 @@ export class ReadingListDetailComponent implements OnInit { orderUpdated(event: IndexUpdateEvent) { if (!this.readingList) return; - this.readingListService.updatePosition(this.readingList.id, event.item.id, event.fromPosition, event.toPosition).subscribe(); + this.readingListService.updatePosition(this.readingList.id, event.item.id, event.fromPosition, event.toPosition).subscribe(() => { + this.getListItems(); + }); } itemRemoved(item: ReadingListItem, position: number) { diff --git a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.html b/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.html index 0db48a3c8..f6fb73443 100644 --- a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.html +++ b/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.html @@ -35,6 +35,7 @@
diff --git a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.scss b/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.scss index 8b1378917..158863aa7 100644 --- a/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.scss +++ b/UI/Web/src/app/sidenav/_components/customize-dashboard-modal/customize-dashboard-modal.component.scss @@ -1 +1,9 @@ +.modal-body { + overflow: hidden; + padding: 1rem 0 1rem 1rem; + .tab-content { + max-height: calc(var(--vh) * 100 - 235px); + overflow: auto; + } +} diff --git a/UI/Web/src/app/sidenav/_components/customize-dashboard-streams/customize-dashboard-streams.component.html b/UI/Web/src/app/sidenav/_components/customize-dashboard-streams/customize-dashboard-streams.component.html index 87689c238..f2edfb97c 100644 --- a/UI/Web/src/app/sidenav/_components/customize-dashboard-streams/customize-dashboard-streams.component.html +++ b/UI/Web/src/app/sidenav/_components/customize-dashboard-streams/customize-dashboard-streams.component.html @@ -1,6 +1,6 @@ + [showRemoveButton]="false"> diff --git a/UI/Web/src/app/sidenav/_components/customize-sidenav-streams/customize-sidenav-streams.component.html b/UI/Web/src/app/sidenav/_components/customize-sidenav-streams/customize-sidenav-streams.component.html index 26d189ea9..d16e3cb79 100644 --- a/UI/Web/src/app/sidenav/_components/customize-sidenav-streams/customize-sidenav-streams.component.html +++ b/UI/Web/src/app/sidenav/_components/customize-sidenav-streams/customize-sidenav-streams.component.html @@ -29,7 +29,6 @@ [showRemoveButton]="false" [disabled]="listForm.get('filterSideNavStream')!.value" [bulkMode]="pageOperationsForm.get('bulkMode')!.value" [virtualizeAfter]="virtualizeAfter" - [itemHeight]="60" > diff --git a/UI/Web/src/app/sidenav/_components/manage-external-sources/manage-external-sources.component.html b/UI/Web/src/app/sidenav/_components/manage-external-sources/manage-external-sources.component.html index 84fa1184a..68cb3e24e 100644 --- a/UI/Web/src/app/sidenav/_components/manage-external-sources/manage-external-sources.component.html +++ b/UI/Web/src/app/sidenav/_components/manage-external-sources/manage-external-sources.component.html @@ -1,7 +1,7 @@

{{t('description')}} - {{t('help-link')}} + {{t('help-link')}}

diff --git a/UI/Web/src/app/user-settings/user-preferences/user-preferences.component.ts b/UI/Web/src/app/user-settings/user-preferences/user-preferences.component.ts index 64bddbaae..ac42f6659 100644 --- a/UI/Web/src/app/user-settings/user-preferences/user-preferences.component.ts +++ b/UI/Web/src/app/user-settings/user-preferences/user-preferences.component.ts @@ -219,7 +219,7 @@ export class UserPreferencesComponent implements OnInit, OnDestroy { this.settingsForm.addControl('noTransitions', new FormControl(this.user.preferences.noTransitions, [])); this.settingsForm.addControl('collapseSeriesRelationships', new FormControl(this.user.preferences.collapseSeriesRelationships, [])); this.settingsForm.addControl('shareReviews', new FormControl(this.user.preferences.shareReviews, [])); - this.settingsForm.addControl('locale', new FormControl(this.user.preferences.locale, [])); + this.settingsForm.addControl('locale', new FormControl(this.user.preferences.locale || 'en', [])); if (this.locales.length === 1) { this.settingsForm.get('locale')?.disable(); diff --git a/UI/Web/src/assets/langs/en.json b/UI/Web/src/assets/langs/en.json index c053e3a5c..bd2933899 100644 --- a/UI/Web/src/assets/langs/en.json +++ b/UI/Web/src/assets/langs/en.json @@ -1753,7 +1753,8 @@ "dashboard": "Dashboard", "sidenav": "Side Nav", "external-sources": "External Sources", - "smart-filters": "Smart Filters" + "smart-filters": "Smart Filters", + "help": "{{common.help}}" }, "customize-dashboard-streams": { diff --git a/UI/Web/src/main.ts b/UI/Web/src/main.ts index 521f84533..a844c9b24 100644 --- a/UI/Web/src/main.ts +++ b/UI/Web/src/main.ts @@ -41,7 +41,7 @@ export function preloadUser(userService: AccountService, transloco: TranslocoSer // If no user or locale is available, fallback to the default language ('en') const localStorageLocale = localStorage.getItem(AccountService.localeKey) || 'en'; transloco.setActiveLang(localStorageLocale); - return transloco.load(localStorageLocale) + return transloco.load(localStorageLocale); })).subscribe(); }; } diff --git a/openapi.json b/openapi.json index f4f6b629e..cbeffc9d1 100644 --- a/openapi.json +++ b/openapi.json @@ -7,7 +7,7 @@ "name": "GPL-3.0", "url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE" }, - "version": "0.7.8.12" + "version": "0.7.8.13" }, "servers": [ {