Skip to content

Commit 3fcf23d

Browse files
authored
chat api 변경 내역 반영 및 비로그인 시 프로필 페이지 접근 불가 버그 수정 (#284)
* fix: GetPersonalChatRoomExistedResponse 변경 내역 반영 * feat: 채팅방 입장 api 추가 * feat: 대화걸기 버튼 클릭 훅 작성 * feat: 프로필 페이지 대화걸기 버튼 훅 적용 * feat: 채팅 목록 페이지 로그인 validation 작성 * fix: 사파리에서 버튼 색상이 다른 문제
1 parent ad1376e commit 3fcf23d

File tree

6 files changed

+159
-37
lines changed

6 files changed

+159
-37
lines changed

src/hooks/useChatOnButtonClick.ts

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { useQuery } from '@tanstack/react-query';
2+
import SockJS from 'sockjs-client';
3+
import Stomp from 'stompjs';
4+
5+
import { getAllChatRoomList } from '@api/chat/getAllChatRoomList';
6+
import { getPersonalChatRoomExisted } from '@api/chat/getPersonalChatRoomExisted';
7+
8+
import {
9+
connect,
10+
enter,
11+
stompConfig,
12+
subscribe,
13+
} from '@pages/ChattingPage/stompApi';
14+
15+
import { useCreatePersonalChatRoomMutation } from '@hooks/mutations/useCreatePersonalChatRoomMutation';
16+
17+
import { SendMessageRequest } from '@type/api/chat';
18+
import { Member } from '@type/models';
19+
import { ChatMessage } from '@type/models/ChatMessage';
20+
import { ChatRoom } from '@type/models/ChatRoom';
21+
22+
import { CHAT_ROOM_TAB_TITLE } from '@consts/chatRoomTabTitle';
23+
import { PATH_NAME } from '@consts/pathName';
24+
25+
type useChatOnButtonClickProps = {
26+
targetId: Member['id'];
27+
targetNickname: Member['nickname'];
28+
myId: Member['id'] | null;
29+
navigate: (path: string) => void;
30+
};
31+
32+
export const useChatOnButtonClick = ({
33+
targetId,
34+
targetNickname,
35+
myId = null,
36+
navigate: moveToPage,
37+
}: useChatOnButtonClickProps) => {
38+
const { refetch: fetchPersonalChatRoomExisted } = useQuery({
39+
queryKey: ['personal-room-existed', targetId],
40+
queryFn: () => getPersonalChatRoomExisted({ receiverId: targetId }),
41+
enabled: false,
42+
});
43+
44+
const { refetch: fetchAllChatRoomList } = useQuery({
45+
queryKey: ['all-chat-room-list', CHAT_ROOM_TAB_TITLE.INDIVIDUAL],
46+
queryFn: () => getAllChatRoomList({ type: CHAT_ROOM_TAB_TITLE.INDIVIDUAL }),
47+
enabled: false,
48+
});
49+
50+
const { mutateAsync } = useCreatePersonalChatRoomMutation();
51+
52+
const handleExistingRoom = async (
53+
individualRooms: ChatRoom[],
54+
isSenderActive: boolean
55+
) => {
56+
const { id: roomId } =
57+
individualRooms.find(
58+
({ roomName }: { roomName: string }) => roomName === targetNickname
59+
) || {};
60+
if (!roomId) {
61+
return;
62+
}
63+
64+
if (isSenderActive) {
65+
moveToPage(PATH_NAME.GET_CHAT_PATH(String(roomId)));
66+
} else {
67+
const sock = new SockJS(stompConfig.webSocketEndpoint);
68+
const stompClient = Stomp.over(sock);
69+
70+
connect({
71+
stompClient,
72+
connectEvent: () => {
73+
subscribe({
74+
stompClient,
75+
roomId,
76+
subscribeEvent: (received: ChatMessage) => {
77+
const {
78+
type,
79+
sender: { id: senderId },
80+
} = received;
81+
82+
if (type === '입장' && senderId === myId) {
83+
moveToPage(PATH_NAME.GET_CHAT_PATH(String(received.roomId)));
84+
sock.close();
85+
}
86+
},
87+
});
88+
89+
const sendData: SendMessageRequest = {
90+
senderId: myId!,
91+
content: null,
92+
};
93+
94+
enter({ stompClient, roomId, sendData });
95+
},
96+
});
97+
}
98+
};
99+
100+
const handleClickChattingButton = async () => {
101+
if (!myId) {
102+
moveToPage(PATH_NAME.LOGIN);
103+
return;
104+
}
105+
106+
const { data } = await fetchPersonalChatRoomExisted();
107+
if (!data) return;
108+
109+
const { isRoomExisted, isSenderActive } = data;
110+
111+
if (isRoomExisted) {
112+
const { data: individualRooms } = await fetchAllChatRoomList();
113+
if (individualRooms) {
114+
handleExistingRoom(individualRooms, isSenderActive);
115+
}
116+
} else {
117+
const { id: roomId } = await mutateAsync({
118+
receiverId: targetId,
119+
});
120+
moveToPage(PATH_NAME.GET_CHAT_PATH(String(roomId)));
121+
}
122+
};
123+
124+
return { handleClickChattingButton };
125+
};

src/pages/ChatRoomListPage/ChatRoomListPage.style.ts

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const TabBarButton = styled.button<{ isSelected: boolean }>`
3030
border-bottom: ${({ isSelected }) =>
3131
isSelected ? ' 1px solid black' : ' 1px solid white'};
3232
padding: 10px;
33+
color: black;
3334
`;
3435

3536
export const ChatItemAvatar = styled(Image)`

src/pages/ChatRoomListPage/useChatRoomListPage.ts

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom';
44
import { useAllChatRoomListQuery } from '@hooks/queries/useAllChatRoomListQuery.ts';
55

66
import { useChatRoomTabStore } from '@stores/chatRoomTab.store';
7+
import { useLoginInfoStore } from '@stores/loginInfo.store';
78

89
import { ChatRoom } from '@type/models/ChatRoom.ts';
910

@@ -12,6 +13,11 @@ import { CHAT_ROOM_TAB_TITLE } from '@consts/chatRoomTabTitle.ts';
1213
export const useChatRoomListPage = () => {
1314
const navigate = useNavigate();
1415

16+
const loginInfo = useLoginInfoStore((state) => state.loginInfo);
17+
if (!loginInfo?.id) {
18+
throw new Error('로그인이 필요한 서비스입니다.');
19+
}
20+
1521
const { data: individualRooms } = useAllChatRoomListQuery({
1622
type: CHAT_ROOM_TAB_TITLE.INDIVIDUAL,
1723
});

src/pages/ChattingPage/stompApi.ts

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ export const subscribe = <T>({
5757
});
5858
};
5959

60+
export const enter = <T>({ stompClient, roomId, sendData }: LeaveProps<T>) => {
61+
stompClient.send(stompConfig.enter(roomId), {}, JSON.stringify(sendData));
62+
};
63+
6064
export const send = <T>({ stompClient, roomId, sendData }: SendProps<T>) => {
6165
stompClient.send(stompConfig.send(roomId), {}, JSON.stringify(sendData));
6266
};

src/pages/ProfilePage/Profile.tsx

+19-36
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,15 @@ import { Flex } from '@components/shared/Flex';
77
import { Image } from '@components/shared/Image';
88
import { Text } from '@components/shared/Text';
99

10-
import { useCreatePersonalChatRoomMutation } from '@hooks/mutations/useCreatePersonalChatRoomMutation';
11-
import { useAllChatRoomListQuery } from '@hooks/queries/useAllChatRoomListQuery';
1210
import { useMemberProfileQuery } from '@hooks/queries/useMemberProfileQuery';
13-
import { usePersonalChatRoomExistedQuery } from '@hooks/queries/usePersonalChatRoomExistedQuery';
11+
import { useChatOnButtonClick } from '@hooks/useChatOnButtonClick';
1412

1513
import { theme } from '@styles/theme';
1614

1715
import { useLoginInfoStore } from '@stores/loginInfo.store';
1816

1917
import { Member } from '@type/models';
2018

21-
import { CHAT_ROOM_TAB_TITLE } from '@consts/chatRoomTabTitle';
2219
import { PATH_NAME } from '@consts/pathName';
2320

2421
import Social from '@assets/follow.svg?react';
@@ -55,17 +52,10 @@ type NumberedItemProps = {
5552
};
5653

5754
export const Profile = ({ memberId }: { memberId: Member['id'] }) => {
58-
const myId = useLoginInfoStore((state) => state.loginInfo?.id);
55+
const myId = useLoginInfoStore((state) => state.loginInfo?.id) ?? null;
5956
const navigate = useNavigate();
6057

61-
const { data: profileData } = useMemberProfileQuery({ memberId });
62-
const { data: isChatExisted } = usePersonalChatRoomExistedQuery({
63-
receiverId: memberId,
64-
});
65-
const { data: individualRooms } = useAllChatRoomListQuery({
66-
type: CHAT_ROOM_TAB_TITLE.INDIVIDUAL,
67-
});
68-
const { mutateAsync } = useCreatePersonalChatRoomMutation();
58+
const { data: profile } = useMemberProfileQuery({ memberId });
6959

7060
const [isHeartClicked, setIsHeartClicked] = useState(false);
7161

@@ -77,34 +67,27 @@ export const Profile = ({ memberId }: { memberId: Member['id'] }) => {
7767
navigate(path);
7868
};
7969

80-
const handleClickChattingButton = async () => {
81-
if (!isChatExisted?.existed) {
82-
const { id: roomId } = await mutateAsync({ receiverId: memberId });
83-
84-
moveToPage(PATH_NAME.GET_CHAT_PATH(String(roomId)));
85-
} else {
86-
const { id: roomId } = individualRooms.find(
87-
({ roomName }) => roomName === profileData.nickname
88-
)!;
89-
90-
moveToPage(PATH_NAME.GET_CHAT_PATH(String(roomId)));
91-
}
92-
};
70+
const { handleClickChattingButton } = useChatOnButtonClick({
71+
targetId: memberId,
72+
targetNickname: profile.nickname,
73+
navigate,
74+
myId,
75+
});
9376

9477
return (
9578
<Main>
9679
<FlexItem>
9780
<Flex align="flex-end" gap={8}>
9881
<Text size={24} lineHeight="">
99-
{profileData.nickname}
82+
{profile.nickname}
10083
</Text>
10184
<Text size={12}>
102-
{profileData.addressDepth1 + ' ' + profileData.addressDepth2}
85+
{profile.addressDepth1 + ' ' + profile.addressDepth2}
10386
</Text>
10487
</Flex>
10588
<Flex justify="center" gap={40} align="center">
10689
<Avatar
107-
src={profileData.profileImageUrl}
90+
src={profile.profileImageUrl}
10891
size={100}
10992
border={`1px solid ${theme.PALETTE.GRAY_400}`}
11093
/>
@@ -115,13 +98,13 @@ export const Profile = ({ memberId }: { memberId: Member['id'] }) => {
11598
<NumberedItem
11699
text="매너스코어"
117100
icon={<Heart />}
118-
count={profileData.mannerScore}
101+
count={profile.mannerScore}
119102
color="pink"
120103
/>
121104
<NumberedItem
122105
text="평가한 사람"
123106
icon={<HandHeart />}
124-
count={profileData.mannerScoreCount}
107+
count={profile.mannerScoreCount}
125108
color="black"
126109
/>
127110
</NumberedItemWrapper>
@@ -140,16 +123,16 @@ export const Profile = ({ memberId }: { memberId: Member['id'] }) => {
140123
</Flex>
141124
)}
142125
<ProfileField category="포지션">
143-
{profileData.positions.length
144-
? profileData.positions.map((position) => (
126+
{profile.positions.length
127+
? profile.positions.map((position) => (
145128
<ItemBox key={position}>{position}</ItemBox>
146129
))
147130
: '없음'}
148131
</ProfileField>
149132
<ProfileField category="소속 크루">
150133
<CrewGroup>
151-
{profileData.crews.length
152-
? profileData.crews.map((crew) => (
134+
{profile.crews.length
135+
? profile.crews.map((crew) => (
153136
<ItemBox border="none" key={crew.id}>
154137
<Image
155138
src={crew.profileImageUrl}
@@ -164,7 +147,7 @@ export const Profile = ({ memberId }: { memberId: Member['id'] }) => {
164147
<ProfileField category="획득한 뱃지">{'없음'}</ProfileField>
165148
<ProfileField category="자기소개">
166149
<Introduce>
167-
<Text>{profileData.introduction}</Text>
150+
<Text>{profile.introduction}</Text>
168151
</Introduce>
169152
</ProfileField>
170153
</FlexItem>

src/type/api/chat.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ export type GetPersonalChatRoomExistedRequest = {
1111
receiverId: number;
1212
};
1313

14-
export type GetPersonalChatRoomExistedResponse = { existed: boolean };
14+
export type GetPersonalChatRoomExistedResponse = {
15+
isRoomExisted: boolean;
16+
isSenderActive: boolean;
17+
};
1518

1619
export type GetAllChatRoomListRequest = {
1720
type: ChatRoom['type'];

0 commit comments

Comments
 (0)