import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { delay, firstValueFrom, map, Observable, of } from 'rxjs'; import { RecipeUploadClientModel } from '../client-models/RecipeUploadClientModel'; import { RecipeUploadStep } from '../client-models/RecipeUploadStep'; import { RecipeDraftViewModel } from '../models/RecipeDraftView.model'; import { EndpointService } from './EndpointService'; 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', }) 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, inferredAt: new Date(rawView.lastInference.inferredAt), } : undefined, }; } public getInProgressDrafts(): Promise { return firstValueFrom( this.http.get[]>(this.endpointService.getUrl('recipeDrafts')).pipe( map((rawViews) => { return rawViews.map((rawView) => this.hydrateView(rawView)); }), ), ); } public getRecipeUploadClientModel(draftId: string): Observable { return this.http .get>(this.endpointService.getUrl('recipeDrafts', [draftId])) .pipe( map((rawDraft) => { return this.hydrateView(rawDraft); }), map((draft) => { return { draft, inProgressStep: draft.state === 'ENTER_DATA' ? RecipeUploadStep.ENTER_DATA : RecipeUploadStep.INFER, }; }), ); } public createManualDraft(): Promise { return firstValueFrom( this.http .post< WithStringDates >(this.endpointService.getUrl('recipeDrafts', ['manual']), null) .pipe( map((rawDraft) => this.hydrateView(rawDraft)), map((draft) => ({ draft, inProgressStep: RecipeUploadStep.ENTER_DATA, })), ), ); } public updateDraft( id: string, data: { title?: string | null; slug?: string | null; ingredients?: Array<{ amount?: string | null; name: string; notes?: string | null; }>; mainImage?: ImageView | null; rawText?: string | null; }, ): Promise { return firstValueFrom( this.http .put>(this.endpointService.getUrl('recipeDrafts', [id]), { ...data, mainImage: data.mainImage ? ({ username: data.mainImage.owner.username, userFilename: data.mainImage.filename, } satisfies SetImageBody) : undefined, }) .pipe( map((rawView) => this.hydrateView(rawView)), map((draft) => ({ draft, inProgressStep: RecipeUploadStep.ENTER_DATA, })), ), ); } public publish(id: string): Promise { return firstValueFrom( this.http.post(this.endpointService.getUrl('recipeDrafts', [id, 'publish']), null), ); } public deleteDraft(id: string): Promise { return firstValueFrom(this.http.delete(this.endpointService.getUrl('recipeDrafts', [id]))); } public doInference(model: RecipeUploadClientModel): Observable { return of({ inProgressStep: RecipeUploadStep.ENTER_DATA, id: 16, inferredTitle: 'Some recipe', inferredSlug: 'some-recipe', inferredText: 'Some text.', inferredIngredients: [], }).pipe(delay(5_000)); } }