Compare commits
No commits in common. "714fe60d9fb2aa5a1e5f67f213200c7b38e90e57" and "b8110e893a71fb228216252f403e66409ea4fae5" have entirely different histories.
714fe60d9f
...
b8110e893a
@ -2,8 +2,7 @@
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"cli": {
|
||||
"packageManager": "npm",
|
||||
"analytics": false
|
||||
"packageManager": "npm"
|
||||
},
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import { ResourceOwner } from './ResourceOwner.model';
|
||||
|
||||
export interface RecipeComments {
|
||||
slice: {
|
||||
number: number;
|
||||
size: number;
|
||||
};
|
||||
content: RecipeComment[];
|
||||
}
|
||||
|
||||
export interface RecipeComment {
|
||||
id: number;
|
||||
created: string;
|
||||
modified: string | null;
|
||||
text: string;
|
||||
rawText: string | null;
|
||||
owner: ResourceOwner;
|
||||
recipeId: number;
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
<div id="comments-list-container">
|
||||
<h2>Comments</h2>
|
||||
@if (isLoggedIn()) {
|
||||
<div id="add-comment-container">
|
||||
<h3>Add Comment</h3>
|
||||
<form [formGroup]="addCommentForm" (ngSubmit)="onCommentSubmit()">
|
||||
<label for="comment">Comment</label>
|
||||
<input id="comment" formControlName="comment" type="text" />
|
||||
|
||||
<button type="submit" [disabled]="!addCommentForm.valid">Post Comment</button>
|
||||
</form>
|
||||
</div>
|
||||
} @else {
|
||||
<p>You must be logged in to comment.</p>
|
||||
}
|
||||
<h3>Comments</h3>
|
||||
@if (commentsQuery.isLoading()) {
|
||||
<p>Loading comments...</p>
|
||||
} @else if (commentsQuery.isError()) {
|
||||
<p>There was an error loading the comments.</p>
|
||||
} @else if (commentsQuery.isSuccess()) {
|
||||
@let comments = commentsQuery.data();
|
||||
@if (comments.length) {
|
||||
<ul>
|
||||
@if (addCommentMutation.isPending()) {
|
||||
<li style="opacity: 0.5">
|
||||
<p>{{ username() }}</p>
|
||||
<div>{{ addCommentMutation.variables() }}</div>
|
||||
</li>
|
||||
}
|
||||
@for (comment of comments; track $index) {
|
||||
<li>
|
||||
<p>{{ comment.owner.username }} at {{ comment.created }}</p>
|
||||
<div [innerHTML]="comment.text"></div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
} @else {
|
||||
<p>There are no comments yet.</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@ -1,22 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RecipeCommentsList } from './recipe-comments-list.component';
|
||||
|
||||
describe('CommentsList', () => {
|
||||
let component: RecipeCommentsList;
|
||||
let fixture: ComponentFixture<RecipeCommentsList>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [RecipeCommentsList],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RecipeCommentsList);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -1,40 +0,0 @@
|
||||
import { Component, computed, inject, input } from '@angular/core';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { RecipeService } from '../service/recipe.service';
|
||||
import { injectMutation, injectQuery } from '@tanstack/angular-query-experimental';
|
||||
import { AuthService } from '../service/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-comments-list',
|
||||
imports: [ReactiveFormsModule],
|
||||
templateUrl: './recipe-comments-list.component.html',
|
||||
styleUrl: './recipe-comments-list.component.css',
|
||||
})
|
||||
export class RecipeCommentsList {
|
||||
public readonly recipeUsername = input.required<string>();
|
||||
public readonly recipeSlug = input.required<string>();
|
||||
|
||||
private readonly recipeService = inject(RecipeService);
|
||||
private readonly authService = inject(AuthService);
|
||||
|
||||
protected readonly username = this.authService.username;
|
||||
protected readonly isLoggedIn = computed(() => !!this.authService.accessToken());
|
||||
|
||||
protected readonly commentsQuery = injectQuery(() => ({
|
||||
queryKey: ['recipeComments', this.recipeUsername(), this.recipeSlug()],
|
||||
queryFn: () => this.recipeService.getComments(this.recipeUsername(), this.recipeSlug()),
|
||||
}));
|
||||
|
||||
protected readonly addCommentForm = new FormGroup({
|
||||
comment: new FormControl('', Validators.required),
|
||||
});
|
||||
|
||||
protected readonly addCommentMutation = injectMutation(() => ({
|
||||
mutationFn: (commentText: string) =>
|
||||
this.recipeService.addComment(this.recipeUsername(), this.recipeSlug(), commentText),
|
||||
}));
|
||||
|
||||
protected onCommentSubmit() {
|
||||
this.addCommentMutation.mutate(this.addCommentForm.value.comment!);
|
||||
}
|
||||
}
|
||||
@ -38,5 +38,4 @@
|
||||
/>
|
||||
}
|
||||
<div [innerHTML]="recipe.text"></div>
|
||||
<app-comments-list [recipeUsername]="recipe.owner.username" [recipeSlug]="recipe.slug" />
|
||||
</article>
|
||||
|
||||
@ -6,11 +6,10 @@ import { faGlobe, faLock, faStar, faUser } from '@fortawesome/free-solid-svg-ico
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { RecipeService } from '../../service/recipe.service';
|
||||
import { AuthService } from '../../service/auth.service';
|
||||
import { RecipeCommentsList } from '../../recipe-comments-list/recipe-comments-list.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-recipe-page-content',
|
||||
imports: [FaIconComponent, RecipeCommentsList],
|
||||
imports: [FaIconComponent],
|
||||
templateUrl: './recipe-page-content.html',
|
||||
styleUrl: './recipe-page-content.css',
|
||||
})
|
||||
|
||||
@ -4,7 +4,6 @@ import { firstValueFrom, lastValueFrom, map } from 'rxjs';
|
||||
import { Recipe, RecipeInfoViews, RecipeView } from '../model/Recipe.model';
|
||||
import { AuthService } from './auth.service';
|
||||
import { QueryClient } from '@tanstack/angular-query-experimental';
|
||||
import { RecipeComment, RecipeComments } from '../model/RecipeComment.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@ -22,7 +21,7 @@ export class RecipeService {
|
||||
);
|
||||
}
|
||||
|
||||
public getRecipeView(username: string, slug: string): Promise<RecipeView> {
|
||||
public async getRecipeView(username: string, slug: string): Promise<RecipeView> {
|
||||
return firstValueFrom(
|
||||
this.http.get<RecipeView>(`http://localhost:8080/recipes/${username}/${slug}`),
|
||||
);
|
||||
@ -32,7 +31,7 @@ export class RecipeService {
|
||||
return `http://localhost:8080/recipes/${recipeView.recipe.owner.username}/${recipeView.recipe.slug}`;
|
||||
}
|
||||
|
||||
public async toggleStar(recipeView: RecipeView): Promise<void> {
|
||||
public async toggleStar(recipeView: RecipeView) {
|
||||
if (this.authService.accessToken()) {
|
||||
if (recipeView.isStarred) {
|
||||
await lastValueFrom(this.http.delete(this.getRecipeUrl(recipeView) + '/star'));
|
||||
@ -46,31 +45,4 @@ export class RecipeService {
|
||||
throw new Error('Cannot star a recipe when not logged in.');
|
||||
}
|
||||
}
|
||||
|
||||
public getComments(username: string, slug: string): Promise<RecipeComment[]> {
|
||||
return firstValueFrom(
|
||||
this.http
|
||||
.get<RecipeComments>(`http://localhost:8080/recipes/${username}/${slug}/comments`)
|
||||
.pipe(map((res) => res.content)),
|
||||
);
|
||||
}
|
||||
|
||||
public async addComment(
|
||||
username: string,
|
||||
slug: string,
|
||||
commentText: string,
|
||||
): Promise<RecipeComment> {
|
||||
const comment = await firstValueFrom(
|
||||
this.http.post<RecipeComment>(
|
||||
`http://localhost:8080/recipes/${username}/${slug}/comments`,
|
||||
{
|
||||
text: commentText,
|
||||
},
|
||||
),
|
||||
);
|
||||
await this.queryClient.invalidateQueries({
|
||||
queryKey: ['recipeComments', username, slug],
|
||||
});
|
||||
return comment;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user