MME-34 Remove tanstack from recipe service and toggle star mutation.
This commit is contained in:
parent
b7c9e06d05
commit
4a271ecd10
@ -5,11 +5,22 @@
|
|||||||
<h1>{{ recipe.title }}</h1>
|
<h1>{{ recipe.title }}</h1>
|
||||||
<div class="recipe-actions">
|
<div class="recipe-actions">
|
||||||
@if (isLoggedIn()) {
|
@if (isLoggedIn()) {
|
||||||
<button id="star" matButton="filled" (click)="starMutation.mutate()">
|
<button
|
||||||
|
id="star"
|
||||||
|
[matButton]="recipeView().isStarred ? 'filled' : 'outlined'"
|
||||||
|
(click)="onToggleStar()"
|
||||||
|
>
|
||||||
<div id="star-label">
|
<div id="star-label">
|
||||||
<fa-icon [icon]="faStar" />
|
<fa-icon [icon]="faStar" />
|
||||||
<span>Star</span>
|
@if (recipeView().isStarred) {
|
||||||
|
<span>Starred</span>
|
||||||
|
} @else {
|
||||||
|
<span>Star</span>
|
||||||
|
}
|
||||||
<span id="star-count">{{ recipe.starCount }}</span>
|
<span id="star-count">{{ recipe.starCount }}</span>
|
||||||
|
@if (togglingStar()) {
|
||||||
|
<app-spinner size="12px"></app-spinner>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
} @else {
|
} @else {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { Component, computed, inject, input, OnInit, signal } from '@angular/core';
|
import { Component, computed, inject, input, OnInit, output, signal } from '@angular/core';
|
||||||
import { FullRecipeViewWrapper } from '../../../shared/models/Recipe.model';
|
import { FullRecipeViewWrapper } from '../../../shared/models/Recipe.model';
|
||||||
import { injectMutation } from '@tanstack/angular-query-experimental';
|
|
||||||
import { ImageService } from '../../../shared/services/ImageService';
|
import { ImageService } from '../../../shared/services/ImageService';
|
||||||
import { faEllipsis, faGlobe, faLock, faStar, faUser } from '@fortawesome/free-solid-svg-icons';
|
import { faEllipsis, faGlobe, faLock, faStar, faUser } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
@ -23,7 +22,8 @@ import { ToastrService } from 'ngx-toastr';
|
|||||||
styleUrl: './recipe-page-content.css',
|
styleUrl: './recipe-page-content.css',
|
||||||
})
|
})
|
||||||
export class RecipePageContent implements OnInit {
|
export class RecipePageContent implements OnInit {
|
||||||
public recipeView = input.required<FullRecipeViewWrapper>();
|
public readonly recipeView = input.required<FullRecipeViewWrapper>();
|
||||||
|
public readonly requireHotReload = output<void>();
|
||||||
|
|
||||||
private readonly imageService = inject(ImageService);
|
private readonly imageService = inject(ImageService);
|
||||||
private readonly recipeService = inject(RecipeService);
|
private readonly recipeService = inject(RecipeService);
|
||||||
@ -42,6 +42,8 @@ export class RecipePageContent implements OnInit {
|
|||||||
return !!recipe.preparationTime || !!recipe.cookingTime || !!recipe.totalTime;
|
return !!recipe.preparationTime || !!recipe.cookingTime || !!recipe.totalTime;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
protected readonly togglingStar = signal(false);
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
const recipe = this.recipeView().recipe;
|
const recipe = this.recipeView().recipe;
|
||||||
if (recipe.mainImage) {
|
if (recipe.mainImage) {
|
||||||
@ -60,10 +62,6 @@ export class RecipePageContent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly starMutation = injectMutation(() => ({
|
|
||||||
mutationFn: () => this.recipeService.toggleStar(this.recipeView()),
|
|
||||||
}));
|
|
||||||
|
|
||||||
private readonly dialog = inject(MatDialog);
|
private readonly dialog = inject(MatDialog);
|
||||||
private readonly toastrService = inject(ToastrService);
|
private readonly toastrService = inject(ToastrService);
|
||||||
|
|
||||||
@ -90,6 +88,22 @@ export class RecipePageContent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected onToggleStar(): void {
|
||||||
|
this.togglingStar.set(true);
|
||||||
|
const recipe = this.recipeView().recipe;
|
||||||
|
this.recipeService.toggleStar(recipe.owner.username, recipe.slug).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.togglingStar.set(false);
|
||||||
|
this.requireHotReload.emit();
|
||||||
|
},
|
||||||
|
error: (e) => {
|
||||||
|
this.togglingStar.set(false);
|
||||||
|
this.toastrService.error('There was an error toggling the star');
|
||||||
|
console.error(e);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected readonly faStar = faStar;
|
protected readonly faStar = faStar;
|
||||||
protected readonly faUser = faUser;
|
protected readonly faUser = faUser;
|
||||||
protected readonly faGlobe = faGlobe;
|
protected readonly faGlobe = faGlobe;
|
||||||
|
|||||||
@ -3,5 +3,5 @@
|
|||||||
} @else if (loadRecipeError()) {
|
} @else if (loadRecipeError()) {
|
||||||
<p>There was an error loading the recipe.</p>
|
<p>There was an error loading the recipe.</p>
|
||||||
} @else if (recipe(); as recipe) {
|
} @else if (recipe(); as recipe) {
|
||||||
<app-recipe-page-content [recipeView]="recipe"></app-recipe-page-content>
|
<app-recipe-page-content [recipeView]="recipe" (requireHotReload)="onRequireHotReload()"></app-recipe-page-content>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,4 +35,16 @@ export class RecipePage implements OnInit {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected onRequireHotReload(): void {
|
||||||
|
this.recipeService.getRecipeView(this.username, this.slug).subscribe({
|
||||||
|
next: (recipe) => {
|
||||||
|
this.recipe.set(recipe);
|
||||||
|
},
|
||||||
|
error: (e) => {
|
||||||
|
this.loadRecipeError.set(e);
|
||||||
|
console.error(e);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { lastValueFrom, map, Observable } from 'rxjs';
|
import { map, Observable } from 'rxjs';
|
||||||
import { FullRecipeView, FullRecipeViewWrapper, RecipeInfoView } from '../models/Recipe.model';
|
import { FullRecipeView, FullRecipeViewWrapper, RecipeInfoView } from '../models/Recipe.model';
|
||||||
import { AuthService } from './AuthService';
|
|
||||||
import { QueryClient } from '@tanstack/angular-query-experimental';
|
|
||||||
import { RecipeComment } from '../models/RecipeComment.model';
|
import { RecipeComment } from '../models/RecipeComment.model';
|
||||||
import { QueryParams } from '../models/Query.model';
|
import { QueryParams } from '../models/Query.model';
|
||||||
import { EndpointService } from './EndpointService';
|
import { EndpointService } from './EndpointService';
|
||||||
@ -30,8 +28,6 @@ export class RecipeService {
|
|||||||
public static readonly RecipeCommentProperties = ['id', 'created', 'modified'] as const;
|
public static readonly RecipeCommentProperties = ['id', 'created', 'modified'] as const;
|
||||||
|
|
||||||
private readonly http = inject(HttpClient);
|
private readonly http = inject(HttpClient);
|
||||||
private readonly authService = inject(AuthService);
|
|
||||||
private readonly queryClient = inject(QueryClient);
|
|
||||||
private readonly endpointService = inject(EndpointService);
|
private readonly endpointService = inject(EndpointService);
|
||||||
private readonly imageService = inject(ImageService);
|
private readonly imageService = inject(ImageService);
|
||||||
|
|
||||||
@ -106,23 +102,8 @@ export class RecipeService {
|
|||||||
.pipe(map((raw) => this.hydrateFullRecipeViewWrapper(raw)));
|
.pipe(map((raw) => this.hydrateFullRecipeViewWrapper(raw)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRecipeBaseUrl(recipeView: FullRecipeViewWrapper): string {
|
public toggleStar(username: string, slug: string): Observable<void> {
|
||||||
return this.endpointService.getUrl('recipes', [recipeView.recipe.owner.username, recipeView.recipe.slug]);
|
return this.http.post<void>(this.endpointService.getUrl('recipes', [username, slug, 'star', 'toggle']), null);
|
||||||
}
|
|
||||||
|
|
||||||
public async toggleStar(recipeView: FullRecipeViewWrapper): Promise<void> {
|
|
||||||
if (this.authService.accessToken()) {
|
|
||||||
if (recipeView.isStarred) {
|
|
||||||
await lastValueFrom(this.http.delete(this.getRecipeBaseUrl(recipeView) + '/star'));
|
|
||||||
} else {
|
|
||||||
await lastValueFrom(this.http.post(this.getRecipeBaseUrl(recipeView) + '/star', null));
|
|
||||||
}
|
|
||||||
await this.queryClient.invalidateQueries({
|
|
||||||
queryKey: ['recipe', recipeView.recipe.owner.username, recipeView.recipe.slug],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error('Cannot star a recipe when not logged in.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getComments(
|
public getComments(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user