meals-made-easy-app/src/app/shared/services/ImageService.ts

148 lines
5.1 KiB
TypeScript

import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom, map, 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 { QueryClient, QueryOptions, queryOptions } from '@tanstack/angular-query-experimental';
import { ImageViewWithBlobUrl } from '../client-models/ImageViewWithBlobUrl';
@Injectable({
providedIn: 'root',
})
export class ImageService {
public static ImageProps = [
'id',
'created',
'modified',
'userFilename',
'mimeType',
'alt',
'caption',
'objectName',
'height',
'width',
'owner',
'viewers',
] as const;
private readonly httpClient = inject(HttpClient);
private readonly endpointService = inject(EndpointService);
private readonly queryClient = inject(QueryClient);
public getOwnedImages(queryParams?: QueryParams<typeof ImageService.ImageProps>): Promise<SliceView<ImageView>> {
return firstValueFrom(
this.httpClient.get<SliceView<ImageView>>(this.endpointService.getUrl('images', [], queryParams)),
);
}
public getImageViewWithBlobUrl(imageView: ImageView): Promise<ImageViewWithBlobUrl> {
return firstValueFrom(
this.httpClient
.get(this.endpointService.getUrl('images', [imageView.owner.username, imageView.filename]), {
responseType: 'blob',
})
.pipe(
map((blob) => URL.createObjectURL(blob)),
map(
(blobUrl) =>
({
...imageView,
blobUrl,
}) satisfies ImageViewWithBlobUrl,
),
),
);
}
public getImage(
imageView: ImageView,
): QueryOptions<ImageViewWithBlobUrl, Error, ImageViewWithBlobUrl, [string, string, string]> {
return queryOptions({
queryKey: ['image-views-with-blob-urls', imageView.owner.username, imageView.filename],
queryFn: () => this.getImageViewWithBlobUrl(imageView),
});
}
public uploadImage(
image: File,
filename?: string,
alt?: string,
caption?: string,
isPublic?: boolean,
): Promise<ImageView> {
const formData = new FormData();
formData.append('image', image);
formData.append('filename', filename ?? image.name);
if (alt) {
formData.append('alt', alt);
}
if (caption) {
formData.append('caption', caption);
}
if (isPublic !== undefined) {
formData.append('isPublic', isPublic.toString());
}
return firstValueFrom(
this.httpClient.post<ImageView>(this.endpointService.getUrl('images'), formData).pipe(
tap(async () => {
await this.queryClient.invalidateQueries({
queryKey: ['image-views'],
});
}),
),
);
}
public imageExists(username: string, filename: string): Promise<boolean> {
return firstValueFrom(
this.httpClient
.get<{ exists: boolean }>(this.endpointService.getUrl('images', [username, filename, 'exists']))
.pipe(map((view) => view.exists)),
);
}
public deleteImage(username: string, filename: string): Promise<void> {
return firstValueFrom(
this.httpClient.delete<void>(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 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<ImageView> {
return firstValueFrom(
this.httpClient.put<ImageView>(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],
});
}),
),
);
}
}