Skip to content
9 changes: 6 additions & 3 deletions src/apis/gathering-list/query/use-get-gathering-mine-list.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import queryKeys from "@/apis/query-keys";
import { GetGatheringMineListRequest } from "@/types/gathering-list";
import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
import {
useSuspenseInfiniteQuery,
useSuspenseQuery,
} from "@tanstack/react-query";
import { getGatheringMineList } from "../gathering-list.api";

const useGetGatheringMineList = (params: GetGatheringMineListRequest) => {
return useQuery({
return useSuspenseQuery({
queryKey: queryKeys.gatheringList.mine(params),
queryFn: () => getGatheringMineList(params),
});
Expand All @@ -13,7 +16,7 @@ const useGetGatheringMineList = (params: GetGatheringMineListRequest) => {
const useGetGatheringMineListInfinite = (
params: Omit<GetGatheringMineListRequest, "page">
) => {
return useInfiniteQuery({
return useSuspenseInfiniteQuery({
queryKey: queryKeys.gatheringList.mineInfinite({ ...params, page: 0 }),
queryFn: ({ pageParam }) =>
getGatheringMineList({ ...params, page: pageParam }),
Expand Down
21 changes: 21 additions & 0 deletions src/apis/user/query/use-get-user-info-suspense.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import queryKeys from "@/apis/query-keys";
import { useSuspenseQuery } from "@tanstack/react-query";
import { getUserInfo } from "../user.api";

const useGetUserInfoSuspense = () => {
return useSuspenseQuery({
queryKey: queryKeys.user.all,
queryFn: getUserInfo,
retry: false,
staleTime: 1000 * 60 * 5,
select: (response) => ({
...response,
profileImageUrl: response.profileImageUrl
? `${response.profileImageUrl}?date=${Date.now()}`
: "",
}),
structuralSharing: false,
});
};

export default useGetUserInfoSuspense;
22 changes: 19 additions & 3 deletions src/app/(root)/(main)/my-page/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
import { Gatherings, UserProfile } from "@/components/section";
import { GatheringListSkeleton, UserProfile } from "@/components/section";
import UserProfileSkeleton from "@/components/section/fallback/user-profile-skeleton";
import {
HostGatherings,
MemberGatherings,
} from "@/components/section/user/gatherings";
import { Suspense } from "react";

const MyPage = () => {
return (
<div className="pc:pt-[50px] tb:pt-9 mo:pt-6 pc:gap-[74px] tb:gap-[50px] mo:gap-10 pc:pb-[107px] tb:pb-[160px] mo:pb-[93px] flex flex-col bg-white">
<UserProfile />
<Gatherings />
<Suspense fallback={<UserProfileSkeleton />}>
<UserProfile />
</Suspense>

<div className="tb:gap-[74px] mo:gap-15 flex flex-col">
<Suspense fallback={<GatheringListSkeleton subTitle={false} />}>
<HostGatherings />
</Suspense>
<Suspense fallback={<GatheringListSkeleton subTitle={false} />}>
<MemberGatherings />
</Suspense>
</div>
</div>
);
};
Expand Down
7 changes: 7 additions & 0 deletions src/components/section/fallback/auth-status-skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const AuthStatusSkeleton = () => {
return (
<div className="bg-gray-neutral-300 tb:flex hidden size-10 animate-pulse rounded-full"></div>
);
};

export default AuthStatusSkeleton;
12 changes: 7 additions & 5 deletions src/components/section/fallback/gathering-list-skeleton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const GatheringListSkeleton = () => {
const GatheringListSkeleton = ({ subTitle = true }: { subTitle?: boolean }) => {
const skeletonCards = Array.from({ length: 4 }, (_, index) => (
<li key={index}>
<div className="tb:w-[275px] mo:w-[200px] group relative">
Expand All @@ -7,13 +7,13 @@ const GatheringListSkeleton = () => {
{/* ๋ณธ๋ฌธ */}
<div className="tb:pt-[18px] mo:pt-[12px] pr-1 pl-1">
{/* ๋ชจ์ž„ ๋ช… */}
<div className="bg-gray-neutral-200 tb:h-5 mo:h-4 mb-2 w-25 animate-pulse rounded" />
<div className="bg-gray-neutral-200 tb:h-5 mo:h-4 w-25 animate-pulse rounded" />
{/* ์นดํ…Œ๊ณ ๋ฆฌ ๋ฑƒ์ง€ ๋ฐ ์ธ์› ์ˆ˜ */}
<div className="tb:pt-4 mo:pt-2 flex items-center justify-between">
{/* ์นดํ…Œ๊ณ ๋ฆฌ ๋ฑƒ์ง€ */}
<div className="bg-gray-neutral-200 tb:px-[8px] tb:py-[6px] tb:h-5 mo:h-4 w-16 animate-pulse rounded" />
<div className="bg-gray-neutral-200 tb:px-[8px] tb:py-[6px] tb:h-6.5 mo:h-5.5 w-16 animate-pulse rounded" />
{/* ์ธ์› ์ˆ˜ */}
<div className="bg-gray-neutral-200 tb:h-4 mo:h-[13px] w-20 animate-pulse rounded" />
<div className="bg-gray-neutral-200 tb:h-4 mo:h-[13px] w-15 animate-pulse rounded" />
</div>
</div>
</div>
Expand All @@ -26,7 +26,9 @@ const GatheringListSkeleton = () => {
<header className="pc:mb-7 mo:mb-[22px] flex flex-row items-center justify-between">
<div>
<div className="bg-gray-neutral-200 pc:mb-3 mo:mb-2 tb:h-6 mo:h-[18px] w-54 animate-pulse rounded" />
<div className="bg-gray-neutral-200 tb:h-[18px] mo:h-[14px] w-48 animate-pulse rounded" />
{subTitle && (
<div className="bg-gray-neutral-200 tb:h-[18px] mo:h-[14px] w-48 animate-pulse rounded" />
)}
</div>
<div className="bg-gray-neutral-200 tb:h-[18px] mo:h-[14px] w-12 animate-pulse rounded" />
</header>
Expand Down
23 changes: 23 additions & 0 deletions src/components/section/fallback/user-profile-skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const UserProfileSkeleton = () => {
return (
<section className="tb:flex w-full items-center justify-center">
<div className="mo:self-stretch tb:self-auto pc:px-13 pc:py-7 tb:px-[30px] tb:py-6 mo:px-5 mo:py-6 pc:gap-6 tb:gap-5 mo:gap-[19.47px] bg-blue-25 pc:rounded-[50px] tb:rounded-[36px] mo:rounded-[30px] tb:w-[385px] pc:h-[335px] tb:h-[287px] mo:h-[214px] flex flex-col items-center justify-center border border-blue-300">
<div className="pc:gap-[18px] tb:gap-[14px] mo:gap-2 flex flex-col items-center justify-center">
<div className="mo:p-[5px] tb:p-0 flex items-center justify-center">
<div className="bg-gray-neutral-300 tb:w-[114px] tb:h-[114px] mo:w-11 mo:h-11 h-[40px] w-[40px] animate-pulse rounded-full" />
</div>
<div className="pc:gap-2 tb:gap-[6px] mo:gap-1 flex flex-col items-center justify-center">
<div className="bg-gray-neutral-300 h-5 w-20 animate-pulse rounded-full"></div>
<div className="bg-gray-neutral-300 h-5 w-50 animate-pulse rounded-full"></div>
</div>
</div>
<div className="pc:gap-3 tb:gap-[9.735px] mo:gap-[9.74px] flex items-center justify-center">
<div className="bg-gray-neutral-300 flex h-[38px] w-[109px] animate-pulse rounded-[10px]"></div>
<div className="bg-gray-neutral-300 flex h-[38px] w-[109px] animate-pulse rounded-[10px]"></div>
</div>
</div>
</section>
);
};

export default UserProfileSkeleton;
49 changes: 25 additions & 24 deletions src/components/section/user/gatherings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,46 @@
import { useGetGatheringMineList } from "@/apis/gathering-list/query/use-get-gathering-mine-list";
import GatheringList from "../gathering/list/gathering-list";

const Gatherings = () => {
const {
data: hostGathering,
isPending: isHostLoading,
isError: isHostError,
} = useGetGatheringMineList({
const HostGatherings = () => {
const { data: hostGathering } = useGetGatheringMineList({
role: "HOST",
size: 10,
page: 0,
});

const {
data: memberGathering,
isPending: isMemberLoading,
isError: isMemberError,
} = useGetGatheringMineList({
return (
<GatheringList
title={"๋‚ด๊ฐ€ ์ƒ์„ฑํ•œ ๋ชจ์ž„"}
moreLink={{ pathname: "/my-page/list", query: { role: "host" } }}
gatheringList={hostGathering.content}
/>
);
};

const MemberGatherings = () => {
const { data: memberGathering } = useGetGatheringMineList({
role: "MEMBER",
size: 10,
page: 0,
});

if (isHostLoading || isMemberLoading) return <div>Loading...</div>;

if (isHostError || isMemberError) return <div>Error</div>;
return (
<GatheringList
title={"๋‚ด๊ฐ€ ๊ฐ€์ž…ํ•œ ๋ชจ์ž„"}
moreLink={{ pathname: "/my-page/list", query: { role: "member" } }}
gatheringList={memberGathering.content}
/>
);
};

const Gatherings = () => {
return (
<div className="tb:gap-[74px] mo:gap-15 flex flex-col">
<GatheringList
title={"๋‚ด๊ฐ€ ์ƒ์„ฑํ•œ ๋ชจ์ž„"}
moreLink={{ pathname: "/my-page/list", query: { role: "host" } }}
gatheringList={hostGathering.content}
/>
<GatheringList
title={"๋‚ด๊ฐ€ ๊ฐ€์ž…ํ•œ ๋ชจ์ž„"}
moreLink={{ pathname: "/my-page/list", query: { role: "member" } }}
gatheringList={memberGathering.content}
/>
<HostGatherings />
<MemberGatherings />
</div>
);
};

export default Gatherings;
export { HostGatherings, MemberGatherings };
11 changes: 4 additions & 7 deletions src/components/section/user/user-profile.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
"use client";
import useGetUserInfo from "@/apis/user/query/use-get-user-info";
import useGetUserInfoSuspense from "@/apis/user/query/use-get-user-info-suspense";
import { PasswordEditModal, Profile, ProfileEditModal } from "@/components/ui";

const UserProfile = () => {
const { data, isPending, isError } = useGetUserInfo();

if (isPending) return <div>Loading...</div>;
if (isError) return <div>Error</div>;
const { data } = useGetUserInfoSuspense();

return (
<section className="tb:flex w-full items-center justify-center">
<div className="mo:self-stretch tb:self-auto pc:px-13 pc:py-7 tb:px-[30px] tb:py-6 mo:px-5 mo:py-6 pc:gap-6 tb:gap-5 mo:gap-[19.47px] bg-blue-25 pc:rounded-[50px] tb:rounded-[36px] mo:rounded-[30px] flex flex-col items-center justify-center border border-blue-300">
<div className="mo:self-stretch tb:self-auto pc:px-13 pc:py-7 tb:px-[30px] tb:py-6 mo:px-5 mo:py-6 pc:gap-6 tb:gap-5 mo:gap-[19.47px] bg-blue-25 pc:rounded-[50px] tb:rounded-[36px] mo:rounded-[30px] tb:w-[385px] pc:h-[335px] tb:h-[287px] mo:h-[214px] flex flex-col items-center justify-center border border-blue-300">
<div className="pc:gap-[18px] tb:gap-[14px] mo:gap-2 flex flex-col items-center justify-center">
<div className="mo:p-[5px] tb:p-0 flex items-center justify-center">
<Profile
Expand All @@ -20,7 +17,7 @@ const UserProfile = () => {
/>
</div>
<div className="pc:gap-2 tb:gap-[6px] mo:gap-1 flex flex-col items-center justify-center">
<span className="text-gray-neutral-800 pc:typo-title-sm-semibold tb:typo-title-sm-bold mo:typo-title-2xs-bold">
<span className="tb:truncate tb:max-w-[323px] text-gray-neutral-800 pc:typo-title-sm-semibold tb:typo-title-sm-bold mo:typo-title-2xs-bold">
{data.nickname}
</span>
<span className="text-gray-neutral-400 typo-body-sm-medium">
Expand Down
49 changes: 25 additions & 24 deletions src/components/ui/button/auth-status-button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";
import useSignOut from "@/apis/auth/mutation/use-sign-out";
import useGetUserInfo from "@/apis/user/query/use-get-user-info";
import AuthStatusSkeleton from "@/components/section/fallback/auth-status-skeleton";
import { Button, Dropdown, Profile } from "@/components/ui";
import { useAuthStore } from "@/store/auth-store";
import { cn } from "@/utils/cn";
Expand All @@ -17,30 +18,30 @@ const AuthStatusButton = ({ className }: AuthStatusButtonProps) => {
const isSignedIn = useAuthStore((state) => state.authStatus);

if (isSignedIn) {
return (
user && (
<Dropdown
trigger={
<Profile
profileImageUrl={user?.profileImageUrl}
className={className}
size="sm"
/>
}
items={[
{
text: "๋งˆ์ดํŽ˜์ด์ง€",
onClick: () => router.push("/my-page"),
},
{
text: "๋กœ๊ทธ์•„์›ƒ",
onClick: signOut,
},
]}
itemClassName="hover:text-gray-neutral-700 text-gray-neutral-500 justify-center"
contentAlign="end"
/>
)
return user ? (
<Dropdown
trigger={
<Profile
profileImageUrl={user?.profileImageUrl}
className={className}
size="sm"
/>
}
items={[
{
text: "๋งˆ์ดํŽ˜์ด์ง€",
onClick: () => router.push("/my-page"),
},
{
text: "๋กœ๊ทธ์•„์›ƒ",
onClick: signOut,
},
]}
itemClassName="hover:text-gray-neutral-700 text-gray-neutral-500 justify-center"
contentAlign="end"
/>
) : (
<AuthStatusSkeleton />
);
} else {
return (
Expand Down