Skip to content
Closed
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
26 changes: 21 additions & 5 deletions components/Link/LinkCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { MouseEvent, useEffect, useRef, useState } from "react";
import { useRouter } from "next/router";
import { useLinkCardStore } from "@/store/useLinkCardStore";
import { ensureAbsoluteUrl } from "@/lib/utils";
Expand Down Expand Up @@ -44,7 +44,8 @@ const LinkCard = ({ info }: LinkCardProps) => {
});

// 즐겨찾기 버튼 클릭 시 호출되는 함수
const handleFavoriteToggle = async () => {
const handleFavoriteToggle = async (e: MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
setIsSubscribed((prev) => !prev);
try {
updateFavorite(info.id, !isSubscribed);
Expand All @@ -54,7 +55,10 @@ const LinkCard = ({ info }: LinkCardProps) => {
};

// dropdown 버튼
const toggleDropdown = () => setIsDropdownOpen((prev) => !prev);
const toggleDropdown = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
setIsDropdownOpen((prev) => !prev);
};

const handleModalOpen = (
type: "EditLink" | "DeleteLinkModal",
Expand All @@ -64,6 +68,12 @@ const LinkCard = ({ info }: LinkCardProps) => {
openModal(type, { link, linkId });
};

const handleNavigate = (url: string) => {
if (!isDropdownOpen)
window.location.href =
url.slice(0, 4) === "http" ? url : `https://${url}`;
};

const dropdownItems = [
{
label: "수정하기",
Expand All @@ -76,7 +86,10 @@ const LinkCard = ({ info }: LinkCardProps) => {
];

return (
<div className="w-[340px] h-[344px] rounded-[12px] shadow-lg overflow-hidden cursor-pointer hover:scale-105 hover:duration-300">
<div
className="w-[340px] h-[344px] rounded-[12px] shadow-lg overflow-hidden cursor-pointer hover:scale-105 hover:duration-300"
onClick={() => handleNavigate(info.url)}
>
<section className="relative w-full h-[60%]">
<Image
src={ensureAbsoluteUrl(info.imageSource) || `/images/no-content.svg`}
Expand Down Expand Up @@ -120,7 +133,10 @@ const LinkCard = ({ info }: LinkCardProps) => {
</div>
)}
</div>
<div className="text-black100 y-[42px] line-clamp-2">
<div
className="text-black100 y-[42px] line-clamp-2"
onClick={() => handleNavigate(info.url)}
>
{info.description || "설명"}
</div>
<div className="text-sm">{formattedDate || "2024.11.06"}</div>
Expand Down
19 changes: 9 additions & 10 deletions hooks/useFetchLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import useViewport from "./useViewport";

// 링크페이지의 query가 바뀌면 그에 맞는 링크들을 보여주는 훅
const useFetchLinks = (
setLinkCardList: React.Dispatch<React.SetStateAction<LinkData[]>>,
setTotalCount?: React.Dispatch<React.SetStateAction<number>>,
setLinkCardList: (list: LinkData[], totalCount: number) => void,
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>,
query?: ParsedUrlQuery,
pathname?: string
) => {
const { isTablet } = useViewport();
const { isMobile, isTablet } = useViewport();

useEffect(() => {
const fetchLinks = async () => {
setIsLoading(true);
// 경로에 따라 API 엔드포인트 분기
let endpoint =
pathname === "/favorite"
Expand All @@ -26,18 +27,16 @@ const useFetchLinks = (
const res = await proxy.get(endpoint, {
params: {
page: query?.page,
pageSize: isTablet ? 6 : 10,
pageSize: isMobile ? 10 : isTablet ? 6 : 9,
search: query?.search,
},
});
console.log("query가 바뀌었을 때 다시 받아온 리스트:", res.data.list);
setLinkCardList(res.data.list);
{
setTotalCount && setTotalCount(res.data.totalCount);
}
console.log("useFetchLinks 함수에서 다시 받아온 리스트:", res.data.list);
setLinkCardList(res.data.list, res.data.totalCount);
setIsLoading(false);
};
if (query) fetchLinks();
}, [setLinkCardList, query, isTablet]);
}, [setLinkCardList, query, isTablet, isMobile]);
};

export default useFetchLinks;
24 changes: 24 additions & 0 deletions hooks/useFolderName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { getFolder } from "@/lib/api/folder";
import { useEffect, useState } from "react";

const useFolderName = (folderId: string | string[] | undefined) => {
const [folderName, setFolderName] = useState("전체");

useEffect(() => {
if (!folderId) return;

const fetchFolderInfo = async () => {
try {
const res = await getFolder(folderId as string);
setFolderName(res.name);
} catch (error) {
console.error("Failed to fetch folder info:", error);
}
};
fetchFolderInfo();
}, [folderId]);

return [folderName, setFolderName];
};

export default useFolderName;
10 changes: 0 additions & 10 deletions lib/api/fetchProxy.ts

This file was deleted.

1 change: 1 addition & 0 deletions lib/toastMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const toastMessages = {
sameFolderName: "같은 이름으로는 수정할 수 없습니다",
invalidLink: "잘못된 링크 형식입니다",
invalidLinkCount: "폴더 정보를 받아오는데 실패했습니다",
needLogin: "로그인 후 이용해주세요",
},
};

Expand Down
42 changes: 30 additions & 12 deletions pages/link/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LinkData } from "@/types/linkTypes";
import { FolderData } from "@/types/folderTypes";
import { Modal } from "@/components/modal/modalManager/ModalManager";
import { SearchInput } from "../../components/Search/SearchInput";
import { useLinkCardStore } from "@/store/useLinkCardStore";
import axiosInstance from "@/lib/api/axiosInstanceApi";
import useModalStore from "@/store/useModalStore";
import Pagination from "@/components/Pagination";
Expand All @@ -20,6 +21,8 @@ import LinkCard from "@/components/Link/LinkCard";
import RenderEmptyLinkMessage from "@/components/Link/RenderEmptyLinkMessage";
import useFetchLinks from "@/hooks/useFetchLinks";
import useViewport from "@/hooks/useViewport";
import useFolderName from "@/hooks/useFolderName";
import LoadingSpinner from "@/components/LoadingSpinner";

interface LinkPageProps {
linkList: LinkData[];
Expand All @@ -35,6 +38,16 @@ export const getServerSideProps = async (
const cookies = parse(req.headers.cookie || "");
const accessToken = cookies.accessToken;

// accessToken이 없으면 클라이언트에서 실행될 때 /login 페이지로 이동시킴.
if (!accessToken) {
return {
redirect: {
destination: "/login",
permanent: false,
},
};
}

const fetchData = async (endpoint: string) => {
const res = await axiosInstance.get(endpoint, {
headers: {
Expand Down Expand Up @@ -67,13 +80,14 @@ const LinkPage = ({
const { search, folder } = router.query;
const { isOpen } = useModalStore();
const { isMobile } = useViewport();
const [linkCardList, setLinkCardList] = useState(initialLinkList);
const { totalCount, linkCardList, setLinkCardList } =
useLinkCardStore.getState();
const [isLoading, setIsLoading] = useState(false);
const [folderName] = useFolderName(folder);
const [folderList, setFolderList] = useState(initialFolderList);
const [totalCount, setTotalCount] = useState(initialTotalCount);

// 링크페이지의 query가 바뀌면 새로운 리스트로 업데이트 해주는 훅
useFetchLinks(setLinkCardList, setTotalCount, router.query, router.pathname);

useFetchLinks(setLinkCardList, setIsLoading, router.query, router.pathname);
console.log(linkCardList);

return (
Expand All @@ -90,23 +104,27 @@ const LinkPage = ({
{!isMobile && <AddFolderButton setFolderList={setFolderList} />}
</div>
<div className="flex justify-between items-center my-[24px]">
<h1 className="text-2xl ">유용한 글</h1>
{folder && (
<FolderActionsMenu
setFolderList={setFolderList}
folderId={folder}
linkCount={totalCount}
/>
<>
<h1 className="text-2xl ">{folderName as string}</h1>
<FolderActionsMenu
setFolderList={setFolderList}
folderId={folder}
linkCount={totalCount as number}
/>
</>
)}
</div>
{linkCardList ? (
{isLoading ? (
<LoadingSpinner /> // 로딩 상태일 때 로딩 스피너 표시
) : linkCardList.length !== 0 ? (
<>
<CardsLayout>
{linkCardList.map((link) => (
<LinkCard key={link.id} info={link} />
))}
</CardsLayout>
<Pagination totalCount={totalCount} />
<Pagination totalCount={totalCount as number} />
</>
) : (
<RenderEmptyLinkMessage />
Expand Down
Loading