Fixed a bug where when no devices, the submenu item would still render. (#1558)

This commit is contained in:
Joseph Milazzo 2022-09-23 20:04:18 -05:00 committed by GitHub
parent 56622ce800
commit 97642cf742
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 44 additions and 16 deletions

View File

@ -6,6 +6,7 @@ using API.Data.Repositories;
using API.DTOs.Device; using API.DTOs.Device;
using API.Extensions; using API.Extensions;
using API.Services; using API.Services;
using Kavita.Common;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -81,7 +82,15 @@ public class DeviceController : BaseApiController
if (await _emailService.IsDefaultEmailService()) if (await _emailService.IsDefaultEmailService())
return BadRequest("Send to device cannot be used with Kavita's email service. Please configure your own."); return BadRequest("Send to device cannot be used with Kavita's email service. Please configure your own.");
if (await _deviceService.SendTo(dto.ChapterId, dto.DeviceId)) return Ok(); try
{
var success = await _deviceService.SendTo(dto.ChapterId, dto.DeviceId);
if (success) return Ok();
}
catch (KavitaException ex)
{
return BadRequest(ex.Message);
}
return BadRequest("There was an error sending the file to the device"); return BadRequest("There was an error sending the file to the device");
} }

View File

@ -93,13 +93,14 @@ export interface ActionItem<T> {
requiresAdmin: boolean; requiresAdmin: boolean;
children: Array<ActionItem<T>>; children: Array<ActionItem<T>>;
/** /**
* Indicates that there exists a separate list will be loaded from an API * Indicates that there exists a separate list will be loaded from an API.
* Rule: If using this, only one child should exist in children with the Action for dynamicList.
*/ */
dynamicList?: Observable<{title: string, data: any}[]> | undefined; dynamicList?: Observable<{title: string, data: any}[]> | undefined;
/** /**
* Extra data that needs to be sent back from the card item. Used mainly for dynamicList. This will be the item from dyanamicList return * Extra data that needs to be sent back from the card item. Used mainly for dynamicList. This will be the item from dyanamicList return
*/ */
_extra?: any; _extra?: {title: string, data: any};
} }
@Injectable({ @Injectable({
@ -416,13 +417,16 @@ export class ActionFactoryService {
title: 'Send To', title: 'Send To',
callback: this.dummyCallback, callback: this.dummyCallback,
requiresAdmin: false, requiresAdmin: false,
// dynamicList: this.deviceService.devices$.pipe(map((devices: Array<Device>) => devices.map(d => {
// return {'title': d.name, 'data': d};
// }), shareReplay())),
children: [ children: [
{ {
action: Action.SendTo, action: Action.SendTo,
title: '', title: '',
callback: this.dummyCallback, callback: this.dummyCallback,
requiresAdmin: false, requiresAdmin: false,
dynamicList: this.deviceService.devices$.pipe(map(devices => devices.map(d => { dynamicList: this.deviceService.devices$.pipe(map((devices: Array<Device>) => devices.map(d => {
return {'title': d.name, 'data': d}; return {'title': d.name, 'data': d};
}), shareReplay())), }), shareReplay())),
children: [] children: []

View File

@ -221,7 +221,7 @@ export class CardDetailDrawerComponent implements OnInit, OnDestroy {
break; break;
case (Action.SendTo): case (Action.SendTo):
{ {
const device = (action._extra.data as Device); const device = (action._extra!.data as Device);
this.deviceSerivce.sendTo(chapter.id, device.id).subscribe(() => { this.deviceSerivce.sendTo(chapter.id, device.id).subscribe(() => {
this.toastr.success('File emailed to ' + device.name); this.toastr.success('File emailed to ' + device.name);
}); });

View File

@ -7,23 +7,29 @@
</div> </div>
<ng-template #submenu let-list="list"> <ng-template #submenu let-list="list">
<ng-container *ngFor="let action of list"> <ng-container *ngFor="let action of list">
<ng-container *ngIf="action.children === undefined || action?.children?.length === 0 else submenuDropdown"> <!-- Non Submenu items -->
<ng-container *ngIf="action.dynamicList != undefined; else justItem"> <ng-container *ngIf="action.children === undefined || action?.children?.length === 0 || action.dynamicList != undefined; else submenuDropdown">
<ng-container *ngFor="let dynamicItem of (action.dynamicList | async)">
<ng-container *ngIf="action.dynamicList != undefined && toDList(action.dynamicList | async) as dList; else justItem">
<ng-container *ngFor="let dynamicItem of dList">
<button ngbDropdownItem (click)="performDynamicClick($event, action, dynamicItem)">{{dynamicItem.title}}</button> <button ngbDropdownItem (click)="performDynamicClick($event, action, dynamicItem)">{{dynamicItem.title}}</button>
</ng-container> </ng-container>
</ng-container> </ng-container>
<ng-template #justItem> <ng-template #justItem>
<button ngbDropdownItem *ngIf="willRenderAction(action)" (click)="performAction($event, action)">{{action.title}}</button> <button ngbDropdownItem *ngIf="willRenderAction(action)" (click)="performAction($event, action)">{{action.title}}</button>
</ng-template> </ng-template>
</ng-container> </ng-container>
<ng-template #submenuDropdown> <ng-template #submenuDropdown>
<!-- Submenu items -->
<ng-container *ngIf="shouldRenderSubMenu(action, action.children[0].dynamicList | async)">
<div ngbDropdown #subMenuHover="ngbDropdown" placement="right" (mouseover)="preventEvent($event); openSubmenu(action.title, subMenuHover)" (mouseleave)="preventEvent($event)"> <div ngbDropdown #subMenuHover="ngbDropdown" placement="right" (mouseover)="preventEvent($event); openSubmenu(action.title, subMenuHover)" (mouseleave)="preventEvent($event)">
<button id="actions-{{action.title}}" class="submenu-toggle" ngbDropdownToggle>{{action.title}} <i class="fa-solid fa-angle-right submenu-icon"></i></button> <button id="actions-{{action.title}}" class="submenu-toggle" ngbDropdownToggle>{{action.title}} <i class="fa-solid fa-angle-right submenu-icon"></i></button>
<div ngbDropdownMenu attr.aria-labelledby="actions-{{action.title}}"> <div ngbDropdownMenu attr.aria-labelledby="actions-{{action.title}}">
<ng-container *ngTemplateOutlet="submenu; context: { list: action.children }"></ng-container> <ng-container *ngTemplateOutlet="submenu; context: { list: action.children }"></ng-container>
</div> </div>
</div> </div>
</ng-container>
</ng-template> </ng-template>
</ng-container> </ng-container>
</ng-template> </ng-template>

View File

@ -47,10 +47,14 @@ export class CardActionablesComponent implements OnInit {
} }
} }
willRenderAction(action: ActionItem<any>): boolean { willRenderAction(action: ActionItem<any>) {
return (action.requiresAdmin && this.isAdmin) return (action.requiresAdmin && this.isAdmin)
|| (action.action === Action.Download && (this.canDownload || this.isAdmin)) || (action.action === Action.Download && (this.canDownload || this.isAdmin))
|| (!action.requiresAdmin && action.action !== Action.Download) || (!action.requiresAdmin && action.action !== Action.Download);
}
shouldRenderSubMenu(action: ActionItem<any>, dynamicList: null | Array<any>) {
return (action.children[0].dynamicList === undefined || action.children[0].dynamicList === null) || (dynamicList !== null && dynamicList.length > 0);
} }
openSubmenu(actionTitle: string, subMenu: NgbDropdown) { openSubmenu(actionTitle: string, subMenu: NgbDropdown) {
@ -71,4 +75,9 @@ export class CardActionablesComponent implements OnInit {
this.performAction(event, action); this.performAction(event, action);
} }
toDList(d: any) {
console.log('d: ', d);
if (d === undefined || d === null) return [];
return d as {title: string, data: any}[];
}
} }

View File

@ -446,7 +446,7 @@ export class SeriesDetailComponent implements OnInit, OnDestroy, AfterContentChe
break; break;
case (Action.SendTo): case (Action.SendTo):
{ {
const device = (action._extra.data as Device); const device = (action._extra!.data as Device);
this.deviceSerivce.sendTo(chapter.id, device.id).subscribe(() => { this.deviceSerivce.sendTo(chapter.id, device.id).subscribe(() => {
this.toastr.success('File emailed to ' + device.name); this.toastr.success('File emailed to ' + device.name);
}); });