diff --git a/src/app/app.config.ts b/src/app/app.config.ts
index 9317cf6..b0671c6 100644
--- a/src/app/app.config.ts
+++ b/src/app/app.config.ts
@@ -12,6 +12,15 @@ export const appConfig: ApplicationConfig = {
provideBrowserGlobalErrorListeners(),
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor])),
- provideTanStackQuery(new QueryClient(), withDevtools()),
+ provideTanStackQuery(
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ experimental_prefetchInRender: true,
+ },
+ },
+ }),
+ withDevtools(),
+ ),
],
};
diff --git a/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.html b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.html
index 1b8a264..934e178 100644
--- a/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.html
+++ b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.html
@@ -1,17 +1,23 @@
-
+ @if (imageViewQuery.isLoading()) {
+
+ } @else if (imageViewQuery.isError()) {
+ There was an error.
+ } @else {
+
+ }
diff --git a/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.ts b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.ts
index ac94769..58836b1 100644
--- a/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.ts
+++ b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.ts
@@ -5,18 +5,27 @@ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angula
import { ImageView } from '../../../../../../shared/models/ImageView.model';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatButton } from '@angular/material/button';
+import { injectMutation, injectQuery } from '@tanstack/angular-query-experimental';
import { ImageService } from '../../../../../../shared/services/ImageService';
+import { notNullOrUndefined } from '../../../../../../shared/util';
+import { Spinner } from '../../../../../../shared/components/spinner/spinner';
@Component({
selector: 'app-edit-image-dialog',
- imports: [DialogContainer, MatFormField, MatLabel, MatInput, ReactiveFormsModule, MatButton],
+ imports: [DialogContainer, MatFormField, MatLabel, MatInput, ReactiveFormsModule, MatButton, Spinner],
templateUrl: './edit-image-dialog.html',
styleUrl: './edit-image-dialog.css',
})
export class EditImageDialog implements OnInit {
- protected readonly imageView: ImageView = inject(MAT_DIALOG_DATA);
- private readonly imageService = inject(ImageService);
+ private readonly usernameFilename: [username: string, filename: string] = inject(MAT_DIALOG_DATA);
private readonly dialogRef = inject(MatDialogRef);
+ private readonly imageService = inject(ImageService);
+
+ protected readonly imageViewQuery = injectQuery(() =>
+ this.imageService.getImageView(this.usernameFilename[0], this.usernameFilename[1]),
+ );
+
+ private readonly imageViewMutation = injectMutation(() => this.imageService.updateImage2());
protected readonly imageForm = new FormGroup({
filename: new FormControl(
@@ -31,19 +40,29 @@ export class EditImageDialog implements OnInit {
});
public ngOnInit(): void {
- this.imageForm.patchValue({
- filename: this.imageView.filename,
- alt: this.imageView.alt,
- caption: this.imageView.alt,
+ this.imageViewQuery.promise().then((imageView) => {
+ this.imageForm.patchValue({
+ filename: imageView.filename,
+ alt: imageView.alt,
+ caption: imageView.caption,
+ });
});
}
public async onSubmit(event: SubmitEvent): Promise {
event.preventDefault();
- await this.imageService.updateImage(this.imageView.owner.username, this.imageView.filename, {
- alt: this.imageForm.value.alt,
- caption: this.imageForm.value.caption,
+ const imageView: ImageView = this.imageViewQuery.data()!;
+ const formValue = this.imageForm.value;
+ if (notNullOrUndefined(formValue.alt)) {
+ imageView.alt = formValue.alt;
+ }
+ if (notNullOrUndefined(formValue.caption)) {
+ imageView.caption = formValue.caption;
+ }
+ this.imageViewMutation.mutate(imageView, {
+ onSuccess: () => {
+ this.dialogRef.close();
+ },
});
- this.dialogRef.close();
}
}
diff --git a/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select.ts b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select.ts
index 9c7efe6..a8d6ce6 100644
--- a/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select.ts
+++ b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select.ts
@@ -105,7 +105,7 @@ export class ImageSelect {
protected editImage(imageView: ImageView): void {
this.dialog.open(EditImageDialog, {
- data: imageView,
+ data: [imageView.owner.username, imageView.filename],
});
}
diff --git a/src/app/shared/models/ImageView.model.ts b/src/app/shared/models/ImageView.model.ts
index c155026..fe5e021 100644
--- a/src/app/shared/models/ImageView.model.ts
+++ b/src/app/shared/models/ImageView.model.ts
@@ -1,10 +1,15 @@
import { ResourceOwner } from './ResourceOwner.model';
export interface ImageView {
- alt: string;
- filename: string;
- height: number | null;
- owner: ResourceOwner;
url: string;
+ created: Date;
+ modified?: Date | null;
+ filename: string;
+ mimeType: string;
+ alt?: string | null;
+ caption?: string | null;
+ owner: ResourceOwner;
+ isPublic?: boolean;
+ height: number | null;
width: number | null;
}
diff --git a/src/app/shared/services/ImageService.ts b/src/app/shared/services/ImageService.ts
index 7122981..5a23926 100644
--- a/src/app/shared/services/ImageService.ts
+++ b/src/app/shared/services/ImageService.ts
@@ -5,8 +5,9 @@ import { EndpointService } from './EndpointService';
import { ImageView } from '../models/ImageView.model';
import { SliceView } from '../models/SliceView.model';
import { QueryParams } from '../models/Query.model';
-import { QueryClient, QueryOptions, queryOptions } from '@tanstack/angular-query-experimental';
+import { mutationOptions, QueryClient, QueryOptions, queryOptions } from '@tanstack/angular-query-experimental';
import { ImageViewWithBlobUrl } from '../client-models/ImageViewWithBlobUrl';
+import { WithStringDates } from '../util';
@Injectable({
providedIn: 'root',
@@ -31,6 +32,14 @@ export class ImageService {
private readonly endpointService = inject(EndpointService);
private readonly queryClient = inject(QueryClient);
+ public hydrateImageView(rawImageView: WithStringDates): ImageView {
+ return {
+ ...rawImageView,
+ created: new Date(rawImageView.created),
+ modified: rawImageView.modified ? new Date(rawImageView.modified) : undefined,
+ };
+ }
+
public getOwnedImages(queryParams?: QueryParams): Promise> {
return firstValueFrom(
this.httpClient.get>(this.endpointService.getUrl('images', [], queryParams)),
@@ -65,6 +74,20 @@ export class ImageService {
});
}
+ public getImageView(username: string, filename: string) {
+ return queryOptions({
+ queryKey: ['image-views', username, filename],
+ queryFn: () =>
+ firstValueFrom(
+ this.httpClient
+ .get<
+ WithStringDates
+ >(this.endpointService.getUrl('images', [username, filename, 'view']))
+ .pipe(map((rawImageView) => this.hydrateImageView(rawImageView))),
+ ),
+ });
+ }
+
public uploadImage(
image: File,
filename?: string,
@@ -144,4 +167,22 @@ export class ImageService {
),
);
}
+
+ public updateImage2() {
+ return mutationOptions({
+ mutationKey: ['image-views'],
+ mutationFn: (imageView: ImageView) =>
+ firstValueFrom(
+ this.httpClient.put(
+ this.endpointService.getUrl('images', [imageView.owner.username, imageView.filename]),
+ imageView,
+ ),
+ ),
+ onSuccess: async () => {
+ await this.queryClient.invalidateQueries({
+ queryKey: ['image-views'],
+ });
+ },
+ });
+ }
}
diff --git a/src/app/shared/services/RecipeDraftService.ts b/src/app/shared/services/RecipeDraftService.ts
index f0a1ef2..5745819 100644
--- a/src/app/shared/services/RecipeDraftService.ts
+++ b/src/app/shared/services/RecipeDraftService.ts
@@ -9,6 +9,7 @@ import { WithStringDates } from '../util';
import { Recipe } from '../models/Recipe.model';
import { ImageView } from '../models/ImageView.model';
import { SetImageBody } from '../models/SetImageBody';
+import { ImageService } from './ImageService';
@Injectable({
providedIn: 'root',
@@ -16,12 +17,14 @@ import { SetImageBody } from '../models/SetImageBody';
export class RecipeDraftService {
private readonly http = inject(HttpClient);
private readonly endpointService = inject(EndpointService);
+ private readonly imageService = inject(ImageService);
private hydrateView(rawView: WithStringDates): RecipeDraftViewModel {
return {
...rawView,
created: new Date(rawView.created),
modified: rawView.modified ? new Date(rawView.modified) : undefined,
+ mainImage: rawView.mainImage ? this.imageService.hydrateImageView(rawView.mainImage) : undefined,
lastInference: rawView.lastInference
? {
...rawView.lastInference,
diff --git a/src/app/shared/util.ts b/src/app/shared/util.ts
index 3208242..4553882 100644
--- a/src/app/shared/util.ts
+++ b/src/app/shared/util.ts
@@ -33,3 +33,7 @@ export type WithStringDates = {
? WithStringDates | null | undefined
: T[K];
};
+
+export const notNullOrUndefined = (t: T | null | undefined): t is T => {
+ return t !== null && t !== undefined;
+};