Add infinite query to comments, some endpoint api stuff.
This commit is contained in:
parent
714fe60d9f
commit
0bc434251e
3
src/app/endpoints.ts
Normal file
3
src/app/endpoints.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const Endpoints = {
|
||||||
|
recipes: 'recipes',
|
||||||
|
};
|
||||||
11
src/app/model/Query.model.ts
Normal file
11
src/app/model/Query.model.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export interface QueryParams {
|
||||||
|
page?: number;
|
||||||
|
size?: number;
|
||||||
|
sort?: Array<string | Sort>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Sort {
|
||||||
|
property: string;
|
||||||
|
order?: 'ASC' | 'DESC';
|
||||||
|
ignoreCase?: boolean;
|
||||||
|
}
|
||||||
@ -1,10 +1,8 @@
|
|||||||
import { ResourceOwner } from './ResourceOwner.model';
|
import { ResourceOwner } from './ResourceOwner.model';
|
||||||
|
import { SliceView } from './SliceView.model';
|
||||||
|
|
||||||
export interface RecipeComments {
|
export interface RecipeComments {
|
||||||
slice: {
|
slice: SliceView;
|
||||||
number: number;
|
|
||||||
size: number;
|
|
||||||
};
|
|
||||||
content: RecipeComment[];
|
content: RecipeComment[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
src/app/model/SliceView.model.ts
Normal file
5
src/app/model/SliceView.model.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export interface SliceView {
|
||||||
|
hasNext: boolean;
|
||||||
|
number: number;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
@ -14,13 +14,11 @@
|
|||||||
<p>You must be logged in to comment.</p>
|
<p>You must be logged in to comment.</p>
|
||||||
}
|
}
|
||||||
<h3>Comments</h3>
|
<h3>Comments</h3>
|
||||||
@if (commentsQuery.isLoading()) {
|
@if (commentsQuery.isPending()) {
|
||||||
<p>Loading comments...</p>
|
<p>Loading comments...</p>
|
||||||
} @else if (commentsQuery.isError()) {
|
} @else if (commentsQuery.isError()) {
|
||||||
<p>There was an error loading the comments.</p>
|
<p>There was an error loading the comments.</p>
|
||||||
} @else if (commentsQuery.isSuccess()) {
|
} @else {
|
||||||
@let comments = commentsQuery.data();
|
|
||||||
@if (comments.length) {
|
|
||||||
<ul>
|
<ul>
|
||||||
@if (addCommentMutation.isPending()) {
|
@if (addCommentMutation.isPending()) {
|
||||||
<li style="opacity: 0.5">
|
<li style="opacity: 0.5">
|
||||||
@ -28,15 +26,23 @@
|
|||||||
<div>{{ addCommentMutation.variables() }}</div>
|
<div>{{ addCommentMutation.variables() }}</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@for (comment of comments; track $index) {
|
@for (recipeComments of commentsQuery.data()?.pages; track $index) {
|
||||||
|
@for (recipeComment of recipeComments.content; track recipeComment.id) {
|
||||||
<li>
|
<li>
|
||||||
<p>{{ comment.owner.username }} at {{ comment.created }}</p>
|
<p>{{ recipeComment.owner.username }} at {{ recipeComment.created }}</p>
|
||||||
<div [innerHTML]="comment.text"></div>
|
<div [innerHTML]="recipeComment.text"></div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
|
||||||
} @else {
|
|
||||||
<p>There are no comments yet.</p>
|
|
||||||
}
|
}
|
||||||
|
</ul>
|
||||||
|
<div>
|
||||||
|
@if (commentsQuery.hasNextPage() && !commentsQuery.isFetchingNextPage()) {
|
||||||
|
<button (click)="commentsQuery.fetchNextPage()">Load more comments</button>
|
||||||
|
} @else if (commentsQuery.isFetchingNextPage()) {
|
||||||
|
<p>Loading comments...</p>
|
||||||
|
} @else if (!commentsQuery.hasNextPage()) {
|
||||||
|
<p>No additional comments to load.</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { Component, computed, inject, input } from '@angular/core';
|
import { Component, computed, inject, input } from '@angular/core';
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { RecipeService } from '../service/recipe.service';
|
import { RecipeService } from '../service/recipe.service';
|
||||||
import { injectMutation, injectQuery } from '@tanstack/angular-query-experimental';
|
import { injectInfiniteQuery, injectMutation } from '@tanstack/angular-query-experimental';
|
||||||
import { AuthService } from '../service/auth.service';
|
import { AuthService } from '../service/auth.service';
|
||||||
|
import { RecipeComments } from '../model/RecipeComment.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-comments-list',
|
selector: 'app-comments-list',
|
||||||
@ -20,9 +21,23 @@ export class RecipeCommentsList {
|
|||||||
protected readonly username = this.authService.username;
|
protected readonly username = this.authService.username;
|
||||||
protected readonly isLoggedIn = computed(() => !!this.authService.accessToken());
|
protected readonly isLoggedIn = computed(() => !!this.authService.accessToken());
|
||||||
|
|
||||||
protected readonly commentsQuery = injectQuery(() => ({
|
protected commentsQuery = injectInfiniteQuery(() => ({
|
||||||
|
initialPageParam: 0,
|
||||||
|
getNextPageParam: (previousPage: RecipeComments) =>
|
||||||
|
previousPage.slice.hasNext ? previousPage.slice.number + 1 : undefined,
|
||||||
queryKey: ['recipeComments', this.recipeUsername(), this.recipeSlug()],
|
queryKey: ['recipeComments', this.recipeUsername(), this.recipeSlug()],
|
||||||
queryFn: () => this.recipeService.getComments(this.recipeUsername(), this.recipeSlug()),
|
queryFn: ({ pageParam }) => {
|
||||||
|
return this.recipeService.getComments(this.recipeUsername(), this.recipeSlug(), {
|
||||||
|
page: pageParam,
|
||||||
|
size: 1,
|
||||||
|
sort: [
|
||||||
|
{
|
||||||
|
property: 'created',
|
||||||
|
order: 'DESC',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
protected readonly addCommentForm = new FormGroup({
|
protected readonly addCommentForm = new FormGroup({
|
||||||
@ -32,6 +47,7 @@ export class RecipeCommentsList {
|
|||||||
protected readonly addCommentMutation = injectMutation(() => ({
|
protected readonly addCommentMutation = injectMutation(() => ({
|
||||||
mutationFn: (commentText: string) =>
|
mutationFn: (commentText: string) =>
|
||||||
this.recipeService.addComment(this.recipeUsername(), this.recipeSlug(), commentText),
|
this.recipeService.addComment(this.recipeUsername(), this.recipeSlug(), commentText),
|
||||||
|
onSuccess: () => this.commentsQuery.fetchNextPage(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
protected onCommentSubmit() {
|
protected onCommentSubmit() {
|
||||||
|
|||||||
48
src/app/service/endpoint.service.ts
Normal file
48
src/app/service/endpoint.service.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Endpoints } from '../endpoints';
|
||||||
|
import { QueryParams } from '../model/Query.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class EndpointService {
|
||||||
|
public getUrl(
|
||||||
|
endpoint: keyof typeof Endpoints,
|
||||||
|
pathParams?: string[],
|
||||||
|
queryParams?: QueryParams,
|
||||||
|
): string {
|
||||||
|
const urlSearchParams = new URLSearchParams();
|
||||||
|
if (queryParams?.page !== undefined) {
|
||||||
|
urlSearchParams.set('page', queryParams.page.toString());
|
||||||
|
}
|
||||||
|
if (queryParams?.size !== undefined) {
|
||||||
|
urlSearchParams.set('size', queryParams.size.toString());
|
||||||
|
}
|
||||||
|
queryParams?.sort?.forEach((sort) => {
|
||||||
|
if (typeof sort === 'string') {
|
||||||
|
urlSearchParams.append('sort', sort);
|
||||||
|
} else {
|
||||||
|
let sortString = sort.property;
|
||||||
|
if (sort.order) {
|
||||||
|
sortString += ',' + sort.order;
|
||||||
|
}
|
||||||
|
if (sort.ignoreCase) {
|
||||||
|
sortString += ',IgnoreCase';
|
||||||
|
}
|
||||||
|
urlSearchParams.append('sort', sortString);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let pathString = pathParams?.join('/');
|
||||||
|
if (pathString?.length) {
|
||||||
|
pathString = '/' + pathString;
|
||||||
|
}
|
||||||
|
|
||||||
|
let queryString = urlSearchParams.toString();
|
||||||
|
if (queryString.length) {
|
||||||
|
queryString = '?' + queryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `http://localhost:8080/${Endpoints[endpoint]}${pathString}${queryString}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,8 @@ import { Recipe, RecipeInfoViews, RecipeView } from '../model/Recipe.model';
|
|||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { QueryClient } from '@tanstack/angular-query-experimental';
|
import { QueryClient } from '@tanstack/angular-query-experimental';
|
||||||
import { RecipeComment, RecipeComments } from '../model/RecipeComment.model';
|
import { RecipeComment, RecipeComments } from '../model/RecipeComment.model';
|
||||||
|
import { QueryParams } from '../model/Query.model';
|
||||||
|
import { EndpointService } from './endpoint.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -13,6 +15,7 @@ export class RecipeService {
|
|||||||
private readonly http = inject(HttpClient);
|
private readonly http = inject(HttpClient);
|
||||||
private readonly authService = inject(AuthService);
|
private readonly authService = inject(AuthService);
|
||||||
private readonly queryClient = inject(QueryClient);
|
private readonly queryClient = inject(QueryClient);
|
||||||
|
private readonly endpointService = inject(EndpointService);
|
||||||
|
|
||||||
public getRecipes(): Promise<Recipe[]> {
|
public getRecipes(): Promise<Recipe[]> {
|
||||||
return firstValueFrom(
|
return firstValueFrom(
|
||||||
@ -47,11 +50,15 @@ export class RecipeService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getComments(username: string, slug: string): Promise<RecipeComment[]> {
|
public getComments(
|
||||||
|
username: string,
|
||||||
|
slug: string,
|
||||||
|
queryParams?: QueryParams,
|
||||||
|
): Promise<RecipeComments> {
|
||||||
return firstValueFrom(
|
return firstValueFrom(
|
||||||
this.http
|
this.http.get<RecipeComments>(
|
||||||
.get<RecipeComments>(`http://localhost:8080/recipes/${username}/${slug}/comments`)
|
this.endpointService.getUrl('recipes', [username, slug, 'comments'], queryParams),
|
||||||
.pipe(map((res) => res.content)),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user