Skip to content
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
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 "@/util/ensureAbsoluteUrl";
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
28 changes: 10 additions & 18 deletions hooks/useFetchLinks.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
import { useState, useEffect } from "react";
import { useEffect } from "react";
import { useRouter } from "next/router";
import { proxy } from "@/lib/api/axiosInstanceApi";
import { LinkData } from "@/types/linkTypes";
import { ParsedUrlQuery } from "querystring";
import useViewport from "./useViewport";

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

useEffect(() => {
const fetchLinks = async () => {
if (isTablet === undefined) return; // isTablet이 정의되지 않았으면 API 호출을 막음

setLoading(true); // API 호출 시작 시 로딩 상태 true

setIsLoading(true);
// 경로에 따라 API 엔드포인트 분기
let endpoint =
pathname === "/favorite"
Expand All @@ -35,16 +31,12 @@ const useFetchLinks = (
search: query?.search,
},
});
setLinkCardList(res.data.list);
{
setTotalCount && setTotalCount(res.data.totalCount);
}
setLinkCardList(res.data.list, res.data.totalCount);
setIsLoading(false);
};

if (query) fetchLinks();
}, [setLinkCardList, query, isTablet]);

return loading; // 로딩 상태 반환
}, [setLinkCardList, setIsLoading, pathname, query, isTablet]);
};

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
12 changes: 7 additions & 5 deletions pages/favorite/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useState } from "react";
import { useRouter } from "next/router";
import { GetServerSideProps, GetServerSidePropsContext } from "next";
import { parse } from "cookie";
import axiosInstance from "@/lib/api/axiosInstanceApi";
import CardsLayout from "@/components/Layout/CardsLayout";
import Container from "@/components/Layout/Container";
import LinkCard from "@/components/Link/LinkCard";
import Pagination from "@/components/Pagination";
import useFetchLinks from "@/hooks/useFetchLinks";
import { useRouter } from "next/router";
import { useState } from "react";
import { parse } from "cookie";
import LoadingSpinner from "@/components/LoadingSpinner";
import EmptyFavoriteList from "@/components/Favorite/EmptyFavoriteList";

Expand Down Expand Up @@ -62,9 +62,11 @@ const FavoritePage = ({
const router = useRouter();
const [linkCardList, setLinkCardList] =
useState<FavoriteDataType[]>(favoriteList);
const [isLoading, setIsLoading] = useState(false);
const [totalCount, setTotalCount] = useState(initialTotalCount);

const loading = useFetchLinks(setLinkCardList, setTotalCount, router.query);
useFetchLinks(setLinkCardList, setIsLoading);
//

// 마이링크 페이지로 돌아감
const returnButton = () => {
Expand All @@ -86,7 +88,7 @@ const FavoritePage = ({
</div>

{/* 로딩 중일 때 */}
{loading ? (
{isLoading ? (
<div className="text-center">
<LoadingSpinner />
</div>
Expand Down
57 changes: 34 additions & 23 deletions pages/link/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { GetServerSidePropsContext } from "next";
import { useRouter } from "next/router";
import { parse } from "cookie";
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,7 @@ 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 {
Expand All @@ -36,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 @@ -68,17 +80,18 @@ 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);

useEffect(() => {
setLinkCardList(initialLinkList, initialTotalCount);
}, [initialLinkList, initialTotalCount, setLinkCardList]);

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

return (
<>
Expand All @@ -94,29 +107,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>

{/* 로딩 중일 때 */}
{loading ? (
<div className="text-center">
<LoadingSpinner />
</div>
) : linkCardList.length > 0 ? (
{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