Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import Link from 'next/link';
import { Suspense } from 'react';
import { UseSuspenseInfiniteQueryResult } from '@tanstack/react-query';
import { ProfileImage } from '@/components/shared';
import { ModalContent, ModalDescription, ModalTitle, useModal } from '@/components/ui';
import { useGetFolloweesInfinite, useGetFollowersInfinite } from '@/hooks/use-follower';
import { useIntersectionObserver } from '@/hooks/use-intersection-observer';
import { CommonErrorResponse } from '@/types/service/common';
import { FollowItem, FollowType } from '@/types/service/follow';
import { User } from '@/types/service/user';
interface Props {
user: User;
type: FollowType;
}
export const ProfileFollowsModal = ({ user, type }: Props) => {
const title: Record<FollowType, string> = {
followers: '팔로워',
followees: '팔로잉',
};
const followsCount: Record<FollowType, number> = {
followers: user.followersCnt,
followees: user.followeesCnt,
};
return (
<ModalContent className='max-w-90'>
<ModalTitle className='flex flex-row gap-1'>
<span>{title[type]}</span>
<span className='text-mint-500'>{followsCount[type]}</span>
</ModalTitle>
<ModalDescription className='sr-only'>
{`${title[type]} 목록을 확인할 수 있는 모달입니다.`}
</ModalDescription>
{/* todo: suspense fallback 디자인 필요 */}
<Suspense fallback={<span>로딩중...</span>}>
{type === 'followers' && <Followers user={user} />}
{type === 'followees' && <Followees user={user} />}
</Suspense>
</ModalContent>
);
};
const Followers = ({ user }: { user: User }) => {
const query = useGetFollowersInfinite({ userId: user.userId });
return <FollowList query={query} />;
};
const Followees = ({ user }: { user: User }) => {
const query = useGetFolloweesInfinite({ userId: user.userId });
return <FollowList query={query} />;
};
const FollowList = ({
query,
}: {
query: UseSuspenseInfiniteQueryResult<FollowItem[], CommonErrorResponse>;
}) => {
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = query;
const { close } = useModal();
const fetchObserverRef = useIntersectionObserver({
onIntersect: () => {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
},
});
return (
<div className='scrollbar-thin mt-4 flex h-96 flex-col overflow-y-scroll'>
{data?.map((item) => (
<Link
key={item.userId}
href={`/profile/${item.userId}`}
className='flow-row flex gap-4 py-2'
onClick={close}
>
<ProfileImage size='md' src={item.profileImage} />
<div>
<p className='text-text-md-bold text-gray-800'>{item.nickname}</p>
<p className='text-text-sm-medium text-gray-600'>{item.profileMessage}</p>
</div>
</Link>
))}
{hasNextPage && <div ref={fetchObserverRef}></div>}
</div>
);
};
|