mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Release Polishing (#2325)
Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
parent
2d3a0c1eda
commit
8e85ba069c
@ -54,7 +54,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||||
<PackageReference Include="CsvHelper" Version="30.0.1" />
|
|
||||||
<PackageReference Include="Docnet.Core" Version="2.6.0" />
|
<PackageReference Include="Docnet.Core" Version="2.6.0" />
|
||||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.1" />
|
<PackageReference Include="EasyCaching.InMemory" Version="1.9.1" />
|
||||||
<PackageReference Include="ExCSS" Version="4.2.2" />
|
<PackageReference Include="ExCSS" Version="4.2.2" />
|
||||||
|
@ -268,6 +268,7 @@ public class AccountController : BaseApiController
|
|||||||
dto.RefreshToken = await _tokenService.CreateRefreshToken(user);
|
dto.RefreshToken = await _tokenService.CreateRefreshToken(user);
|
||||||
dto.KavitaVersion = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion))
|
dto.KavitaVersion = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion))
|
||||||
.Value;
|
.Value;
|
||||||
|
dto.Preferences = _mapper.Map<UserPreferencesDto>(user.UserPreferences);
|
||||||
return Ok(dto);
|
return Ok(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
- 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
|
- Rich web readers supporting webtoon, continuous reading mode (continue without leaving the reader), virtual pages (epub), etc
|
||||||
- Full Localization Support
|
- 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
|
## Support
|
||||||
@ -110,10 +110,6 @@ Thank you to [Weblate](https://hosted.weblate.org/engage/kavita/) who hosts our
|
|||||||
<img src="https://hosted.weblate.org/widgets/kavita/-/horizontal-blue.svg" alt="Translation status" />
|
<img src="https://hosted.weblate.org/widgets/kavita/-/horizontal-blue.svg" alt="Translation status" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
## 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
|
## 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
|
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.
|
allows you to easily spin up a server. 20% of app revenues are contributed back to Kavita via OpenCollective.
|
||||||
|
@ -7,4 +7,4 @@ Security is maintained on latest stable version only.
|
|||||||
## Reporting a Vulnerability
|
## 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.
|
||||||
|
@ -43,8 +43,7 @@ export class ActionService implements OnDestroy {
|
|||||||
|
|
||||||
constructor(private libraryService: LibraryService, private seriesService: SeriesService,
|
constructor(private libraryService: LibraryService, private seriesService: SeriesService,
|
||||||
private readerService: ReaderService, private toastr: ToastrService, private modalService: NgbModal,
|
private readerService: ReaderService, private toastr: ToastrService, private modalService: NgbModal,
|
||||||
private confirmService: ConfirmService, private memberService: MemberService, private deviceService: DeviceService,
|
private confirmService: ConfirmService, private memberService: MemberService, private deviceService: DeviceService) { }
|
||||||
private translocoService: TranslocoService) { }
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.onDestroy.next();
|
this.onDestroy.next();
|
||||||
@ -66,7 +65,7 @@ export class ActionService implements OnDestroy {
|
|||||||
const force = false; // await this.promptIfForce();
|
const force = false; // await this.promptIfForce();
|
||||||
|
|
||||||
this.libraryService.scan(library.id, force).pipe(take(1)).subscribe((res: any) => {
|
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) {
|
if (callback) {
|
||||||
callback(library);
|
callback(library);
|
||||||
}
|
}
|
||||||
@ -95,7 +94,7 @@ export class ActionService implements OnDestroy {
|
|||||||
const forceUpdate = true; //await this.promptIfForce();
|
const forceUpdate = true; //await this.promptIfForce();
|
||||||
|
|
||||||
this.libraryService.refreshMetadata(library?.id, forceUpdate).pipe(take(1)).subscribe((res: any) => {
|
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) {
|
if (callback) {
|
||||||
callback(library);
|
callback(library);
|
||||||
}
|
}
|
||||||
@ -129,7 +128,7 @@ export class ActionService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.libraryService.analyze(library?.id).pipe(take(1)).subscribe((res: any) => {
|
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) {
|
if (callback) {
|
||||||
callback(library);
|
callback(library);
|
||||||
}
|
}
|
||||||
@ -144,7 +143,7 @@ export class ActionService implements OnDestroy {
|
|||||||
markSeriesAsRead(series: Series, callback?: SeriesActionCallback) {
|
markSeriesAsRead(series: Series, callback?: SeriesActionCallback) {
|
||||||
this.seriesService.markRead(series.id).pipe(take(1)).subscribe(res => {
|
this.seriesService.markRead(series.id).pipe(take(1)).subscribe(res => {
|
||||||
series.pagesRead = series.pages;
|
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) {
|
if (callback) {
|
||||||
callback(series);
|
callback(series);
|
||||||
}
|
}
|
||||||
@ -159,7 +158,7 @@ export class ActionService implements OnDestroy {
|
|||||||
markSeriesAsUnread(series: Series, callback?: SeriesActionCallback) {
|
markSeriesAsUnread(series: Series, callback?: SeriesActionCallback) {
|
||||||
this.seriesService.markUnread(series.id).pipe(take(1)).subscribe(res => {
|
this.seriesService.markUnread(series.id).pipe(take(1)).subscribe(res => {
|
||||||
series.pagesRead = 0;
|
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) {
|
if (callback) {
|
||||||
callback(series);
|
callback(series);
|
||||||
}
|
}
|
||||||
@ -173,7 +172,7 @@ export class ActionService implements OnDestroy {
|
|||||||
*/
|
*/
|
||||||
async scanSeries(series: Series, callback?: SeriesActionCallback) {
|
async scanSeries(series: Series, callback?: SeriesActionCallback) {
|
||||||
this.seriesService.scan(series.libraryId, series.id).pipe(take(1)).subscribe((res: any) => {
|
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) {
|
if (callback) {
|
||||||
callback(series);
|
callback(series);
|
||||||
}
|
}
|
||||||
@ -187,7 +186,7 @@ export class ActionService implements OnDestroy {
|
|||||||
*/
|
*/
|
||||||
analyzeFilesForSeries(series: Series, callback?: SeriesActionCallback) {
|
analyzeFilesForSeries(series: Series, callback?: SeriesActionCallback) {
|
||||||
this.seriesService.analyzeFiles(series.libraryId, series.id).pipe(take(1)).subscribe((res: any) => {
|
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) {
|
if (callback) {
|
||||||
callback(series);
|
callback(series);
|
||||||
}
|
}
|
||||||
@ -208,7 +207,7 @@ export class ActionService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.seriesService.refreshMetadata(series).pipe(take(1)).subscribe((res: any) => {
|
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) {
|
if (callback) {
|
||||||
callback(series);
|
callback(series);
|
||||||
}
|
}
|
||||||
@ -225,7 +224,7 @@ export class ActionService implements OnDestroy {
|
|||||||
this.readerService.markVolumeRead(seriesId, volume.id).pipe(take(1)).subscribe(() => {
|
this.readerService.markVolumeRead(seriesId, volume.id).pipe(take(1)).subscribe(() => {
|
||||||
volume.pagesRead = volume.pages;
|
volume.pagesRead = volume.pages;
|
||||||
volume.chapters?.forEach(c => c.pagesRead = c.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) {
|
if (callback) {
|
||||||
callback(volume);
|
callback(volume);
|
||||||
@ -243,7 +242,7 @@ export class ActionService implements OnDestroy {
|
|||||||
this.readerService.markVolumeUnread(seriesId, volume.id).subscribe(() => {
|
this.readerService.markVolumeUnread(seriesId, volume.id).subscribe(() => {
|
||||||
volume.pagesRead = 0;
|
volume.pagesRead = 0;
|
||||||
volume.chapters?.forEach(c => c.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) {
|
if (callback) {
|
||||||
callback(volume);
|
callback(volume);
|
||||||
}
|
}
|
||||||
@ -259,7 +258,7 @@ export class ActionService implements OnDestroy {
|
|||||||
markChapterAsRead(libraryId: number, seriesId: number, chapter: Chapter, callback?: ChapterActionCallback) {
|
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 => {
|
this.readerService.saveProgress(libraryId, seriesId, chapter.volumeId, chapter.id, chapter.pages).pipe(take(1)).subscribe(results => {
|
||||||
chapter.pagesRead = chapter.pages;
|
chapter.pagesRead = chapter.pages;
|
||||||
this.toastr.success(this.translocoService.translate('toasts.mark-read'));
|
this.toastr.success(translate('toasts.mark-read'));
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(chapter);
|
callback(chapter);
|
||||||
}
|
}
|
||||||
@ -275,7 +274,7 @@ export class ActionService implements OnDestroy {
|
|||||||
markChapterAsUnread(libraryId: number, seriesId: number, chapter: Chapter, callback?: ChapterActionCallback) {
|
markChapterAsUnread(libraryId: number, seriesId: number, chapter: Chapter, callback?: ChapterActionCallback) {
|
||||||
this.readerService.saveProgress(libraryId, seriesId, chapter.volumeId, chapter.id, 0).pipe(take(1)).subscribe(results => {
|
this.readerService.saveProgress(libraryId, seriesId, chapter.volumeId, chapter.id, 0).pipe(take(1)).subscribe(results => {
|
||||||
chapter.pagesRead = 0;
|
chapter.pagesRead = 0;
|
||||||
this.toastr.success(this.translocoService.translate('toasts.mark-unread'));
|
this.toastr.success(translate('toasts.mark-unread'));
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(chapter);
|
callback(chapter);
|
||||||
}
|
}
|
||||||
@ -296,7 +295,7 @@ export class ActionService implements OnDestroy {
|
|||||||
volume.chapters?.forEach(c => c.pagesRead = c.pages);
|
volume.chapters?.forEach(c => c.pagesRead = c.pages);
|
||||||
});
|
});
|
||||||
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) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
@ -317,7 +316,7 @@ export class ActionService implements OnDestroy {
|
|||||||
volume.chapters?.forEach(c => c.pagesRead = 0);
|
volume.chapters?.forEach(c => c.pagesRead = 0);
|
||||||
});
|
});
|
||||||
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) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
@ -335,7 +334,7 @@ export class ActionService implements OnDestroy {
|
|||||||
series.forEach(s => {
|
series.forEach(s => {
|
||||||
s.pagesRead = s.pages;
|
s.pagesRead = s.pages;
|
||||||
});
|
});
|
||||||
this.toastr.success(this.translocoService.translate('toasts.mark-read'));
|
this.toastr.success(translate('toasts.mark-read'));
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
@ -353,7 +352,7 @@ export class ActionService implements OnDestroy {
|
|||||||
series.forEach(s => {
|
series.forEach(s => {
|
||||||
s.pagesRead = s.pages;
|
s.pagesRead = s.pages;
|
||||||
});
|
});
|
||||||
this.toastr.success(this.translocoService.translate('toasts.mark-unread'));
|
this.toastr.success(translate('toasts.mark-unread'));
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
@ -396,7 +395,7 @@ export class ActionService implements OnDestroy {
|
|||||||
|
|
||||||
removeMultipleSeriesFromWantToReadList(seriesIds: Array<number>, callback?: VoidActionCallback) {
|
removeMultipleSeriesFromWantToReadList(seriesIds: Array<number>, callback?: VoidActionCallback) {
|
||||||
this.memberService.removeSeriesToWantToRead(seriesIds).subscribe(() => {
|
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) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -547,7 +546,7 @@ export class ActionService implements OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.seriesService.deleteMultipleSeries(seriesIds.map(s => s.id)).pipe(take(1)).subscribe(() => {
|
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) {
|
if (callback) {
|
||||||
callback(true);
|
callback(true);
|
||||||
@ -565,7 +564,7 @@ export class ActionService implements OnDestroy {
|
|||||||
|
|
||||||
this.seriesService.delete(series.id).subscribe((res: boolean) => {
|
this.seriesService.delete(series.id).subscribe((res: boolean) => {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
this.toastr.success(this.translocoService.translate('toasts.series-deleted'));
|
this.toastr.success(translate('toasts.series-deleted'));
|
||||||
callback(res);
|
callback(res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -573,7 +572,7 @@ export class ActionService implements OnDestroy {
|
|||||||
|
|
||||||
sendToDevice(chapterIds: Array<number>, device: Device, callback?: VoidActionCallback) {
|
sendToDevice(chapterIds: Array<number>, device: Device, callback?: VoidActionCallback) {
|
||||||
this.deviceService.sendTo(chapterIds, device.id).subscribe(() => {
|
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) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -582,7 +581,7 @@ export class ActionService implements OnDestroy {
|
|||||||
|
|
||||||
sendSeriesToDevice(seriesId: number, device: Device, callback?: VoidActionCallback) {
|
sendSeriesToDevice(seriesId: number, device: Device, callback?: VoidActionCallback) {
|
||||||
this.deviceService.sendSeriesTo(seriesId, device.id).subscribe(() => {
|
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) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
@ -12,3 +12,7 @@
|
|||||||
.muted {
|
.muted {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.read-more-link {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import {Component, DestroyRef, HostListener, inject, Inject, OnInit} from '@angular/core';
|
import {Component, DestroyRef, HostListener, inject, Inject, OnInit} from '@angular/core';
|
||||||
import { NavigationStart, Router, RouterOutlet } from '@angular/router';
|
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 { AccountService } from './_services/account.service';
|
||||||
import { LibraryService } from './_services/library.service';
|
import { LibraryService } from './_services/library.service';
|
||||||
import { NavService } from './_services/nav.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 { SideNavComponent } from './sidenav/_components/side-nav/side-nav.component';
|
||||||
import {NavHeaderComponent} from "./nav/_components/nav-header/nav-header.component";
|
import {NavHeaderComponent} from "./nav/_components/nav-header/nav-header.component";
|
||||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||||
import {translate, TranslocoService} from "@ngneat/transloco";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@ -26,7 +25,6 @@ export class AppComponent implements OnInit {
|
|||||||
transitionState$!: Observable<boolean>;
|
transitionState$!: Observable<boolean>;
|
||||||
|
|
||||||
destroyRef = inject(DestroyRef);
|
destroyRef = inject(DestroyRef);
|
||||||
translocoService = inject(TranslocoService);
|
|
||||||
|
|
||||||
constructor(private accountService: AccountService, public navService: NavService,
|
constructor(private accountService: AccountService, public navService: NavService,
|
||||||
private libraryService: LibraryService,
|
private libraryService: LibraryService,
|
||||||
|
@ -18,7 +18,7 @@ import { ReadingList } from 'src/app/_models/reading-list';
|
|||||||
import { CollectionTagService } from 'src/app/_services/collection-tag.service';
|
import { CollectionTagService } from 'src/app/_services/collection-tag.service';
|
||||||
import {CommonModule} from "@angular/common";
|
import {CommonModule} from "@angular/common";
|
||||||
import {FilterPipe} from "../../../pipe/filter.pipe";
|
import {FilterPipe} from "../../../pipe/filter.pipe";
|
||||||
import {TranslocoDirective, TranslocoService} from "@ngneat/transloco";
|
import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bulk-add-to-collection',
|
selector: 'app-bulk-add-to-collection',
|
||||||
@ -46,7 +46,6 @@ export class BulkAddToCollectionComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
collectionTitleTrackby = (index: number, item: CollectionTag) => `${item.title}`;
|
collectionTitleTrackby = (index: number, item: CollectionTag) => `${item.title}`;
|
||||||
|
|
||||||
translocoService = inject(TranslocoService);
|
|
||||||
|
|
||||||
@ViewChild('title') inputElem!: ElementRef<HTMLInputElement>;
|
@ViewChild('title') inputElem!: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
@ -83,7 +82,7 @@ export class BulkAddToCollectionComponent implements OnInit, AfterViewInit {
|
|||||||
create() {
|
create() {
|
||||||
const tagName = this.listForm.value.title;
|
const tagName = this.listForm.value.title;
|
||||||
this.collectionService.addByMultiple(0, this.seriesIds, tagName).subscribe(() => {
|
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();
|
this.modal.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -92,7 +91,7 @@ export class BulkAddToCollectionComponent implements OnInit, AfterViewInit {
|
|||||||
if (this.seriesIds.length === 0) return;
|
if (this.seriesIds.length === 0) return;
|
||||||
|
|
||||||
this.collectionService.addByMultiple(tag.id, this.seriesIds, '').subscribe(() => {
|
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();
|
this.modal.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ import { UploadService } from 'src/app/_services/upload.service';
|
|||||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||||
import {CommonModule} from "@angular/common";
|
import {CommonModule} from "@angular/common";
|
||||||
import {CoverImageChooserComponent} from "../../cover-image-chooser/cover-image-chooser.component";
|
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 {
|
enum TabID {
|
||||||
@ -65,7 +65,6 @@ export class EditCollectionTagsComponent implements OnInit {
|
|||||||
imageUrls: Array<string> = [];
|
imageUrls: Array<string> = [];
|
||||||
selectedCover: string = '';
|
selectedCover: string = '';
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
translocoService = inject(TranslocoService);
|
|
||||||
|
|
||||||
get hasSomeSelected() {
|
get hasSomeSelected() {
|
||||||
return this.selections != null && this.selections.hasSomeSelected();
|
return this.selections != null && this.selections.hasSomeSelected();
|
||||||
@ -172,7 +171,7 @@ export class EditCollectionTagsComponent implements OnInit {
|
|||||||
tag.id = this.tag.id;
|
tag.id = this.tag.id;
|
||||||
|
|
||||||
if (unselectedIds.length == this.series.length &&
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +186,7 @@ export class EditCollectionTagsComponent implements OnInit {
|
|||||||
|
|
||||||
forkJoin(apis).subscribe(() => {
|
forkJoin(apis).subscribe(() => {
|
||||||
this.modal.close({success: true, coverImageUpdated: selectedIndex > 0});
|
this.modal.close({success: true, coverImageUpdated: selectedIndex > 0});
|
||||||
this.toastr.success(this.translocoService.translate('toasts.collection-updated'));
|
this.toastr.success(translate('toasts.collection-updated'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ import {BytesPipe} from "../../pipe/bytes.pipe";
|
|||||||
import {BadgeExpanderComponent} from "../../shared/badge-expander/badge-expander.component";
|
import {BadgeExpanderComponent} from "../../shared/badge-expander/badge-expander.component";
|
||||||
import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component";
|
import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component";
|
||||||
import {PersonBadgeComponent} from "../../shared/person-badge/person-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";
|
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
|
||||||
|
|
||||||
enum TabID {
|
enum TabID {
|
||||||
@ -73,7 +73,6 @@ export class CardDetailDrawerComponent implements OnInit {
|
|||||||
@Input() libraryId: number = 0;
|
@Input() libraryId: number = 0;
|
||||||
@Input({required: true}) data!: Volume | Chapter;
|
@Input({required: true}) data!: Volume | Chapter;
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
private readonly translocoService = inject(TranslocoService);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -209,7 +208,7 @@ export class CardDetailDrawerComponent implements OnInit {
|
|||||||
|
|
||||||
resetCoverImage() {
|
resetCoverImage() {
|
||||||
this.uploadService.resetChapterCoverLock(this.chapter.id).subscribe(() => {
|
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) {
|
readChapter(chapter: Chapter, incognito: boolean = false) {
|
||||||
if (chapter.pages === 0) {
|
if (chapter.pages === 0) {
|
||||||
this.toastr.error(this.translocoService.translate('toasts.no-pages'));
|
this.toastr.error(translate('toasts.no-pages'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +272,7 @@ export class CardDetailDrawerComponent implements OnInit {
|
|||||||
|
|
||||||
download(chapter: Chapter) {
|
download(chapter: Chapter) {
|
||||||
if (this.downloadInProgress) {
|
if (this.downloadInProgress) {
|
||||||
this.toastr.info(this.translocoService.translate('toasts.download-in-progress'));
|
this.toastr.info(translate('toasts.download-in-progress'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import {ImageComponent} from "../../shared/image/image.component";
|
|||||||
import {DownloadIndicatorComponent} from "../download-indicator/download-indicator.component";
|
import {DownloadIndicatorComponent} from "../download-indicator/download-indicator.component";
|
||||||
import {EntityInfoCardsComponent} from "../entity-info-cards/entity-info-cards.component";
|
import {EntityInfoCardsComponent} from "../entity-info-cards/entity-info-cards.component";
|
||||||
import {NgbProgressbar, NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
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";
|
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -90,7 +90,6 @@ export class ListItemComponent implements OnInit {
|
|||||||
|
|
||||||
@Output() read: EventEmitter<void> = new EventEmitter<void>();
|
@Output() read: EventEmitter<void> = new EventEmitter<void>();
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
private readonly translocoService = inject(TranslocoService);
|
|
||||||
|
|
||||||
actionInProgress: boolean = false;
|
actionInProgress: boolean = false;
|
||||||
summary: string = '';
|
summary: string = '';
|
||||||
@ -136,7 +135,7 @@ export class ListItemComponent implements OnInit {
|
|||||||
performAction(action: ActionItem<any>) {
|
performAction(action: ActionItem<any>) {
|
||||||
if (action.action == Action.Download) {
|
if (action.action == Action.Download) {
|
||||||
if (this.downloadInProgress) {
|
if (this.downloadInProgress) {
|
||||||
this.toastr.info(this.translocoService.translate('toasts.download-in-progress'));
|
this.toastr.info(translate('toasts.download-in-progress'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import {CommonModule} from "@angular/common";
|
|||||||
import {CardItemComponent} from "../card-item/card-item.component";
|
import {CardItemComponent} from "../card-item/card-item.component";
|
||||||
import {RelationshipPipe} from "../../pipe/relationship.pipe";
|
import {RelationshipPipe} from "../../pipe/relationship.pipe";
|
||||||
import {Device} from "../../_models/device/device";
|
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";
|
import {SeriesPreviewDrawerComponent} from "../../_single-module/series-preview-drawer/series-preview-drawer.component";
|
||||||
|
|
||||||
function deepClone(obj: any): any {
|
function deepClone(obj: any): any {
|
||||||
@ -96,7 +96,6 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||||||
actions: ActionItem<Series>[] = [];
|
actions: ActionItem<Series>[] = [];
|
||||||
imageUrl: string = '';
|
imageUrl: string = '';
|
||||||
|
|
||||||
private readonly translocoService = inject(TranslocoService);
|
|
||||||
private readonly offcanvasService = inject(NgbOffcanvas);
|
private readonly offcanvasService = inject(NgbOffcanvas);
|
||||||
|
|
||||||
constructor(private router: Router, private cdRef: ChangeDetectorRef,
|
constructor(private router: Router, private cdRef: ChangeDetectorRef,
|
||||||
@ -206,7 +205,7 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
async scanLibrary(series: Series) {
|
async scanLibrary(series: Series) {
|
||||||
this.seriesService.scan(series.libraryId, series.id).subscribe((res: any) => {
|
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}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ import {NgbNav, NgbNavContent, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavOut
|
|||||||
import {
|
import {
|
||||||
SideNavCompanionBarComponent
|
SideNavCompanionBarComponent
|
||||||
} from '../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
|
} 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 {SeriesFilterV2} from "../_models/metadata/v2/series-filter-v2";
|
||||||
import {MetadataService} from "../_services/metadata.service";
|
import {MetadataService} from "../_services/metadata.service";
|
||||||
import {FilterComparison} from "../_models/metadata/v2/filter-comparison";
|
import {FilterComparison} from "../_models/metadata/v2/filter-comparison";
|
||||||
@ -68,11 +68,8 @@ export class LibraryDetailComponent implements OnInit {
|
|||||||
filterActive: boolean = false;
|
filterActive: boolean = false;
|
||||||
filterActiveCheck!: SeriesFilterV2;
|
filterActiveCheck!: SeriesFilterV2;
|
||||||
refresh: EventEmitter<void> = new EventEmitter();
|
refresh: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
jumpKeys: Array<JumpKey> = [];
|
jumpKeys: Array<JumpKey> = [];
|
||||||
|
|
||||||
translocoService = inject(TranslocoService);
|
|
||||||
|
|
||||||
tabs: Array<{title: string, fragment: string, icon: string}> = [
|
tabs: Array<{title: string, fragment: string, icon: string}> = [
|
||||||
{title: 'library-tab', fragment: '', icon: 'fa-landmark'},
|
{title: 'library-tab', fragment: '', icon: 'fa-landmark'},
|
||||||
{title: 'recommended-tab', fragment: 'recommended', icon: 'fa-award'},
|
{title: 'recommended-tab', fragment: 'recommended', icon: 'fa-award'},
|
||||||
|
@ -121,7 +121,10 @@ enum KeyDirection {
|
|||||||
])
|
])
|
||||||
],
|
],
|
||||||
standalone: true,
|
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 {
|
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();
|
private pageNumSubject: Subject<{pageNum: number, maxPages: number}> = new ReplaySubject();
|
||||||
pageNum$: Observable<{pageNum: number, maxPages: number}> = this.pageNumSubject.asObservable();
|
pageNum$: Observable<{pageNum: number, maxPages: number}> = this.pageNumSubject.asObservable();
|
||||||
|
|
||||||
private readonly translocoService = inject(TranslocoService);
|
|
||||||
|
|
||||||
getPageUrl = (pageNum: number, chapterId: number = this.chapterId) => {
|
getPageUrl = (pageNum: number, chapterId: number = this.chapterId) => {
|
||||||
if (this.bookmarkMode) return this.readerService.getBookmarkPageUrl(this.seriesId, this.user.apiKey, pageNum);
|
if (this.bookmarkMode) return this.readerService.getBookmarkPageUrl(this.seriesId, this.user.apiKey, pageNum);
|
||||||
return this.readerService.getPageUrl(chapterId, 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 => {
|
this.memberService.hasReadingProgress(this.libraryId).pipe(take(1)).subscribe(progress => {
|
||||||
if (!progress) {
|
if (!progress) {
|
||||||
this.toggleMenu();
|
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')?.setValue(LayoutMode.Single);
|
||||||
this.generalSettingsForm.get('layoutMode')?.disable();
|
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();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1223,7 +1224,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
loadNextChapter() {
|
loadNextChapter() {
|
||||||
if (this.nextPageDisabled || this.nextChapterDisabled || this.bookmarkMode) {
|
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.isLoading = false;
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
return;
|
return;
|
||||||
@ -1241,7 +1242,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
loadPrevChapter() {
|
loadPrevChapter() {
|
||||||
if (this.prevPageDisabled || this.prevChapterDisabled || this.bookmarkMode) {
|
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.isLoading = false;
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
return;
|
return;
|
||||||
@ -1608,7 +1609,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.incognitoMode = false;
|
this.incognitoMode = false;
|
||||||
const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId);
|
const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId);
|
||||||
window.history.replaceState({}, '', newRoute);
|
window.history.replaceState({}, '', newRoute);
|
||||||
this.toastr.info(this.translocoService.translate('toasts.incognito-off'));
|
this.toastr.info(translate('toasts.incognito-off'));
|
||||||
if (!this.bookmarkMode) {
|
if (!this.bookmarkMode) {
|
||||||
this.readerService.saveProgress(this.libraryId, this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
|
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: '↓', description: 'next-page'},
|
||||||
{key: 'G', description: 'go-to'},
|
{key: 'G', description: 'go-to'},
|
||||||
{key: 'B', description: 'bookmark'},
|
{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: 'ESC', description: 'close-reader'},
|
||||||
{key: 'SPACE', description: 'toggle-menu'},
|
{key: 'SPACE', description: 'toggle-menu'},
|
||||||
];
|
];
|
||||||
@ -1646,7 +1647,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
data.pageSplitOption = parseInt(modelSettings.pageSplitOption, 10);
|
data.pageSplitOption = parseInt(modelSettings.pageSplitOption, 10);
|
||||||
|
|
||||||
this.accountService.updatePreferences(data).subscribe(updatedPrefs => {
|
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) {
|
if (this.user) {
|
||||||
this.user.preferences = updatedPrefs;
|
this.user.preferences = updatedPrefs;
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
@ -21,7 +21,7 @@ import { SeriesService } from 'src/app/_services/series.service';
|
|||||||
import { ThemeService } from 'src/app/_services/theme.service';
|
import { ThemeService } from 'src/app/_services/theme.service';
|
||||||
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { NgIf, NgStyle, AsyncPipe } from '@angular/common';
|
import { NgIf, NgStyle, AsyncPipe } from '@angular/common';
|
||||||
import {TranslocoDirective, TranslocoService} from "@ngneat/transloco";
|
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-pdf-reader',
|
selector: 'app-pdf-reader',
|
||||||
@ -87,8 +87,6 @@ export class PdfReaderComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
bookMode: PageViewModeType = 'multiple';
|
bookMode: PageViewModeType = 'multiple';
|
||||||
|
|
||||||
private readonly translocoService = inject(TranslocoService);
|
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private router: Router, public accountService: AccountService,
|
constructor(private route: ActivatedRoute, private router: Router, public accountService: AccountService,
|
||||||
private seriesService: SeriesService, public readerService: ReaderService,
|
private seriesService: SeriesService, public readerService: ReaderService,
|
||||||
private navService: NavService, private toastr: ToastrService,
|
private navService: NavService, private toastr: ToastrService,
|
||||||
@ -178,7 +176,7 @@ export class PdfReaderComponent implements OnInit, OnDestroy {
|
|||||||
this.incognitoMode = false;
|
this.incognitoMode = false;
|
||||||
const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId);
|
const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId);
|
||||||
window.history.replaceState({}, '', newRoute);
|
window.history.replaceState({}, '', newRoute);
|
||||||
this.toastr.info(this.translocoService.translate('toasts.incognito-off'));
|
this.toastr.info(translate('toasts.incognito-off'));
|
||||||
this.saveProgress();
|
this.saveProgress();
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,6 @@
|
|||||||
|
|
||||||
<ng-container *ngIf="items.length > virtualizeAfter; else dragList">
|
<ng-container *ngIf="items.length > virtualizeAfter; else dragList">
|
||||||
<div class="example-list list-group-flush">
|
<div class="example-list list-group-flush">
|
||||||
<!-- <li-virtual-scroll [items]="items" [itemHeight]="itemHeight" [viewCache]="BufferAmount">-->
|
|
||||||
<!-- <div *liVirtualItem="let item; let i=index" class="d-flex list-container">-->
|
|
||||||
<!-- <ng-container [ngTemplateOutlet]="handle" [ngTemplateOutletContext]="{ $implicit: item, idx: i, isVirtualized: true }"></ng-container>-->
|
|
||||||
<!-- <ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item, idx: i }"></ng-container>-->
|
|
||||||
|
|
||||||
<!-- <ng-container [ngTemplateOutlet]="removeBtn" [ngTemplateOutletContext]="{$implicit: item, idx: i}"></ng-container>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
|
|
||||||
<!-- </li-virtual-scroll>-->
|
|
||||||
<virtual-scroller #scroll [items]="items" [bufferAmount]="BufferAmount" [parentScroll]="parentScroll">
|
<virtual-scroller #scroll [items]="items" [bufferAmount]="BufferAmount" [parentScroll]="parentScroll">
|
||||||
<div class="example-box" *ngFor="let item of scroll.viewPortItems; index as i; trackBy: trackByIdentity">
|
<div class="example-box" *ngFor="let item of scroll.viewPortItems; index as i; trackBy: trackByIdentity">
|
||||||
|
|
||||||
@ -46,7 +37,7 @@
|
|||||||
<ng-container *ngIf="accessibilityMode">
|
<ng-container *ngIf="accessibilityMode">
|
||||||
<label for="reorder-{{idx}}" class="form-label visually-hidden">{{t('reorder-label')}}</label>
|
<label for="reorder-{{idx}}" class="form-label visually-hidden">{{t('reorder-label')}}</label>
|
||||||
<input id="reorder-{{idx}}" class="form-control manual-input" type="number" inputmode="numeric" min="0"
|
<input id="reorder-{{idx}}" class="form-control manual-input" type="number" inputmode="numeric" min="0"
|
||||||
[max]="items.length - 1" [value]="idx"
|
[max]="items.length - 1" [value]="item.order"
|
||||||
(focusout)="updateIndex(idx, item)" (keydown.enter)="updateIndex(idx, item)" aria-describedby="instructions">
|
(focusout)="updateIndex(idx, item)" (keydown.enter)="updateIndex(idx, item)" aria-describedby="instructions">
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="bulkMode">
|
<ng-container *ngIf="bulkMode">
|
||||||
|
@ -66,8 +66,10 @@ export class DraggableOrderedListComponent {
|
|||||||
* When enabled, draggability is disabled and a checkbox renders instead of order box or drag handle
|
* When enabled, draggability is disabled and a checkbox renders instead of order box or drag handle
|
||||||
*/
|
*/
|
||||||
@Input() bulkMode: boolean = false;
|
@Input() bulkMode: boolean = false;
|
||||||
@Input({required: true}) itemHeight: number = 60;
|
|
||||||
@Input() trackByIdentity: TrackByFunction<any> = (index: number, item: any) => `${item.id}_${item.order}_${item.title}`;
|
@Input() trackByIdentity: TrackByFunction<any> = (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<IndexUpdateEvent> = new EventEmitter<IndexUpdateEvent>();
|
@Output() orderUpdated: EventEmitter<IndexUpdateEvent> = new EventEmitter<IndexUpdateEvent>();
|
||||||
@Output() itemRemove: EventEmitter<ItemRemoveEvent> = new EventEmitter<ItemRemoveEvent>();
|
@Output() itemRemove: EventEmitter<ItemRemoveEvent> = new EventEmitter<ItemRemoveEvent>();
|
||||||
@ContentChild('draggableItem') itemTemplate!: TemplateRef<any>;
|
@ContentChild('draggableItem') itemTemplate!: TemplateRef<any>;
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<app-draggable-ordered-list [items]="items" (orderUpdated)="orderUpdated($event)" [accessibilityMode]="accessibilityMode"
|
<app-draggable-ordered-list [items]="items" (orderUpdated)="orderUpdated($event)" [accessibilityMode]="accessibilityMode"
|
||||||
[showRemoveButton]="false" [itemHeight]="148" [virtualizeAfter]="10">
|
[showRemoveButton]="false">
|
||||||
<ng-template #draggableItem let-item let-position="idx">
|
<ng-template #draggableItem let-item let-position="idx">
|
||||||
<app-reading-list-item [ngClass]="{'content-container': items.length < 100, 'non-virtualized-container': items.length >= 100}" [item]="item" [position]="position" [libraryTypes]="libraryTypes"
|
<app-reading-list-item [ngClass]="{'content-container': items.length < 100, 'non-virtualized-container': items.length >= 100}" [item]="item" [position]="position" [libraryTypes]="libraryTypes"
|
||||||
[promoted]="item.promoted" (read)="readChapter($event)" (remove)="itemRemoved($event, position)"></app-reading-list-item>
|
[promoted]="item.promoted" (read)="readChapter($event)" (remove)="itemRemoved($event, position)"></app-reading-list-item>
|
||||||
|
@ -46,7 +46,11 @@ import {Title} from "@angular/platform-browser";
|
|||||||
styleUrls: ['./reading-list-detail.component.scss'],
|
styleUrls: ['./reading-list-detail.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
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 {
|
export class ReadingListDetailComponent implements OnInit {
|
||||||
items: Array<ReadingListItem> = [];
|
items: Array<ReadingListItem> = [];
|
||||||
@ -56,11 +60,7 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
isAdmin: boolean = false;
|
isAdmin: boolean = false;
|
||||||
isLoading: boolean = false;
|
isLoading: boolean = false;
|
||||||
accessibilityMode: boolean = false;
|
accessibilityMode: boolean = false;
|
||||||
|
|
||||||
// Downloading
|
|
||||||
hasDownloadingRole: boolean = false;
|
hasDownloadingRole: boolean = false;
|
||||||
downloadInProgress: boolean = false;
|
|
||||||
|
|
||||||
readingListSummary: string = '';
|
readingListSummary: string = '';
|
||||||
|
|
||||||
libraryTypes: {[key: number]: LibraryType} = {};
|
libraryTypes: {[key: number]: LibraryType} = {};
|
||||||
@ -131,7 +131,7 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
||||||
this.readingListService.getListItems(this.listId).subscribe(items => {
|
this.readingListService.getListItems(this.listId).subscribe(items => {
|
||||||
this.items = items;
|
this.items = [...items];
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
@ -178,7 +178,9 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
|
|
||||||
orderUpdated(event: IndexUpdateEvent) {
|
orderUpdated(event: IndexUpdateEvent) {
|
||||||
if (!this.readingList) return;
|
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) {
|
itemRemoved(item: ReadingListItem, position: number) {
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
<a class="btn btn-icon" href="https://wiki.kavitareader.com/en/guides/customization" target="_blank" rel="noopener noreferrer">{{t('help')}}</a>
|
||||||
<button type="button" class="btn btn-primary" (click)="close()">{{t('close')}}</button>
|
<button type="button" class="btn btn-primary" (click)="close()">{{t('close')}}</button>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -1 +1,9 @@
|
|||||||
|
.modal-body {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 1rem 0 1rem 1rem;
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
max-height: calc(var(--vh) * 100 - 235px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<ng-container *transloco="let t; read: 'customize-dashboard-streams'">
|
<ng-container *transloco="let t; read: 'customize-dashboard-streams'">
|
||||||
<app-draggable-ordered-list [items]="items" (orderUpdated)="orderUpdated($event)" [accessibilityMode]="accessibilityMode"
|
<app-draggable-ordered-list [items]="items" (orderUpdated)="orderUpdated($event)" [accessibilityMode]="accessibilityMode"
|
||||||
[showRemoveButton]="false" [itemHeight]="60">
|
[showRemoveButton]="false">
|
||||||
<ng-template #draggableItem let-position="idx" let-item>
|
<ng-template #draggableItem let-position="idx" let-item>
|
||||||
<app-dashboard-stream-list-item [item]="item" [position]="position" (hide)="updateVisibility($event, position)"></app-dashboard-stream-list-item>
|
<app-dashboard-stream-list-item [item]="item" [position]="position" (hide)="updateVisibility($event, position)"></app-dashboard-stream-list-item>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
[showRemoveButton]="false" [disabled]="listForm.get('filterSideNavStream')!.value"
|
[showRemoveButton]="false" [disabled]="listForm.get('filterSideNavStream')!.value"
|
||||||
[bulkMode]="pageOperationsForm.get('bulkMode')!.value"
|
[bulkMode]="pageOperationsForm.get('bulkMode')!.value"
|
||||||
[virtualizeAfter]="virtualizeAfter"
|
[virtualizeAfter]="virtualizeAfter"
|
||||||
[itemHeight]="60"
|
|
||||||
>
|
>
|
||||||
<ng-template #draggableItem let-position="idx" let-item>
|
<ng-template #draggableItem let-position="idx" let-item>
|
||||||
<app-sidenav-stream-list-item [item]="item" [position]="position" (hide)="updateVisibility($event, position)"></app-sidenav-stream-list-item>
|
<app-sidenav-stream-list-item [item]="item" [position]="position" (hide)="updateVisibility($event, position)"></app-sidenav-stream-list-item>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<ng-container *transloco="let t; read: 'manage-external-sources'">
|
<ng-container *transloco="let t; read: 'manage-external-sources'">
|
||||||
<p>
|
<p>
|
||||||
{{t('description')}}
|
{{t('description')}}
|
||||||
<a href="https://wiki.kavitareader.com/en/guides/customization/external-sources" target="_blank" rel="noopener noreferrer">{{t('help-link')}}</a>
|
<a href="https://wiki.kavitareader.com/en/guides/customization#external-source" target="_blank" rel="noopener noreferrer">{{t('help-link')}}</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
|
|||||||
this.settingsForm.addControl('noTransitions', new FormControl(this.user.preferences.noTransitions, []));
|
this.settingsForm.addControl('noTransitions', new FormControl(this.user.preferences.noTransitions, []));
|
||||||
this.settingsForm.addControl('collapseSeriesRelationships', new FormControl(this.user.preferences.collapseSeriesRelationships, []));
|
this.settingsForm.addControl('collapseSeriesRelationships', new FormControl(this.user.preferences.collapseSeriesRelationships, []));
|
||||||
this.settingsForm.addControl('shareReviews', new FormControl(this.user.preferences.shareReviews, []));
|
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) {
|
if (this.locales.length === 1) {
|
||||||
this.settingsForm.get('locale')?.disable();
|
this.settingsForm.get('locale')?.disable();
|
||||||
|
@ -1753,7 +1753,8 @@
|
|||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
"sidenav": "Side Nav",
|
"sidenav": "Side Nav",
|
||||||
"external-sources": "External Sources",
|
"external-sources": "External Sources",
|
||||||
"smart-filters": "Smart Filters"
|
"smart-filters": "Smart Filters",
|
||||||
|
"help": "{{common.help}}"
|
||||||
},
|
},
|
||||||
|
|
||||||
"customize-dashboard-streams": {
|
"customize-dashboard-streams": {
|
||||||
|
@ -41,7 +41,7 @@ export function preloadUser(userService: AccountService, transloco: TranslocoSer
|
|||||||
// If no user or locale is available, fallback to the default language ('en')
|
// If no user or locale is available, fallback to the default language ('en')
|
||||||
const localStorageLocale = localStorage.getItem(AccountService.localeKey) || 'en';
|
const localStorageLocale = localStorage.getItem(AccountService.localeKey) || 'en';
|
||||||
transloco.setActiveLang(localStorageLocale);
|
transloco.setActiveLang(localStorageLocale);
|
||||||
return transloco.load(localStorageLocale)
|
return transloco.load(localStorageLocale);
|
||||||
})).subscribe();
|
})).subscribe();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"name": "GPL-3.0",
|
"name": "GPL-3.0",
|
||||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||||
},
|
},
|
||||||
"version": "0.7.8.12"
|
"version": "0.7.8.13"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user