mirror of
https://github.com/Kareadita/Kavita.git
synced 2026-06-04 22:05:21 -04:00
Release Polish (#4648)
This commit is contained in:
@@ -124,4 +124,6 @@ export enum Action {
|
||||
* A special action to just navigate somewhere
|
||||
*/
|
||||
Navigate = 38,
|
||||
AddToDashboard = 39,
|
||||
AddToSideNav = 40,
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ export enum WikiLink {
|
||||
Scanner = 'https://wiki.kavitareader.com/guides/scanner',
|
||||
ScannerExclude = 'https://wiki.kavitareader.com/guides/admin-settings/libraries#exclude-patterns',
|
||||
Library = 'https://wiki.kavitareader.com/guides/admin-settings/libraries',
|
||||
UpdateNative = 'https://wiki.kavitareader.com/guides/updating/updating-native',
|
||||
UpdateDocker = 'https://wiki.kavitareader.com/guides/updating/updating-docker',
|
||||
UpdateNative = 'https://wiki.kavitareader.com/guides/installation/native/',
|
||||
UpdateDocker = 'https://wiki.kavitareader.com/guides/installation/docker/',
|
||||
OpdsClients = 'https://wiki.kavitareader.com/guides/features/opds/#opds-capable-clients',
|
||||
Guides = 'https://wiki.kavitareader.com/guides',
|
||||
ReadingProfiles = "https://wiki.kavitareader.com/guides/user-settings/reading-profiles/",
|
||||
|
||||
@@ -1211,6 +1211,40 @@ export class ActionFactoryService {
|
||||
];
|
||||
|
||||
this.smartFilterActions = [
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'add-to',
|
||||
description: '',
|
||||
|
||||
callback: this.dummyCallback,
|
||||
shouldRender: this.dummyShouldRender,
|
||||
|
||||
requiredRoles: [],
|
||||
children: [
|
||||
{
|
||||
action: Action.AddToDashboard,
|
||||
title: 'add-to-dashboard',
|
||||
description: 'add-to-dashboard-tooltip',
|
||||
|
||||
callback: this.dummyCallback,
|
||||
shouldRender: this.dummyShouldRender,
|
||||
|
||||
requiredRoles: [],
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
action: Action.AddToSideNav,
|
||||
title: 'add-to-side-nav',
|
||||
description: 'add-to-side-nav-tooltip',
|
||||
|
||||
callback: this.dummyCallback,
|
||||
shouldRender: this.dummyShouldRender,
|
||||
|
||||
requiredRoles: [],
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
action: Action.Edit,
|
||||
title: 'rename',
|
||||
|
||||
@@ -63,6 +63,7 @@ import {ModalResult} from "../_models/modal/modal-result";
|
||||
import {addToModal, editModal} from "../_models/modal/modal-options";
|
||||
import {ModalService, TypedModalRef} from "./modal.service";
|
||||
import {FilterService} from "src/app/_services/filter.service";
|
||||
import {DashboardService} from "./dashboard.service";
|
||||
|
||||
|
||||
export type LibraryActionCallback = (library: Partial<Library>) => void;
|
||||
@@ -101,6 +102,7 @@ export class ActionService {
|
||||
private readonly annotationsService = inject(AnnotationService);
|
||||
private readonly sideNavService = inject(NavService);
|
||||
private readonly filterService = inject(FilterService);
|
||||
private readonly dashboardService = inject(DashboardService);
|
||||
|
||||
private readingListModalRef: TypedModalRef<BulkSetReadingProfileModalComponent> | TypedModalRef<ListSelectModalComponent<ReadingList>> | null = null;
|
||||
private collectionModalRef: TypedModalRef<ListSelectModalComponent<UserCollection>> | null = null;
|
||||
@@ -821,6 +823,14 @@ export class ActionService {
|
||||
*/
|
||||
handleSmartFilterAction(action: ActionItem<SmartFilter>, smartFilter: SmartFilter, allFilters: SmartFilter[]) {
|
||||
switch (action.action) {
|
||||
case Action.AddToDashboard:
|
||||
return this.dashboardService.createDashboardStream(smartFilter.id).pipe(
|
||||
map(() => this.fromAction(action, smartFilter, 'none'))
|
||||
);
|
||||
case Action.AddToSideNav:
|
||||
return this.sideNavService.createSideNavStream(smartFilter.id).pipe(
|
||||
map(() => this.fromAction(action, smartFilter, 'none'))
|
||||
);
|
||||
case Action.Edit:
|
||||
const ref = this.modalService.open(EditSmartFilterModalComponent, editModal());
|
||||
ref.componentInstance.smartFilter = smartFilter;
|
||||
|
||||
@@ -302,7 +302,20 @@ export class DashboardComponent {
|
||||
}
|
||||
|
||||
async handleFilterSectionClick(stream: DashboardStream) {
|
||||
await this.router.navigateByUrl('all-series?' + stream.smartFilterEncoded);
|
||||
switch (stream.entityType) {
|
||||
case FilterEntityType.Series:
|
||||
await this.router.navigateByUrl('all-series?' + stream.smartFilterEncoded);
|
||||
break;
|
||||
case FilterEntityType.ReadingList:
|
||||
await this.router.navigateByUrl('lists?' + stream.smartFilterEncoded);
|
||||
break;
|
||||
case FilterEntityType.Annotation:
|
||||
await this.router.navigateByUrl('browse/annotations?' + stream.smartFilterEncoded);
|
||||
break;
|
||||
case FilterEntityType.Person:
|
||||
await this.router.navigateByUrl('browse/people?' + stream.smartFilterEncoded);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: See if we can put this into the carousel and have a custom tokens (not in the original list) to forward to a callback handler
|
||||
|
||||
@@ -51,10 +51,10 @@
|
||||
@let normalizedSearchTerm = searchTerm.toLowerCase().trim();
|
||||
|
||||
<span class="ms-1">
|
||||
@if (item.name.toLowerCase().trim().indexOf(normalizedSearchTerm) >= 0) {
|
||||
{{item.name}}
|
||||
@if (item.localizedName.toLowerCase().trim().indexOf(normalizedSearchTerm) >= 0) {
|
||||
{{item.localizedName}}
|
||||
} @else {
|
||||
<span [innerHTML]="item.localizedName"></span>
|
||||
{{item.name}}
|
||||
}
|
||||
</span>
|
||||
<div class="text-light fst-italic" style="font-size: 0.8rem;">in {{item.libraryName}}</div>
|
||||
|
||||
+47
-4
@@ -4,7 +4,7 @@ import {
|
||||
computed,
|
||||
DestroyRef,
|
||||
inject,
|
||||
input,
|
||||
input, OnInit,
|
||||
signal,
|
||||
TemplateRef,
|
||||
viewChild
|
||||
@@ -17,7 +17,7 @@ import {APP_BASE_HREF, AsyncPipe} from "@angular/common";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {CarouselReelComponent} from "../../../carousel/_components/carousel-reel/carousel-reel.component";
|
||||
import {SeriesCardComponent} from "../../../cards/series-card/series-card.component";
|
||||
import {Observable, switchMap, tap} from "rxjs";
|
||||
import {filter, Observable, switchMap, tap} from "rxjs";
|
||||
import {SeriesService} from "../../../_services/series.service";
|
||||
import {QueryContext} from "../../../_models/metadata/v2/query-context";
|
||||
import {map, shareReplay} from "rxjs/operators";
|
||||
@@ -34,6 +34,12 @@ import {CardConfigFactory} from "../../../_services/card-config-factory.service"
|
||||
import {EntityCardComponent} from "../../../cards/entity-card/entity-card.component";
|
||||
import {PromotedIconComponent} from "../../../shared/_components/promoted-icon/promoted-icon.component";
|
||||
import {CardEntity, CardEntityFactory} from "../../../_models/card/card-entity";
|
||||
import {Action} from "../../../_models/actionables/action";
|
||||
import {User} from "../../../_models/user/user";
|
||||
import {ActionItem} from "../../../_models/actionables/action-item";
|
||||
import {EVENTS, MessageHubService} from "../../../_services/message-hub.service";
|
||||
import {DashboardService} from "../../../_services/dashboard.service";
|
||||
import {NavService} from "../../../_services/nav.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-smart-filters',
|
||||
@@ -42,7 +48,7 @@ import {CardEntity, CardEntityFactory} from "../../../_models/card/card-entity";
|
||||
styleUrls: ['./manage-smart-filters.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ManageSmartFiltersComponent {
|
||||
export class ManageSmartFiltersComponent implements OnInit {
|
||||
|
||||
private readonly filterService = inject(FilterService);
|
||||
private readonly filterUtilityService = inject(FilterUtilitiesService);
|
||||
@@ -53,6 +59,9 @@ export class ManageSmartFiltersComponent {
|
||||
private readonly actionFactoryService = inject(ActionFactoryService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly cardConfigFactory = inject(CardConfigFactory);
|
||||
private readonly messageHub = inject(MessageHubService);
|
||||
private readonly dashboardService = inject(DashboardService);
|
||||
private readonly sideNavService = inject(NavService);
|
||||
protected readonly baseUrl = inject(APP_BASE_HREF);
|
||||
|
||||
target = input<'_self' | '_blank'>('_blank');
|
||||
@@ -77,9 +86,11 @@ export class ManageSmartFiltersComponent {
|
||||
'entityType': new FormControl<FilterEntityType>(FilterEntityType.Series, []),
|
||||
});
|
||||
protected readonly filterApiMap = signal<{ [key: number]: Observable<any> }>({});
|
||||
protected readonly actions = computed(() => this.actionFactoryService.getSmartFilterActions(this.filters()));
|
||||
protected readonly actions = computed(() => this.actionFactoryService.getSmartFilterActions(this.filters(), this.shouldRenderFunc.bind(this)));
|
||||
protected readonly filterQuery = signal<string>('');
|
||||
protected readonly filterEntityType = signal<FilterEntityType>(FilterEntityType.Series);
|
||||
private readonly dashboardFilters = signal<Set<number>>(new Set<number>());
|
||||
private readonly sideNavFilters = signal<Set<number>>(new Set<number>());
|
||||
|
||||
protected titleTemplateRef = viewChild<TemplateRef<{ $implicit: CardEntity }>>('title');
|
||||
protected readonly readingListConfig = computed(() => this.cardConfigFactory.forReadingList({titleRef: this.titleTemplateRef(), overrides: {allowSelection: false, actionableFunc: () => []}}));
|
||||
@@ -96,12 +107,44 @@ export class ManageSmartFiltersComponent {
|
||||
tap(val => this.filterEntityType.set(parseInt(val + '', 10)))
|
||||
).subscribe();
|
||||
|
||||
this.messageHub.messages$.pipe(
|
||||
tap(msg => {
|
||||
if (msg.event === EVENTS.SideNavUpdate) {
|
||||
this.sideNavService.getSideNavStreams(true).subscribe(streams => {
|
||||
this.sideNavFilters.set(new Set(streams.map(stream => stream.id)));
|
||||
});
|
||||
} else if (msg.event === EVENTS.DashboardUpdate) {
|
||||
this.dashboardService.getDashboardStreams(true).subscribe(streams => {
|
||||
this.dashboardFilters.set(new Set(streams.map(stream => stream.id)));
|
||||
});
|
||||
}
|
||||
}),
|
||||
).subscribe();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.sideNavService.getSideNavStreams(true).subscribe(streams => {
|
||||
this.sideNavFilters.set(new Set(streams.map(stream => stream.smartFilterId)));
|
||||
});
|
||||
this.dashboardService.getDashboardStreams(true).subscribe(streams => {
|
||||
this.dashboardFilters.set(new Set(streams.map(stream => stream.smartFilterId)));
|
||||
});
|
||||
}
|
||||
|
||||
getFilterLink(filter: SmartFilter) {
|
||||
return this.baseUrl + FilterUtilitiesService.getFilterLink(filter.entityType, filter.filter);
|
||||
}
|
||||
|
||||
shouldRenderFunc(action: ActionItem<SmartFilter>, item: SmartFilter, user: User) {
|
||||
switch (action.action) {
|
||||
case Action.AddToDashboard:
|
||||
return !this.dashboardFilters().has(item.id);
|
||||
case Action.AddToSideNav:
|
||||
return !this.sideNavFilters().has(item.id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
loadData() {
|
||||
this.filterService.getAllFilters().subscribe(filters => {
|
||||
|
||||
@@ -3826,7 +3826,11 @@
|
||||
"export-v2": "CBL v2",
|
||||
"export-v2-tooltip": "Use CBL v2 (JSON)",
|
||||
"cbl-manager": "CBL Manager",
|
||||
"cbl-manager-tooltip": "Manage/Sync CBLs"
|
||||
"cbl-manager-tooltip": "Manage/Sync CBLs",
|
||||
"add-to-dashboard": "Add to Dashboard",
|
||||
"add-to-dashboard-tooltip": "Adds the smart filter to the bottom of your Dashboard",
|
||||
"add-to-side-nav": "Add to Side Nav",
|
||||
"add-to-side-nav-tooltip": "Adds the smart filter to the bottom of your Side Nav"
|
||||
},
|
||||
|
||||
"preferences": {
|
||||
|
||||
Reference in New Issue
Block a user