Bugfixes! (#2187)
* Updated readme to have progress bars on localization to help motivate users. * Fixed a bug where downloads could trigger on lazy loaded module boundaries. * Updated all packages to latest * Fixed a bug where remove from on deck would show on all series cards when it shouldn't have. * Fixed a bug where virtualized reading list page wasn't showing the correct order on the UI * Localization fixes from shakeout * Fixed fullscreen mode broken in nightly from localization. * Fixed a bug where duplicate series add events could show duplicate items in library detail page. * Translations update from Hosted Weblate (#2184) * Added translation using Weblate (Kannada) * Added translation using Weblate (Hindi) * Added translation using Weblate (German) * Added translation using Weblate (Russian) * Added translation using Weblate (Malay) * Translated using Weblate (Dutch) Currently translated at 20.8% (296 of 1423 strings) Translation: Kavita/ui Translate-URL: https://hosted.weblate.org/projects/kavita/ui/nl/ * Translated using Weblate (Portuguese) Currently translated at 5.2% (75 of 1423 strings) Translation: Kavita/ui Translate-URL: https://hosted.weblate.org/projects/kavita/ui/pt/ * Translated using Weblate (German) Currently translated at 98.7% (156 of 158 strings) Translation: Kavita/backend Translate-URL: https://hosted.weblate.org/projects/kavita/backend/de/ * Translated using Weblate (Japanese) Currently translated at 1.9% (28 of 1423 strings) Translation: Kavita/ui Translate-URL: https://hosted.weblate.org/projects/kavita/ui/ja/ * Translated using Weblate (Spanish) Currently translated at 1.8% (3 of 158 strings) Translation: Kavita/backend Translate-URL: https://hosted.weblate.org/projects/kavita/backend/es/ * Translated using Weblate (Hindi) Currently translated at 100.0% (158 of 158 strings) Translation: Kavita/backend Translate-URL: https://hosted.weblate.org/projects/kavita/backend/hi/ * Translated using Weblate (German) Currently translated at 7.3% (105 of 1423 strings) Translation: Kavita/ui Translate-URL: https://hosted.weblate.org/projects/kavita/ui/de/ * Deleted translation using Weblate (Undetermined) * Added translation using Weblate (Italian) --------- Co-authored-by: Shashank Pujari <shashankppujari@gmail.com> Co-authored-by: Andre <andruecha32@gmail.com> Co-authored-by: Hans Kalisvaart <hans.kalisvaart@gmail.com> Co-authored-by: Duarte Silva <smallflake@protonmail.com> Co-authored-by: Andre Smith <andrepsmithjr@gmail.com> Co-authored-by: ThePromidius <thepromidiusyt@gmail.com> Co-authored-by: majora2007 <kavitareader@gmail.com> Co-authored-by: Tomas Battistini <tomas.battistini@gmail.com> --------- Co-authored-by: Weblate (bot) <hosted@weblate.org> Co-authored-by: Shashank Pujari <shashankppujari@gmail.com> Co-authored-by: Andre <andruecha32@gmail.com> Co-authored-by: Hans Kalisvaart <hans.kalisvaart@gmail.com> Co-authored-by: Duarte Silva <smallflake@protonmail.com> Co-authored-by: Andre Smith <andrepsmithjr@gmail.com> Co-authored-by: ThePromidius <thepromidiusyt@gmail.com> Co-authored-by: majora2007 <kavitareader@gmail.com> Co-authored-by: Tomas Battistini <tomas.battistini@gmail.com>
This commit is contained in:
parent
e9f8ecfc27
commit
7358ba7220
30 changed files with 7838 additions and 771 deletions
|
|
@ -41,7 +41,7 @@ export class ErrorInterceptor implements HttpInterceptor {
|
|||
break;
|
||||
default:
|
||||
// Don't throw multiple Something unexpected went wrong
|
||||
const genericError = this.translocoService.translate('errors.generic');
|
||||
let genericError = translate('errors.generic');
|
||||
if (this.toastr.previousToastMessage !== 'Something unexpected went wrong.' && this.toastr.previousToastMessage !== genericError) {
|
||||
this.toast(genericError);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<ng-container *transloco="let t; read: 'book-reader'">
|
||||
<div class="container-flex {{darkMode ? 'dark-mode' : ''}} reader-container {{ColumnLayout}} {{WritingStyleClass}}" tabindex="0" #reader>
|
||||
<div class="container-flex {{darkMode ? 'dark-mode' : ''}} reader-container {{ColumnLayout}} {{WritingStyleClass}}" tabindex="0" #reader>
|
||||
<ng-container *transloco="let t; read: 'book-reader'">
|
||||
<div class="fixed-top" #stickyTop>
|
||||
<a class="visually-hidden-focusable focus-visible" href="javascript:void(0);" (click)="moveFocus()">{{t('skip-header')}}</a>
|
||||
<ng-container [ngTemplateOutlet]="actionBar"></ng-container>
|
||||
|
|
@ -141,8 +141,8 @@
|
|||
</div>
|
||||
</ng-container>
|
||||
<ng-template #showTitle>
|
||||
<span *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" [attr.aria-label]="t('incognito-mode-alt')">
|
||||
(<i class="fa fa-glasses" aria-hidden="true"></i><span class="visually-hidden">{{t('incognito-mode-label')}}</span>)</span>
|
||||
<span *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" [attr.aria-label]="t('incognito-mode-alt')">
|
||||
(<i class="fa fa-glasses" aria-hidden="true"></i><span class="visually-hidden">{{t('incognito-mode-label')}}</span>)</span>
|
||||
<span class="book-title-text ms-1" [ngbTooltip]="bookTitle">{{bookTitle}}</span>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
|
@ -154,6 +154,5 @@
|
|||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
@ViewChild('readingHtml', {static: false}) bookContentElemRef!: ElementRef<HTMLDivElement>;
|
||||
@ViewChild('readingSection', {static: false}) readingSectionElemRef!: ElementRef<HTMLDivElement>;
|
||||
@ViewChild('stickyTop', {static: false}) stickyTopElemRef!: ElementRef<HTMLDivElement>;
|
||||
@ViewChild('reader', {static: true}) reader!: ElementRef;
|
||||
@ViewChild('reader', {static: false}) reader!: ElementRef;
|
||||
|
||||
|
||||
get BookPageLayoutMode() {
|
||||
|
|
@ -855,7 +855,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
|
||||
promptForPage() {
|
||||
const question = 'There are ' + (this.maxPages - 1) + ' pages. What page do you want to go to?';
|
||||
const question = translate('book-reader.go-to-page-prompt', {totalPages: this.maxPages - 1});
|
||||
const goToPageNum = window.prompt(question, '');
|
||||
if (goToPageNum === null || goToPageNum.trim().length === 0) { return null; }
|
||||
return goToPageNum;
|
||||
|
|
@ -1597,6 +1597,4 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
refreshPersonalToC() {
|
||||
this.refreshPToC.emit();
|
||||
}
|
||||
|
||||
protected readonly undefined = undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,30 @@ import {RelationshipPipe} from "../../pipe/relationship.pipe";
|
|||
import {Device} from "../../_models/device/device";
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
|
||||
function deepClone(obj: any): any {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(item => deepClone(item));
|
||||
}
|
||||
|
||||
const clonedObj: any = {};
|
||||
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
||||
clonedObj[key] = deepClone(obj[key]);
|
||||
} else {
|
||||
clonedObj[key] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clonedObj;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-series-card',
|
||||
standalone: true,
|
||||
|
|
@ -85,11 +109,12 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
|
||||
ngOnChanges(changes: any) {
|
||||
if (this.data) {
|
||||
this.actions = this.actionFactoryService.getSeriesActions((action: ActionItem<Series>, series: Series) => this.handleSeriesActionCallback(action, series));
|
||||
this.actions = [...this.actionFactoryService.getSeriesActions((action: ActionItem<Series>, series: Series) => this.handleSeriesActionCallback(action, series))];
|
||||
if (this.isOnDeck) {
|
||||
const othersIndex = this.actions.findIndex(obj => obj.title === 'others');
|
||||
if (this.actions[othersIndex].children.findIndex(o => o.action === Action.RemoveFromOnDeck) < 0) {
|
||||
this.actions[othersIndex].children.push({
|
||||
const othersAction = deepClone(this.actions[othersIndex]) as ActionItem<Series>;
|
||||
if (othersAction.children.findIndex(o => o.action === Action.RemoveFromOnDeck) < 0) {
|
||||
othersAction.children.push({
|
||||
action: Action.RemoveFromOnDeck,
|
||||
title: 'remove-from-on-deck',
|
||||
callback: (action: ActionItem<Series>, series: Series) => this.handleSeriesActionCallback(action, series),
|
||||
|
|
@ -97,6 +122,7 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
requiresAdmin: false,
|
||||
children: [],
|
||||
});
|
||||
this.actions[othersIndex] = othersAction;
|
||||
}
|
||||
}
|
||||
this.cdRef.markForCheck();
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ export class LibraryDetailComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
this.seriesService.getSeries(seriesAdded.seriesId).subscribe(s => {
|
||||
if (this.series.filter(sObj => s.id === sObj.id).length > 0) return;
|
||||
this.series = [...this.series, s].sort((s1: Series, s2: Series) => {
|
||||
if (s1.sortName < s2.sortName) return -1;
|
||||
if (s1.sortName > s2.sortName) return 1;
|
||||
|
|
|
|||
|
|
@ -1497,7 +1497,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
// This is menu only code
|
||||
promptForPage() {
|
||||
const goToPageNum = window.prompt('There are ' + this.maxPages + ' pages. What page would you like to go to?', '');
|
||||
const question = translate('book-reader.go-to-page-prompt', {totalPages: this.maxPages});
|
||||
const goToPageNum = window.prompt(question, '');
|
||||
if (goToPageNum === null || goToPageNum.trim().length === 0) { return null; }
|
||||
return goToPageNum;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
<div class="d-flex list-container">
|
||||
<div class="me-3 align-middle">
|
||||
<div style="padding-top: 40px">
|
||||
<label for="reorder-{{i}}" class="form-label visually-hidden">{{t('reorder')}}</label>
|
||||
<input *ngIf="accessibilityMode" id="reorder-{{i}}" class="form-control" type="number" inputmode="numeric" min="0" [max]="items.length - 1" [value]="i" style="width: 60px"
|
||||
<label for="reorder-{{i}}" class="form-label visually-hidden">{{t('reorder-label')}}</label>
|
||||
<input *ngIf="accessibilityMode" id="reorder-{{i}}" class="form-control" type="number" inputmode="numeric" min="0" [max]="items.length - 1" [value]="item.order" style="width: 60px"
|
||||
(focusout)="updateIndex(i, item)" (keydown.enter)="updateIndex(i, item)" aria-describedby="instructions">
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,27 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import {DestroyRef, inject, Inject, Injectable} from '@angular/core';
|
||||
import { Series } from 'src/app/_models/series';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { ConfirmService } from '../confirm.service';
|
||||
import { Chapter } from 'src/app/_models/chapter';
|
||||
import { Volume } from 'src/app/_models/volume';
|
||||
import { asyncScheduler, BehaviorSubject, Observable, tap, finalize, of, filter } from 'rxjs';
|
||||
import {
|
||||
asyncScheduler,
|
||||
BehaviorSubject,
|
||||
Observable,
|
||||
tap,
|
||||
finalize,
|
||||
of,
|
||||
filter,
|
||||
} from 'rxjs';
|
||||
import { SAVER, Saver } from '../_providers/saver.provider';
|
||||
import { download, Download } from '../_models/download';
|
||||
import { PageBookmark } from 'src/app/_models/readers/page-bookmark';
|
||||
import { switchMap, takeWhile, throttleTime } from 'rxjs/operators';
|
||||
import {switchMap, take, takeWhile, throttleTime} from 'rxjs/operators';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { BytesPipe } from 'src/app/pipe/bytes.pipe';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
export const DEBOUNCE_TIME = 100;
|
||||
|
||||
|
|
@ -42,6 +51,7 @@ export type DownloadEntityType = 'volume' | 'chapter' | 'series' | 'bookmark' |
|
|||
*/
|
||||
export type DownloadEntity = Series | Volume | Chapter | PageBookmark[] | undefined;
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
|
|
@ -56,10 +66,12 @@ export class DownloadService {
|
|||
private downloadsSource: BehaviorSubject<DownloadEvent[]> = new BehaviorSubject<DownloadEvent[]>([]);
|
||||
public activeDownloads$ = this.downloadsSource.asObservable();
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
constructor(private httpClient: HttpClient, private confirmService: ConfirmService,
|
||||
@Inject(SAVER) private save: Saver, private accountService: AccountService) { }
|
||||
|
||||
|
||||
/**
|
||||
* Returns the entity subtitle (for the event widget) for a given entity
|
||||
* @param downloadEntityType
|
||||
|
|
@ -117,7 +129,7 @@ export class DownloadService {
|
|||
}
|
||||
|
||||
|
||||
this.accountService.currentUser$.pipe(switchMap(user => {
|
||||
this.accountService.currentUser$.pipe(take(1), switchMap(user => {
|
||||
if (user && user.preferences.promptForDownloadSize) {
|
||||
return sizeCheckCall;
|
||||
}
|
||||
|
|
@ -138,7 +150,8 @@ export class DownloadService {
|
|||
finalize(() => {
|
||||
if (callback) callback(undefined);
|
||||
}))
|
||||
})).subscribe(() => {});
|
||||
}), takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe(() => {});
|
||||
}
|
||||
|
||||
private downloadSeriesSize(seriesId: number) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export class DevicePlatformPipe implements PipeTransform {
|
|||
case DevicePlatform.Kindle: return 'Kindle';
|
||||
case DevicePlatform.Kobo: return 'Kobo';
|
||||
case DevicePlatform.PocketBook: return 'PocketBook';
|
||||
case DevicePlatform.Custom: return this.translocoService.translate('device.platform-pipe.custom');
|
||||
case DevicePlatform.Custom: return this.translocoService.translate('device-platform-pipe.custom');
|
||||
default: return value + '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue