Skip to content

Commit

Permalink
feat/#49/myprojects 무한스크롤 및 불러오기 구현 완료
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinmj12 committed Jan 14, 2025
1 parent 8e6ca4c commit 3fcd105
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 86 deletions.
30 changes: 0 additions & 30 deletions src/components/Mypage/MyProjects.tsx

This file was deleted.

107 changes: 107 additions & 0 deletions src/components/Mypage/MyProjects/MyProjects.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { SEARCH_PROJECT } from "@/services/gql/searchProject";
import { useSearchProject } from "@/stores/searchProjectStore";
import { useQuery } from "@apollo/client";
import { useEffect, useRef, useState } from "react";
import MypageProjectCard from "@/components/ProjectCard/Mypage/MypageProjectCard";

export default function MyProjectComponents() {
const { page, hasNext, projects, setPage, setHasNext, setProjects, reset } =
useSearchProject();

const SIZE = 5;
const [isLoading, setIsLoading] = useState(true);
const observerRef = useRef<HTMLDivElement>(null);

const { data, loading, fetchMore } = useQuery(SEARCH_PROJECT, {
variables: {
page: page,
size: SIZE,
title: "",
stackNames: [],
categories: [],
},
});

useEffect(() => {
reset();
}, []);

// 무한 스크롤
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting && hasNext && !loading) {
loadMoreProjects();
}
},
{ threshold: 0.8 },
);

if (observerRef.current) observer.observe(observerRef.current);
return () => observer.disconnect();
}, [observerRef, data, hasNext, loading]);

// projects 배열에 새로운 데이터 추가
useEffect(() => {
if (data) {
if (page === 0) {
// 초기 페이지일 경우, 기존 데이터를 초기화
setProjects(data.searchProject.projects);
} else {
// 추가 데이터만 병합
setProjects([...projects, ...data.searchProject.projects]);
}
setHasNext(data.searchProject.hasNext);
setIsLoading(false);
}
}, [data]);

const loadMoreProjects = async () => {
await fetchMore({
variables: {
page: page,
size: 36,
title: "",
stackNames: [],
categories: [],
},
updateQuery: (prevResult, { fetchMoreResult }) => {
if (!fetchMoreResult) return prevResult;
else {
setHasNext(fetchMoreResult.searchProject.hasNext);
setPage(page + 1);
}
return {
searchProject: {
...fetchMoreResult.searchProject,
projects: [
// ...prevResult.getAllProjectsByPagination.projects,
...fetchMoreResult.searchProject.projects,
],
},
};
},
});
};

return (
<div>
{!isLoading && projects.length === 0 ? (
<p style={{ marginTop: "20px", fontSize: "20px" }}>
등록한 프로젝트가 없습니다.
</p>
) : (
<div>
{projects.map((project, index) => (
<MypageProjectCard key={index} projectCard={project} />
))}

<div
ref={observerRef}
style={{ height: "1px", backgroundColor: "transparent" }}
/>
</div>
)}
</div>
);
}
24 changes: 11 additions & 13 deletions src/components/Mypage/MypageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import Image from "next/image";
import Link from "next/link";
import { useSidebarStore } from "@/stores/mypageSidebarStore";
import { LuFileText, LuHeart, LuSettings } from "react-icons/lu";
import { useAuthStore } from "@/stores/authStore";

function MypageMyInfo() {
const { user } = useAuthStore();
return (
<Styles.MypageMyinfo>
<Image
Expand All @@ -14,8 +16,8 @@ function MypageMyInfo() {
height={200}
style={{ borderRadius: "50%" }}
/>
<Styles.MypageNickname>kevinmj12</Styles.MypageNickname>
<Styles.MypageEmail>[email protected]</Styles.MypageEmail>
<Styles.MypageNickname>{user?.data?.username}</Styles.MypageNickname>
<Styles.MypageEmail>{user?.data?.email}</Styles.MypageEmail>
</Styles.MypageMyinfo>
);
}
Expand Down Expand Up @@ -71,16 +73,12 @@ type MypageLayoutProps = {

export default function MypageLayout({ children }: MypageLayoutProps) {
return (
<div>
<Styles.MypageContainer>
<Styles.MypageSidebarContainer>
<MypageMyInfo />
<MypageSidebar />
</Styles.MypageSidebarContainer>
<Styles.MypageContentContainer>
{children}
</Styles.MypageContentContainer>
</Styles.MypageContainer>
</div>
<Styles.MypageContainer>
<Styles.MypageSidebarContainer>
<MypageMyInfo />
<MypageSidebar />
</Styles.MypageSidebarContainer>
<Styles.MypageContentContainer>{children}</Styles.MypageContentContainer>
</Styles.MypageContainer>
);
}
5 changes: 1 addition & 4 deletions src/components/Mypage/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,5 @@ export const MypageSidebar = styled.div`

export const MypageContentContainer = styled.div`
flex: 1;
overflow-y: auto;
border: solid black 3px;
min-width: 400px;
height: 2000px;
/* min-width: 400px; */
`;
29 changes: 18 additions & 11 deletions src/components/Navigation/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import userIcon from "../../../public/icons/user.svg";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";

import { Button } from "../ui/button";
import Link from "next/link";

const Navigation: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
Expand Down Expand Up @@ -116,7 +117,7 @@ const Navigation: React.FC = () => {
<Logo href="/">POFO</Logo>
<NavItems>
<StyledNavLink href="/newpost">Home</StyledNavLink>
<StyledNavLink href="/mypage">MyPage</StyledNavLink>
{/* <StyledNavLink href="/mypage">MyPage</StyledNavLink> */}
</NavItems>
</div>
</div>
Expand All @@ -141,16 +142,22 @@ const Navigation: React.FC = () => {
/>
</PopoverTrigger>
<PopoverContent>
<Button variant="ghost" className="w-full justify-start">
<Image
style={{ cursor: "pointer", marginRight: "8px" }}
src={"/icons/user_2.svg"}
width={18}
height={18}
alt="mypage"
/>
내 정보
</Button>
<Link href="/mypage">
<Button
variant="ghost"
className="w-full justify-start"
onClick={() => {}}
>
<Image
style={{ cursor: "pointer", marginRight: "8px" }}
src={"/icons/user_2.svg"}
width={18}
height={18}
alt="mypage"
/>
내 정보
</Button>
</Link>
<Button variant="ghost" className="w-full justify-start">
<Image
style={{ cursor: "pointer", marginRight: "8px" }}
Expand Down
54 changes: 54 additions & 0 deletions src/components/ProjectCard/Mypage/MypageProjectCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from "react";
import * as Styles from "./styles";
import empty_heart from "../../../../public/icons/empty_heart.svg";
import fill_heart from "../../../../public/icons/fill_heart.svg";
import Image from "next/image";
import Link from "next/link";
import { IProjectCardProps } from "@/lib/interface/iProjectCard";

export default function MypageProjectCard(projectCard: IProjectCardProps) {
// const [likeCount, setLikeCount] = useState(likes);
// const [liked, setLiked] = useState(false);

const handleLike = () => {
// setLikeCount(likeCount + (liked ? -1 : 1));
// setLiked(!liked);
};

const { id, title, imageUrls, bio } = projectCard.projectCard;
console.log(projectCard);
return (
<Link style={{ width: "100%" }} href={`/project/${id}`}>
<Styles.Card>
<Styles.ImageWrapper>
<Image
src={
imageUrls !== null && imageUrls.length > 0
? imageUrls[0]
: "https://velog.velcdn.com/images/yena1025/post/295eb434-5b73-421f-bbe4-6bc13acd4c33/image.png"
}
alt={title}
layout="fill"
objectFit="cover"
/>
</Styles.ImageWrapper>
<Styles.Content>
<Styles.Title>{title}</Styles.Title>
<Styles.Description>{bio}</Styles.Description>
<Styles.Author>{id}</Styles.Author>
<Styles.LikeSection>
<Styles.LikeButton onClick={handleLike}>
<Image
src={true ? fill_heart : empty_heart}
alt="like button"
width={24}
height={24}
/>
</Styles.LikeButton>
<Styles.LikeCount>{100} likes</Styles.LikeCount>
</Styles.LikeSection>
</Styles.Content>
</Styles.Card>
</Link>
);
}
58 changes: 58 additions & 0 deletions src/components/ProjectCard/Mypage/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import styled from "@emotion/styled";

export const Card = styled.div`
display: flex;
align-items: center;
border-bottom: 1px solid black;
gap: 50px;
padding-top: 50px;
padding-bottom: 50px;
overflow: hidden;
cursor: pointer;
`;

export const ImageWrapper = styled.div`
position: relative;
width: 180px;
min-width: 180px;
height: 180px;
min-height: 180px;
overflow: hidden;
`;

export const Content = styled.div``;

export const Title = styled.h2`
font-size: 25px;
margin-bottom: 8px;
`;

export const Description = styled.p`
font-size: 18px;
color: #555;
margin-bottom: 8px;
`;

export const Author = styled.p`
font-size: 16px;
color: #888;
margin-bottom: 8px;
`;

export const LikeSection = styled.div`
display: flex;
align-items: center;
`;

export const LikeButton = styled.button`
background: none;
border: none;
cursor: pointer;
font-size: 20px;
`;

export const LikeCount = styled.span`
margin-left: 8px;
font-size: 14px;
color: #333;
`;
2 changes: 1 addition & 1 deletion src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function Home() {
resetType();
}, [resetStack, resetType]);

const SIZE = 36;
const SIZE = 5;

const observerRef = useRef<HTMLDivElement>(null);
const { data, loading, fetchMore, refetch } = useQuery(SEARCH_PROJECT, {
Expand Down
14 changes: 13 additions & 1 deletion src/pages/mypage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import { useAuthStore } from "@/stores/authStore";
import { useRouter } from "next/router";
import { useEffect } from "react";

export default function Mypage() {
const router = useRouter();
router.replace("/mypage/myprojects");
const { isLoggedIn } = useAuthStore();

useEffect(() => {
if (isLoggedIn) {
router.replace("/mypage/myprojects");
} else {
alert("로그인이 필요합니다");
router.replace("/");
}
}, [isLoggedIn, router]);

return null;
}
2 changes: 1 addition & 1 deletion src/pages/mypage/myprojects.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import MypageLayout from "@/components/Mypage/MypageLayout";
import MyProjectComponents from "@/components/Mypage/MyProjects";
import MyProjectComponents from "@/components/Mypage/MyProjects/MyProjects";

const MyProjects: React.FC = () => {
return (
Expand Down
Loading

0 comments on commit 3fcd105

Please sign in to comment.