Collection of smaller bugs

#3793, #3806, #3810, #3808
This commit is contained in:
Amelia 2025-05-15 14:20:22 +02:00
parent 6288d89651
commit db77e08264
11 changed files with 43 additions and 15 deletions

View file

@ -128,6 +128,7 @@ public class UsersController : BaseApiController
existingPreferences.PromptForDownloadSize = preferencesDto.PromptForDownloadSize; existingPreferences.PromptForDownloadSize = preferencesDto.PromptForDownloadSize;
existingPreferences.NoTransitions = preferencesDto.NoTransitions; existingPreferences.NoTransitions = preferencesDto.NoTransitions;
existingPreferences.SwipeToPaginate = preferencesDto.SwipeToPaginate; existingPreferences.SwipeToPaginate = preferencesDto.SwipeToPaginate;
existingPreferences.AllowAutomaticWebtoonReaderDetection = preferencesDto.AllowAutomaticWebtoonReaderDetection;
existingPreferences.CollapseSeriesRelationships = preferencesDto.CollapseSeriesRelationships; existingPreferences.CollapseSeriesRelationships = preferencesDto.CollapseSeriesRelationships;
existingPreferences.ShareReviews = preferencesDto.ShareReviews; existingPreferences.ShareReviews = preferencesDto.ShareReviews;

View file

@ -15,7 +15,7 @@
<div class="d-grid gap-2"> <div class="d-grid gap-2">
@for (action of currentItems; track action.title) { @for (action of currentItems; track action.title) {
@if (willRenderAction(action)) { @if (willRenderAction(action, user!)) {
<button class="btn btn-outline-primary text-start d-flex justify-content-between align-items-center w-100" <button class="btn btn-outline-primary text-start d-flex justify-content-between align-items-center w-100"
(click)="handleItemClick(action)"> (click)="handleItemClick(action)">
{{action.title}} {{action.title}}

View file

@ -38,7 +38,7 @@ export class ActionableModalComponent implements OnInit {
@Input() entity: ActionableEntity = null; @Input() entity: ActionableEntity = null;
@Input() actions: ActionItem<any>[] = []; @Input() actions: ActionItem<any>[] = [];
@Input() willRenderAction!: (action: ActionItem<any>) => boolean; @Input() willRenderAction!: (action: ActionItem<any>, user: User) => boolean;
@Input() shouldRenderSubMenu!: (action: ActionItem<any>, dynamicList: null | Array<any>) => boolean; @Input() shouldRenderSubMenu!: (action: ActionItem<any>, dynamicList: null | Array<any>) => boolean;
@Output() actionPerformed = new EventEmitter<ActionItem<any>>(); @Output() actionPerformed = new EventEmitter<ActionItem<any>>();

View file

@ -94,7 +94,7 @@
<span class="card-actions"> <span class="card-actions">
@if (actions && actions.length > 0) { @if (actions && actions.length > 0) {
<app-card-actionables (actionHandler)="performAction($event)" [inputActions]="actions" [labelBy]="title"></app-card-actionables> <app-card-actionables [entity]="actionEntity" (actionHandler)="performAction($event)" [inputActions]="actions" [labelBy]="title"></app-card-actionables>
} }
</span> </span>
</div> </div>

View file

@ -26,7 +26,7 @@ import {Series} from 'src/app/_models/series';
import {User} from 'src/app/_models/user'; import {User} from 'src/app/_models/user';
import {Volume} from 'src/app/_models/volume'; import {Volume} from 'src/app/_models/volume';
import {AccountService} from 'src/app/_services/account.service'; import {AccountService} from 'src/app/_services/account.service';
import {Action, ActionFactoryService, ActionItem} from 'src/app/_services/action-factory.service'; import {Action, ActionableEntity, ActionFactoryService, ActionItem} from 'src/app/_services/action-factory.service';
import {ImageService} from 'src/app/_services/image.service'; import {ImageService} from 'src/app/_services/image.service';
import {LibraryService} from 'src/app/_services/library.service'; import {LibraryService} from 'src/app/_services/library.service';
import {EVENTS, MessageHubService} from 'src/app/_services/message-hub.service'; import {EVENTS, MessageHubService} from 'src/app/_services/message-hub.service';
@ -118,6 +118,10 @@ export class CardItemComponent implements OnInit {
* This is the entity we are representing. It will be returned if an action is executed. * This is the entity we are representing. It will be returned if an action is executed.
*/ */
@Input({required: true}) entity!: CardEntity; @Input({required: true}) entity!: CardEntity;
/**
* An optional entity to be used in the action callback
*/
@Input() actionEntity: ActionableEntity | null = null;
/** /**
* If the entity is selected or not. * If the entity is selected or not.
*/ */

View file

@ -16,7 +16,7 @@
<button type="button" class="btn-close" [attr.aria-label]="t('close')" (click)="resetField()"></button> <button type="button" class="btn-close" [attr.aria-label]="t('close')" (click)="resetField()"></button>
} }
} @else { } @else {
<div class="input-hint"> <div class="input-hint d-none d-lg-block">
Ctrl+K Ctrl+K
</div> </div>
} }

View file

@ -83,7 +83,7 @@
} }
<div class="col-auto ms-2 d-none d-md-block"> <div class="col-auto ms-2">
<div class="card-actions btn-actions" [ngbTooltip]="t('more-alt')"> <div class="card-actions btn-actions" [ngbTooltip]="t('more-alt')">
<app-card-actionables [entity]="readingList" [inputActions]="actions" [labelBy]="readingList.title" iconClass="fa-ellipsis-h" btnClass="btn"></app-card-actionables> <app-card-actionables [entity]="readingList" [inputActions]="actions" [labelBy]="readingList.title" iconClass="fa-ellipsis-h" btnClass="btn"></app-card-actionables>
</div> </div>

View file

@ -58,6 +58,7 @@ import {DefaultValuePipe} from "../../../_pipes/default-value.pipe";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {DetailsTabComponent} from "../../../_single-module/details-tab/details-tab.component"; import {DetailsTabComponent} from "../../../_single-module/details-tab/details-tab.component";
import {IHasCast} from "../../../_models/common/i-has-cast"; import {IHasCast} from "../../../_models/common/i-has-cast";
import {User} from "../../../_models/user";
enum TabID { enum TabID {
Storyline = 'storyline-tab', Storyline = 'storyline-tab',
@ -251,7 +252,8 @@ export class ReadingListDetailComponent implements OnInit {
if (user) { if (user) {
this.isAdmin = this.accountService.hasAdminRole(user); this.isAdmin = this.accountService.hasAdminRole(user);
this.actions = this.actionFactoryService.getReadingListActions(this.handleReadingListActionCallback.bind(this)) this.actions = this.actionFactoryService
.getReadingListActions(this.handleReadingListActionCallback.bind(this), this.shouldRenderReadingListAction.bind(this))
.filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin)); .filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin));
this.isOwnedReadingList = this.actions.filter(a => a.action === Action.Edit).length > 0; this.isOwnedReadingList = this.actions.filter(a => a.action === Action.Edit).length > 0;
this.cdRef.markForCheck(); this.cdRef.markForCheck();
@ -307,6 +309,17 @@ export class ReadingListDetailComponent implements OnInit {
} }
} }
shouldRenderReadingListAction(action: ActionItem<ReadingList>, entity: ReadingList, user: User) {
switch (action.action) {
case Action.Promote:
return !entity.promoted;
case Action.UnPromote:
return entity.promoted;
default:
return true;
}
}
editReadingList(readingList: ReadingList) { editReadingList(readingList: ReadingList) {
this.actionService.editReadingList(readingList, (readingList: ReadingList) => { this.actionService.editReadingList(readingList, (readingList: ReadingList) => {
// Reload information around list // Reload information around list

View file

@ -21,7 +21,7 @@
[trackByIdentity]="trackByIdentity" [trackByIdentity]="trackByIdentity"
> >
<ng-template #cardItem let-item let-position="idx" > <ng-template #cardItem let-item let-position="idx" >
<app-card-item [title]="item.title" [entity]="item" [actions]="actions[item.id]" <app-card-item [title]="item.title" [entity]="item" [actions]="actions[item.id]" [actionEntity]="item"
[suppressLibraryLink]="true" [imageUrl]="imageService.getReadingListCoverImage(item.id)" [suppressLibraryLink]="true" [imageUrl]="imageService.getReadingListCoverImage(item.id)"
[linkUrl]="'/lists/' + item.id" [linkUrl]="'/lists/' + item.id"
[count]="item.itemCount" [count]="item.itemCount"

View file

@ -24,6 +24,7 @@ import {Title} from "@angular/platform-browser";
import {WikiLink} from "../../../_models/wiki"; import {WikiLink} from "../../../_models/wiki";
import {BulkSelectionService} from "../../../cards/bulk-selection.service"; import {BulkSelectionService} from "../../../cards/bulk-selection.service";
import {BulkOperationsComponent} from "../../../cards/bulk-operations/bulk-operations.component"; import {BulkOperationsComponent} from "../../../cards/bulk-operations/bulk-operations.component";
import {User} from "../../../_models/user";
@Component({ @Component({
selector: 'app-reading-lists', selector: 'app-reading-lists',
@ -69,10 +70,8 @@ export class ReadingListsComponent implements OnInit {
} }
getActions(readingList: ReadingList) { getActions(readingList: ReadingList) {
const d = this.actionFactoryService.getReadingListActions(this.handleReadingListActionCallback.bind(this)) return this.actionFactoryService
.filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin || this.hasPromote)); .getReadingListActions(this.handleReadingListActionCallback.bind(this), this.shouldRenderReadingListAction.bind(this))
return this.actionFactoryService.getReadingListActions(this.handleReadingListActionCallback.bind(this))
.filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin || this.hasPromote)); .filter(action => this.readingListService.actionListFilter(action, readingList, this.isAdmin || this.hasPromote));
} }
@ -172,4 +171,15 @@ export class ReadingListsComponent implements OnInit {
break; break;
} }
} }
shouldRenderReadingListAction(action: ActionItem<ReadingList>, entity: ReadingList, user: User) {
switch (action.action) {
case Action.Promote:
return !entity.promoted;
case Action.UnPromote:
return entity.promoted;
default:
return true;
}
}
} }

View file

@ -2229,8 +2229,8 @@
"tasks-tab": "{{tabs.tasks-tab}}", "tasks-tab": "{{tabs.tasks-tab}}",
"info-tab": "{{tabs.info-tab}}", "info-tab": "{{tabs.info-tab}}",
"pages-label": "{{edit-chapter-modal.pages-count}}", "pages-label": "{{edit-chapter-modal.pages-label}}",
"words-label": "{{edit-chapter-modal.length-title}}", "words-label": "{{edit-chapter-modal.words-label}}",
"pages-count": "{{edit-chapter-modal.pages-count}}", "pages-count": "{{edit-chapter-modal.pages-count}}",
"words-count": "{{edit-chapter-modal.words-count}}", "words-count": "{{edit-chapter-modal.words-count}}",
"reading-time-label": "{{edit-chapter-modal.reading-time-label}}", "reading-time-label": "{{edit-chapter-modal.reading-time-label}}",