Kavita/UI/Web/src/app/_single-module/actionable-modal/actionable-modal.component.ts
Fesaa 193e9b1da9
A collection of bug fixes (#3820)
Co-authored-by: Joseph Milazzo <joseph.v.milazzo@gmail.com>
2025-06-04 00:45:10 -07:00

103 lines
3.2 KiB
TypeScript

import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
DestroyRef,
EventEmitter,
inject,
Input,
OnInit,
Output
} from '@angular/core';
import {translate, TranslocoDirective} from "@jsverse/transloco";
import {Breakpoint, UtilityService} from "../../shared/_services/utility.service";
import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
import {ActionableEntity, ActionItem} from "../../_services/action-factory.service";
import {AccountService} from "../../_services/account.service";
import {tap} from "rxjs";
import {User} from "../../_models/user";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
@Component({
selector: 'app-actionable-modal',
imports: [
TranslocoDirective
],
templateUrl: './actionable-modal.component.html',
styleUrl: './actionable-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ActionableModalComponent implements OnInit {
protected readonly utilityService = inject(UtilityService);
protected readonly modal = inject(NgbActiveModal);
protected readonly accountService = inject(AccountService);
protected readonly cdRef = inject(ChangeDetectorRef);
protected readonly destroyRef = inject(DestroyRef);
protected readonly Breakpoint = Breakpoint;
@Input() entity: ActionableEntity = null;
@Input() actions: ActionItem<any>[] = [];
@Input() willRenderAction!: (action: ActionItem<any>, user: User) => boolean;
@Input() shouldRenderSubMenu!: (action: ActionItem<any>, dynamicList: null | Array<any>) => boolean;
@Output() actionPerformed = new EventEmitter<ActionItem<any>>();
currentLevel: string[] = [];
currentItems: ActionItem<any>[] = [];
user!: User | undefined;
ngOnInit() {
this.currentItems = this.translateOptions(this.actions);
this.accountService.currentUser$.pipe(tap(user => {
this.user = user;
this.cdRef.markForCheck();
}), takeUntilDestroyed(this.destroyRef)).subscribe();
}
handleItemClick(item: ActionItem<any>) {
if (item.children && item.children.length > 0) {
this.currentLevel.push(item.title);
if (item.children.length === 1 && item.children[0].dynamicList) {
item.children[0].dynamicList.subscribe(dynamicItems => {
this.currentItems = dynamicItems.map(di => ({
...item,
children: [], // Required as dynamic list is only one deep
title: di.title,
_extra: di,
action: item.children[0].action // override action to be correct from child
}));
});
} else {
this.currentItems = this.translateOptions(item.children);
}
}
else {
this.actionPerformed.emit(item);
this.modal.close(item);
}
this.cdRef.markForCheck();
}
handleBack() {
if (this.currentLevel.length > 0) {
this.currentLevel.pop();
let items = this.actions;
for (let level of this.currentLevel) {
items = items.find(item => item.title === level)?.children || [];
}
this.currentItems = this.translateOptions(items);
this.cdRef.markForCheck();
}
}
translateOptions(opts: Array<ActionItem<any>>) {
return opts.map(a => {
return {...a, title: translate('actionable.' + a.title)};
})
}
}