meals-made-easy-app/src/app/shared/components/recipe-edit-form/image-select/image-select.ts
2026-02-16 17:21:46 -06:00

141 lines
4.9 KiB
TypeScript

import { Component, inject, input, OnInit, output, signal } from '@angular/core';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { ImageService } from '../../../services/ImageService';
import { Spinner } from '../../spinner/spinner';
import { ImageView } from '../../../models/ImageView.model';
import { MatCard, MatCardActions, MatCardContent, MatCardImage } from '@angular/material/card';
import { MatCheckbox } from '@angular/material/checkbox';
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { faEllipsis } from '@fortawesome/free-solid-svg-icons';
import { MatButton } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import { EditImageDialog } from './edit-image-dialog/edit-image-dialog';
import { SliceView } from '../../../models/SliceView.model';
import { ImageViewWithBlobUrl } from '../../../client-models/ImageViewWithBlobUrl';
@Component({
selector: 'app-image-select',
imports: [
MatPaginator,
Spinner,
MatCard,
MatCardImage,
MatCardContent,
MatCardActions,
MatCheckbox,
MatMenuTrigger,
MatMenu,
FaIconComponent,
MatButton,
MatMenuItem,
],
templateUrl: './image-select.html',
styleUrl: './image-select.css',
})
export class ImageSelect implements OnInit {
public readonly select = output<ImageView | null>();
public readonly selectedUsernameFilename = input<readonly [string, string] | null>(null);
protected readonly currentPage = signal(0);
protected readonly pageSize = signal(9);
private readonly imageService = inject(ImageService);
private readonly dialog = inject(MatDialog);
protected readonly loadingImages = signal(false);
protected readonly loadImagesError = signal<Error | null>(null);
protected readonly imagesSlice = signal<SliceView<ImageViewWithBlobUrl> | null>(null);
protected readonly imageCount = signal(0);
protected readonly deleting = signal(false);
protected readonly deleteError = signal<Error | null>(null);
public ngOnInit(): void {
this.loadImages();
}
private loadImages(): void {
this.loadingImages.set(true);
this.imageService
.getOwnedImageViewsWithBlobUrls({
page: this.currentPage(),
size: this.pageSize(),
sort: [
{
property: 'created',
order: 'DESC',
},
],
})
.subscribe({
next: (sliceView) => {
sliceView.content.sort((a, b) => b.created.valueOf() - a.created.valueOf());
this.loadingImages.set(false);
this.imagesSlice.set(sliceView);
},
error: (e) => {
this.loadingImages.set(false);
this.loadImagesError.set(e);
},
});
this.imageService.getOwnedImagesCount().subscribe({
next: (count) => {
this.imageCount.set(count);
},
});
}
protected onPage(pageEvent: PageEvent): void {
if (pageEvent.pageIndex < this.currentPage()) {
// backward
this.currentPage.update((old) => Math.max(old - 1, 0));
} else {
// forward
this.currentPage.update((old) => (this.imagesSlice()?.slice.hasNext ? old + 1 : old));
}
this.pageSize.set(pageEvent.pageSize);
this.loadImages();
}
protected onImageClick(imageView: ImageView): void {
if (this.isSelected(imageView)) {
this.select.emit(null);
} else {
this.select.emit(imageView);
}
}
protected isSelected(imageView: ImageView): boolean {
const selectedUsernameFilename = this.selectedUsernameFilename();
if (selectedUsernameFilename) {
const [username, filename] = selectedUsernameFilename;
return imageView.owner.username === username && imageView.filename === filename;
}
return false;
}
protected editImage(imageView: ImageView): void {
this.dialog.open(EditImageDialog, {
data: [imageView.owner.username, imageView.filename],
});
}
protected deleteImage(imageView: ImageView): void {
this.deleting.set(true);
this.imageService.deleteImage(imageView.owner.username, imageView.filename).subscribe({
next: () => {
this.deleting.set(false);
this.loadImages();
},
error: (e) => {
this.deleting.set(false);
this.deleteError.set(e);
},
});
}
protected readonly faEllipsis = faEllipsis;
}