* 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:
Joe Milazzo 2023-08-07 09:14:57 -05:00 committed by GitHub
parent e9f8ecfc27
commit 7358ba7220
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 7838 additions and 771 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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