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 96 97 98 99 | 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 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 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | 'use client';
import { useSearchParams } from 'next/navigation';
import { API } from '@/api';
import { ChatList } from '@/components/pages/chat';
import { FollowingList, FollowingNone, FollowingSearch } from '@/components/pages/message';
import { TabNavigation } from '@/components/shared';
import { useInfiniteScroll } from '@/hooks/use-group/use-group-infinite-list';
import { useIntersectionObserver } from '@/hooks/use-intersection-observer';
import { INTERSECTION_OBSERVER_THRESHOLD } from '@/lib/constants/group-list';
const SOCIAL_TABS = [
{ label: '팔로잉', value: 'following' },
{ label: '메세지', value: 'chat' },
];
interface FollowingContentProps {
initialUserId: number;
}
export const FollowingContent = ({ initialUserId }: FollowingContentProps) => {
const params = useSearchParams();
const tab = params.get('tab') || 'chat';
// 1. 무한 스크롤 훅 호출 (서버에서 prefetch된 데이터가 자동으로 사용됨)
const {
items: followers,
error,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
completedMessage,
} = useInfiniteScroll({
queryFn: async ({ cursor, size }) => {
return await API.followerService.getFollowers({
userId: initialUserId,
cursor,
size,
});
},
queryKey: ['followers', initialUserId],
completedMessage: '모든 팔로잉을 불러왔습니다.',
enabled: !!initialUserId,
});
// 2. IntersectionObserver로 스크롤 감지
const sentinelRef = useIntersectionObserver({
onIntersect: () => {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
},
enabled: hasNextPage && error === null,
threshold: INTERSECTION_OBSERVER_THRESHOLD,
});
return (
<div className='min-h-screen bg-[#F1F5F9]'>
<TabNavigation basePath='/message' defaultValue='chat' tabs={SOCIAL_TABS} />
{tab === 'chat' && <ChatList />}
{tab === 'following' && (
<>
<FollowingSearch userId={initialUserId} />
{!error && followers && followers.length > 0 ? (
<>
<FollowingList items={followers} />
{/* 3. Sentinel 요소 (필수!) */}
{hasNextPage && <div ref={sentinelRef} className='h-1' />}
{/* 4. 다음 페이지 로딩 중 */}
{isFetchingNextPage && (
<div className='flex items-center justify-center p-4'>
<span className='text-gray-500'>더 불러오는 중...</span>
</div>
)}
{/* 5. 완료 메시지 */}
{!hasNextPage && (
<div className='flex items-center justify-center p-4'>
<span className='text-gray-500'>{completedMessage}</span>
</div>
)}
</>
) : (
!error && (
<div className='flex flex-1 items-center justify-center'>
<FollowingNone />
</div>
)
)}
</>
)}
</div>
);
};
|