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 934e178..97de804 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,7 +1,7 @@
- @if (imageViewQuery.isLoading()) {
+ @if (loading()) {
- } @else if (imageViewQuery.isError()) {
+ } @else if (loadError()) {
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.spec.ts b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.spec.ts
index 3659271..1555aad 100644
--- a/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.spec.ts
+++ b/src/app/pages/recipe-upload-page/steps/enter-recipe-data/image-select/edit-image-dialog/edit-image-dialog.spec.ts
@@ -1,22 +1,68 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-
+import { By } from '@angular/platform-browser';
import { EditImageDialog } from './edit-image-dialog';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { Mocked } from 'vitest';
+import { ImageService } from '../../../../../../shared/services/ImageService';
+import { ImageView } from '../../../../../../shared/models/ImageView.model';
+import { of } from 'rxjs';
describe('EditImageDialog', () => {
let component: EditImageDialog;
let fixture: ComponentFixture;
+ let matDialogRef: Partial>>;
+
+ const username = 'test-user';
+ const filename = 'test-file.jpg';
beforeEach(async () => {
+ matDialogRef = {
+ close: vi.fn(),
+ } as Partial>>;
+
await TestBed.configureTestingModule({
imports: [EditImageDialog],
+ providers: [
+ {
+ provide: MAT_DIALOG_DATA,
+ useValue: { username, filename },
+ },
+ {
+ provide: MatDialogRef,
+ useValue: matDialogRef,
+ },
+ {
+ provide: ImageService,
+ useValue: {
+ getImageView2: vi.fn((username, filename) =>
+ of({
+ filename,
+ owner: {
+ username,
+ },
+ } as ImageView),
+ ),
+ updateImage: vi.fn(() => of({} as ImageView)),
+ } as Partial>,
+ },
+ ],
}).compileComponents();
fixture = TestBed.createComponent(EditImageDialog);
component = fixture.componentInstance;
await fixture.whenStable();
+ fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should close dialog after successful submit', async () => {
+ const formDebug = fixture.debugElement.query(By.css('form'));
+ expect(formDebug).toBeTruthy();
+ const formElement = formDebug.nativeElement as HTMLFormElement;
+ formElement.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
+ expect(matDialogRef.close).toHaveBeenCalled();
+ });
});
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 58836b1..94adbea 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
@@ -1,11 +1,10 @@
-import { Component, inject, OnInit } from '@angular/core';
+import { Component, inject, OnInit, signal } from '@angular/core';
import { DialogContainer } from '../../../../../../shared/components/dialog-container/dialog-container';
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
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';
@@ -21,11 +20,12 @@ export class EditImageDialog implements OnInit {
private readonly dialogRef = inject(MatDialogRef);
private readonly imageService = inject(ImageService);
- protected readonly imageViewQuery = injectQuery(() =>
- this.imageService.getImageView(this.usernameFilename[0], this.usernameFilename[1]),
- );
+ protected readonly loading = signal(false);
+ protected readonly loadError = signal(null);
+ protected readonly imageView = signal(null);
- private readonly imageViewMutation = injectMutation(() => this.imageService.updateImage2());
+ protected readonly submitting = signal(false);
+ protected readonly submitError = signal(null);
protected readonly imageForm = new FormGroup({
filename: new FormControl(
@@ -40,18 +40,27 @@ export class EditImageDialog implements OnInit {
});
public ngOnInit(): void {
- this.imageViewQuery.promise().then((imageView) => {
- this.imageForm.patchValue({
- filename: imageView.filename,
- alt: imageView.alt,
- caption: imageView.caption,
- });
+ this.loading.set(true);
+ this.imageService.getImageView2(this.usernameFilename[0], this.usernameFilename[1]).subscribe({
+ next: (imageView) => {
+ this.loading.set(false); // shouldn't need this
+ this.imageView.set(imageView);
+ this.imageForm.patchValue({
+ filename: imageView.filename,
+ alt: imageView.alt,
+ caption: imageView.caption,
+ });
+ },
+ error: (e) => {
+ this.loading.set(false);
+ this.loadError.set(e);
+ },
});
}
- public async onSubmit(event: SubmitEvent): Promise {
+ protected async onSubmit(event: SubmitEvent): Promise {
event.preventDefault();
- const imageView: ImageView = this.imageViewQuery.data()!;
+ const imageView = this.imageView()!;
const formValue = this.imageForm.value;
if (notNullOrUndefined(formValue.alt)) {
imageView.alt = formValue.alt;
@@ -59,10 +68,17 @@ export class EditImageDialog implements OnInit {
if (notNullOrUndefined(formValue.caption)) {
imageView.caption = formValue.caption;
}
- this.imageViewMutation.mutate(imageView, {
- onSuccess: () => {
+ this.submitting.set(true);
+ this.imageService.updateImage(imageView.owner.username, imageView.filename, imageView).subscribe({
+ next: (imageView) => {
+ this.submitting.set(false);
+ this.imageView.set(imageView);
this.dialogRef.close();
},
+ error: (e) => {
+ this.submitting.set(false);
+ this.submitError.set(e);
+ },
});
}
}
diff --git a/src/app/shared/bodies/ImageUpdateBody.ts b/src/app/shared/bodies/ImageUpdateBody.ts
new file mode 100644
index 0000000..4bb1954
--- /dev/null
+++ b/src/app/shared/bodies/ImageUpdateBody.ts
@@ -0,0 +1,8 @@
+export interface ImageUpdateBody {
+ alt?: string | null;
+ caption?: string | null;
+ isPublic?: boolean | null;
+ viewersToAdd?: string[] | null;
+ viewersToRemove?: string[] | null;
+ clearAllViewers?: boolean | null;
+}
diff --git a/src/app/shared/services/ImageService.ts b/src/app/shared/services/ImageService.ts
index 5a23926..a76572b 100644
--- a/src/app/shared/services/ImageService.ts
+++ b/src/app/shared/services/ImageService.ts
@@ -1,13 +1,14 @@
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
-import { firstValueFrom, map, tap } from 'rxjs';
+import { firstValueFrom, map, Observable, tap } from 'rxjs';
import { EndpointService } from './EndpointService';
import { ImageView } from '../models/ImageView.model';
import { SliceView } from '../models/SliceView.model';
import { QueryParams } from '../models/Query.model';
-import { mutationOptions, QueryClient, QueryOptions, queryOptions } from '@tanstack/angular-query-experimental';
+import { QueryClient, QueryOptions, queryOptions } from '@tanstack/angular-query-experimental';
import { ImageViewWithBlobUrl } from '../client-models/ImageViewWithBlobUrl';
import { WithStringDates } from '../util';
+import { ImageUpdateBody } from '../bodies/ImageUpdateBody';
@Injectable({
providedIn: 'root',
@@ -74,18 +75,10 @@ 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 getImageView2(username: string, filename: string): Observable {
+ return this.httpClient
+ .get>(this.endpointService.getUrl('images', [username, filename, 'view']))
+ .pipe(map((withStringDates) => this.hydrateImageView(withStringDates)));
}
public uploadImage(
@@ -142,47 +135,9 @@ export class ImageService {
);
}
- public updateImage(
- username: string,
- filename: string,
- data: {
- alt?: string | null;
- caption?: string | null;
- isPublic?: boolean | null;
- viewersToAdd?: string[] | null;
- viewersToRemove?: string[] | null;
- clearAllViewers?: boolean | null;
- },
- ): Promise {
- return firstValueFrom(
- this.httpClient.put(this.endpointService.getUrl('images', [username, filename]), data).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 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'],
- });
- },
- });
+ public updateImage(username: string, filename: string, data: ImageUpdateBody): Observable {
+ return this.httpClient
+ .put>(this.endpointService.getUrl('images', [username, filename]), data)
+ .pipe(map((withStringDates) => this.hydrateImageView(withStringDates)));
}
}