import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; 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'; import { QueryParams } from '../models/Query.model'; 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', }) 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 hydrateImageView(rawImageView: WithStringDates): ImageView { return { ...rawImageView, created: new Date(rawImageView.created), modified: rawImageView.modified ? new Date(rawImageView.modified) : undefined, }; } 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 { 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 { return queryOptions({ queryKey: ['image-views-with-blob-urls', imageView.owner.username, imageView.filename], queryFn: () => this.getImageViewWithBlobUrl(imageView), }); } 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( image: File, filename?: string, alt?: string, caption?: string, isPublic?: boolean, ): Promise { 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(this.endpointService.getUrl('images'), formData).pipe( tap(async () => { await this.queryClient.invalidateQueries({ queryKey: ['image-views'], }); }), ), ); } public imageExists(username: string, filename: string): Promise { 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): Observable { return this.httpClient.delete(this.endpointService.getUrl('images', [username, filename])); } 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))); } }