-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat/#49/myprojects 무한스크롤 및 불러오기 구현 완료
- Loading branch information
Showing
12 changed files
with
309 additions
and
86 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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> | ||
); | ||
} | ||
|
@@ -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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.