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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import useInfinityScroll from "@/utils/useInfinityScroll";

const BOTTOM_PROXIMITY_THRESHOLD = 80;
const OUTBOUND_TEXT_SCROLL_CONFIRM_TIMEOUT_MS = 5000;
const INITIAL_SCROLL_SETTLE_DELAYS_MS = [100, 350, 800];

interface PendingOutboundTextScroll {
id: string;
Expand Down Expand Up @@ -210,20 +211,42 @@ const useChatListHandler = (chatId: number) => {
shouldForceScrollToBottomRef.current = false;
}, [chatId, clearPendingOutboundTextScrolls]);

// 초기 히스토리 로딩 완료 후, 최초 1회만 하단으로 이동합니다.
// 초기 메시지가 렌더링되면 최초 1회만 하단으로 이동합니다.
useEffect(() => {
if (isLoading || isFetchingNextPage || submittedMessages.length === 0 || hasInitialAutoScrolledRef.current) {
if (isLoading || submittedMessages.length === 0 || hasInitialAutoScrolledRef.current) {
return;
}

const rafId = requestAnimationFrame(() => {
const rafIds: number[] = [];
const timeoutIds: ReturnType<typeof setTimeout>[] = [];

const scheduleScrollToBottom = () => {
const rafId = requestAnimationFrame(() => {
scrollToBottom();
});
rafIds.push(rafId);
};

scheduleScrollToBottom();
const doubleRafId = requestAnimationFrame(() => {
scrollToBottom();
hasInitialAutoScrolledRef.current = true;
prevMessageCountRef.current = submittedMessages.length;
scheduleScrollToBottom();
});
rafIds.push(doubleRafId);

return () => cancelAnimationFrame(rafId);
}, [isLoading, isFetchingNextPage, scrollToBottom, submittedMessages.length]);
INITIAL_SCROLL_SETTLE_DELAYS_MS.forEach((delayMs) => {
const timeoutId = setTimeout(scrollToBottom, delayMs);
timeoutIds.push(timeoutId);
});

hasInitialAutoScrolledRef.current = true;
prevMessageCountRef.current = submittedMessages.length;

return () => {
rafIds.forEach((rafId) => cancelAnimationFrame(rafId));
timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
};
}, [isLoading, scrollToBottom, submittedMessages.length]);

// 신규 메시지 도착 시 하단 근처라면 유지하고, 내가 보낸 메시지는 현재 위치와 무관하게 하단으로 이동합니다.
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface ChatMessageBoxProps {
message: ChatMessage;
currentUserId?: number; // 현재 사용자 ID
partnerNickname?: string; // 상대방 닉네임
partnerProfileUrl?: string | null;
isPartnerMentor?: boolean;
}

Expand Down Expand Up @@ -135,6 +136,7 @@ const ChatMessageBox = ({
message,
currentUserId = 1,
partnerNickname = "상대방",
partnerProfileUrl = null,
isPartnerMentor = false,
}: ChatMessageBoxProps) => {
const isMine = message.senderId === Number(currentUserId);
Expand Down Expand Up @@ -198,7 +200,7 @@ const ChatMessageBox = ({
) : (
<div className="flex min-w-0 justify-start">
<div className="flex max-w-[min(100%,28rem)] min-w-0 flex-row gap-2">
<ProfileWithBadge isMentor={isPartnerMentor} width={32} height={32} />
<ProfileWithBadge profileImageUrl={partnerProfileUrl} isMentor={isPartnerMentor} width={32} height={32} />
<div className="flex min-w-0 flex-col items-start">
<span className="mb-1 text-k-900 typo-medium-5">{partnerNickname}</span>
<div className="flex min-w-0 items-end gap-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ const ChatContent = ({ chatId }: ChatContentProps) => {
message={message}
currentUserId={userId}
partnerNickname={nickname}
partnerProfileUrl={profileUrl}
isPartnerMentor={isPartnerMentor}
/>
</div>
Expand Down
Loading