fix: Consistent dialogs (#1842)

* fix/consistent-dialogs

* fix: enforce consistent mobile dialog width

* fix: cover search dialog image size
This commit is contained in:
Muppetteer
2025-12-14 15:08:43 +11:00
committed by GitHub
parent b64c30f3bc
commit 0486a4f070
25 changed files with 333 additions and 420 deletions

View File

@@ -1,5 +1,6 @@
import {inject, Injectable} from '@angular/core'; import {inject, Injectable} from '@angular/core';
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog'; import {DynamicDialogRef} from 'primeng/dynamicdialog';
import {DialogLauncherService} from '../../../../shared/services/dialog-launcher.service';
import {ShelfAssignerComponent} from '../shelf-assigner/shelf-assigner.component'; import {ShelfAssignerComponent} from '../shelf-assigner/shelf-assigner.component';
import {LockUnlockMetadataDialogComponent} from './lock-unlock-metadata-dialog/lock-unlock-metadata-dialog.component'; import {LockUnlockMetadataDialogComponent} from './lock-unlock-metadata-dialog/lock-unlock-metadata-dialog.component';
import {MetadataRefreshType} from '../../../metadata/model/request/metadata-refresh-type.enum'; import {MetadataRefreshType} from '../../../metadata/model/request/metadata-refresh-type.enum';
@@ -8,50 +9,61 @@ import {MultiBookMetadataEditorComponent} from '../../../metadata/component/mult
import {MultiBookMetadataFetchComponent} from '../../../metadata/component/multi-book-metadata-fetch/multi-book-metadata-fetch-component'; import {MultiBookMetadataFetchComponent} from '../../../metadata/component/multi-book-metadata-fetch/multi-book-metadata-fetch-component';
import {FileMoverComponent} from '../../../../shared/components/file-mover/file-mover-component'; import {FileMoverComponent} from '../../../../shared/components/file-mover/file-mover-component';
import {ShelfCreatorComponent} from '../shelf-creator/shelf-creator.component'; import {ShelfCreatorComponent} from '../shelf-creator/shelf-creator.component';
import {BookSenderComponent} from '../book-sender/book-sender.component';
import {MetadataFetchOptionsComponent} from '../../../metadata/component/metadata-options-dialog/metadata-fetch-options/metadata-fetch-options.component';
import {BookMetadataCenterComponent} from '../../../metadata/component/book-metadata-center/book-metadata-center.component';
import {CoverSearchComponent} from '../../../metadata/component/cover-search/cover-search.component';
import {Book} from '../../model/book.model';
import {AdditionalFileUploaderComponent} from '../additional-file-uploader/additional-file-uploader.component';
@Injectable({providedIn: 'root'}) @Injectable({providedIn: 'root'})
export class BookDialogHelperService { export class BookDialogHelperService {
private dialogService = inject(DialogService); private dialogLauncherService = inject(DialogLauncherService);
private openDialog(component: any, options: {}): DynamicDialogRef | null {
return this.dialogLauncherService.openDialog(component, options);
}
openShelfAssigner(bookIds: Set<number>): DynamicDialogRef | null { openBookDetailsDialog(bookId: number): DynamicDialogRef | null {
return this.dialogService.open(ShelfAssignerComponent, { return this.openDialog(BookMetadataCenterComponent, {
showHeader: false, header: 'Book Details',
modal: true, styleClass: 'book-details-dialog dialog-maximal',
closable: true,
contentStyle: {overflow: 'hidden'},
styleClass: 'dynamic-dialog-minimal',
baseZIndex: 10,
data: { data: {
isMultiBooks: true, bookId: bookId,
bookIds,
}, },
}); });
} }
openShelfCreator(): DynamicDialogRef { openShelfAssignerDialog(book: Book | null, bookIds: Set<number> | null): DynamicDialogRef | null {
return this.dialogService.open(ShelfCreatorComponent, { const data:any = {};
if (book !== null) {
data.isMultiBooks = false;
data.book = book;
} else if (bookIds !== null) {
data.isMultiBooks = true;
data.bookIds = bookIds;
} else {
return null;
}
return this.openDialog(ShelfAssignerComponent, {
showHeader: false,
data: data,
styleClass: 'dynamic-dialog-minimal',
});
}
openShelfCreatorDialog(): DynamicDialogRef {
return this.openDialog(ShelfCreatorComponent, {
showHeader: false, showHeader: false,
modal: true,
draggable: false,
dismissableMask: true,
closable: true,
contentStyle: {overflow: 'auto'},
styleClass: 'dynamic-dialog-minimal', styleClass: 'dynamic-dialog-minimal',
baseZIndex: 10,
style: {
position: 'absolute',
top: '15%',
},
})!; })!;
} }
openLockUnlockMetadataDialog(bookIds: Set<number>): DynamicDialogRef | null { openLockUnlockMetadataDialog(bookIds: Set<number>): DynamicDialogRef | null {
const count = bookIds.size; const count = bookIds.size;
return this.dialogService.open(LockUnlockMetadataDialogComponent, { return this.openDialog(LockUnlockMetadataDialogComponent, {
header: `Lock or Unlock Metadata for ${count} Selected Book${count > 1 ? 's' : ''}`, header: `Lock or Unlock Metadata for ${count} Selected Book${count > 1 ? 's' : ''}`,
modal: true,
closable: true,
data: { data: {
bookIds: Array.from(bookIds), bookIds: Array.from(bookIds),
}, },
@@ -59,70 +71,82 @@ export class BookDialogHelperService {
} }
openMetadataRefreshDialog(bookIds: Set<number>): DynamicDialogRef | null { openMetadataRefreshDialog(bookIds: Set<number>): DynamicDialogRef | null {
return this.dialogService.open(MultiBookMetadataFetchComponent, { return this.openDialog(MultiBookMetadataFetchComponent, {
header: 'Metadata Refresh Options', header: 'Metadata Refresh Options',
modal: true,
closable: true,
data: { data: {
bookIds: Array.from(bookIds), bookIds: Array.from(bookIds),
metadataRefreshType: MetadataRefreshType.BOOKS, metadataRefreshType: MetadataRefreshType.BOOKS,
}, },
styleClass: 'dialog-maximal',
}); });
} }
openBulkMetadataEditDialog(bookIds: Set<number>): DynamicDialogRef | null { openBulkMetadataEditDialog(bookIds: Set<number>): DynamicDialogRef | null {
return this.dialogService.open(BulkMetadataUpdateComponent, { return this.openDialog(BulkMetadataUpdateComponent, {
header: 'Bulk Edit Metadata', header: 'Bulk Edit Metadata',
modal: true,
closable: true,
style: {
width: '90vw',
maxWidth: '1200px',
position: 'absolute'
},
data: { data: {
bookIds: Array.from(bookIds) bookIds: Array.from(bookIds),
}, },
styleClass: 'dialog-maximal',
}); });
} }
openMultibookMetadataEditorDialog(bookIds: Set<number>): DynamicDialogRef | null { openMultibookMetadataEditorDialog(bookIds: Set<number>): DynamicDialogRef | null {
return this.dialogService.open(MultiBookMetadataEditorComponent, { return this.openDialog(MultiBookMetadataEditorComponent, {
header: 'Bulk Edit Metadata', header: 'Multi-Book Metadata Editor',
showHeader: false,
modal: true,
closable: true,
closeOnEscape: true,
dismissableMask: true,
style: {
width: '95vw',
overflow: 'none',
},
data: { data: {
bookIds: Array.from(bookIds) bookIds: Array.from(bookIds),
}, },
styleClass: 'dialog-maximal',
}); });
} }
openFileMoverDialog(selectedBooks: Set<number>) { openFileMoverDialog(bookIds: Set<number>): DynamicDialogRef | null {
const count = selectedBooks.size; const count = bookIds.size;
return this.dialogService.open(FileMoverComponent, { return this.openDialog(FileMoverComponent, {
header: `Organize Book Files (${count} book${count !== 1 ? 's' : ''})`, header: `Organize Book Files (${count} book${count !== 1 ? 's' : ''})`,
showHeader: true,
maximizable: true,
modal: true,
closable: true,
closeOnEscape: false,
dismissableMask: false,
style: {
width: '95vw',
maxWidth: '97.5vw',
height: '90vh',
maxHeight: '95vh'
},
data: { data: {
bookIds: selectedBooks bookIds: Array.from(bookIds),
}, },
styleClass: 'dialog-maximal',
});
}
openCustomSendDialog(bookId: number): DynamicDialogRef | null {
return this.openDialog(BookSenderComponent, {
header: 'Send Book to Email',
data: {
bookId: bookId,
}
});
}
openCoverSearchDialog(bookId: number): DynamicDialogRef | null {
return this.openDialog(CoverSearchComponent, {
header: "Search Cover",
data: {
bookId: bookId,
},
styleClass: 'dialog-maximal',
});
}
openMetadataFetchOptionsDialog(bookId: number): DynamicDialogRef | null {
return this.openDialog(MetadataFetchOptionsComponent, {
header: 'Metadata Refresh Options',
data: {
bookIds: [bookId],
metadataRefreshType: MetadataRefreshType.BOOKS,
}
});
}
openAdditionalFileUploaderDialog(book: Book): DynamicDialogRef | null {
return this.openDialog(AdditionalFileUploaderComponent, {
header: 'Upload Additional File',
data: {
book: book,
}
}); });
} }
} }

View File

@@ -639,7 +639,7 @@ export class BookBrowserComponent implements OnInit, AfterViewInit {
} }
openShelfAssigner(): void { openShelfAssigner(): void {
this.dynamicDialogRef = this.dialogHelperService.openShelfAssigner(this.selectedBooks); this.dynamicDialogRef = this.dialogHelperService.openShelfAssignerDialog(null, this.selectedBooks);
} }
lockUnlockMetadata(): void { lockUnlockMetadata(): void {

View File

@@ -4,8 +4,6 @@ import {AdditionalFile, Book, ReadStatus} from '../../../model/book.model';
import {Button} from 'primeng/button'; import {Button} from 'primeng/button';
import {MenuModule} from 'primeng/menu'; import {MenuModule} from 'primeng/menu';
import {ConfirmationService, MenuItem, MessageService} from 'primeng/api'; import {ConfirmationService, MenuItem, MessageService} from 'primeng/api';
import {DialogService} from 'primeng/dynamicdialog';
import {ShelfAssignerComponent} from '../../shelf-assigner/shelf-assigner.component';
import {BookService} from '../../../service/book.service'; import {BookService} from '../../../service/book.service';
import {CheckboxChangeEvent, CheckboxModule} from 'primeng/checkbox'; import {CheckboxChangeEvent, CheckboxModule} from 'primeng/checkbox';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
@@ -16,16 +14,13 @@ import {UserService} from '../../../../settings/user-management/user.service';
import {filter, Subject} from 'rxjs'; import {filter, Subject} from 'rxjs';
import {EmailService} from '../../../../settings/email-v2/email.service'; import {EmailService} from '../../../../settings/email-v2/email.service';
import {TieredMenu} from 'primeng/tieredmenu'; import {TieredMenu} from 'primeng/tieredmenu';
import {BookSenderComponent} from '../../book-sender/book-sender.component';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {ProgressBar} from 'primeng/progressbar'; import {ProgressBar} from 'primeng/progressbar';
import {BookMetadataCenterComponent} from '../../../../metadata/component/book-metadata-center/book-metadata-center.component';
import {take, takeUntil} from 'rxjs/operators'; import {take, takeUntil} from 'rxjs/operators';
import {readStatusLabels} from '../book-filter/book-filter.component'; import {readStatusLabels} from '../book-filter/book-filter.component';
import {ResetProgressTypes} from '../../../../../shared/constants/reset-progress-type'; import {ResetProgressTypes} from '../../../../../shared/constants/reset-progress-type';
import {ReadStatusHelper} from '../../../helpers/read-status.helper'; import {ReadStatusHelper} from '../../../helpers/read-status.helper';
import {BookDialogHelperService} from '../BookDialogHelperService'; import {BookDialogHelperService} from '../BookDialogHelperService';
import {MetadataFetchOptionsComponent} from '../../../../metadata/component/metadata-options-dialog/metadata-fetch-options/metadata-fetch-options.component';
import {TaskHelperService} from '../../../../settings/task-management/task-helper.service'; import {TaskHelperService} from '../../../../settings/task-management/task-helper.service';
@Component({ @Component({
@@ -59,7 +54,6 @@ export class BookCardComponent implements OnInit, OnChanges, OnDestroy {
private bookService = inject(BookService); private bookService = inject(BookService);
private taskHelperService = inject(TaskHelperService); private taskHelperService = inject(TaskHelperService);
private dialogService = inject(DialogService);
private userService = inject(UserService); private userService = inject(UserService);
private emailService = inject(EmailService); private emailService = inject(EmailService);
private messageService = inject(MessageService); private messageService = inject(MessageService);
@@ -293,18 +287,7 @@ export class BookCardComponent implements OnInit, OnChanges, OnDestroy {
label: 'Custom Send', label: 'Custom Send',
icon: 'pi pi-envelope', icon: 'pi pi-envelope',
command: () => { command: () => {
this.dialogService.open(BookSenderComponent, { this.bookDialogHelperService.openCustomSendDialog(this.book.id);
header: 'Send Book to Email',
modal: true,
closable: true,
style: {
position: 'absolute',
top: '15%',
},
data: {
bookId: this.book.id,
}
});
} }
} }
] ]
@@ -341,15 +324,7 @@ export class BookCardComponent implements OnInit, OnChanges, OnDestroy {
label: 'Custom Fetch', label: 'Custom Fetch',
icon: 'pi pi-sync', icon: 'pi pi-sync',
command: () => { command: () => {
this.dialogService.open(MetadataFetchOptionsComponent, { this.bookDialogHelperService.openMetadataRefreshDialog(new Set([this.book!.id]))
header: 'Metadata Refresh Options',
modal: true,
closable: true,
data: {
bookIds: [this.book!.id],
metadataRefreshType: MetadataRefreshType.BOOKS,
},
});
}, },
} }
] ]
@@ -457,19 +432,7 @@ export class BookCardComponent implements OnInit, OnChanges, OnDestroy {
} }
private openShelfDialog(): void { private openShelfDialog(): void {
this.dialogService.open(ShelfAssignerComponent, { this.bookDialogHelperService.openShelfAssignerDialog(this.book, null);
header: `Update Book's Shelves`,
showHeader: false,
modal: true,
dismissableMask: true,
closable: true,
contentStyle: {overflow: 'hidden'},
styleClass: 'dynamic-dialog-minimal',
baseZIndex: 10,
data: {
book: this.book,
},
});
} }
openSeriesInfo(): void { openSeriesInfo(): void {
@@ -488,21 +451,7 @@ export class BookCardComponent implements OnInit, OnChanges, OnDestroy {
queryParams: {tab: 'view'} queryParams: {tab: 'view'}
}); });
} else { } else {
this.dialogService.open(BookMetadataCenterComponent, { this.bookDialogHelperService.openBookDetailsDialog(book.id);
width: '90%',
height: '90%',
data: {bookId: book.id},
modal: true,
dismissableMask: true,
showHeader: true,
closable: true,
closeOnEscape: true,
draggable: false,
maximizable: false,
resizable: false,
header: 'Book Details',
styleClass: 'book-details-dialog'
});
} }
} }

View File

@@ -13,8 +13,6 @@ import {MessageService} from 'primeng/api';
import {Router, RouterLink} from '@angular/router'; import {Router, RouterLink} from '@angular/router';
import {filter, Subject} from 'rxjs'; import {filter, Subject} from 'rxjs';
import {UserService} from '../../../../settings/user-management/user.service'; import {UserService} from '../../../../settings/user-management/user.service';
import {BookMetadataCenterComponent} from '../../../../metadata/component/book-metadata-center/book-metadata-center.component';
import {DialogService} from 'primeng/dynamicdialog';
import {take, takeUntil} from 'rxjs/operators'; import {take, takeUntil} from 'rxjs/operators';
import {ReadStatusHelper} from '../../../helpers/read-status.helper'; import {ReadStatusHelper} from '../../../helpers/read-status.helper';

View File

@@ -13,7 +13,7 @@ import { Tab, TabList, TabPanel, TabPanels, Tabs } from "primeng/tabs";
import { Tag } from "primeng/tag"; import { Tag } from "primeng/tag";
import { VirtualScrollerModule } from "@iharbeck/ngx-virtual-scroller"; import { VirtualScrollerModule } from "@iharbeck/ngx-virtual-scroller";
import { ProgressSpinner } from "primeng/progressspinner"; import { ProgressSpinner } from "primeng/progressspinner";
import { DialogService, DynamicDialogRef } from "primeng/dynamicdialog"; import { DynamicDialogRef } from "primeng/dynamicdialog";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
@Component({ @Component({

View File

@@ -91,7 +91,7 @@ export class ShelfAssignerComponent implements OnInit {
} }
createShelfDialog(): void { createShelfDialog(): void {
const dialogRef = this.bookDialogHelper.openShelfCreator(); const dialogRef = this.bookDialogHelper.openShelfCreatorDialog();
dialogRef.onClose.subscribe((created: boolean) => { dialogRef.onClose.subscribe((created: boolean) => {
if (created) { if (created) {

View File

@@ -5,19 +5,13 @@ import {LibraryService} from './library.service';
import {ShelfService} from './shelf.service'; import {ShelfService} from './shelf.service';
import {Library} from '../model/library.model'; import {Library} from '../model/library.model';
import {Shelf} from '../model/shelf.model'; import {Shelf} from '../model/shelf.model';
import {DialogService} from 'primeng/dynamicdialog';
import {MetadataRefreshType} from '../../metadata/model/request/metadata-refresh-type.enum'; import {MetadataRefreshType} from '../../metadata/model/request/metadata-refresh-type.enum';
import {LibraryCreatorComponent} from '../../library-creator/library-creator.component';
import {ShelfEditDialogComponent} from '../components/shelf-edit-dialog/shelf-edit-dialog.component';
import {MagicShelf, MagicShelfService} from '../../magic-shelf/service/magic-shelf.service'; import {MagicShelf, MagicShelfService} from '../../magic-shelf/service/magic-shelf.service';
import {MetadataFetchOptionsComponent} from '../../metadata/component/metadata-options-dialog/metadata-fetch-options/metadata-fetch-options.component';
import {MagicShelfComponent} from '../../magic-shelf/component/magic-shelf-component';
import {TaskCreateRequest, TaskType} from '../../settings/task-management/task.service';
import {MetadataRefreshRequest} from '../../metadata/model/request/metadata-refresh-request.model';
import {TaskHelperService} from '../../settings/task-management/task-helper.service'; import {TaskHelperService} from '../../settings/task-management/task-helper.service';
import {UserService} from "../../settings/user-management/user.service"; import {UserService} from "../../settings/user-management/user.service";
import {LoadingService} from '../../../core/services/loading.service'; import {LoadingService} from '../../../core/services/loading.service';
import {finalize} from 'rxjs'; import {finalize} from 'rxjs';
import {DialogLauncherService} from '../../../shared/services/dialog-launcher.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@@ -30,7 +24,7 @@ export class LibraryShelfMenuService {
private shelfService = inject(ShelfService); private shelfService = inject(ShelfService);
private taskHelperService = inject(TaskHelperService); private taskHelperService = inject(TaskHelperService);
private router = inject(Router); private router = inject(Router);
private dialogService = inject(DialogService); private dialogLauncherService = inject(DialogLauncherService);
private magicShelfService = inject(MagicShelfService); private magicShelfService = inject(MagicShelfService);
private userService = inject(UserService); private userService = inject(UserService);
private loadingService = inject(LoadingService); private loadingService = inject(LoadingService);
@@ -44,17 +38,7 @@ export class LibraryShelfMenuService {
label: 'Edit Library', label: 'Edit Library',
icon: 'pi pi-pen-to-square', icon: 'pi pi-pen-to-square',
command: () => { command: () => {
this.dialogService.open(LibraryCreatorComponent, { this.dialogLauncherService.openLibraryEditDialog(<number>entity?.id);
header: 'Edit Library',
modal: true,
closable: true,
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
data: {
mode: 'edit',
libraryId: entity?.id
}
});
} }
}, },
{ {
@@ -93,15 +77,7 @@ export class LibraryShelfMenuService {
label: 'Custom Fetch Metadata', label: 'Custom Fetch Metadata',
icon: 'pi pi-sync', icon: 'pi pi-sync',
command: () => { command: () => {
this.dialogService.open(MetadataFetchOptionsComponent, { this.dialogLauncherService.openLibraryMetadataFetchDialog(<number>entity?.id);
header: 'Metadata Refresh Options',
modal: true,
closable: true,
data: {
libraryId: entity?.id,
metadataRefreshType: MetadataRefreshType.LIBRARY
}
})
} }
}, },
{ {
@@ -168,16 +144,7 @@ export class LibraryShelfMenuService {
label: 'Edit Shelf', label: 'Edit Shelf',
icon: 'pi pi-pen-to-square', icon: 'pi pi-pen-to-square',
command: () => { command: () => {
this.dialogService.open(ShelfEditDialogComponent, { this.dialogLauncherService.openShelfEditDialog(<number>entity?.id);
header: 'Edit Shelf',
modal: true,
closable: true,
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
data: {
shelfId: entity?.id
},
})
} }
}, },
{ {
@@ -230,17 +197,7 @@ export class LibraryShelfMenuService {
icon: 'pi pi-pen-to-square', icon: 'pi pi-pen-to-square',
disabled: disableOptions, disabled: disableOptions,
command: () => { command: () => {
this.dialogService.open(MagicShelfComponent, { this.dialogLauncherService.openMagicShelfEditDialog(<number>entity?.id);
header: 'Edit Magic Shelf',
modal: true,
closable: true,
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
data: {
id: entity?.id,
editMode: true,
}
})
} }
}, },
{ {

View File

@@ -18,7 +18,6 @@ import {Observable, Subscription} from 'rxjs';
import {AppSettings} from '../../../../shared/model/app-settings.model'; import {AppSettings} from '../../../../shared/model/app-settings.model';
import {AppSettingsService} from '../../../../shared/service/app-settings.service'; import {AppSettingsService} from '../../../../shared/service/app-settings.service';
import {DialogService} from 'primeng/dynamicdialog';
import {BookMetadata} from '../../../book/model/book.model'; import {BookMetadata} from '../../../book/model/book.model';
import {UrlHelperService} from '../../../../shared/service/url-helper.service'; import {UrlHelperService} from '../../../../shared/service/url-helper.service';
import {Checkbox} from 'primeng/checkbox'; import {Checkbox} from 'primeng/checkbox';
@@ -26,7 +25,7 @@ import {NgClass, NgStyle} from '@angular/common';
import {Paginator} from 'primeng/paginator'; import {Paginator} from 'primeng/paginator';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {BookdropFileMetadataPickerComponent} from '../bookdrop-file-metadata-picker/bookdrop-file-metadata-picker.component'; import {BookdropFileMetadataPickerComponent} from '../bookdrop-file-metadata-picker/bookdrop-file-metadata-picker.component';
import {BookdropFinalizeResultDialogComponent} from '../bookdrop-finalize-result-dialog/bookdrop-finalize-result-dialog-component'; import {DialogLauncherService} from '../../../../shared/services/dialog-launcher.service';
export interface BookdropFileUI { export interface BookdropFileUI {
file: BookdropFile; file: BookdropFile;
@@ -64,7 +63,7 @@ export class BookdropFileReviewComponent implements OnInit {
private readonly libraryService = inject(LibraryService); private readonly libraryService = inject(LibraryService);
private readonly confirmationService = inject(ConfirmationService); private readonly confirmationService = inject(ConfirmationService);
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
private readonly dialogService = inject(DialogService); private readonly dialogLauncherService = inject(DialogLauncherService);
private readonly appSettingsService = inject(AppSettingsService); private readonly appSettingsService = inject(AppSettingsService);
private readonly messageService = inject(MessageService); private readonly messageService = inject(MessageService);
private readonly urlHelper = inject(UrlHelperService); private readonly urlHelper = inject(UrlHelperService);
@@ -494,13 +493,7 @@ export class BookdropFileReviewComponent implements OnInit {
detail: 'Import process finished. See details below.', detail: 'Import process finished. See details below.',
}); });
this.dialogService.open(BookdropFinalizeResultDialogComponent, { this.dialogLauncherService.openBookdropFinalizeResultDialog(result);
header: 'Import Summary',
modal: true,
closable: true,
closeOnEscape: true,
data: {result: result},
});
const finalizedIds = new Set(files.map(f => f.fileId)); const finalizedIds = new Set(files.map(f => f.fileId));
Object.keys(this.fileUiCache).forEach(idStr => { Object.keys(this.fileUiCache).forEach(idStr => {

View File

@@ -1,13 +1,11 @@
.dashboard-settings { .dashboard-settings {
width: 1000px; max-width: 600px;
max-width: 1200px;
min-height: 300px; min-height: 300px;
padding: 2rem 1rem 0 1rem; padding: 2rem 1rem 0 1rem;
margin: 0 auto; margin: 0 auto;
@media (max-width: 768px) { @media (max-width: 768px) {
width: 100%; width: 100%;
max-width: 100%;
padding: 3rem 1rem 0 1rem; padding: 3rem 1rem 0 1rem;
box-sizing: border-box; box-sizing: border-box;
} }

View File

@@ -14,7 +14,6 @@ import {ProgressSpinner} from 'primeng/progressspinner';
import {TooltipModule} from 'primeng/tooltip'; import {TooltipModule} from 'primeng/tooltip';
import {DashboardConfigService} from '../../services/dashboard-config.service'; import {DashboardConfigService} from '../../services/dashboard-config.service';
import {ScrollerConfig, ScrollerType} from '../../models/dashboard-config.model'; import {ScrollerConfig, ScrollerType} from '../../models/dashboard-config.model';
import {DashboardSettingsComponent} from '../dashboard-settings/dashboard-settings.component';
import {MagicShelfService} from '../../../magic-shelf/service/magic-shelf.service'; import {MagicShelfService} from '../../../magic-shelf/service/magic-shelf.service';
import {BookRuleEvaluatorService} from '../../../magic-shelf/service/book-rule-evaluator.service'; import {BookRuleEvaluatorService} from '../../../magic-shelf/service/book-rule-evaluator.service';
import {GroupRule} from '../../../magic-shelf/component/magic-shelf-component'; import {GroupRule} from '../../../magic-shelf/component/magic-shelf-component';
@@ -39,7 +38,6 @@ const DEFAULT_MAX_ITEMS = 20;
standalone: true standalone: true
}) })
export class MainDashboardComponent implements OnInit { export class MainDashboardComponent implements OnInit {
ref: DynamicDialogRef | undefined | null;
private bookService = inject(BookService); private bookService = inject(BookService);
private dialogLauncher = inject(DialogLauncherService); private dialogLauncher = inject(DialogLauncherService);
@@ -203,14 +201,10 @@ export class MainDashboardComponent implements OnInit {
} }
openDashboardSettings(): void { openDashboardSettings(): void {
this.ref = this.dialogLauncher.open({ this.dialogLauncher.openDashboardSettingsDialog();
component: DashboardSettingsComponent,
header: 'Configure Dashboard',
showHeader: false
});
} }
createNewLibrary() { createNewLibrary() {
this.dialogLauncher.openLibraryCreatorDialog(); this.dialogLauncher.openLibraryCreateDialog();
} }
} }

View File

@@ -1,6 +1,5 @@
import {Component, inject, OnInit} from '@angular/core'; import {Component, inject, OnInit} from '@angular/core';
import {DialogService, DynamicDialogConfig, DynamicDialogRef} from 'primeng/dynamicdialog'; import {DynamicDialogConfig, DynamicDialogRef} from 'primeng/dynamicdialog';
import {DirectoryPickerComponent} from '../../shared/components/directory-picker/directory-picker.component';
import {MessageService} from 'primeng/api'; import {MessageService} from 'primeng/api';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {LibraryService} from '../book/service/library.service'; import {LibraryService} from '../book/service/library.service';
@@ -15,6 +14,7 @@ import {IconPickerService, IconSelection} from '../../shared/service/icon-picker
import {Select} from 'primeng/select'; import {Select} from 'primeng/select';
import {Button} from 'primeng/button'; import {Button} from 'primeng/button';
import {IconDisplayComponent} from '../../shared/components/icon-display/icon-display.component'; import {IconDisplayComponent} from '../../shared/components/icon-display/icon-display.component';
import {DialogLauncherService} from '../../shared/services/dialog-launcher.service';
@Component({ @Component({
selector: 'app-library-creator', selector: 'app-library-creator',
@@ -31,7 +31,6 @@ export class LibraryCreatorComponent implements OnInit {
mode!: string; mode!: string;
library!: Library | undefined; library!: Library | undefined;
editModeLibraryName: string = ''; editModeLibraryName: string = '';
directoryPickerDialogRef!: DynamicDialogRef<DirectoryPickerComponent> | null;
watch: boolean = false; watch: boolean = false;
scanMode: LibraryScanMode = 'FILE_AS_BOOK'; scanMode: LibraryScanMode = 'FILE_AS_BOOK';
defaultBookFormat: BookFileType | undefined = undefined; defaultBookFormat: BookFileType | undefined = undefined;
@@ -48,7 +47,7 @@ export class LibraryCreatorComponent implements OnInit {
{label: 'CBX/CBZ/CBR', value: 'CBX'} {label: 'CBX/CBZ/CBR', value: 'CBX'}
]; ];
private dialogService = inject(DialogService); private dialogLauncherService = inject(DialogLauncherService);
private dynamicDialogRef = inject(DynamicDialogRef); private dynamicDialogRef = inject(DynamicDialogRef);
private dynamicDialogConfig = inject(DynamicDialogConfig); private dynamicDialogConfig = inject(DynamicDialogConfig);
private libraryService = inject(LibraryService); private libraryService = inject(LibraryService);
@@ -85,16 +84,8 @@ export class LibraryCreatorComponent implements OnInit {
} }
openDirectoryPicker(): void { openDirectoryPicker(): void {
this.directoryPickerDialogRef = this.dialogService.open(DirectoryPickerComponent, { const ref = this.dialogLauncherService.openDirectoryPickerDialog();
header: 'Select Media Directory', ref?.onClose.subscribe((selectedFolders: string[] | null) => {
showHeader: false,
modal: true,
closable: true,
styleClass: 'dynamic-dialog-minimal',
contentStyle: {overflow: 'hidden'},
baseZIndex: 10
});
this.directoryPickerDialogRef?.onClose.subscribe((selectedFolders: string[] | null) => {
if (selectedFolders && selectedFolders.length > 0) { if (selectedFolders && selectedFolders.length > 0) {
selectedFolders.forEach(folder => { selectedFolders.forEach(folder => {
if (!this.folders.includes(folder)) { if (!this.folders.includes(folder)) {

View File

@@ -10,6 +10,10 @@
width: 250px; width: 250px;
} }
::ng-deep .cover-item p-image {
width: 150px;
}
::ng-deep p-image img { ::ng-deep p-image img {
margin: 0 auto; margin: 0 auto;
} }

View File

@@ -14,7 +14,6 @@ import {BookService} from "../../../../book/service/book.service";
import {ProgressSpinner} from "primeng/progressspinner"; import {ProgressSpinner} from "primeng/progressspinner";
import {Tooltip} from "primeng/tooltip"; import {Tooltip} from "primeng/tooltip";
import {filter, take} from "rxjs/operators"; import {filter, take} from "rxjs/operators";
import {DialogService} from "primeng/dynamicdialog";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {MetadataRefreshType} from "../../../model/request/metadata-refresh-type.enum"; import {MetadataRefreshType} from "../../../model/request/metadata-refresh-type.enum";
import {AutoComplete} from "primeng/autocomplete"; import {AutoComplete} from "primeng/autocomplete";
@@ -22,8 +21,8 @@ import {DatePicker} from "primeng/datepicker";
import {Textarea} from "primeng/textarea"; import {Textarea} from "primeng/textarea";
import {Image} from "primeng/image"; import {Image} from "primeng/image";
import {LazyLoadImageModule} from "ng-lazyload-image"; import {LazyLoadImageModule} from "ng-lazyload-image";
import {CoverSearchComponent} from '../../cover-search/cover-search.component';
import {TaskHelperService} from '../../../../settings/task-management/task-helper.service'; import {TaskHelperService} from '../../../../settings/task-management/task-helper.service';
import {BookDialogHelperService} from "../../../../book/components/book-browser/BookDialogHelperService";
@Component({ @Component({
selector: "app-metadata-editor", selector: "app-metadata-editor",
@@ -61,7 +60,7 @@ export class MetadataEditorComponent implements OnInit {
private bookService = inject(BookService); private bookService = inject(BookService);
private taskHelperService = inject(TaskHelperService); private taskHelperService = inject(TaskHelperService);
protected urlHelper = inject(UrlHelperService); protected urlHelper = inject(UrlHelperService);
private dialogService = inject(DialogService); private bookDialogHelperService = inject(BookDialogHelperService);
private destroyRef = inject(DestroyRef); private destroyRef = inject(DestroyRef);
metadataForm: FormGroup; metadataForm: FormGroup;
@@ -684,21 +683,7 @@ export class MetadataEditorComponent implements OnInit {
} }
openCoverSearch() { openCoverSearch() {
const ref = this.dialogService.open(CoverSearchComponent, { const ref = this.bookDialogHelperService.openCoverSearchDialog(this.currentBookId);
header: "Search Cover",
modal: true,
closable: true,
data: {
bookId: [this.currentBookId],
},
style: {
width: "90vw",
height: "90vh",
maxWidth: "1200px",
position: "absolute",
},
});
ref?.onClose.subscribe((result) => { ref?.onClose.subscribe((result) => {
if (result) { if (result) {
this.metadataForm.get("thumbnailUrl")?.setValue(result); this.metadataForm.get("thumbnailUrl")?.setValue(result);

View File

@@ -10,10 +10,8 @@ import {UrlHelperService} from '../../../../../shared/service/url-helper.service
import {UserService} from '../../../../settings/user-management/user.service'; import {UserService} from '../../../../settings/user-management/user.service';
import {SplitButton} from 'primeng/splitbutton'; import {SplitButton} from 'primeng/splitbutton';
import {ConfirmationService, MenuItem, MessageService} from 'primeng/api'; import {ConfirmationService, MenuItem, MessageService} from 'primeng/api';
import {BookSenderComponent} from '../../../../book/components/book-sender/book-sender.component'; import {DynamicDialogRef} from 'primeng/dynamicdialog';
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog';
import {EmailService} from '../../../../settings/email-v2/email.service'; import {EmailService} from '../../../../settings/email-v2/email.service';
import {ShelfAssignerComponent} from '../../../../book/components/shelf-assigner/shelf-assigner.component';
import {Tooltip} from 'primeng/tooltip'; import {Tooltip} from 'primeng/tooltip';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {Editor} from 'primeng/editor'; import {Editor} from 'primeng/editor';
@@ -29,13 +27,10 @@ import {DatePicker} from 'primeng/datepicker';
import {Tab, TabList, TabPanel, TabPanels, Tabs} from 'primeng/tabs'; import {Tab, TabList, TabPanel, TabPanels, Tabs} from 'primeng/tabs';
import {BookReviewsComponent} from '../../../../book/components/book-reviews/book-reviews.component'; import {BookReviewsComponent} from '../../../../book/components/book-reviews/book-reviews.component';
import {ProgressSpinner} from 'primeng/progressspinner'; import {ProgressSpinner} from 'primeng/progressspinner';
import {TieredMenu} from 'primeng/tieredmenu'; import {TieredMenu} from 'primeng/tieredmenu';
import {AdditionalFileUploaderComponent} from '../../../../book/components/additional-file-uploader/additional-file-uploader.component';
import {Image} from 'primeng/image'; import {Image} from 'primeng/image';
import {BookDialogHelperService} from '../../../../book/components/book-browser/BookDialogHelperService'; import {BookDialogHelperService} from '../../../../book/components/book-browser/BookDialogHelperService';
import {TagColor, TagComponent} from '../../../../../shared/components/tag/tag.component'; import {TagColor, TagComponent} from '../../../../../shared/components/tag/tag.component';
import {MetadataFetchOptionsComponent} from '../../metadata-options-dialog/metadata-fetch-options/metadata-fetch-options.component';
import {BookNotesComponent} from '../../../../book/components/book-notes/book-notes-component'; import {BookNotesComponent} from '../../../../book/components/book-notes/book-notes-component';
import {TaskHelperService} from '../../../../settings/task-management/task-helper.service'; import {TaskHelperService} from '../../../../settings/task-management/task-helper.service';
import { import {
@@ -57,7 +52,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
@ViewChild(Editor) quillEditor!: Editor; @ViewChild(Editor) quillEditor!: Editor;
private originalRecommendedBooks: BookRecommendation[] = []; private originalRecommendedBooks: BookRecommendation[] = [];
private dialogService = inject(DialogService); private bookDialogHelperService = inject(BookDialogHelperService)
private emailService = inject(EmailService); private emailService = inject(EmailService);
private messageService = inject(MessageService); private messageService = inject(MessageService);
private bookService = inject(BookService); private bookService = inject(BookService);
@@ -65,7 +60,6 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
protected urlHelper = inject(UrlHelperService); protected urlHelper = inject(UrlHelperService);
protected userService = inject(UserService); protected userService = inject(UserService);
private confirmationService = inject(ConfirmationService); private confirmationService = inject(ConfirmationService);
private bookDialogHelperService = inject(BookDialogHelperService);
private router = inject(Router); private router = inject(Router);
private destroyRef = inject(DestroyRef); private destroyRef = inject(DestroyRef);
@@ -105,13 +99,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
{ {
label: 'Custom Send', label: 'Custom Send',
command: () => { command: () => {
this.dialogService.open(BookSenderComponent, { this.bookDialogHelperService.openCustomSendDialog(metadata.bookId);
header: 'Send Book to Email',
modal: true,
closable: true,
style: {position: 'absolute', top: '20%'},
data: {bookId: metadata.bookId}
});
} }
} }
]) ])
@@ -124,15 +112,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
label: 'Custom Fetch', label: 'Custom Fetch',
icon: 'pi pi-sync', icon: 'pi pi-sync',
command: () => { command: () => {
this.dialogService.open(MetadataFetchOptionsComponent, { this.bookDialogHelperService.openMetadataFetchOptionsDialog(book.id);
header: 'Metadata Refresh Options',
modal: true,
closable: true,
data: {
bookIds: [book.id],
metadataRefreshType: MetadataRefreshType.BOOKS,
},
});
} }
} }
]) ])
@@ -197,16 +177,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
label: 'Upload File', label: 'Upload File',
icon: 'pi pi-upload', icon: 'pi pi-upload',
command: () => { command: () => {
this.dialogService.open(AdditionalFileUploaderComponent, { this.bookDialogHelperService.openAdditionalFileUploaderDialog(book);
header: 'Upload Additional File',
modal: true,
closable: true,
style: {
position: 'absolute',
top: '10%',
},
data: {book}
});
}, },
}, },
{ {
@@ -425,16 +396,7 @@ export class MetadataViewerComponent implements OnInit, OnChanges {
} }
assignShelf(bookId: number) { assignShelf(bookId: number) {
this.dialogService.open(ShelfAssignerComponent, { this.bookDialogHelperService.openShelfAssignerDialog(<Book>this.bookService.getBookByIdFromState(bookId), null);
header: `Update Book's Shelves`,
showHeader: false,
dismissableMask: true,
modal: true,
closable: true,
contentStyle: {overflow: 'hidden'},
baseZIndex: 10,
data: {book: this.bookService.getBookByIdFromState(bookId)}
});
} }
updateReadStatus(status: ReadStatus): void { updateReadStatus(status: ReadStatus): void {

View File

@@ -1,10 +1,5 @@
@if (!loading) { @if (!loading) {
<div class="px-4 pt-4 pb-2 h-full flex flex-col"> <div class="px-4 pt-4 pb-2 h-full flex flex-col">
<div class="flex items-center">
<h2 class="text-xl font-semibold">Review Metadata Proposal </h2>
</div>
<div class="flex-grow overflow-auto"> <div class="flex-grow overflow-auto">
@if (currentProposal?.metadataJson; as proposed) { @if (currentProposal?.metadataJson; as proposed) {
<app-metadata-picker <app-metadata-picker

View File

@@ -1,17 +1,16 @@
import {Component, inject, OnInit} from '@angular/core'; import {Component, inject, OnInit} from '@angular/core';
import {Button} from 'primeng/button'; import {Button} from 'primeng/button';
import {Checkbox} from 'primeng/checkbox'; import {Checkbox} from 'primeng/checkbox';
import {MessageService, PrimeTemplate} from 'primeng/api'; import {MessageService, PrimeTemplate} from 'primeng/api';
import {RadioButton} from 'primeng/radiobutton'; import {RadioButton} from 'primeng/radiobutton';
import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {TableModule} from 'primeng/table'; import {TableModule} from 'primeng/table';
import {Tooltip} from 'primeng/tooltip'; import {Tooltip} from 'primeng/tooltip';
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog'; import {DynamicDialogRef} from 'primeng/dynamicdialog';
import {EmailV2ProviderService} from './email-v2-provider.service'; import {EmailV2ProviderService} from './email-v2-provider.service';
import {CreateEmailProviderDialogComponent} from '../create-email-provider-dialog/create-email-provider-dialog.component';
import {EmailProvider} from '../email-provider.model'; import {EmailProvider} from '../email-provider.model';
import {UserService} from '../../user-management/user.service'; import {UserService} from '../../user-management/user.service';
import {DialogLauncherService} from '../../../../shared/services/dialog-launcher.service';
@Component({ @Component({
selector: 'app-email-v2-provider', selector: 'app-email-v2-provider',
@@ -32,7 +31,7 @@ export class EmailV2ProviderComponent implements OnInit {
emailProviders: EmailProvider[] = []; emailProviders: EmailProvider[] = [];
editingProviderIds: number[] = []; editingProviderIds: number[] = [];
ref: DynamicDialogRef | undefined | null; ref: DynamicDialogRef | undefined | null;
private dialogService = inject(DialogService); private dialogLauncherService = inject(DialogLauncherService);
private emailProvidersService = inject(EmailV2ProviderService); private emailProvidersService = inject(EmailV2ProviderService);
private messageService = inject(MessageService); private messageService = inject(MessageService);
private userService = inject(UserService); private userService = inject(UserService);
@@ -124,13 +123,7 @@ export class EmailV2ProviderComponent implements OnInit {
} }
openCreateProviderDialog() { openCreateProviderDialog() {
this.ref = this.dialogService.open(CreateEmailProviderDialogComponent, { this.ref = this.dialogLauncherService.openEmailProviderDialog();
header: 'Create Email Provider',
modal: true,
closable: true,
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
});
this.ref?.onClose.subscribe((result) => { this.ref?.onClose.subscribe((result) => {
if (result) { if (result) {
this.loadEmailProviders(); this.loadEmailProviders();

View File

@@ -1,15 +1,14 @@
import {Component, inject, OnInit} from '@angular/core'; import {Component, inject, OnInit} from '@angular/core';
import {Button} from 'primeng/button'; import {Button} from 'primeng/button';
import {MessageService, PrimeTemplate} from 'primeng/api'; import {MessageService, PrimeTemplate} from 'primeng/api';
import {RadioButton} from 'primeng/radiobutton'; import {RadioButton} from 'primeng/radiobutton';
import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {TableModule} from 'primeng/table'; import {TableModule} from 'primeng/table';
import {Tooltip} from 'primeng/tooltip'; import {Tooltip} from 'primeng/tooltip';
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog'; import {DynamicDialogRef} from 'primeng/dynamicdialog';
import {EmailV2RecipientService} from './email-v2-recipient.service'; import {EmailV2RecipientService} from './email-v2-recipient.service';
import {EmailRecipient} from '../email-recipient.model'; import {EmailRecipient} from '../email-recipient.model';
import {CreateEmailRecipientDialogComponent} from '../create-email-recipient-dialog/create-email-recipient-dialog.component'; import {DialogLauncherService} from '../../../../shared/services/dialog-launcher.service';
@Component({ @Component({
selector: 'app-email-v2-recipient', selector: 'app-email-v2-recipient',
@@ -29,7 +28,7 @@ export class EmailV2RecipientComponent implements OnInit {
recipientEmails: EmailRecipient[] = []; recipientEmails: EmailRecipient[] = [];
editingRecipientIds: number[] = []; editingRecipientIds: number[] = [];
ref: DynamicDialogRef | undefined | null; ref: DynamicDialogRef | undefined | null;
private dialogService = inject(DialogService); private dialogLauncherService = inject(DialogLauncherService);
private emailRecipientService = inject(EmailV2RecipientService); private emailRecipientService = inject(EmailV2RecipientService);
private messageService = inject(MessageService); private messageService = inject(MessageService);
defaultRecipientId: any; defaultRecipientId: any;
@@ -111,13 +110,7 @@ export class EmailV2RecipientComponent implements OnInit {
} }
openAddRecipientDialog() { openAddRecipientDialog() {
this.ref = this.dialogService.open(CreateEmailRecipientDialogComponent, { this.ref = this.dialogLauncherService.openEmailRecipientDialog();
header: 'Add New Recipient',
modal: true,
closable: true,
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
});
this.ref?.onClose.subscribe((result) => { this.ref?.onClose.subscribe((result) => {
if (result) { if (result) {
this.loadRecipientEmails(); this.loadRecipientEmails();

View File

@@ -1,8 +1,7 @@
import {Component, inject, OnDestroy, OnInit} from '@angular/core'; import {Component, inject, OnDestroy, OnInit} from '@angular/core';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {Button} from 'primeng/button'; import {Button} from 'primeng/button';
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog'; import {DynamicDialogRef} from 'primeng/dynamicdialog';
import {CreateUserDialogComponent} from './create-user-dialog/create-user-dialog.component';
import {TableModule} from 'primeng/table'; import {TableModule} from 'primeng/table';
import {LowerCasePipe, TitleCasePipe} from '@angular/common'; import {LowerCasePipe, TitleCasePipe} from '@angular/common';
import {User, UserService} from './user.service'; import {User, UserService} from './user.service';
@@ -16,6 +15,7 @@ import {Password} from 'primeng/password';
import {filter, take, takeUntil} from 'rxjs/operators'; import {filter, take, takeUntil} from 'rxjs/operators';
import {Subject} from 'rxjs'; import {Subject} from 'rxjs';
import {Tooltip} from 'primeng/tooltip'; import {Tooltip} from 'primeng/tooltip';
import {DialogLauncherService} from '../../../shared/services/dialog-launcher.service';
@Component({ @Component({
selector: 'app-user-management', selector: 'app-user-management',
@@ -36,7 +36,7 @@ import {Tooltip} from 'primeng/tooltip';
}) })
export class UserManagementComponent implements OnInit, OnDestroy { export class UserManagementComponent implements OnInit, OnDestroy {
ref: DynamicDialogRef | undefined | null; ref: DynamicDialogRef | undefined | null;
private dialogService = inject(DialogService); private dialogLauncherService = inject(DialogLauncherService);
private userService = inject(UserService); private userService = inject(UserService);
private libraryService = inject(LibraryService); private libraryService = inject(LibraryService);
private messageService = inject(MessageService); private messageService = inject(MessageService);
@@ -105,13 +105,7 @@ export class UserManagementComponent implements OnInit, OnDestroy {
} }
openCreateUserDialog() { openCreateUserDialog() {
this.ref = this.dialogService.open(CreateUserDialogComponent, { this.ref = this.dialogLauncherService.openCreateUserDialog();
header: 'Create New User',
showHeader: false,
modal: true,
closable: true,
styleClass: 'dynamic-dialog-minimal',
});
this.ref?.onClose.subscribe((result) => { this.ref?.onClose.subscribe((result) => {
if (result) { if (result) {
this.loadUsers(); this.loadUsers();

View File

@@ -6,15 +6,14 @@ import {ProgressBarModule} from 'primeng/progressbar';
import {ButtonModule} from 'primeng/button'; import {ButtonModule} from 'primeng/button';
import {Divider} from 'primeng/divider'; import {Divider} from 'primeng/divider';
import {Tooltip} from 'primeng/tooltip'; import {Tooltip} from 'primeng/tooltip';
import {DialogService} from 'primeng/dynamicdialog';
import {MessageService} from 'primeng/api'; import {MessageService} from 'primeng/api';
import {MetadataBatchProgressNotification, MetadataBatchStatus, MetadataBatchStatusLabels} from '../../model/metadata-batch-progress.model'; import {MetadataBatchProgressNotification, MetadataBatchStatus, MetadataBatchStatusLabels} from '../../model/metadata-batch-progress.model';
import {MetadataProgressService} from '../../service/metadata-progress-service'; import {MetadataProgressService} from '../../service/metadata-progress-service';
import {MetadataReviewDialogComponent} from '../../../features/metadata/component/metadata-review-dialog/metadata-review-dialog-component';
import {MetadataTaskService} from '../../../features/book/service/metadata-task'; import {MetadataTaskService} from '../../../features/book/service/metadata-task';
import {Tag} from 'primeng/tag'; import {Tag} from 'primeng/tag';
import {TaskService} from '../../../features/settings/task-management/task.service'; import {TaskService} from '../../../features/settings/task-management/task.service';
import {DialogLauncherService} from '../../services/dialog-launcher.service';
@Component({ @Component({
selector: 'app-metadata-progress-widget', selector: 'app-metadata-progress-widget',
@@ -27,7 +26,7 @@ export class MetadataProgressWidgetComponent implements OnInit, OnDestroy {
activeTasks: { [taskId: string]: MetadataBatchProgressNotification } = {}; activeTasks: { [taskId: string]: MetadataBatchProgressNotification } = {};
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
private dialogService = inject(DialogService); private dialogLauncherService = inject(DialogLauncherService);
private metadataProgressService = inject(MetadataProgressService); private metadataProgressService = inject(MetadataProgressService);
private metadataTaskService = inject(MetadataTaskService); private metadataTaskService = inject(MetadataTaskService);
private taskService = inject(TaskService); private taskService = inject(TaskService);
@@ -102,14 +101,7 @@ export class MetadataProgressWidgetComponent implements OnInit, OnDestroy {
} }
reviewTask(taskId: string): void { reviewTask(taskId: string): void {
this.dialogService.open(MetadataReviewDialogComponent, { this.dialogLauncherService.openMetadataReviewDialog(taskId);
showHeader: false,
width: '90vw',
height: '90vh',
data: {taskId},
closable: false,
modal: true
});
} }
cancelTask(taskId: string): void { cancelTask(taskId: string): void {

View File

@@ -9,10 +9,10 @@ import {ShelfService} from '../../../../features/book/service/shelf.service';
import {BookService} from '../../../../features/book/service/book.service'; import {BookService} from '../../../../features/book/service/book.service';
import {LibraryShelfMenuService} from '../../../../features/book/service/library-shelf-menu.service'; import {LibraryShelfMenuService} from '../../../../features/book/service/library-shelf-menu.service';
import {AppVersion, VersionService} from '../../../service/version.service'; import {AppVersion, VersionService} from '../../../service/version.service';
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog'; import {DynamicDialogRef} from 'primeng/dynamicdialog';
import {VersionChangelogDialogComponent} from './version-changelog-dialog/version-changelog-dialog.component';
import {UserService} from '../../../../features/settings/user-management/user.service'; import {UserService} from '../../../../features/settings/user-management/user.service';
import {MagicShelfService, MagicShelfState} from '../../../../features/magic-shelf/service/magic-shelf.service'; import {MagicShelfService, MagicShelfState} from '../../../../features/magic-shelf/service/magic-shelf.service';
import {DialogLauncherService} from '../../../services/dialog-launcher.service';
@Component({ @Component({
selector: 'app-menu', selector: 'app-menu',
@@ -34,7 +34,7 @@ export class AppMenuComponent implements OnInit {
private bookService = inject(BookService); private bookService = inject(BookService);
private versionService = inject(VersionService); private versionService = inject(VersionService);
private libraryShelfMenuService = inject(LibraryShelfMenuService); private libraryShelfMenuService = inject(LibraryShelfMenuService);
private dialogService = inject(DialogService); private dialogLauncherService = inject(DialogLauncherService);
private userService = inject(UserService); private userService = inject(UserService);
private magicShelfService = inject(MagicShelfService); private magicShelfService = inject(MagicShelfService);
@@ -198,20 +198,7 @@ export class AppMenuComponent implements OnInit {
} }
openChangelogDialog() { openChangelogDialog() {
const isMobile = window.innerWidth <= 768; this.dialogLauncherService.openVersionChangelogDialog();
this.dynamicDialogRef = this.dialogService.open(VersionChangelogDialogComponent, {
header: 'Whats New',
modal: true,
closable: true,
style: {
position: 'absolute',
top: '10%',
bottom: '10%',
width: isMobile ? '90vw' : '800px',
maxWidth: isMobile ? '90vw' : '800px',
minWidth: isMobile ? '90vw' : '800px',
},
});
} }
getVersionUrl(version: string | undefined): string { getVersionUrl(version: string | undefined): string {

View File

@@ -155,13 +155,13 @@ export class AppMenuitemComponent implements OnInit, OnDestroy {
openDialog(item: any) { openDialog(item: any) {
if (item.type === 'library' && this.canManipulateLibrary) { if (item.type === 'library' && this.canManipulateLibrary) {
this.dialogLauncher.openLibraryCreatorDialog(); this.dialogLauncher.openLibraryCreateDialog();
} }
if (item.type === 'magicShelf') { if (item.type === 'magicShelf') {
this.dialogLauncher.openMagicShelfDialog(); this.dialogLauncher.openMagicShelfCreateDialog();
} }
if (item.type === 'shelf') { if (item.type === 'shelf') {
this.bookDialogHelperService.openShelfCreator(); this.bookDialogHelperService.openShelfCreatorDialog();
} }
} }

View File

@@ -2,7 +2,7 @@ import {Component, ElementRef, OnDestroy, ViewChild} from '@angular/core';
import {MenuItem} from 'primeng/api'; import {MenuItem} from 'primeng/api';
import {LayoutService} from '../layout-main/service/app.layout.service'; import {LayoutService} from '../layout-main/service/app.layout.service';
import {Router, RouterLink} from '@angular/router'; import {Router, RouterLink} from '@angular/router';
import {DialogService as PrimeDialogService, DynamicDialogRef} from 'primeng/dynamicdialog'; import {DynamicDialogRef} from 'primeng/dynamicdialog';
import {TooltipModule} from 'primeng/tooltip'; import {TooltipModule} from 'primeng/tooltip';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {InputTextModule} from 'primeng/inputtext'; import {InputTextModule} from 'primeng/inputtext';
@@ -72,7 +72,6 @@ export class AppTopBarComponent implements OnDestroy {
constructor( constructor(
public layoutService: LayoutService, public layoutService: LayoutService,
public dialogService: PrimeDialogService,
private notificationService: NotificationEventService, private notificationService: NotificationEventService,
private router: Router, private router: Router,
private authService: AuthService, private authService: AuthService,
@@ -120,7 +119,7 @@ export class AppTopBarComponent implements OnDestroy {
} }
openLibraryCreatorDialog(): void { openLibraryCreatorDialog(): void {
this.dialogLauncher.openLibraryCreatorDialog(); this.dialogLauncher.openLibraryCreateDialog();
} }
openFileUploadDialog(): void { openFileUploadDialog(): void {

View File

@@ -1,7 +1,6 @@
import {inject, Injectable} from '@angular/core'; import {inject, Injectable} from '@angular/core';
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog';
import {IconPickerComponent} from '../components/icon-picker/icon-picker-component';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
import {DialogLauncherService} from '../services/dialog-launcher.service';
export interface IconSelection { export interface IconSelection {
type: 'PRIME_NG' | 'CUSTOM_SVG'; type: 'PRIME_NG' | 'CUSTOM_SVG';
@@ -10,23 +9,10 @@ export interface IconSelection {
@Injectable({providedIn: 'root'}) @Injectable({providedIn: 'root'})
export class IconPickerService { export class IconPickerService {
private dialog = inject(DialogService); private dialogLauncherService = inject(DialogLauncherService);
open(): Observable<IconSelection> { open(): Observable<IconSelection> {
const isMobile = window.innerWidth <= 768; const ref = this.dialogLauncherService.openIconPickerDialog();
const ref: DynamicDialogRef | null = this.dialog.open(IconPickerComponent, {
header: 'Choose an Icon',
modal: true,
closable: true,
style: {
position: 'absolute',
top: '10%',
bottom: '10%',
width: isMobile ? '90vw' : '800px',
maxWidth: isMobile ? '90vw' : '800px',
minWidth: isMobile ? '90vw' : '800px',
}
});
return ref!.onClose as Observable<IconSelection>; return ref!.onClose as Observable<IconSelection>;
} }
} }

View File

@@ -5,6 +5,19 @@ import {LibraryCreatorComponent} from '../../features/library-creator/library-cr
import {BookUploaderComponent} from '../components/book-uploader/book-uploader.component'; import {BookUploaderComponent} from '../components/book-uploader/book-uploader.component';
import {UserProfileDialogComponent} from '../../features/settings/user-profile-dialog/user-profile-dialog.component'; import {UserProfileDialogComponent} from '../../features/settings/user-profile-dialog/user-profile-dialog.component';
import {MagicShelfComponent} from '../../features/magic-shelf/component/magic-shelf-component'; import {MagicShelfComponent} from '../../features/magic-shelf/component/magic-shelf-component';
import {DashboardSettingsComponent} from '../../features/dashboard/components/dashboard-settings/dashboard-settings.component';
import {VersionChangelogDialogComponent} from '../layout/component/layout-menu/version-changelog-dialog/version-changelog-dialog.component';
import {CreateUserDialogComponent} from '../../features/settings/user-management/create-user-dialog/create-user-dialog.component';
import {CreateEmailRecipientDialogComponent} from '../../features/settings/email-v2/create-email-recipient-dialog/create-email-recipient-dialog.component';
import {CreateEmailProviderDialogComponent} from '../../features/settings/email-v2/create-email-provider-dialog/create-email-provider-dialog.component';
import {DirectoryPickerComponent} from '../components/directory-picker/directory-picker.component';
import {BookdropFinalizeResultDialogComponent} from '../../features/bookdrop/component/bookdrop-finalize-result-dialog/bookdrop-finalize-result-dialog-component';
import {BookdropFinalizeResult} from '../../features/bookdrop/service/bookdrop.service';
import {MetadataReviewDialogComponent} from '../../features/metadata/component/metadata-review-dialog/metadata-review-dialog-component';
import {MetadataRefreshType} from '../../features/metadata/model/request/metadata-refresh-type.enum';
import {MetadataFetchOptionsComponent} from '../../features/metadata/component/metadata-options-dialog/metadata-fetch-options/metadata-fetch-options.component';
import {ShelfEditDialogComponent} from '../../features/book/components/shelf-edit-dialog/shelf-edit-dialog.component';
import {IconPickerComponent} from '../components/icon-picker/icon-picker-component';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@@ -13,73 +26,164 @@ export class DialogLauncherService {
dialogService = inject(DialogService); dialogService = inject(DialogService);
open(options: { component: any; header: string; top?: string; width?: string; showHeader?: boolean; styleClass?: string }): DynamicDialogRef | null { private defaultDialogOptions = {
const isMobile = window.innerWidth <= 768; baseZIndex: 10,
const {component, header, top, width, showHeader = true, styleClass} = options; closable: true,
dismissableMask: true,
draggable: false,
modal: true,
resizable: false,
showHeader: true,
}
openDialog(component: any, options: {}): DynamicDialogRef | null {
return this.dialogService.open(component, { return this.dialogService.open(component, {
header, ...this.defaultDialogOptions,
showHeader, ...options,
modal: true, });
closable: true, }
styleClass: styleClass,
style: { openDashboardSettingsDialog(): DynamicDialogRef | null {
position: 'absolute', return this.openDialog(DashboardSettingsComponent, {
...(top ? {top} : {}), header: 'Configure Dashboard',
...(isMobile });
? { }
width: '90vw',
maxWidth: '90vw', openGithubSupportDialog(): DynamicDialogRef | null {
minWidth: '90vw', return this.openDialog(GithubSupportDialog, {
} header: 'Support Booklore',
: width });
? {width} }
: {}),
openLibraryCreateDialog(): DynamicDialogRef | null {
return this.openDialog(LibraryCreatorComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
});
}
openDirectoryPickerDialog(): DynamicDialogRef | null {
return this.openDialog(DirectoryPickerComponent, {
header: 'Select Media Directory',
styleClass: 'dynamic-dialog-minimal',
});
}
openLibraryEditDialog(libraryId: number): DynamicDialogRef | null {
return this.openDialog(LibraryCreatorComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
data: {
mode: 'edit',
libraryId: libraryId
}
});
}
openLibraryMetadataFetchDialog(libraryId: number): DynamicDialogRef | null {
return this.openDialog(MetadataFetchOptionsComponent, {
header: 'Metadata Refresh Options',
data: {
libraryId: libraryId,
metadataRefreshType: MetadataRefreshType.LIBRARY,
}
});
}
openShelfEditDialog(shelfId: number): DynamicDialogRef | null {
return this.openDialog(ShelfEditDialogComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
data: {
shelfId: shelfId
},
})
}
openFileUploadDialog(): DynamicDialogRef | null {
return this.openDialog(BookUploaderComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
});
}
openCreateUserDialog(): DynamicDialogRef | null {
return this.openDialog(CreateUserDialogComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
});
}
openUserProfileDialog(): DynamicDialogRef | null {
return this.openDialog(UserProfileDialogComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
});
}
openMagicShelfCreateDialog(): DynamicDialogRef | null {
return this.openDialog(MagicShelfComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
});
}
openMagicShelfEditDialog(shelfId: number): DynamicDialogRef | null {
return this.openDialog(MagicShelfComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
data: {
id: shelfId,
editMode: true,
}
})
}
openVersionChangelogDialog(): DynamicDialogRef | null {
return this.openDialog(VersionChangelogDialogComponent, {
header: "What's New",
styleClass: 'dialog-maximal',
});
}
openEmailRecipientDialog(): DynamicDialogRef | null {
return this.openDialog(CreateEmailRecipientDialogComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
});
}
openEmailProviderDialog(): DynamicDialogRef | null {
return this.openDialog(CreateEmailProviderDialogComponent, {
showHeader: false,
styleClass: 'dynamic-dialog-minimal',
});
}
openBookdropFinalizeResultDialog(result: BookdropFinalizeResult): DynamicDialogRef | null {
return this.openDialog(BookdropFinalizeResultDialogComponent, {
header: 'Import Summary',
data: {
result: result,
}, },
}); });
} }
openGithubSupportDialog(): void { openMetadataReviewDialog(taskId: string): DynamicDialogRef | null {
this.open({ return this.openDialog(MetadataReviewDialogComponent, {
component: GithubSupportDialog, header: 'Review Metadata Proposal',
header: 'Support Booklore', data: {
showHeader: true, taskId,
top: '15%' },
styleClass: 'dialog-maximal',
}); });
} }
openLibraryCreatorDialog(): void { openIconPickerDialog(): DynamicDialogRef | null {
this.open({ return this.openDialog(IconPickerComponent, {
component: LibraryCreatorComponent, header: 'Choose an Icon',
header: 'Create New Library', styleClass: 'dialog-maximal',
styleClass: 'dynamic-dialog-minimal',
showHeader: false
}); });
} }
openFileUploadDialog(): void { }
this.open({
component: BookUploaderComponent,
header: 'Book Uploader',
showHeader: false,
styleClass: 'dynamic-dialog-minimal'
});
}
openUserProfileDialog(): void {
this.open({
component: UserProfileDialogComponent,
header: 'User Profile Information',
styleClass: 'dynamic-dialog-minimal',
showHeader: false
});
}
openMagicShelfDialog(): void {
this.open({
component: MagicShelfComponent,
header: 'Magic Shelf Creator',
styleClass: 'dynamic-dialog-minimal',
showHeader: false
});
}
}

View File

@@ -4,6 +4,14 @@
display: none; display: none;
} }
.p-dialog {
@media (max-width: 768px) {
width: 95vw !important;
max-width: 95vw !important;
max-height: 95vh !important;
}
}
.dynamic-dialog-minimal.p-dialog { .dynamic-dialog-minimal.p-dialog {
border: none; border: none;
@@ -12,6 +20,13 @@
} }
} }
.dialog-maximal.p-dialog {
width: 95vw;
max-width: 1200px;
height: 95vh;
max-height: 95vh;
}
.gradient-divider { .gradient-divider {
min-height: 1px; min-height: 1px;
height: 1px; height: 1px;