Dashboard Customization Polish (#2295)
This commit is contained in:
parent
25e759d301
commit
25ffb2ffe1
42 changed files with 255 additions and 258 deletions
|
@ -120,7 +120,6 @@ export class AccountService {
|
|||
const user = response;
|
||||
if (user) {
|
||||
this.setCurrentUser(user);
|
||||
this.messageHub.createHubConnection(user, this.hasAdminRole(user));
|
||||
}
|
||||
}),
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
|
@ -150,7 +149,8 @@ export class AccountService {
|
|||
this.stopRefreshTokenTimer();
|
||||
|
||||
if (this.currentUser) {
|
||||
this.messageHub.createHubConnection(this.currentUser, this.hasAdminRole(this.currentUser));
|
||||
this.messageHub.stopHubConnection();
|
||||
this.messageHub.createHubConnection(this.currentUser);
|
||||
this.hasValidLicense().subscribe();
|
||||
this.startRefreshTokenTimer();
|
||||
}
|
||||
|
|
|
@ -114,8 +114,6 @@ export class MessageHubService {
|
|||
*/
|
||||
public onlineUsers$ = this.onlineUsersSource.asObservable();
|
||||
|
||||
isAdmin: boolean = false;
|
||||
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
|
@ -132,9 +130,7 @@ export class MessageHubService {
|
|||
return event.event === eventType;
|
||||
}
|
||||
|
||||
createHubConnection(user: User, isAdmin: boolean) {
|
||||
this.isAdmin = isAdmin;
|
||||
|
||||
createHubConnection(user: User) {
|
||||
this.hubConnection = new HubConnectionBuilder()
|
||||
.withUrl(this.hubUrl + 'messages', {
|
||||
accessTokenFactory: () => user.token
|
||||
|
@ -186,7 +182,6 @@ export class MessageHubService {
|
|||
});
|
||||
|
||||
this.hubConnection.on(EVENTS.DashboardUpdate, resp => {
|
||||
console.log('dashboard update event came in')
|
||||
this.messagesSource.next({
|
||||
event: EVENTS.DashboardUpdate,
|
||||
payload: resp.body as DashboardUpdateEvent
|
||||
|
|
|
@ -92,7 +92,7 @@ export class LicenseComponent implements OnInit {
|
|||
}
|
||||
|
||||
async deleteLicense() {
|
||||
if (!await this.confirmService.confirm(translate('k+-delete-key'))) {
|
||||
if (!await this.confirmService.confirm(translate('toasts.k+-delete-key'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, injec
|
|||
import {Title} from '@angular/platform-browser';
|
||||
import {Router, RouterLink} from '@angular/router';
|
||||
import {Observable, of, ReplaySubject, Subject, switchMap} from 'rxjs';
|
||||
import {map, shareReplay, take, tap, throttleTime} from 'rxjs/operators';
|
||||
import {debounceTime, map, shareReplay, take, tap, throttleTime} from 'rxjs/operators';
|
||||
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
||||
import {Library} from 'src/app/_models/library';
|
||||
import {RecentlyAddedItem} from 'src/app/_models/recently-added-item';
|
||||
|
@ -57,6 +57,7 @@ export class DashboardComponent implements OnInit {
|
|||
streams: Array<DashboardStream> = [];
|
||||
genre: Genre | undefined;
|
||||
refreshStreams$ = new Subject<void>();
|
||||
refreshStreamsFromDashboardUpdate$ = new Subject<void>();
|
||||
|
||||
|
||||
/**
|
||||
|
@ -80,6 +81,13 @@ export class DashboardComponent implements OnInit {
|
|||
|
||||
this.loadDashboard();
|
||||
|
||||
this.refreshStreamsFromDashboardUpdate$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(1000),
|
||||
tap(() => {
|
||||
console.log('Loading Dashboard')
|
||||
this.loadDashboard()
|
||||
}))
|
||||
.subscribe();
|
||||
|
||||
this.refreshStreams$.pipe(takeUntilDestroyed(this.destroyRef), throttleTime(10_000),
|
||||
tap(() => {
|
||||
this.loadDashboard()
|
||||
|
@ -87,29 +95,12 @@ export class DashboardComponent implements OnInit {
|
|||
.subscribe();
|
||||
|
||||
|
||||
// TODO: Solve how Websockets will work with these dyanamic streams
|
||||
this.messageHub.messages$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(res => {
|
||||
|
||||
if (res.event === EVENTS.DashboardUpdate) {
|
||||
console.log('dashboard update triggered')
|
||||
this.refreshStreams$.next();
|
||||
this.refreshStreamsFromDashboardUpdate$.next();
|
||||
} else if (res.event === EVENTS.SeriesAdded) {
|
||||
// const seriesAddedEvent = res.payload as SeriesAddedEvent;
|
||||
|
||||
// this.seriesService.getSeries(seriesAddedEvent.seriesId).subscribe(series => {
|
||||
// if (this.recentlyAddedSeries.filter(s => s.id === series.id).length > 0) return;
|
||||
// this.recentlyAddedSeries = [series, ...this.recentlyAddedSeries];
|
||||
// this.cdRef.markForCheck();
|
||||
// });
|
||||
this.refreshStreams$.next();
|
||||
} else if (res.event === EVENTS.SeriesRemoved) {
|
||||
//const seriesRemovedEvent = res.payload as SeriesRemovedEvent;
|
||||
|
||||
//
|
||||
// this.inProgress = this.inProgress.filter(item => item.id != seriesRemovedEvent.seriesId);
|
||||
// this.recentlyAddedSeries = this.recentlyAddedSeries.filter(item => item.id != seriesRemovedEvent.seriesId);
|
||||
// this.recentlyUpdatedSeries = this.recentlyUpdatedSeries.filter(item => item.seriesId != seriesRemovedEvent.seriesId);
|
||||
// this.cdRef.markForCheck();
|
||||
this.refreshStreams$.next();
|
||||
} else if (res.event === EVENTS.ScanSeries) {
|
||||
// We don't have events for when series are updated, but we do get events when a scan update occurs. Refresh recentlyAdded at that time.
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
<span class="visually-hidden">{{t('shortcuts-menu-alt')}}</span>
|
||||
</button>
|
||||
<button *ngIf="!bookmarkMode && hasBookmarkRights" class="btn btn-icon" role="checkbox" [attr.aria-checked]="CurrentPageBookmarked"
|
||||
title="{{CurrentPageBookmarked ? 'Unbookmark Page' : 'Bookmark Page'}}" (click)="bookmarkPage()">
|
||||
title="{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}" (click)="bookmarkPage()">
|
||||
<i class="{{CurrentPageBookmarked ? 'fa' : 'far'}} fa-bookmark" aria-hidden="true"></i>
|
||||
<span class="visually-hidden">{{CurrentPageBookmarked ? 'Unbookmark Page' : 'Bookmark Page'}}</span>
|
||||
<span class="visually-hidden">{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -737,7 +737,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
getPage(pageNum: number, chapterId: number = this.chapterId, forceNew: boolean = false) {
|
||||
|
||||
let img;
|
||||
if (this.bookmarkMode) img = this.cachedImages.find(img => this.readerService.imageUrlToPageNum(img.src) === pageNum);
|
||||
if (this.bookmarkMode) img = this.cachedImages.find(img => this.readerService.imageUrlToPageNum(img.src) === pageNum);
|
||||
else img = this.cachedImages.find(img => this.readerService.imageUrlToPageNum(img.src) === pageNum
|
||||
&& (this.readerService.imageUrlToChapterId(img.src) == chapterId || this.readerService.imageUrlToChapterId(img.src) === -1)
|
||||
);
|
||||
|
@ -1208,9 +1208,14 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
setCanvasImage() {
|
||||
if (this.cachedImages === undefined) return;
|
||||
this.canvasImage = this.getPage(this.pageNum, this.chapterId, this.layoutMode !== LayoutMode.Single);
|
||||
this.canvasImage.addEventListener('load', () => {
|
||||
if (!this.canvasImage.complete) {
|
||||
this.canvasImage.addEventListener('load', () => {
|
||||
this.currentImage.next(this.canvasImage);
|
||||
}, false);
|
||||
} else {
|
||||
this.currentImage.next(this.canvasImage);
|
||||
}, false);
|
||||
}
|
||||
|
||||
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
@ -1329,7 +1334,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
// const pages = this.cachedImages.map(img => [this.readerService.imageUrlToChapterId(img.src), this.readerService.imageUrlToPageNum(img.src)]);
|
||||
//const pages = this.cachedImages.map(img => [this.readerService.imageUrlToChapterId(img.src), this.readerService.imageUrlToPageNum(img.src)]);
|
||||
// console.log(this.pageNum, ' Prefetched pages: ', pages.map(p => {
|
||||
// if (this.pageNum === p[1]) return '[' + p + ']';
|
||||
// return '' + p
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<ng-template #mobileView>
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 col-10">
|
||||
<div class="col-md-4 col-10">
|
||||
<select class="form-select" formControlName="comparison">
|
||||
<option *ngFor="let opt of groupOptions" [value]="opt.value">{{opt.title}}</option>
|
||||
</select>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
}
|
||||
|
||||
|
||||
::ng-deep .ngb-dp-content, ::ng-deep .ngb-dp-header, ::ng-deep .dropdown-menu{
|
||||
::ng-deep .ngb-dp-content, ::ng-deep .ngb-dp-header{
|
||||
background: var(--bs-body-bg);
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
|
|
|
@ -1,22 +1,26 @@
|
|||
<ng-container *transloco="let t; read: 'metadata-filter'">
|
||||
<ng-container *ngIf="toggleService.toggleState$ | async as isOpen">
|
||||
<div *ngIf="utilityService.getActiveBreakpoint() >= Breakpoint.Tablet">
|
||||
<div #collapse="ngbCollapse" [ngbCollapse]="!isOpen" (ngbCollapseChange)="setToggle($event)">
|
||||
<ng-container [ngTemplateOutlet]="filterSection"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="utilityService.getActiveBreakpoint() < Breakpoint.Desktop">
|
||||
<app-drawer #commentDrawer="drawer" [isOpen]="isOpen" [options]="{topOffset: 56}" (drawerClosed)="toggleService.set(false)">
|
||||
<h5 header>
|
||||
{{t('filter-title')}}
|
||||
</h5>
|
||||
<div body class="drawer-body">
|
||||
<!-- TODO: BUG: Filter section is instantiated twice if this isn't ngIf'd -->
|
||||
<ng-container *ngIf="utilityService.getActiveBreakpoint() as activeBreakpoint">
|
||||
<div *ngIf="activeBreakpoint >= Breakpoint.Tablet; else mobileView">
|
||||
<div #collapse="ngbCollapse" [ngbCollapse]="!isOpen" (ngbCollapseChange)="setToggle($event)">
|
||||
<ng-container [ngTemplateOutlet]="filterSection"></ng-container>
|
||||
</div>
|
||||
</app-drawer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #mobileView>
|
||||
<div>
|
||||
<app-drawer #commentDrawer="drawer" [isOpen]="isOpen" [options]="{topOffset: 56}" (drawerClosed)="toggleService.set(false)" [width]="600">
|
||||
<h5 header>
|
||||
{{t('filter-title')}}
|
||||
</h5>
|
||||
<div body class="drawer-body">
|
||||
<!-- TODO: BUG: Filter section is instantiated twice if this isn't ngIf'd -->
|
||||
<ng-container [ngTemplateOutlet]="filterSection"></ng-container>
|
||||
</div>
|
||||
</app-drawer>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #filterSection>
|
||||
|
@ -30,13 +34,13 @@
|
|||
</div>
|
||||
<form [formGroup]="sortGroup" class="container-fluid">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 col-sm-2">
|
||||
<div class="col-md-2 col-sm-3">
|
||||
<div class="form-group pe-1">
|
||||
<label for="limit-to" class="form-label">{{t('limit-label')}}</label>
|
||||
<input id="limit-to" type="number" inputmode="numeric" class="form-control" formControlName="limitTo">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-10">
|
||||
<div class="col-md-3 col-sm-9">
|
||||
<label for="sort-options" class="form-label">{{t('sort-by-label')}}</label>
|
||||
<button class="btn btn-sm btn-secondary-outline" (click)="updateSortOrder()" style="height: 25px; padding-bottom: 0;" [disabled]="filterSettings.sortDisabled">
|
||||
<i class="fa fa-arrow-up" [title]="t('ascending-alt')" *ngIf="isAscendingSort; else descSort"></i>
|
||||
|
@ -48,17 +52,16 @@
|
|||
<option *ngFor="let field of allSortFields" [value]="field">{{field | sortField}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-10">
|
||||
<div class="col-md-4 col-sm-12" [ngClass]="{'mt-3': utilityService.getActiveBreakpoint() <= Breakpoint.Mobile}">
|
||||
<label for="filter-name" class="form-label">{{t('filter-name-label')}}</label>
|
||||
<input id="filter-name" type="text" class="form-control" formControlName="name">
|
||||
<!-- <select2 [data]="smartFilters"-->
|
||||
<!-- id="filter-name"-->
|
||||
<!-- formControlName="name"-->
|
||||
<!-- (update)="updateFilterValue($event)"-->
|
||||
<!-- (update)="loadSavedFilter($event)"-->
|
||||
<!-- (autoCreateItem)="createFilterValue($event)"-->
|
||||
<!-- [autoCreate]="true"-->
|
||||
<!-- [multiple]="false"-->
|
||||
<!-- [infiniteScroll]="false"-->
|
||||
<!-- [hideSelectedItems]="true"-->
|
||||
<!-- displaySearchStatus="always"-->
|
||||
<!-- [resettable]="true">-->
|
||||
<!-- </select2>-->
|
||||
</div>
|
||||
|
@ -73,11 +76,11 @@
|
|||
</ng-template>
|
||||
<ng-template #buttons>
|
||||
<!-- TODO: I might want to put a Clear button which blanks out the whole filter -->
|
||||
<div class="col-md-2 col-sm-6 mt-4 pt-2 d-flex justify-content-between">
|
||||
<div class="col-md-6 col-sm-6 mt-4 pt-2 d-flex justify-content-between">
|
||||
<button class="btn btn-secondary col-6 me-1" (click)="clear()"><i class="fa-solid fa-arrow-rotate-left me-1" aria-hidden="true"></i>{{t('reset')}}</button>
|
||||
<button class="btn btn-primary col-6" (click)="apply()"><i class="fa-solid fa-play me-1" aria-hidden="true"></i>{{t('apply')}}</button>
|
||||
</div>
|
||||
<div class="col-md-1 col-sm-6 mt-4 pt-2">
|
||||
<div class="col-md-2 col-sm-6 mt-4 pt-2">
|
||||
<button class="btn btn-primary col-12" (click)="save()" [disabled]="filterSettings.saveDisabled || !this.sortGroup.get('name')?.value">
|
||||
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||
{{t('save')}}
|
||||
|
|
|
@ -21,7 +21,7 @@ import {SeriesFilterV2} from '../_models/metadata/v2/series-filter-v2';
|
|||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {TypeaheadComponent} from '../typeahead/_components/typeahead.component';
|
||||
import {DrawerComponent} from '../shared/drawer/drawer.component';
|
||||
import {AsyncPipe, NgForOf, NgIf, NgTemplateOutlet} from '@angular/common';
|
||||
import {AsyncPipe, NgClass, NgForOf, NgIf, NgTemplateOutlet} from '@angular/common';
|
||||
import {translate, TranslocoModule} from "@ngneat/transloco";
|
||||
import {SortFieldPipe} from "../pipe/sort-field.pipe";
|
||||
import {MetadataBuilderComponent} from "./_components/metadata-builder/metadata-builder.component";
|
||||
|
@ -30,7 +30,13 @@ import {MetadataService} from "../_services/metadata.service";
|
|||
import {FilterUtilitiesService} from "../shared/_services/filter-utilities.service";
|
||||
import {FilterService} from "../_services/filter.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {Select2Module, Select2Option, Select2UpdateEvent} from "ng-select2-component";
|
||||
import {
|
||||
Select2AutoCreateEvent,
|
||||
Select2Module,
|
||||
Select2Option,
|
||||
Select2UpdateEvent,
|
||||
Select2UpdateValue
|
||||
} from "ng-select2-component";
|
||||
import {SmartFilter} from "../_models/metadata/v2/smart-filter";
|
||||
|
||||
@Component({
|
||||
|
@ -40,7 +46,7 @@ import {SmartFilter} from "../_models/metadata/v2/smart-filter";
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [NgIf, NgbCollapse, NgTemplateOutlet, DrawerComponent, NgbTooltip, TypeaheadComponent,
|
||||
ReactiveFormsModule, FormsModule, NgbRating, AsyncPipe, TranslocoModule, SortFieldPipe, MetadataBuilderComponent, NgForOf, Select2Module]
|
||||
ReactiveFormsModule, FormsModule, NgbRating, AsyncPipe, TranslocoModule, SortFieldPipe, MetadataBuilderComponent, NgForOf, Select2Module, NgClass]
|
||||
})
|
||||
export class MetadataFilterComponent implements OnInit {
|
||||
|
||||
|
@ -61,6 +67,7 @@ export class MetadataFilterComponent implements OnInit {
|
|||
@ContentChild('[ngbCollapse]') collapse!: NgbCollapse;
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
public readonly utilityService = inject(UtilityService);
|
||||
public readonly filterUtilitiesService = inject(FilterUtilitiesService);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -114,8 +121,36 @@ export class MetadataFilterComponent implements OnInit {
|
|||
this.loadFromPresetsAndSetup();
|
||||
}
|
||||
|
||||
updateFilterValue(event: Select2UpdateEvent<any>) {
|
||||
console.log('event: ', event);
|
||||
loadSavedFilter(event: Select2UpdateEvent<any>) {
|
||||
// Load the filter from the backend and update the screen
|
||||
if (event.value === undefined || typeof(event.value) === 'string') return;
|
||||
const smartFilter = event.value as SmartFilter;
|
||||
this.filterV2 = this.filterUtilitiesService.decodeSeriesFilter(smartFilter.filter);
|
||||
this.cdRef.markForCheck();
|
||||
console.log('update event: ', event);
|
||||
}
|
||||
|
||||
createFilterValue(event: Select2AutoCreateEvent<any>) {
|
||||
// Create a new name and filter
|
||||
if (!this.filterV2) return;
|
||||
this.filterV2.name = event.value;
|
||||
this.filterService.saveFilter(this.filterV2).subscribe(() => {
|
||||
|
||||
const item = {
|
||||
value: {
|
||||
filter: this.filterUtilitiesService.encodeSeriesFilter(this.filterV2!),
|
||||
name: event.value,
|
||||
} as SmartFilter,
|
||||
label: event.value
|
||||
};
|
||||
this.smartFilters.push(item);
|
||||
this.sortGroup.get('name')?.setValue(item);
|
||||
this.cdRef.markForCheck();
|
||||
this.toastr.success(translate('toasts.smart-filter-updated'));
|
||||
this.apply();
|
||||
});
|
||||
|
||||
console.log('create event: ', event);
|
||||
}
|
||||
|
||||
|
||||
|
@ -204,7 +239,7 @@ export class MetadataFilterComponent implements OnInit {
|
|||
|
||||
apply() {
|
||||
this.applyFilter.emit({isFirst: this.updateApplied === 0, filterV2: this.filterV2!});
|
||||
|
||||
|
||||
if (this.utilityService.getActiveBreakpoint() === Breakpoint.Mobile && this.updateApplied !== 0) {
|
||||
this.toggleSelected();
|
||||
}
|
||||
|
@ -219,7 +254,7 @@ export class MetadataFilterComponent implements OnInit {
|
|||
this.filterService.saveFilter(this.filterV2).subscribe(() => {
|
||||
this.toastr.success(translate('toasts.smart-filter-updated'));
|
||||
this.apply();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
toggleSelected() {
|
||||
|
@ -231,5 +266,5 @@ export class MetadataFilterComponent implements OnInit {
|
|||
this.toggleService.set(!this.filteringCollapsed);
|
||||
}
|
||||
|
||||
protected readonly Breakpoint = Breakpoint;
|
||||
protected readonly Breakpoint = Breakpoint;
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ export class ProviderNamePipe implements PipeTransform {
|
|||
return 'MAL';
|
||||
case ScrobbleProvider.Kavita:
|
||||
return 'Kavita';
|
||||
case ScrobbleProvider.GoogleBooks:
|
||||
return 'Google Books';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export class DrawerOptions {
|
|||
@Component({
|
||||
selector: 'app-drawer',
|
||||
standalone: true,
|
||||
imports: [CommonModule, TranslocoDirective],
|
||||
imports: [CommonModule, TranslocoDirective],
|
||||
templateUrl: './drawer.component.html',
|
||||
styleUrls: ['./drawer.component.scss'],
|
||||
exportAs: "drawer",
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
{{filter.name}}
|
||||
<button class="btn btn-icon" (click)="addFilterToStream(filter)">
|
||||
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||
Add
|
||||
{{t('add')}}
|
||||
</button>
|
||||
</li>
|
||||
<li class="list-group-item" *ngIf="smartFilters.length === 0">
|
||||
All Smart filters added to Dashboard or none created yet.
|
||||
{{t('no-data')}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,6 @@ export class CustomizeDashboardModalComponent {
|
|||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
|
||||
constructor(public modal: NgbActiveModal) {
|
||||
|
||||
forkJoin([this.dashboardService.getDashboardStreams(false), this.filterService.getAllFilters()]).subscribe(results => {
|
||||
this.items = results[0];
|
||||
const smartFilterStreams = new Set(results[0].filter(d => !d.isProvided).map(d => d.name));
|
||||
|
|
|
@ -792,7 +792,7 @@
|
|||
"allow-scrobbling-label": "Allow Scrobbling",
|
||||
"allow-scrobbling-tooltip": "Should Kavita scrobble reading events, want to read status, ratings, and reviews to configured providers. This will only occur if the server has an active Kavita+ Subscription.",
|
||||
"folder-watching-label": "Folder Watching",
|
||||
"folder-watching-tooltip": "Override Server folder watching for this library. If off, folder watching won't run on the folders this library contains. If libraries share folders, then folders may still be ran against.",
|
||||
"folder-watching-tooltip": "Override Server folder watching for this library. If off, folder watching won't run on the folders this library contains. If libraries share folders, then folders may still be ran against. Will always wait 10 minutes before triggering scan.",
|
||||
"include-in-dashboard-label": "Include in Dashboard",
|
||||
"include-in-dashboard-tooltip": "Should series from the library be included on the Dashboard. This affects all streams, like On Deck, Recently Updated, Recently Added, or any custom additions.",
|
||||
"include-in-recommendation-label": "Include in Recommended",
|
||||
|
@ -1493,6 +1493,8 @@
|
|||
"swipe-enabled-label": "Swipe Enabled",
|
||||
"enable-comic-book-label": "Emulate comic book",
|
||||
"brightness-label": "Brightness",
|
||||
"bookmark-page-tooltip": "Bookmark Page",
|
||||
"unbookmark-page-tooltip": "Unbookmark Page",
|
||||
|
||||
"first-time-reading-manga": "Tap the image at any time to open the menu. You can configure different settings or go to page by clicking progress bar. Tap sides of image move to next/prev page.",
|
||||
"layout-mode-switched": "Layout mode switched to Single due to insufficient space to render double layout",
|
||||
|
@ -1555,7 +1557,7 @@
|
|||
"last-chapter-added": "Item Added",
|
||||
"time-to-read": "Time to Read",
|
||||
"release-year": "Release Year",
|
||||
"read-progress": "Read Progress"
|
||||
"read-progress": "Last Read"
|
||||
},
|
||||
|
||||
"edit-series-modal": {
|
||||
|
@ -1728,8 +1730,10 @@
|
|||
|
||||
"customize-dashboard-modal": {
|
||||
"title": "Customize Dashboard",
|
||||
"no-data": "All Smart filters added to Dashboard or none created yet.",
|
||||
"close": "{{common.close}}",
|
||||
"save": "{{common.save}}"
|
||||
"save": "{{common.save}}",
|
||||
"add": "{{common.add}}"
|
||||
},
|
||||
|
||||
"filter-field-pipe": {
|
||||
|
|
|
@ -5,6 +5,6 @@ export const environment = {
|
|||
production: true,
|
||||
apiUrl: `${BASE_URL}api/`,
|
||||
hubUrl:`${BASE_URL}hubs/`,
|
||||
buyLink: 'https://buy.stripe.com/3cs7uw67p2Re7JK4gj?prefilled_promo_code=FREETRIAL',
|
||||
buyLink: 'https://buy.stripe.com/00gcOQanFajG0hi5ko?prefilled_promo_code=FREETRIAL',
|
||||
manageLink: 'https://billing.stripe.com/p/login/28oaFRa3HdHWb5ecMM'
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue