diff --git a/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select.html b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select.html
index f4ce7ed..8b54763 100644
--- a/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select.html
+++ b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select.html
@@ -1,43 +1,41 @@
-@if (imageViewsQuery.isLoading()) {
+@if (loadingImages()) {
-} @else if (imageViewsQuery.isError()) {
+} @else if (loadImagesError()) {
There was an error loading images.
-} @else if (imageViewsQuery.isSuccess()) {
+} @else {
- @for (imageQuery of imageViewsWithBlobUrlsQueries(); track $index) {
- @if (imageQuery.isLoading()) {
-
- } @else if (imageQuery.isError()) {
-
There was an error loading this image.
- } @else {
- @let imageView = imageQuery.data()!;
-
-
-
- {{ imageView.filename }}
-
-
- Main?
-
-
-
-
-
-
-
- }
+ @for (image of imagesSlice()!.content; track $index) {
+
+
+
+ {{ image.filename }}
+
+
+ Main?
+
+
+
+
+
+
+
}
+
+ @if (deleting()) {
+
+ } @else if (deleteError()) {
+ There was an error deleting the image.
+ }
+
();
public readonly selectedUsernameFilename = input(null);
@@ -43,37 +43,49 @@ export class ImageSelect {
private readonly imageService = inject(ImageService);
private readonly dialog = inject(MatDialog);
- protected readonly imageViewsQuery = injectQuery(() => ({
- queryKey: ['image-views', this.currentPage(), this.pageSize()],
- queryFn: () =>
- this.imageService.getOwnedImages({
+ protected readonly loadingImages = signal(false);
+ protected readonly loadImagesError = signal(null);
+ protected readonly imagesSlice = signal | null>(null);
+
+ protected readonly imageCount = signal(0);
+
+ protected readonly deleting = signal(false);
+ protected readonly deleteError = signal(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',
},
],
- size: this.pageSize(),
- }),
- placeholderData: keepPreviousData,
- }));
-
- protected readonly imageCount = computed(() => {
- if (this.imageViewsQuery.isSuccess()) {
- return this.imageViewsQuery.data()!.count;
- } else {
- return 0;
- }
- });
-
- protected readonly imageViewsWithBlobUrlsQueries = injectQueries(() => ({
- queries:
- this.imageViewsQuery.data()?.content.map((imageView) => ({
- queryKey: ['image-views-with-blob-urls', imageView.owner.username, imageView.filename],
- queryFn: () => this.imageService.getImageViewWithBlobUrl(imageView),
- })) ?? [],
- }));
+ })
+ .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()) {
@@ -81,9 +93,10 @@ export class ImageSelect {
this.currentPage.update((old) => Math.max(old - 1, 0));
} else {
// forward
- this.currentPage.update((old) => (this.imageViewsQuery.data()?.slice.hasNext ? old + 1 : old));
+ this.currentPage.update((old) => (this.imagesSlice()?.slice.hasNext ? old + 1 : old));
}
this.pageSize.set(pageEvent.pageSize);
+ this.loadImages();
}
protected onImageClick(imageView: ImageView): void {
@@ -109,8 +122,18 @@ export class ImageSelect {
});
}
- protected async deleteImage(imageView: ImageView): Promise {
- await this.imageService.deleteImage(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;
diff --git a/src/app/shared/models/Query.model.ts b/src/app/shared/models/Query.model.ts
index fc2b331..8a09c70 100644
--- a/src/app/shared/models/Query.model.ts
+++ b/src/app/shared/models/Query.model.ts
@@ -2,6 +2,7 @@ export interface QueryParams {
page?: number;
size?: number;
sort?: Array>;
+ custom?: Record;
}
export interface Sort {
diff --git a/src/app/shared/services/EndpointService.ts b/src/app/shared/services/EndpointService.ts
index 3d1d1fe..e461dc3 100644
--- a/src/app/shared/services/EndpointService.ts
+++ b/src/app/shared/services/EndpointService.ts
@@ -33,6 +33,11 @@ export class EndpointService {
urlSearchParams.append('sort', sortString);
}
});
+ if (queryParams?.custom !== undefined) {
+ Object.entries(queryParams.custom).forEach(([key, value]) => {
+ urlSearchParams.append(key, value.toString());
+ });
+ }
let pathString = pathParts?.join('/') || '';
if (pathString?.length) {
diff --git a/src/app/shared/services/ImageService.ts b/src/app/shared/services/ImageService.ts
index a76572b..4f89cd2 100644
--- a/src/app/shared/services/ImageService.ts
+++ b/src/app/shared/services/ImageService.ts
@@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
-import { firstValueFrom, map, Observable, tap } from 'rxjs';
+import { firstValueFrom, from, map, mergeMap, Observable, tap, toArray } from 'rxjs';
import { EndpointService } from './EndpointService';
import { ImageView } from '../models/ImageView.model';
import { SliceView } from '../models/SliceView.model';
@@ -41,10 +41,53 @@ export class ImageService {
};
}
- public getOwnedImages(queryParams?: QueryParams): Promise> {
- return firstValueFrom(
- this.httpClient.get>(this.endpointService.getUrl('images', [], queryParams)),
- );
+ public getOwnedImageViewsWithBlobUrls(
+ queryParams?: QueryParams,
+ ): Observable> {
+ return this.httpClient
+ .get>>(this.endpointService.getUrl('images', [], queryParams))
+ .pipe(
+ map((sliceView) => ({
+ ...sliceView,
+ content: sliceView.content.map((withStringDates) => this.hydrateImageView(withStringDates)),
+ })),
+ mergeMap((sliceView) => {
+ return from(sliceView.content).pipe(
+ mergeMap((imageView) => {
+ return this.httpClient
+ .get(
+ this.endpointService.getUrl('images', [
+ imageView.owner.username,
+ imageView.filename,
+ ]),
+ {
+ responseType: 'blob',
+ },
+ )
+ .pipe(
+ map((blob) => URL.createObjectURL(blob)),
+ map((blobUrl) => ({
+ ...imageView,
+ blobUrl,
+ })),
+ );
+ }),
+ toArray(),
+ map((content) => {
+ return {
+ ...sliceView,
+ content,
+ } satisfies SliceView;
+ }),
+ );
+ }),
+ );
+ }
+
+ public getOwnedImagesCount(): Observable {
+ return this.httpClient
+ .get<{ count: number }>(this.endpointService.getUrl('images', [], { custom: { count: true } }))
+ .pipe(map((res) => res.count));
}
public getImageViewWithBlobUrl(imageView: ImageView): Promise {
@@ -120,19 +163,8 @@ export class ImageService {
);
}
- public deleteImage(username: string, filename: string): Promise {
- return firstValueFrom(
- this.httpClient.delete(this.endpointService.getUrl('images', [username, filename])).pipe(
- tap(async () => {
- await this.queryClient.refetchQueries({
- queryKey: ['image-views', username, filename],
- });
- await this.queryClient.refetchQueries({
- queryKey: ['image-views-with-blob-urls', username, filename],
- });
- }),
- ),
- );
+ public deleteImage(username: string, filename: string): Observable {
+ return this.httpClient.delete(this.endpointService.getUrl('images', [username, filename]));
}
public updateImage(username: string, filename: string, data: ImageUpdateBody): Observable {