Skip to content

Commit

Permalink
매칭 설문 페이지 API 연동 (#272)
Browse files Browse the repository at this point in the history
* ✨ #271 - 매칭 종료 후 매칭 설문 페이지 redirection 처리 및 매칭 이력 조회 API 연동

* 🐛 #271 - 매칭 중 상대방이 의도하지 않은 방법으로 통신을 종료하는 경우 이벤트 추가

* ✨ #271 - 피드백 생성 API 적용

* ✨ #271 - 매칭 후 블랙리스트 등록 API 연동

* 🩹 #271 - 매칭 피드백 완료 후 메인 이동 시 뒤로가기 못하도록 replace로 변경

* 🩹 #271 - 매칭 피드백 생성 axios 인스턴스 코드 포맷 통일 관련 작업

* ✏️ #271 - 블랙리스트 추가 관련 API 메소드 명 변경

* 🏷️ #271 - 매칭 히스토리 조회 응답 타입 수정

* fix: 구조분해 할당 데이터 위치 변경 #271

* fix: form 안에 있는 인터렉션 태그들 react hook form 하나로 관리 #271
  • Loading branch information
yws1502 authored Jan 6, 2025
1 parent 46a4a5b commit 0a29ba8
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 24 deletions.
14 changes: 14 additions & 0 deletions src/api/blacklist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { OnlyMessageResponse, SuccessResponse } from 'types/response';

import { API_PATH } from 'constants/services';
import axios from 'lib/axios';

export const addToBlackList = async (blockedUserId: string) => {
const {
data: { data },
} = await axios.post<SuccessResponse<OnlyMessageResponse>>(
API_PATH.blacklist.addToBlackList(blockedUserId),
);

return data.message;
};
2 changes: 2 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ export * from './bookmark';
export * from './profile';
export * from './activities';
export * from './badges';
export * from './matchingHistories';
export * from './blacklist';
34 changes: 34 additions & 0 deletions src/api/matchingHistories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type {
CreateMatchingFeedbackRequest,
CreateMatchingFeedbackResponse,
MatchingHistoryResponse,
} from 'types/matching';
import type { SuccessResponse } from 'types/response';

import { API_PATH } from 'constants/services';
import axios from 'lib/axios';

export const getRecentMatchingHistory = async () => {
const {
data: { data },
} = await axios.get<SuccessResponse<MatchingHistoryResponse>>(
API_PATH.matchingHistories.recent,
);

return data;
};

export const createMatchingFeedback = async (
payload: CreateMatchingFeedbackRequest,
) => {
const { matchingHistoryId, ...feedbackForm } = payload;

const {
data: { data },
} = await axios.post<SuccessResponse<CreateMatchingFeedbackResponse>>(
API_PATH.matchingHistories.feedback(matchingHistoryId),
feedbackForm,
);

return data;
};
8 changes: 4 additions & 4 deletions src/components/matching/FeedbackTypeCheckbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@ const FeedbackTypeCheckbox = ({ register }: FeedbackTypeCheckboxProps) => {
id: 'isNice',
icon: <NiceIcon />,
description: '친절해요',
hookFormProps: register('feedbackType.isNice'),
hookFormProps: register('isNice'),
},
{
id: 'isFluent',
icon: <EngIcon />,
description: '영어를 잘해요',
hookFormProps: register('feedbackType.isFluent'),
hookFormProps: register('isFluent'),
},
{
id: 'isFun',
icon: <FunIcon />,
description: '재밌어요',
hookFormProps: register('feedbackType.isFun'),
hookFormProps: register('isFun'),
},
{
id: 'isBad',
icon: <BadIcon />,
description: '불쾌해요',
hookFormProps: register('feedbackType.isBad'),
hookFormProps: register('isBad'),
},
]);

Expand Down
5 changes: 2 additions & 3 deletions src/components/matching/MatchingController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const MatchingController = () => {
await matching.signaling(audioElement, {
role: query.r as MatchingInformation['role'],
socketId: query.ms as MatchingInformation['socketId'],
userId: query.mu as MatchingInformation['userId'],
userId: query.mu as MatchingInformation['userId'], // FIXME: username으로 변경 필요
});
} catch (error) {
console.log(error);
Expand All @@ -59,9 +59,8 @@ const MatchingController = () => {
}, []);

const handleEndMatching = () => {
// FIXME: 매칭 설문 페이지로 이동할 예정입니다.
matching.disconnect();
void router.replace(PAGE_PATH.main);
void router.replace(PAGE_PATH.matching.survey);
};

const handleCloseAlert = () => {
Expand Down
1 change: 1 addition & 0 deletions src/constants/common/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const PAGE_PATH = {
index: '/matching',
queue: '/matching/queue',
matchUp: '/matching/match-up',
survey: '/matching/survey',
},

diary: {
Expand Down
8 changes: 8 additions & 0 deletions src/constants/services/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ export const API_PATH = {
index: '/badges',
users: '/badges/users',
},
matchingHistories: {
index: '/matching-histories',
recent: '/matching-histories/recent',
feedback: (id: string) => `/matching-histories/${id}/feedback`,
},
blacklist: {
addToBlackList: (blockedUserId: string) => `/blacklist/${blockedUserId}`,
},
} as const;
1 change: 1 addition & 0 deletions src/constants/services/queryKeys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const queryKeys = {
recentMatchingHistory: 'recentMatchingHistory',
badges: 'badges',
bookmark: 'bookmark',
comments: 'comments',
Expand Down
8 changes: 8 additions & 0 deletions src/hooks/services/mutations/useBlockUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useMutation } from '@tanstack/react-query';
import * as api from 'api';

export const useAddToBlackList = () => {
return useMutation(
async (blockedUserId: string) => await api.addToBlackList(blockedUserId),
);
};
11 changes: 11 additions & 0 deletions src/hooks/services/mutations/useCreateMatchingFeedback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useMutation } from '@tanstack/react-query';

import type { CreateMatchingFeedbackRequest } from 'types/matching';
import * as api from 'api';

export const useCreateMatchingFeedback = () => {
return useMutation(
async (payload: CreateMatchingFeedbackRequest) =>
await api.createMatchingFeedback(payload),
);
};
13 changes: 13 additions & 0 deletions src/hooks/services/queries/useRecentMatchingHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useQuery } from '@tanstack/react-query';
import * as api from 'api';

import { queryKeys } from 'constants/services';

export const useRecentMatchingHistory = () => {
const response = useQuery(
[queryKeys.recentMatchingHistory],
async () => await api.getRecentMatchingHistory(),
);

return response;
};
54 changes: 42 additions & 12 deletions src/pages/matching/survey.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled from '@emotion/styled';
import { useRouter } from 'next/router';
import { useForm, useWatch } from 'react-hook-form';
import { useForm } from 'react-hook-form';

import type { SubmitHandler } from 'react-hook-form';
import type { MatchingFeedbackForm } from 'types/matching';
Expand All @@ -9,23 +9,53 @@ import { CheckedOffIcon, CheckedOnIcon } from 'assets/icons';
import { Seo } from 'components/common';
import FeedbackTypeCheckbox from 'components/matching/FeedbackTypeCheckbox';
import { PAGE_PATH } from 'constants/common';
import { useAddToBlackList } from 'hooks/services/mutations/useBlockUser';
import { useCreateMatchingFeedback } from 'hooks/services/mutations/useCreateMatchingFeedback';
import { useRecentMatchingHistory } from 'hooks/services/queries/useRecentMatchingHistory';
import { ScreenReaderOnly } from 'styles';

const MatchingSurvey = () => {
const router = useRouter();

const { control, register, handleSubmit } = useForm<MatchingFeedbackForm>();
const { register, handleSubmit, watch } = useForm<MatchingFeedbackForm>();

const onSubmit: SubmitHandler<MatchingFeedbackForm> = async (data) => {
console.log(data); // FIXME: 실제 API 연동할 때 사용될 데이터 console.log 입니다.
const isBlockUser = watch('isBlockUser');

await router.push(PAGE_PATH.main);
const { data: matchingHistory } = useRecentMatchingHistory();

const { mutate: createFeedbackMutate } = useCreateMatchingFeedback();

const { mutate: addToBlackListMutate } = useAddToBlackList();

const onRequestError = () => {
alert('의도하지 않은 에러가 발생하였습니다.');
void router.push(PAGE_PATH.main);
};

const isBlockedMatching = useWatch({
control,
name: 'isBlockedMatching',
});
const onSubmit: SubmitHandler<MatchingFeedbackForm> = (formData) => {
if (!matchingHistory) return;

const { id, matchedUser } = matchingHistory;
const { isBlockUser, ...feedbackFormData } = formData;

if (isBlockUser) {
addToBlackListMutate(matchedUser.id, { onError: onRequestError });
}

createFeedbackMutate(
{
...feedbackFormData,
matchingHistoryId: id,
matchedUserId: matchedUser.id,
},
{
onSuccess: () => {
void router.replace(PAGE_PATH.main);
},
onError: onRequestError,
},
);
};

return (
<>
Expand All @@ -45,11 +75,11 @@ const MatchingSurvey = () => {
</RegularParagraph07>
<TextArea
placeholder="피드백을 남겨주세요."
{...register('message')}
{...register('content')}
/>
<CheckBoxLabel>
<input type="checkbox" {...register('isBlockedMatching')} />
{isBlockedMatching ? <CheckedOnIcon /> : <CheckedOffIcon />}
<input type="checkbox" {...register('isBlockUser')} />
{isBlockUser ? <CheckedOnIcon /> : <CheckedOffIcon />}
<p>이 사람이랑 전화하지 않을래요.</p>
</CheckBoxLabel>
<Button type="submit">피드백 작성 완료</Button>
Expand Down
29 changes: 25 additions & 4 deletions src/types/matching.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import type { User } from 'next-auth';

interface FeedbackType {
isNice: boolean;
isFluent: boolean;
isFun: boolean;
isBad: boolean;
}

export interface MatchingFeedbackForm {
feedbackType: FeedbackType;
message: string;
isBlockedMatching: boolean;
export interface MatchingFeedbackForm extends FeedbackType {
isBlockUser: boolean;
content: string;
}

export interface MatchingInformation {
Expand All @@ -20,3 +21,23 @@ export interface MatchingInformation {
export interface PeerEventHandler {
handleDisconnected: () => void;
}

/* API */
export interface CreateMatchingFeedbackRequest
extends Omit<MatchingFeedbackForm, 'isBlockUser'> {
matchingHistoryId: string;
matchedUserId: string;
}

export interface CreateMatchingFeedbackResponse extends MatchingFeedbackForm {
writer: Omit<User, 'accessToken'>;
recipient: Omit<User, 'accessToken'>;
matchingHistory: Omit<MatchingHistoryResponse, 'matchedUser'>;
}

export interface MatchingHistoryResponse {
id: string;
matchTime: number;
matchedUser: Omit<User, 'accessToken'>;
createdAt: string;
}
24 changes: 23 additions & 1 deletion src/utils/Matching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,13 @@ export class Matching {

statistics.forEach((report) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
switch (report.type) {
switch (report.type as RTCStatsType) {
case 'inbound-rtp': {
// TODO: 상대방 audioLevel에 따른 UI 이벤트 적용 예정
// const InboundRTPReport = report as RTCInboundRtpStreamStats;
// console.log(InboundRTPReport.audioLevel); // 매칭 상대가 보내는 audio level
break;
}
case 'transport':
{
const transportReport = report as RTCTransportStats;
Expand All @@ -195,6 +201,22 @@ export class Matching {
}
});
}, 1000);

// 상대방이 의도하지 않는 방법으로 페이지를 이탈한 경우 처리(새로고침, 브라우저 닫기 등)
this.peer?.addEventListener('iceconnectionstatechange', () => {
if (this.peer === null) return;

const { iceConnectionState } = this.peer;

if (
iceConnectionState === 'disconnected' ||
iceConnectionState === 'failed' ||
iceConnectionState === 'closed'
) {
handleDisconnected();
clearInterval(interval);
}
});
}

public disconnect() {
Expand Down

0 comments on commit 0a29ba8

Please sign in to comment.