Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WEB-UI: Add infinite scroll to blog posts #354

Merged
merged 5 commits into from
Sep 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Type } from "class-transformer";

Check warning on line 1 in services/api/src/core/query/application/contracts/queries/base.query.ts

View check run for this annotation

Codecov / codecov/patch

services/api/src/core/query/application/contracts/queries/base.query.ts#L1

Added line #L1 was not covered by tests
import { IsOptional, Min } from "class-validator";

export class BaseQuery {
@IsOptional()
@Type(() => Number)

Check warning on line 6 in services/api/src/core/query/application/contracts/queries/base.query.ts

View check run for this annotation

Codecov / codecov/patch

services/api/src/core/query/application/contracts/queries/base.query.ts#L6

Added line #L6 was not covered by tests
@Min(0)
skip?: number;

@IsOptional()
@Type(() => Number)

Check warning on line 11 in services/api/src/core/query/application/contracts/queries/base.query.ts

View check run for this annotation

Codecov / codecov/patch

services/api/src/core/query/application/contracts/queries/base.query.ts#L11

Added line #L11 was not covered by tests
@Min(0)
top?: number;
}
1 change: 1 addition & 0 deletions services/web-ui/src/core/infinite-scroll/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useInfiniteScroll } from "./useInfiniteScroll";

Check warning on line 1 in services/web-ui/src/core/infinite-scroll/index.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/index.ts#L1

Added line #L1 was not covered by tests
35 changes: 35 additions & 0 deletions services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useEffect, useRef } from "react";

Check warning on line 1 in services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts#L1

Added line #L1 was not covered by tests

interface useInfiniteScrollProps {
onNext: () => void;
}

export function useInfiniteScroll({ onNext }: useInfiniteScrollProps) {
const observerTarget = useRef(null);

Check warning on line 8 in services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts#L7-L8

Added lines #L7 - L8 were not covered by tests

useEffect(() => {
const current = observerTarget.current;

Check warning on line 11 in services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts#L10-L11

Added lines #L10 - L11 were not covered by tests

const observer = new IntersectionObserver(
([entry]) => {

Check warning on line 14 in services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts#L13-L14

Added lines #L13 - L14 were not covered by tests
if (entry.isIntersecting) {
onNext();

Check warning on line 16 in services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts#L16

Added line #L16 was not covered by tests
}
},
{ threshold: 0.1 }
);

if (current) {
observer.observe(current);

Check warning on line 23 in services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts#L23

Added line #L23 was not covered by tests
}

return () => {

Check warning on line 26 in services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts#L26

Added line #L26 was not covered by tests
if (current) {
observer.unobserve(current);

Check warning on line 28 in services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts#L28

Added line #L28 was not covered by tests
}
};
/* eslint-disable-next-line react-hooks/exhaustive-deps */
}, [observerTarget.current]);

return { observerTarget };

Check warning on line 34 in services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/core/infinite-scroll/useInfiniteScroll.ts#L34

Added line #L34 was not covered by tests
}
2 changes: 1 addition & 1 deletion services/web-ui/src/core/query/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { useQuery } from "react-query";
export { useInfiniteQuery, useQuery } from "react-query";
export { CacheKeysConstants } from "./cache-keys.constants";
68 changes: 37 additions & 31 deletions services/web-ui/src/pages/BlogPostPage/BlogPostPage.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import { BlogPostMenu } from "./components/BlogPostMenu";

interface BlogPostPageComponentProps {
data: BlogPostDetails[];
data: BlogPostDetails[][];
}

export function BlogPostPageComponent({
Expand All @@ -24,41 +24,47 @@
<BlogPostForm />
</Box>

{data.map((post) => (
<Card
key={post.id}
sx={{ marginBottom: 2, padding: 2 }}
variant="outlined"
>
<Box sx={{ alignItems: "center", display: "flex", paddingBottom: 2 }}>
<Avatar src={post.profile.imageUrl} />
{data.map((group, groupIndex) => (
<React.Fragment key={groupIndex}>

Check warning on line 28 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.component.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.component.tsx#L28

Added line #L28 was not covered by tests
{group.map((post) => (
<Card

Check warning on line 30 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.component.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.component.tsx#L30

Added line #L30 was not covered by tests
key={post.id}
sx={{ marginBottom: 2, padding: 2 }}
variant="outlined"
>
<Box
sx={{ alignItems: "center", display: "flex", paddingBottom: 2 }}
>
<Avatar src={post.profile.imageUrl} />

<Box sx={{ flexGrow: 1, paddingLeft: 1 }}>
<Typography>
<b>{post.profile.name}</b> {t("published")}
</Typography>
<Typography variant="subtitle2">
{timeSince(post.createdAt)}
</Typography>
</Box>
<Box sx={{ flexGrow: 1, paddingLeft: 1 }}>
<Typography>
<b>{post.profile.name}</b> {t("published")}
</Typography>
<Typography variant="subtitle2">
{timeSince(post.createdAt)}
</Typography>
</Box>

{post.ownedByCurrentUser && <BlogPostMenu id={post.id} />}
</Box>
{post.ownedByCurrentUser && <BlogPostMenu id={post.id} />}
</Box>

<Typography>{post.content}</Typography>
<Typography>{post.content}</Typography>

{post.photos.map((photo) => (
<img
alt={photo.description || "Blog post image"}
key={photo.id}
src={photo.url}
style={{
padding: 10,
maxWidth: "100%",
}}
/>
{post.photos.map((photo) => (
<img

Check warning on line 55 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.component.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.component.tsx#L55

Added line #L55 was not covered by tests
alt={photo.description || "Blog post image"}
key={photo.id}
src={photo.url}
style={{
padding: 10,
maxWidth: "100%",
}}
/>
))}
</Card>
))}
</Card>
</React.Fragment>
))}
</>
);
Expand Down
46 changes: 38 additions & 8 deletions services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
import React from "react";

import { Box, Typography } from "@mui/material";
import { Box, CircularProgress, Typography } from "@mui/material";

Check warning on line 3 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx#L3

Added line #L3 was not covered by tests

import { blogsApi } from "src/apis";
import { ErrorMessage } from "src/components/ui/ErrorMessage";
import { useTranslation } from "src/core/i18n";
import { CacheKeysConstants, useQuery } from "src/core/query";
import { useInfiniteScroll } from "src/core/infinite-scroll";
import { CacheKeysConstants, useInfiniteQuery } from "src/core/query";

Check warning on line 9 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx#L8-L9

Added lines #L8 - L9 were not covered by tests

import { BlogPostPageComponent } from "./BlogPostPage.component";
import { BlogPostPageHeader } from "./BlogPostPage.header";
import { BlogPostPageSkeleton } from "./BlogPostPage.skeleton";
import { BlogPostForm } from "./components/BlogPostForm";

const ITEMS_PER_PAGE = 10;

Check warning on line 16 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx#L16

Added line #L16 was not covered by tests

export function BlogPostPageContainer(): React.ReactElement {
const { t } = useTranslation("blog");

const { error, data, isLoading } = useQuery(
CacheKeysConstants.BlogPosts,
() => blogsApi.getBlogPosts()
);
const { error, data, isLoading, fetchNextPage, hasNextPage } =
useInfiniteQuery(

Check warning on line 22 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx#L22

Added line #L22 was not covered by tests
[CacheKeysConstants.BlogPosts],
({ pageParam = 0 }) =>
blogsApi.getBlogPosts({

Check warning on line 25 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx#L25

Added line #L25 was not covered by tests
skip: pageParam * ITEMS_PER_PAGE,
top: (pageParam + 1) * ITEMS_PER_PAGE,
}),
{
getNextPageParam: (_, pages) => pages.length - 1,

Check warning on line 30 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx#L30

Added line #L30 was not covered by tests
}
);

const { observerTarget } = useInfiniteScroll({

Check warning on line 34 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx#L34

Added line #L34 was not covered by tests
onNext: fetchNextPage,
});

if (error) {
return (
Expand All @@ -38,7 +53,7 @@
);
}

if (!data || data.length === 0) {
if (!data || data.pages.length === 0) {
return (
<>
<BlogPostPageHeader />
Expand All @@ -56,7 +71,22 @@
<>
<BlogPostPageHeader />

<BlogPostPageComponent data={data} />
<BlogPostPageComponent data={data.pages} />

{hasNextPage && (
<Box

Check warning on line 77 in services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/pages/BlogPostPage/BlogPostPage.container.tsx#L77

Added line #L77 was not covered by tests
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
height: 100,
}}
ref={observerTarget}
>
<CircularProgress />
</Box>
)}
</>
);
}
4 changes: 4 additions & 0 deletions services/web-ui/src/utils/error.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
const response = error.response;

if (response.errorMessage) {
if (Array.isArray(response.errorMessage)) {
return response.errorMessage.join(", ");

Check warning on line 8 in services/web-ui/src/utils/error.utils.ts

View check run for this annotation

Codecov / codecov/patch

services/web-ui/src/utils/error.utils.ts#L8

Added line #L8 was not covered by tests
}

return response.errorMessage;
}

Expand Down