Compare commits
No commits in common. "14acdc3e831b1ba55035b79f6957d46bbebfd6f9" and "ff9ffd9e13348064d944f7baaae05a3ed4912bb5" have entirely different histories.
14acdc3e83
...
ff9ffd9e13
@ -31,18 +31,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (mainImageUrl.isSuccess()) {
|
@if (mainImageUrl.isSuccess()) {
|
||||||
@let maybeMainImageUrl = mainImageUrl.data();
|
<img
|
||||||
@if (!!maybeMainImageUrl) {
|
id="main-image"
|
||||||
<img
|
[src]="mainImageUrl.data()"
|
||||||
id="main-image"
|
[alt]="recipe.mainImage.alt"
|
||||||
[src]="maybeMainImageUrl"
|
[height]="recipe.mainImage.height"
|
||||||
[alt]="recipe.mainImage!.alt"
|
[width]="recipe.mainImage.width"
|
||||||
[height]="recipe.mainImage!.height"
|
/>
|
||||||
[width]="recipe.mainImage!.width"
|
|
||||||
/>
|
|
||||||
} @else {
|
|
||||||
<p>!! Placeholder todo !!</p>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
<div [innerHTML]="recipe.text"></div>
|
<div [innerHTML]="recipe.text"></div>
|
||||||
<app-recipe-comments-list [recipeUsername]="recipe.owner.username" [recipeSlug]="recipe.slug" />
|
<app-recipe-comments-list [recipeUsername]="recipe.owner.username" [recipeSlug]="recipe.slug" />
|
||||||
|
|||||||
@ -27,8 +27,8 @@ export class RecipePageContent {
|
|||||||
protected readonly mainImageUrl = injectQuery(() => {
|
protected readonly mainImageUrl = injectQuery(() => {
|
||||||
const recipe = this.recipeView().recipe;
|
const recipe = this.recipeView().recipe;
|
||||||
return {
|
return {
|
||||||
queryKey: ['recipe-main-images', recipe.owner.username, recipe.slug],
|
queryKey: ['images', recipe.mainImage.owner.username, recipe.mainImage.filename],
|
||||||
queryFn: () => this.imageService.getImage(recipe.mainImage?.url),
|
queryFn: () => this.imageService.getImage(recipe.mainImage.url),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -16,9 +16,7 @@
|
|||||||
} @else if (displayStep() === RecipeUploadStep.INFER) {
|
} @else if (displayStep() === RecipeUploadStep.INFER) {
|
||||||
<app-infer></app-infer>
|
<app-infer></app-infer>
|
||||||
} @else if (displayStep() === RecipeUploadStep.ENTER_DATA) {
|
} @else if (displayStep() === RecipeUploadStep.ENTER_DATA) {
|
||||||
<app-enter-recipe-data [model]="model()" (submit)="onEnterRecipeDataEvent($event)"></app-enter-recipe-data>
|
<app-enter-recipe-data [model]="model()"></app-enter-recipe-data>
|
||||||
} @else if (displayStep() === RecipeUploadStep.REVIEW) {
|
|
||||||
<app-review [draft]="model().draft!" (publish)="onPublish()"></app-review>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|||||||
@ -13,12 +13,10 @@ import { RecipeUploadStep } from '../../shared/client-models/RecipeUploadStep';
|
|||||||
import { FileUploadEvent } from '../../shared/components/file-upload/FileUploadEvent';
|
import { FileUploadEvent } from '../../shared/components/file-upload/FileUploadEvent';
|
||||||
import { tryMaybeInt } from '../../shared/util';
|
import { tryMaybeInt } from '../../shared/util';
|
||||||
import { from, map, switchMap, tap } from 'rxjs';
|
import { from, map, switchMap, tap } from 'rxjs';
|
||||||
import { EnterRecipeDataEvent } from './steps/enter-recipe-data/EnterRecipeDataEvent';
|
|
||||||
import { Review } from './steps/review/review';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-recipe-upload-page',
|
selector: 'app-recipe-upload-page',
|
||||||
imports: [ReactiveFormsModule, AiOrManual, Infer, EnterRecipeData, RecipeUploadTrail, Review],
|
imports: [ReactiveFormsModule, AiOrManual, Infer, EnterRecipeData, RecipeUploadTrail],
|
||||||
templateUrl: './recipe-upload-page.html',
|
templateUrl: './recipe-upload-page.html',
|
||||||
styleUrl: './recipe-upload-page.css',
|
styleUrl: './recipe-upload-page.css',
|
||||||
})
|
})
|
||||||
@ -34,15 +32,7 @@ export class RecipeUploadPage implements OnInit {
|
|||||||
|
|
||||||
private readonly router = inject(Router);
|
private readonly router = inject(Router);
|
||||||
private readonly activatedRoute = inject(ActivatedRoute);
|
private readonly activatedRoute = inject(ActivatedRoute);
|
||||||
private readonly recipeDraftService = inject(RecipeDraftService);
|
private readonly recipeUploadService = inject(RecipeDraftService);
|
||||||
|
|
||||||
private isValidStep(step: number): boolean {
|
|
||||||
if (this.model().draft?.lastInference || this.model().draft?.state === 'INFER') {
|
|
||||||
return step <= RecipeUploadStep.REVIEW;
|
|
||||||
} else {
|
|
||||||
return [0, 2, 3].includes(step);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.activatedRoute.queryParamMap
|
this.activatedRoute.queryParamMap
|
||||||
@ -53,20 +43,21 @@ export class RecipeUploadPage implements OnInit {
|
|||||||
return [paramMap.get('draftId'), step] as const;
|
return [paramMap.get('draftId'), step] as const;
|
||||||
}),
|
}),
|
||||||
switchMap(([draftId, step]) => {
|
switchMap(([draftId, step]) => {
|
||||||
|
const currentModel = this.model();
|
||||||
if (draftId !== null) {
|
if (draftId !== null) {
|
||||||
return this.recipeDraftService.getRecipeUploadClientModel(draftId).pipe(
|
return this.recipeUploadService.getRecipeUploadClientModel(draftId).pipe(
|
||||||
tap(async (recipeUploadClientModel) => {
|
tap((recipeUploadClientModel) => {
|
||||||
await this.switchModel(recipeUploadClientModel);
|
this.switchModel(recipeUploadClientModel);
|
||||||
}),
|
}),
|
||||||
switchMap((updatedModel) => {
|
switchMap((updatedModel) => {
|
||||||
if (step !== null && this.isValidStep(step)) {
|
if (step !== null && step <= updatedModel.inProgressStep) {
|
||||||
return from(this.changeDisplayStep(step));
|
return from(this.changeDisplayStep(step));
|
||||||
} else {
|
} else {
|
||||||
return from(this.changeDisplayStep(updatedModel.inProgressStep));
|
return from(this.changeDisplayStep(updatedModel.inProgressStep));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else if (step !== null && this.isValidStep(step)) {
|
} else if (step !== null && step <= currentModel.inProgressStep) {
|
||||||
return from(this.changeDisplayStep(step));
|
return from(this.changeDisplayStep(step));
|
||||||
} else {
|
} else {
|
||||||
return from(this.changeDisplayStep(RecipeUploadStep.START));
|
return from(this.changeDisplayStep(RecipeUploadStep.START));
|
||||||
@ -76,17 +67,9 @@ export class RecipeUploadPage implements OnInit {
|
|||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async switchModel(
|
private switchModel(model: RecipeUploadClientModel): void {
|
||||||
model: RecipeUploadClientModel,
|
|
||||||
switchStep: boolean | RecipeUploadStep = false,
|
|
||||||
): Promise<void> {
|
|
||||||
this.model.set(model);
|
this.model.set(model);
|
||||||
this.includeInfer.set(!!model.draft?.lastInference);
|
this.includeInfer.set(!!model.draft?.lastInference);
|
||||||
if (switchStep === true) {
|
|
||||||
await this.changeDisplayStep(model.inProgressStep);
|
|
||||||
} else if (typeof switchStep === 'number') {
|
|
||||||
await this.changeDisplayStep(switchStep);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async changeDisplayStep(targetStep: number): Promise<void> {
|
private async changeDisplayStep(targetStep: number): Promise<void> {
|
||||||
@ -120,8 +103,13 @@ export class RecipeUploadPage implements OnInit {
|
|||||||
|
|
||||||
protected async onAiOrManualSubmit(event: AIOrManualSubmitEvent): Promise<void> {
|
protected async onAiOrManualSubmit(event: AIOrManualSubmitEvent): Promise<void> {
|
||||||
if (event.mode === 'manual') {
|
if (event.mode === 'manual') {
|
||||||
const model = await this.recipeDraftService.createManualDraft();
|
this.model.update((model) => ({
|
||||||
await this.switchModel(model, true);
|
...model,
|
||||||
|
inputSourceFile: null,
|
||||||
|
inProgressStep: RecipeUploadStep.ENTER_DATA,
|
||||||
|
}));
|
||||||
|
await this.changeDisplayStep(RecipeUploadStep.ENTER_DATA);
|
||||||
|
this.includeInfer.set(false);
|
||||||
} else {
|
} else {
|
||||||
this.model.update((model) => ({
|
this.model.update((model) => ({
|
||||||
...model,
|
...model,
|
||||||
@ -130,29 +118,97 @@ export class RecipeUploadPage implements OnInit {
|
|||||||
}));
|
}));
|
||||||
await this.changeDisplayStep(RecipeUploadStep.INFER);
|
await this.changeDisplayStep(RecipeUploadStep.INFER);
|
||||||
this.includeInfer.set(true);
|
this.includeInfer.set(true);
|
||||||
this.recipeDraftService.doInference(this.model()).subscribe((updatedModel) => {
|
this.recipeUploadService.doInference(this.model()).subscribe((updatedModel) => {
|
||||||
this.model.set(updatedModel);
|
this.model.set(updatedModel);
|
||||||
this.changeDisplayStep(RecipeUploadStep.ENTER_DATA);
|
this.changeDisplayStep(RecipeUploadStep.ENTER_DATA);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onEnterRecipeDataEvent(event: EnterRecipeDataEvent): Promise<void> {
|
// private readonly sseClient = inject(SseClient);
|
||||||
if (event._type === 'submit') {
|
// private readonly formBuilder = inject(FormBuilder);
|
||||||
const { title, slug, rawText } = event.data;
|
//
|
||||||
const model = await this.recipeDraftService.updateDraft(this.model().draft!.id, {
|
// protected readonly sourceRecipeImage = signal<string | null>(null);
|
||||||
title,
|
// protected readonly inferenceInProgress = signal(false);
|
||||||
slug,
|
//
|
||||||
rawText,
|
// protected readonly recipeUploadForm = this.formBuilder.group({
|
||||||
});
|
// file: this.formBuilder.control<File | null>(null, [Validators.required]),
|
||||||
await this.switchModel(model, RecipeUploadStep.REVIEW);
|
// });
|
||||||
}
|
//
|
||||||
}
|
// protected readonly recipeForm = new FormGroup({
|
||||||
|
// title: new FormControl('', [Validators.required]),
|
||||||
protected async onPublish(): Promise<void> {
|
// recipeText: new FormControl('', Validators.required),
|
||||||
const recipe = await this.recipeDraftService.publish(this.model().draft!.id);
|
// });
|
||||||
await this.router.navigate(['recipes', recipe.owner.username, recipe.slug]);
|
//
|
||||||
}
|
// protected onClear() {
|
||||||
|
// this.recipeUploadForm.reset();
|
||||||
|
// this.sourceRecipeImage.set(null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected onFileChange(event: Event) {
|
||||||
|
// const fileInput = event.target as HTMLInputElement;
|
||||||
|
// if (fileInput.files && fileInput.files.length) {
|
||||||
|
// const file = fileInput.files[0];
|
||||||
|
// this.recipeUploadForm.controls.file.setValue(file);
|
||||||
|
// this.recipeUploadForm.controls.file.markAsTouched();
|
||||||
|
// this.recipeUploadForm.controls.file.updateValueAndValidity();
|
||||||
|
//
|
||||||
|
// // set source image
|
||||||
|
// this.sourceRecipeImage.set(URL.createObjectURL(file));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected onFileSubmit() {
|
||||||
|
// const rawValue = this.recipeUploadForm.getRawValue();
|
||||||
|
//
|
||||||
|
// this.inferenceInProgress.set(true);
|
||||||
|
//
|
||||||
|
// // upload form data
|
||||||
|
// const formData = new FormData();
|
||||||
|
// formData.append('recipeImageFile', rawValue.file!, rawValue.file!.name);
|
||||||
|
// this.sseClient
|
||||||
|
// .stream(
|
||||||
|
// `http://localhost:8080/inferences/recipe-extract-stream`,
|
||||||
|
// {
|
||||||
|
// keepAlive: false,
|
||||||
|
// reconnectionDelay: 1000,
|
||||||
|
// responseType: 'event',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// body: formData,
|
||||||
|
// },
|
||||||
|
// 'PUT',
|
||||||
|
// )
|
||||||
|
// .subscribe({
|
||||||
|
// next: (event) => {
|
||||||
|
// if (event.type === 'error') {
|
||||||
|
// const errorEvent = event as ErrorEvent;
|
||||||
|
// console.error(errorEvent.error, errorEvent.message);
|
||||||
|
// } else {
|
||||||
|
// const messageEvent = event as MessageEvent;
|
||||||
|
// const data: { delta: string } = JSON.parse(messageEvent.data);
|
||||||
|
// this.recipeForm.patchValue({
|
||||||
|
// recipeText: this.recipeForm.value.recipeText + data.delta,
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// // must do this so we auto-resize the textarea
|
||||||
|
// document.getElementById('recipe-text')?.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// complete: () => {
|
||||||
|
// this.inferenceInProgress.set(false);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected onRecipeSubmit() {
|
||||||
|
// console.log(this.recipeForm.value);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected onRecipeTextChange(event: Event) {
|
||||||
|
// const textarea = event.target as HTMLTextAreaElement;
|
||||||
|
// textarea.style.height = 'auto';
|
||||||
|
// textarea.style.height = textarea.scrollHeight + 'px';
|
||||||
|
// }
|
||||||
protected readonly RecipeUploadStep = RecipeUploadStep;
|
protected readonly RecipeUploadStep = RecipeUploadStep;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
export type EnterRecipeDataEvent = EnterRecipeDataSubmitEvent;
|
|
||||||
|
|
||||||
export interface EnterRecipeDataSubmitEvent {
|
|
||||||
_type: 'submit';
|
|
||||||
data: {
|
|
||||||
title: string;
|
|
||||||
slug: string;
|
|
||||||
rawText: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<h2>Enter Recipe</h2>
|
<h2>Enter Recipe</h2>
|
||||||
<form [formGroup]="recipeFormGroup" (submit)="onSubmit($event)">
|
<form [formGroup]="recipeFormGroup">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-label>Title</mat-label>
|
<mat-label>Title</mat-label>
|
||||||
<input matInput [formControl]="recipeFormGroup.controls.title" />
|
<input matInput [formControl]="recipeFormGroup.controls.title" />
|
||||||
@ -17,5 +17,4 @@
|
|||||||
(input)="onRecipeTextChange($event)"
|
(input)="onRecipeTextChange($event)"
|
||||||
></textarea>
|
></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button matButton="filled" type="submit">Review</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -6,25 +6,21 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
input,
|
input,
|
||||||
OnInit,
|
OnInit,
|
||||||
output,
|
|
||||||
runInInjectionContext,
|
runInInjectionContext,
|
||||||
viewChild,
|
viewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { RecipeUploadClientModel } from '../../../../shared/client-models/RecipeUploadClientModel';
|
import { RecipeUploadClientModel } from '../../../../shared/client-models/RecipeUploadClientModel';
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
||||||
import { MatButton } from '@angular/material/button';
|
|
||||||
import { EnterRecipeDataEvent } from './EnterRecipeDataEvent';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-enter-recipe-data',
|
selector: 'app-enter-recipe-data',
|
||||||
imports: [ReactiveFormsModule, MatFormField, MatLabel, MatInput, MatButton],
|
imports: [ReactiveFormsModule, MatFormField, MatLabel, MatInput],
|
||||||
templateUrl: './enter-recipe-data.html',
|
templateUrl: './enter-recipe-data.html',
|
||||||
styleUrl: './enter-recipe-data.css',
|
styleUrl: './enter-recipe-data.css',
|
||||||
})
|
})
|
||||||
export class EnterRecipeData implements OnInit {
|
export class EnterRecipeData implements OnInit {
|
||||||
public readonly model = input.required<RecipeUploadClientModel>();
|
public readonly model = input.required<RecipeUploadClientModel>();
|
||||||
public readonly submit = output<EnterRecipeDataEvent>();
|
|
||||||
|
|
||||||
protected recipeTextTextarea = viewChild.required<ElementRef<HTMLTextAreaElement>>('recipeTextTextarea');
|
protected recipeTextTextarea = viewChild.required<ElementRef<HTMLTextAreaElement>>('recipeTextTextarea');
|
||||||
|
|
||||||
@ -65,17 +61,4 @@ export class EnterRecipeData implements OnInit {
|
|||||||
protected onRecipeTextChange(event: Event): void {
|
protected onRecipeTextChange(event: Event): void {
|
||||||
this.updateTextareaHeight(event.target as HTMLTextAreaElement);
|
this.updateTextareaHeight(event.target as HTMLTextAreaElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onSubmit(event: SubmitEvent): void {
|
|
||||||
event.preventDefault();
|
|
||||||
const { title, slug, text } = this.recipeFormGroup.value;
|
|
||||||
this.submit.emit({
|
|
||||||
_type: 'submit',
|
|
||||||
data: {
|
|
||||||
title: title!,
|
|
||||||
slug: slug!,
|
|
||||||
rawText: text!,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
<section>
|
|
||||||
<h2>Review and Publish</h2>
|
|
||||||
<p>Title: {{ draft().title }}</p>
|
|
||||||
<p>Slug: {{ draft().slug }}</p>
|
|
||||||
<div>
|
|
||||||
<p>Text: todo</p>
|
|
||||||
</div>
|
|
||||||
<button matButton="filled" (click)="onPublish()">Publish</button>
|
|
||||||
</section>
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { Review } from './review';
|
|
||||||
|
|
||||||
describe('Review', () => {
|
|
||||||
let component: Review;
|
|
||||||
let fixture: ComponentFixture<Review>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [Review],
|
|
||||||
}).compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(Review);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
await fixture.whenStable();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
import { Component, input, output } from '@angular/core';
|
|
||||||
import { RecipeDraftViewModel } from '../../../../shared/models/RecipeDraftView.model';
|
|
||||||
import { MatButton } from '@angular/material/button';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-review',
|
|
||||||
imports: [MatButton],
|
|
||||||
templateUrl: './review.html',
|
|
||||||
styleUrl: './review.css',
|
|
||||||
})
|
|
||||||
export class Review {
|
|
||||||
public readonly draft = input.required<RecipeDraftViewModel>();
|
|
||||||
public readonly publish = output<void>();
|
|
||||||
|
|
||||||
protected onPublish(): void {
|
|
||||||
this.publish.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,5 +2,4 @@ export enum RecipeUploadStep {
|
|||||||
START,
|
START,
|
||||||
INFER,
|
INFER,
|
||||||
ENTER_DATA,
|
ENTER_DATA,
|
||||||
REVIEW,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,7 @@
|
|||||||
<article>
|
<article>
|
||||||
<a [routerLink]="recipePageLink()">
|
<a [routerLink]="recipePageLink()">
|
||||||
@if (mainImage.isSuccess()) {
|
@if (mainImage.isSuccess()) {
|
||||||
@let maybeMainImageUrl = mainImage.data();
|
<img [src]="mainImage.data()" id="recipe-card-image" [alt]="recipe.mainImage.alt" />
|
||||||
@if (!!maybeMainImageUrl) {
|
|
||||||
<img [src]="mainImage.data()" id="recipe-card-image" [alt]="recipe.mainImage?.alt" />
|
|
||||||
} @else {
|
|
||||||
<p>!! Placeholder todo !!</p>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</a>
|
</a>
|
||||||
<div id="title-and-visibility">
|
<div id="title-and-visibility">
|
||||||
|
|||||||
@ -30,8 +30,8 @@ export class RecipeCard {
|
|||||||
protected readonly mainImage = injectQuery(() => {
|
protected readonly mainImage = injectQuery(() => {
|
||||||
const recipe = this.recipe();
|
const recipe = this.recipe();
|
||||||
return {
|
return {
|
||||||
queryKey: ['recipe-main-images', recipe.owner.username, recipe.slug],
|
queryKey: ['images', recipe.mainImage.owner.username, recipe.mainImage.filename],
|
||||||
queryFn: () => this.imageService.getImage(recipe.mainImage?.url),
|
queryFn: () => this.imageService.getImage(recipe.mainImage.url),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export interface RecipeView {
|
|||||||
export interface Recipe {
|
export interface Recipe {
|
||||||
id: number;
|
id: number;
|
||||||
isPublic: boolean;
|
isPublic: boolean;
|
||||||
mainImage?: ImageView | null;
|
mainImage: ImageView;
|
||||||
owner: ResourceOwner;
|
owner: ResourceOwner;
|
||||||
slug: string;
|
slug: string;
|
||||||
starCount: number;
|
starCount: number;
|
||||||
|
|||||||
@ -8,17 +8,13 @@ import { firstValueFrom, map } from 'rxjs';
|
|||||||
export class ImageService {
|
export class ImageService {
|
||||||
private readonly httpClient = inject(HttpClient);
|
private readonly httpClient = inject(HttpClient);
|
||||||
|
|
||||||
public getImage(backendUrl?: string | null): Promise<string | null> {
|
public getImage(backendUrl: string): Promise<string> {
|
||||||
if (!!backendUrl) {
|
return firstValueFrom(
|
||||||
return firstValueFrom(
|
this.httpClient
|
||||||
this.httpClient
|
.get(backendUrl, {
|
||||||
.get(backendUrl, {
|
responseType: 'blob',
|
||||||
responseType: 'blob',
|
})
|
||||||
})
|
.pipe(map((blob) => URL.createObjectURL(blob))),
|
||||||
.pipe(map((blob) => URL.createObjectURL(blob))),
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Promise.resolve(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { RecipeUploadStep } from '../client-models/RecipeUploadStep';
|
|||||||
import { RecipeDraftViewModel } from '../models/RecipeDraftView.model';
|
import { RecipeDraftViewModel } from '../models/RecipeDraftView.model';
|
||||||
import { EndpointService } from './EndpointService';
|
import { EndpointService } from './EndpointService';
|
||||||
import { WithStringDates } from '../util';
|
import { WithStringDates } from '../util';
|
||||||
import { Recipe } from '../models/Recipe.model';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -56,49 +55,6 @@ export class RecipeDraftService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createManualDraft(): Promise<RecipeUploadClientModel> {
|
|
||||||
return firstValueFrom(
|
|
||||||
this.http
|
|
||||||
.post<
|
|
||||||
WithStringDates<RecipeDraftViewModel>
|
|
||||||
>(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;
|
|
||||||
rawText?: string | null;
|
|
||||||
},
|
|
||||||
): Promise<RecipeUploadClientModel> {
|
|
||||||
return firstValueFrom(
|
|
||||||
this.http
|
|
||||||
.put<WithStringDates<RecipeDraftViewModel>>(this.endpointService.getUrl('recipeDrafts', [id]), data)
|
|
||||||
.pipe(
|
|
||||||
map((rawView) => this.hydrateView(rawView)),
|
|
||||||
map((draft) => ({
|
|
||||||
draft,
|
|
||||||
inProgressStep: RecipeUploadStep.ENTER_DATA,
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public publish(id: string): Promise<Recipe> {
|
|
||||||
return firstValueFrom(
|
|
||||||
this.http.post<Recipe>(this.endpointService.getUrl('recipeDrafts', [id, 'publish']), null),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public doInference(model: RecipeUploadClientModel): Observable<RecipeUploadClientModel> {
|
public doInference(model: RecipeUploadClientModel): Observable<RecipeUploadClientModel> {
|
||||||
return of({
|
return of({
|
||||||
inProgressStep: RecipeUploadStep.ENTER_DATA,
|
inProgressStep: RecipeUploadStep.ENTER_DATA,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user