- @if (willRenderAction(action)) {
-
+
+ @for(action of list; track action.title) {
+
+ @if (action.children === undefined || action?.children?.length === 0 || action.dynamicList !== undefined) {
+ @if (action.dynamicList !== undefined && (action.dynamicList | async | dynamicList); as dList) {
+ @for(dynamicItem of dList; track dynamicItem.title) {
+
}
-
-
+ } @else if (willRenderAction(action, this.currentUser!)) {
+
+ }
+ } @else {
+ @if (shouldRenderSubMenu(action, action.children?.[0].dynamicList | async) && hasRenderableChildren(action, this.currentUser!)) {
+
+
-
+ }
}
}
- }
-
+
+ }
}
- }
-
+
diff --git a/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.scss b/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.scss
index 6f1d105e0..19a986986 100644
--- a/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.scss
+++ b/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.scss
@@ -2,6 +2,22 @@
content: none !important;
}
+.submenu-wrapper {
+ position: relative;
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: -10px;
+ width: 10px;
+ height: 100%;
+ background: transparent;
+ cursor: pointer;
+ pointer-events: auto;
+ }
+}
+
.submenu-toggle {
display: block;
width: 100%;
@@ -30,9 +46,3 @@
.btn {
padding: 5px;
}
-
-// Robbie added this but it broke most of the uses
-//.dropdown-toggle {
-// padding-top: 0;
-// padding-bottom: 0;
-//}
diff --git a/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.ts b/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.ts
index 64719a226..3e3522d5f 100644
--- a/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.ts
+++ b/UI/Web/src/app/_single-module/card-actionables/card-actionables.component.ts
@@ -1,31 +1,39 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
- Component, DestroyRef,
+ Component,
+ DestroyRef,
EventEmitter,
inject,
Input,
+ OnChanges,
+ OnDestroy,
OnInit,
Output
} from '@angular/core';
import {NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle, NgbModal} from '@ng-bootstrap/ng-bootstrap';
-import { AccountService } from 'src/app/_services/account.service';
-import { Action, ActionItem } from 'src/app/_services/action-factory.service';
+import {AccountService} from 'src/app/_services/account.service';
+import {ActionableEntity, ActionItem} from 'src/app/_services/action-factory.service';
import {AsyncPipe, NgTemplateOutlet} from "@angular/common";
import {TranslocoDirective} from "@jsverse/transloco";
import {DynamicListPipe} from "./_pipes/dynamic-list.pipe";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {Breakpoint, UtilityService} from "../../shared/_services/utility.service";
import {ActionableModalComponent} from "../actionable-modal/actionable-modal.component";
+import {User} from "../../_models/user";
+
@Component({
- selector: 'app-card-actionables',
- imports: [NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem, DynamicListPipe, TranslocoDirective, AsyncPipe, NgTemplateOutlet],
- templateUrl: './card-actionables.component.html',
- styleUrls: ['./card-actionables.component.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush
+ selector: 'app-card-actionables',
+ imports: [
+ NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem,
+ DynamicListPipe, TranslocoDirective, AsyncPipe, NgTemplateOutlet
+ ],
+ templateUrl: './card-actionables.component.html',
+ styleUrls: ['./card-actionables.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
})
-export class CardActionablesComponent implements OnInit {
+export class CardActionablesComponent implements OnInit, OnChanges, OnDestroy {
private readonly cdRef = inject(ChangeDetectorRef);
private readonly accountService = inject(AccountService);
@@ -37,58 +45,69 @@ export class CardActionablesComponent implements OnInit {
@Input() iconClass = 'fa-ellipsis-v';
@Input() btnClass = '';
- @Input() actions: ActionItem
[] = [];
+ @Input() inputActions: ActionItem[] = [];
@Input() labelBy = 'card';
/**
* Text to display as if actionable was a button
*/
@Input() label = '';
@Input() disabled: boolean = false;
+
+ @Input() entity: ActionableEntity = null;
+ /**
+ * This will only emit when the action is clicked and the entity is null. Otherwise, the entity callback handler will be invoked.
+ */
@Output() actionHandler = new EventEmitter>();
- isAdmin: boolean = false;
- canDownload: boolean = false;
- canPromote: boolean = false;
+ actions: ActionItem[] = [];
+ currentUser: User | undefined = undefined;
submenu: {[key: string]: NgbDropdown} = {};
+ private closeTimeout: any = null;
ngOnInit(): void {
this.accountService.currentUser$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((user) => {
if (!user) return;
- this.isAdmin = this.accountService.hasAdminRole(user);
- this.canDownload = this.accountService.hasDownloadRole(user);
- this.canPromote = this.accountService.hasPromoteRole(user);
-
- // We want to avoid an empty menu when user doesn't have access to anything
- if (!this.isAdmin && this.actions.filter(a => !a.requiresAdmin).length === 0) {
- this.actions = [];
- }
-
+ this.currentUser = user;
+ this.actions = this.inputActions.filter(a => this.willRenderAction(a, user!));
this.cdRef.markForCheck();
});
}
+ ngOnChanges() {
+ this.actions = this.inputActions.filter(a => this.willRenderAction(a, this.currentUser!));
+ this.cdRef.markForCheck();
+ }
+
+ ngOnDestroy() {
+ this.cancelCloseSubmenus();
+ }
+
preventEvent(event: any) {
event.stopPropagation();
event.preventDefault();
}
- performAction(event: any, action: ActionItem) {
+ performAction(event: any, action: ActionItem) {
this.preventEvent(event);
if (typeof action.callback === 'function') {
- this.actionHandler.emit(action);
+ if (this.entity === null) {
+ this.actionHandler.emit(action);
+ } else {
+ action.callback(action, this.entity);
+ }
}
}
- willRenderAction(action: ActionItem) {
- return (action.requiresAdmin && this.isAdmin)
- || (action.action === Action.Download && (this.canDownload || this.isAdmin))
- || (!action.requiresAdmin && action.action !== Action.Download)
- || (action.action === Action.Promote && (this.canPromote || this.isAdmin))
- || (action.action === Action.UnPromote && (this.canPromote || this.isAdmin))
- ;
+ /**
+ * The user has required roles (or no roles defined) and action shouldRender returns true
+ * @param action
+ * @param user
+ */
+ willRenderAction(action: ActionItem, user: User) {
+ return (!action.requiredRoles?.length || this.accountService.hasAnyRole(user, action.requiredRoles)) && action.shouldRender(action, this.entity, user);
}
shouldRenderSubMenu(action: ActionItem, dynamicList: null | Array) {
@@ -109,13 +128,41 @@ export class CardActionablesComponent implements OnInit {
}
closeAllSubmenus() {
- Object.keys(this.submenu).forEach(key => {
- this.submenu[key].close();
+ // Clear any existing timeout to avoid race conditions
+ if (this.closeTimeout) {
+ clearTimeout(this.closeTimeout);
+ }
+
+ // Set a new timeout to close submenus after a short delay
+ this.closeTimeout = setTimeout(() => {
+ Object.keys(this.submenu).forEach(key => {
+ this.submenu[key].close();
delete this.submenu[key];
- });
+ });
+ }, 100); // Small delay to prevent premature closing (dropdown tunneling)
}
- performDynamicClick(event: any, action: ActionItem, dynamicItem: any) {
+ cancelCloseSubmenus() {
+ if (this.closeTimeout) {
+ clearTimeout(this.closeTimeout);
+ this.closeTimeout = null;
+ }
+ }
+
+ hasRenderableChildren(action: ActionItem, user: User): boolean {
+ if (!action.children || action.children.length === 0) return false;
+
+ for (const child of action.children) {
+ const dynamicList = child.dynamicList;
+ if (dynamicList !== undefined) return true; // Dynamic list gets rendered if loaded
+
+ if (this.willRenderAction(child, user)) return true;
+ if (child.children?.length && this.hasRenderableChildren(child, user)) return true;
+ }
+ return false;
+ }
+
+ performDynamicClick(event: any, action: ActionItem, dynamicItem: any) {
action._extra = dynamicItem;
this.performAction(event, action);
}
@@ -124,6 +171,7 @@ export class CardActionablesComponent implements OnInit {
this.preventEvent(event);
const ref = this.modalService.open(ActionableModalComponent, {fullscreen: true, centered: true});
+ ref.componentInstance.entity = this.entity;
ref.componentInstance.actions = this.actions;
ref.componentInstance.willRenderAction = this.willRenderAction.bind(this);
ref.componentInstance.shouldRenderSubMenu = this.shouldRenderSubMenu.bind(this);
diff --git a/UI/Web/src/app/admin/manage-library/manage-library.component.html b/UI/Web/src/app/admin/manage-library/manage-library.component.html
index 95069b6f4..10c535b01 100644
--- a/UI/Web/src/app/admin/manage-library/manage-library.component.html
+++ b/UI/Web/src/app/admin/manage-library/manage-library.component.html
@@ -1,7 +1,8 @@
@@ -72,11 +73,22 @@
@if (useActionables$ | async) {
-
+
} @else {
-
-
-
+
+
+
+
+
}
|
diff --git a/UI/Web/src/app/admin/manage-library/manage-library.component.ts b/UI/Web/src/app/admin/manage-library/manage-library.component.ts
index 307444086..fa8a79bb8 100644
--- a/UI/Web/src/app/admin/manage-library/manage-library.component.ts
+++ b/UI/Web/src/app/admin/manage-library/manage-library.component.ts
@@ -83,12 +83,12 @@ export class ManageLibraryComponent implements OnInit {
lastSelectedIndex: number | null = null;
@HostListener('document:keydown.shift', ['$event'])
- handleKeypress(event: KeyboardEvent) {
+ handleKeypress(_: KeyboardEvent) {
this.isShiftDown = true;
}
@HostListener('document:keyup.shift', ['$event'])
- handleKeyUp(event: KeyboardEvent) {
+ handleKeyUp(_: KeyboardEvent) {
this.isShiftDown = false;
}
@@ -106,7 +106,7 @@ export class ManageLibraryComponent implements OnInit {
ngOnInit(): void {
this.getLibraries();
- // when a progress event comes in, show it on the UI next to library
+ // when a progress event comes in, show it on the UI next to the library
this.hubService.messages$.pipe(takeUntilDestroyed(this.destroyRef),
filter(event => event.event === EVENTS.ScanSeries || event.event === EVENTS.NotificationProgress),
distinctUntilChanged((prev: Message
, curr: Message) =>
@@ -270,7 +270,8 @@ export class ManageLibraryComponent implements OnInit {
}
}
- async handleBulkAction(action: ActionItem, library : Library | null) {
+ async handleBulkAction(action: ActionItem, _: Library) {
+ //Library is null for bulk actions
this.bulkAction = action.action;
this.cdRef.markForCheck();
@@ -284,7 +285,7 @@ export class ManageLibraryComponent implements OnInit {
break;
case (Action.CopySettings):
- // Prompt the user for the library then wait for them to manually trigger applyBulkAction
+ // Prompt the user for the library, then wait for them to manually trigger applyBulkAction
const ref = this.modalService.open(CopySettingsFromLibraryModalComponent, {size: 'lg', fullscreen: 'md'});
ref.componentInstance.libraries = this.libraries;
ref.closed.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((res: number | null) => {
@@ -298,7 +299,6 @@ export class ManageLibraryComponent implements OnInit {
}
}
-
async handleAction(action: ActionItem, library: Library) {
switch (action.action) {
case(Action.Scan):
@@ -321,13 +321,6 @@ export class ManageLibraryComponent implements OnInit {
}
}
- performAction(action: ActionItem, library: Library) {
- if (typeof action.callback === 'function') {
- action.callback(action, library);
- }
- }
-
-
setupSelections() {
this.selections = new SelectionModel(false, this.libraries);
this.cdRef.markForCheck();
diff --git a/UI/Web/src/app/admin/manage-logs/manage-logs.component.html b/UI/Web/src/app/admin/manage-logs/manage-logs.component.html
deleted file mode 100644
index cb6c92a46..000000000
--- a/UI/Web/src/app/admin/manage-logs/manage-logs.component.html
+++ /dev/null
@@ -1,11 +0,0 @@
-@if (logs$ | async; as items) {
-
-
- @for (item of scroll.viewPortItems; track item.timestamp) {
-
- {{item.timestamp | date}} [{{item.level}}] {{item.message}}
-
- }
-
-
-}
diff --git a/UI/Web/src/app/admin/manage-logs/manage-logs.component.scss b/UI/Web/src/app/admin/manage-logs/manage-logs.component.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/UI/Web/src/app/admin/manage-logs/manage-logs.component.ts b/UI/Web/src/app/admin/manage-logs/manage-logs.component.ts
deleted file mode 100644
index c3c42abb3..000000000
--- a/UI/Web/src/app/admin/manage-logs/manage-logs.component.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { Component, OnDestroy, OnInit } from '@angular/core';
-import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
-import { BehaviorSubject, take } from 'rxjs';
-import { AccountService } from 'src/app/_services/account.service';
-import { environment } from 'src/environments/environment';
-import { VirtualScrollerModule } from '@iharbeck/ngx-virtual-scroller';
-import { AsyncPipe, DatePipe } from '@angular/common';
-
-interface LogMessage {
- timestamp: string;
- level: 'Information' | 'Debug' | 'Warning' | 'Error';
- message: string;
- exception: string;
-}
-
-@Component({
- selector: 'app-manage-logs',
- templateUrl: './manage-logs.component.html',
- styleUrls: ['./manage-logs.component.scss'],
- standalone: true,
- imports: [VirtualScrollerModule, AsyncPipe, DatePipe]
-})
-export class ManageLogsComponent implements OnInit, OnDestroy {
-
- hubUrl = environment.hubUrl;
- private hubConnection!: HubConnection;
-
- logsSource = new BehaviorSubject([]);
- public logs$ = this.logsSource.asObservable();
-
- constructor(private accountService: AccountService) { }
-
- ngOnInit(): void {
- // TODO: Come back and implement this one day
- this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
- if (user) {
- this.hubConnection = new HubConnectionBuilder()
- .withUrl(this.hubUrl + 'logs', {
- accessTokenFactory: () => user.token
- })
- .withAutomaticReconnect()
- .build();
-
- console.log('Starting log connection');
-
- this.hubConnection
- .start()
- .catch(err => console.error(err));
-
- this.hubConnection.on('SendLogAsObject', resp => {
- const payload = resp.arguments[0] as LogMessage;
- const logMessage = {timestamp: payload.timestamp, level: payload.level, message: payload.message, exception: payload.exception};
- // NOTE: It might be better to just have a queue to show this
- const values = this.logsSource.getValue();
- values.push(logMessage);
- this.logsSource.next(values);
- });
- }
- });
-
- }
-
- ngOnDestroy(): void {
- // unsubscribe from signalr connection
- if (this.hubConnection) {
- this.hubConnection.stop().catch(err => console.error(err));
- console.log('Stopping log connection');
- }
- }
-
-}
diff --git a/UI/Web/src/app/announcements/_components/update-section/update-section.component.ts b/UI/Web/src/app/announcements/_components/update-section/update-section.component.ts
index cbf70fc66..129174424 100644
--- a/UI/Web/src/app/announcements/_components/update-section/update-section.component.ts
+++ b/UI/Web/src/app/announcements/_components/update-section/update-section.component.ts
@@ -10,6 +10,4 @@ import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
export class UpdateSectionComponent {
@Input({required: true}) items: Array = [];
@Input({required: true}) title: string = '';
-
- // TODO: Implement a read-more-list so that we by default show a configurable number
}
diff --git a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.html b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.html
index e76d352d8..34ae3aec2 100644
--- a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.html
+++ b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.html
@@ -23,7 +23,7 @@
{{t('mark-as-read')}}
}
-
+
diff --git a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts
index 4a14e63f2..b68309782 100644
--- a/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts
+++ b/UI/Web/src/app/cards/bulk-operations/bulk-operations.component.ts
@@ -2,13 +2,14 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
- DestroyRef, HostListener,
+ DestroyRef,
+ HostListener,
inject,
Input,
OnInit
} from '@angular/core';
-import { Action, ActionFactoryService, ActionItem } from 'src/app/_services/action-factory.service';
-import { BulkSelectionService } from '../bulk-selection.service';
+import {Action, ActionFactoryService, ActionItem} from 'src/app/_services/action-factory.service';
+import {BulkSelectionService} from '../bulk-selection.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {AsyncPipe, DecimalPipe, NgStyle} from "@angular/common";
import {TranslocoModule} from "@jsverse/transloco";
@@ -17,18 +18,18 @@ import {CardActionablesComponent} from "../../_single-module/card-actionables/ca
import {KEY_CODES} from "../../shared/_services/utility.service";
@Component({
- selector: 'app-bulk-operations',
- imports: [
- AsyncPipe,
- CardActionablesComponent,
- TranslocoModule,
- NgbTooltip,
- NgStyle,
- DecimalPipe
- ],
- templateUrl: './bulk-operations.component.html',
- styleUrls: ['./bulk-operations.component.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush
+ selector: 'app-bulk-operations',
+ imports: [
+ AsyncPipe,
+ CardActionablesComponent,
+ TranslocoModule,
+ NgbTooltip,
+ NgStyle,
+ DecimalPipe
+ ],
+ templateUrl: './bulk-operations.component.html',
+ styleUrls: ['./bulk-operations.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
})
export class BulkOperationsComponent implements OnInit {
diff --git a/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.html b/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.html
index 6d2f0bc6a..1981efdab 100644
--- a/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.html
+++ b/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.html
@@ -7,7 +7,7 @@
@if (actions.length > 0) {
-
+
}
diff --git a/UI/Web/src/app/cards/card-item/card-item.component.html b/UI/Web/src/app/cards/card-item/card-item.component.html
index b9b69accc..dc89c563c 100644
--- a/UI/Web/src/app/cards/card-item/card-item.component.html
+++ b/UI/Web/src/app/cards/card-item/card-item.component.html
@@ -94,7 +94,7 @@
@if (actions && actions.length > 0) {
-
+
}
diff --git a/UI/Web/src/app/cards/card-item/card-item.component.ts b/UI/Web/src/app/cards/card-item/card-item.component.ts
index d13c0d916..6bdbcaf18 100644
--- a/UI/Web/src/app/cards/card-item/card-item.component.ts
+++ b/UI/Web/src/app/cards/card-item/card-item.component.ts
@@ -344,10 +344,6 @@ export class CardItemComponent implements OnInit {
this.clicked.emit(this.title);
}
- preventClick(event: any) {
- event.stopPropagation();
- event.preventDefault();
- }
performAction(action: ActionItem) {
if (action.action == Action.Download) {
diff --git a/UI/Web/src/app/cards/chapter-card/chapter-card.component.html b/UI/Web/src/app/cards/chapter-card/chapter-card.component.html
index 9c0009860..573fd79a2 100644
--- a/UI/Web/src/app/cards/chapter-card/chapter-card.component.html
+++ b/UI/Web/src/app/cards/chapter-card/chapter-card.component.html
@@ -89,7 +89,7 @@
@if (actions && actions.length > 0) {
-
+
}
diff --git a/UI/Web/src/app/cards/chapter-card/chapter-card.component.ts b/UI/Web/src/app/cards/chapter-card/chapter-card.component.ts
index 0c56a4097..e5e9245c7 100644
--- a/UI/Web/src/app/cards/chapter-card/chapter-card.component.ts
+++ b/UI/Web/src/app/cards/chapter-card/chapter-card.component.ts
@@ -3,9 +3,11 @@ import {
ChangeDetectorRef,
Component,
DestroyRef,
- EventEmitter, HostListener,
+ EventEmitter,
+ HostListener,
inject,
- Input, OnInit,
+ Input,
+ OnInit,
Output
} from '@angular/core';
import {ImageService} from "../../_services/image.service";
@@ -14,7 +16,7 @@ import {DownloadEvent, DownloadService} from "../../shared/_services/download.se
import {EVENTS, MessageHubService} from "../../_services/message-hub.service";
import {AccountService} from "../../_services/account.service";
import {ScrollService} from "../../_services/scroll.service";
-import {Action, ActionFactoryService, ActionItem} from "../../_services/action-factory.service";
+import {ActionItem} from "../../_services/action-factory.service";
import {Chapter} from "../../_models/chapter";
import {Observable} from "rxjs";
import {User} from "../../_models/user";
@@ -28,13 +30,10 @@ import {EntityTitleComponent} from "../entity-title/entity-title.component";
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
import {Router, RouterLink} from "@angular/router";
import {TranslocoDirective} from "@jsverse/transloco";
-import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {filter, map} from "rxjs/operators";
import {UserProgressUpdateEvent} from "../../_models/events/user-progress-update-event";
import {ReaderService} from "../../_services/reader.service";
import {LibraryType} from "../../_models/library/library";
-import {Device} from "../../_models/device/device";
-import {ActionService} from "../../_services/action.service";
import {MangaFormat} from "../../_models/manga-format";
@Component({
@@ -60,15 +59,16 @@ export class ChapterCardComponent implements OnInit {
public readonly imageService = inject(ImageService);
public readonly bulkSelectionService = inject(BulkSelectionService);
private readonly downloadService = inject(DownloadService);
- private readonly actionService = inject(ActionService);
private readonly messageHub = inject(MessageHubService);
private readonly accountService = inject(AccountService);
private readonly scrollService = inject(ScrollService);
private readonly cdRef = inject(ChangeDetectorRef);
- private readonly actionFactoryService = inject(ActionFactoryService);
private readonly router = inject(Router);
private readonly readerService = inject(ReaderService);
+ protected readonly LibraryType = LibraryType;
+ protected readonly MangaFormat = MangaFormat;
+
@Input({required: true}) libraryId: number = 0;
@Input({required: true}) seriesId: number = 0;
@Input({required: true}) chapter!: Chapter;
@@ -143,8 +143,6 @@ export class ChapterCardComponent implements OnInit {
}
ngOnInit() {
- this.filterSendTo();
-
this.accountService.currentUser$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(user => {
this.user = user;
});
@@ -172,30 +170,6 @@ export class ChapterCardComponent implements OnInit {
this.cdRef.detectChanges();
}
-
- filterSendTo() {
- if (!this.actions || this.actions.length === 0) return;
-
- this.actions = this.actionFactoryService.filterSendToAction(this.actions, this.chapter);
- }
-
- performAction(action: ActionItem