Skip to content

Commit

Permalink
Merge pull request #114 from AJD-Archive/feature/113
Browse files Browse the repository at this point in the history
친구 신청&삭제 api 연결
  • Loading branch information
MyungJiwoo authored Nov 22, 2024
2 parents a7ec04e + 1cfd936 commit 024bc3d
Show file tree
Hide file tree
Showing 15 changed files with 332 additions and 66 deletions.
23 changes: 16 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import RouteChangeTracker from './components/RouteChangeTracker';
import PersonalDashboard from './components/PersonalDashboard';
import TeamDashBoard from './components/TeamDashboard';
import { useSSE } from './hooks/useSSE';
import ConnectionsPage from './pages/ConnectionsPage/ConnectionsPage';
import ConnectionsSearchPage from './pages/ConnectionsSearchPage/ConnectionsSearchPage';

import FriendsPage from './pages/FriendsPage/FriendsPage';
import FriendsSearchPage from './pages/FriendsSearchPage/FriendsSearchPage';
import RecommendedFriendsPage from './pages/RecommendedFriendsPage/RecommendedFriendsPage';

const queryClient = new QueryClient();

Expand Down Expand Up @@ -265,19 +265,28 @@ const App = () => {
/>

<Route
path="/connections"
path="/friends"
element={
<ProtectedRoute>
<FriendsPage />
</ProtectedRoute>
}
/>

<Route
path="/friends/search"
element={
<ProtectedRoute>
<ConnectionsPage />
<FriendsSearchPage />
</ProtectedRoute>
}
/>

<Route
path="/connectionsSearch"
path="/friends/recommend"
element={
<ProtectedRoute>
<ConnectionsSearchPage />
<RecommendedFriendsPage />
</ProtectedRoute>
}
/>
Expand Down
24 changes: 20 additions & 4 deletions src/api/ConnectionApi.tsx → src/api/FriendApi.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { axiosInstance } from '../utils/apiConfig';
import { FollowersListData } from '../types/ConnectionType';
import { FollowersListData } from '../types/FriendType';
import { AxiosResponse } from 'axios';

// * 내 친구 목록 get
export const getFollowersList = async (
Expand Down Expand Up @@ -57,14 +58,29 @@ export const getRecommendedFriendsList = async (
};

// * 친구 신청 post
export const postFollow = async (memberId: string): Promise<FollowersListData | null> => {
export const postFollow = async (memberId: string): Promise<AxiosResponse | null> => {
try {
const response = await axiosInstance.post(`/member/follow`, {
memberId: memberId,
});
return response.data.data;

return response;
} catch (error) {
console.error('Error fetching data:', error);
console.log(error);

return null;
}
};

// * 친구 삭제 delete
export const deleteFollow = async (memberId: string): Promise<AxiosResponse | null> => {
try {
const response = await axiosInstance.delete(`/member/follow/${memberId}`);

return response;
} catch (error) {
console.log(error);

return null;
}
};
28 changes: 0 additions & 28 deletions src/components/Connection/Connection.tsx

This file was deleted.

65 changes: 65 additions & 0 deletions src/components/Friend/Friend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { FollowInfo } from '../../types/FriendType';
import * as S from './FriendStyled';
import CustomModal from '../CustomModal';
import useModal from '../../hooks/useModal';
import { useState } from 'react';
import { useDeleteFollow, usePostFollow } from '../../hooks/useFollowersList';

interface FriendProps {
follower: FollowInfo;
}

const Friend = ({ follower }: FriendProps) => {
const { isModalOpen, openModal, handleYesClick, handleNoClick } = useModal(); // 모달창 관련 훅 호출
const [isDelModalOpen, setIsDelModalOpen] = useState<boolean>(false);

const { mutate: followMutate } = usePostFollow(follower.memberId!, follower.name!);
const { mutate: unFollowMutate } = useDeleteFollow(follower.memberId!, follower.name!);

const follow = async () => {
followMutate();
};

const unFollow = async () => {
unFollowMutate();
};

// 친구 삭제를 모달창으로 확인
const submitUnFollow = () => {
setIsDelModalOpen(true);
const handleModalClose = () => setIsDelModalOpen(false);
openModal('yes', unFollow, handleModalClose);
};

return (
<>
<S.FriendLayout>
<S.ProfileImageWrapper src={follower.profileImage} />
<S.FriendUserWrapper>
<p className="name">{follower.name}</p>
<p className="nickName">{follower.nickname}</p>
</S.FriendUserWrapper>
{follower.isFollow ? (
<S.FriendRequestButtonWrapper onClick={submitUnFollow}>
<p>친구 삭제</p>
</S.FriendRequestButtonWrapper>
) : (
<S.FriendRequestButtonWrapper onClick={follow}>
<p>친구 신청</p>
</S.FriendRequestButtonWrapper>
)}
</S.FriendLayout>

{isModalOpen && isDelModalOpen && (
<CustomModal
title="친구를 삭제하시겠습니까?"
subTitle="한 번 삭제된 친구는 다시 추가해야 합니다."
onYesClick={handleYesClick}
onNoClick={handleNoClick}
/>
)}
</>
);
};

export default Friend;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { styled } from 'styled-components';
import theme from '../../styles/Theme/Theme';

export const ConnectionLayout = styled.div`
export const FriendLayout = styled.div`
width: calc(100% / 2 - 5rem);
padding: 1rem;
margin-bottom: 1rem;
Expand Down Expand Up @@ -29,7 +29,7 @@ export const ProfileImageWrapper = styled.img`
color: ${theme.color.text};
`;

export const ConnectionUserWrapper = styled.div`
export const FriendUserWrapper = styled.div`
width: 60%;
.name {
Expand Down
50 changes: 47 additions & 3 deletions src/hooks/useFollowersList.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useQuery } from '@tanstack/react-query';
import { FollowersListData } from '../types/ConnectionType';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { FollowersListData } from '../types/FriendType';
import {
deleteFollow,
getFollowersList,
getRecommendedFriendsList,
getSearchFriendsList,
} from '../api/ConnectionApi';
postFollow,
} from '../api/FriendApi';
import { customErrToast } from '../utils/customErrorToast';
import { axiosInstance } from '../utils/apiConfig';

export const useFollowersList = (page: number, size: number) => {
return useQuery<FollowersListData | null>({
Expand All @@ -29,3 +33,43 @@ export const useRecommendFriendsList = (page: number, size: number) => {
staleTime: 1000 * 60 * 5,
});
};

export const usePostFollow = (id: string, name: string) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => postFollow(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['followersList'] });
queryClient.invalidateQueries({ queryKey: ['recommendedFriendsList'] });
customErrToast(`${name}님에게 친구 신청을 보냈습니다.`);
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onError: (error: any) => {
if (error.response && error.response.status === 403) {
customErrToast(error.response.data.message);
} else {
customErrToast('친구 신청 중 오류가 발생했습니다.');
}
},
});
};

export const useDeleteFollow = (id: string, name: string) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => deleteFollow(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['followersList'] });
queryClient.invalidateQueries({ queryKey: ['recommendedFriendsList'] });
customErrToast(`${name}님을 친구 목록에서 제거했습니다.`);
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onError: (error: any) => {
if (error.response && error.response.status === 403) {
customErrToast(error.response.data.message);
} else {
customErrToast('친구 삭제 중 오류가 발생했습니다.');
}
},
});
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { useState } from 'react';
import { Helmet } from 'react-helmet-async';
import Flex from '../../components/Flex';
import Navbar from '../../components/Navbar';
import * as S from './ConnectionsPageStyled';
import * as S from './FriendsPageStyled';
import leftarrow from '../../img/leftarrow.png';
import Connection from '../../components/Connection/Connection';
import Friend from '../../components/Friend/Friend';
import Pagination from '../../components/CustomPagination';
import { useNavigate } from 'react-router-dom';
import { useFollowersList, useRecommendFriendsList } from '../../hooks/useFollowersList';
import { useState } from 'react';

const ConnectionsPage = () => {
const FriendsPage = () => {
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState<number>(0);

Expand All @@ -35,25 +35,28 @@ const ConnectionsPage = () => {
<S.TitleWrapper>
<p>친구 목록</p>
</S.TitleWrapper>
<S.SecondaryTitleWrapper onClick={() => navigate(`/connectionsSearch`)}>
<S.SecondaryTitleWrapper onClick={() => navigate(`/friends/search`)}>
<p>친구 찾기</p>
</S.SecondaryTitleWrapper>
<S.SecondaryTitleWrapper onClick={() => navigate(`/friends/recommend`)}>
<p>추천 친구</p>
</S.SecondaryTitleWrapper>
</Flex>
</S.HeaderLayout>

<S.SectionTitleWrapper>
{followersList?.followInfoResDto.length == 0 ? <p>추천 친구</p> : <p>내 친구 목록</p>}
</S.SectionTitleWrapper>

<S.ConnectionsWrapper>
<S.FriendsWrapper>
{followersList?.followInfoResDto.length === 0
? recommendList?.followInfoResDto.map((follower, index) => (
<Connection key={index} follower={follower} />
<Friend key={index} follower={follower} />
))
: followersList?.followInfoResDto.map((follower, index) => (
<Connection key={index} follower={follower} />
<Friend key={index} follower={follower} />
))}
</S.ConnectionsWrapper>
</S.FriendsWrapper>

{followersList?.followInfoResDto.length !== 0 && (
<S.PaginationWrapper>
Expand All @@ -70,4 +73,4 @@ const ConnectionsPage = () => {
);
};

export default ConnectionsPage;
export default FriendsPage;
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const SectionTitleWrapper = styled.div`
}
`;

export const ConnectionsWrapper = styled.div`
export const FriendsWrapper = styled.div`
width: 100%;
height: fit-content;
margin-top: 1rem;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Helmet } from 'react-helmet-async';
import Flex from '../../components/Flex';
import Navbar from '../../components/Navbar';
import * as S from './ConnectionsSearchPageStyled';
import * as S from './FriendsSearchPageStyled';
import leftarrow from '../../img/leftarrow.png';
import Connection from '../../components/Connection/Connection';
import Friend from '../../components/Friend/Friend';
import Pagination from '../../components/CustomPagination';
import { useNavigate } from 'react-router-dom';
import { useState } from 'react';
import SearchIcon from './SearchIcon';
import { useSearchFriendsList } from '../../hooks/useFollowersList';
import { useDebounce } from '../../hooks/useDebounce';

const ConnectionsPage = () => {
const FriendsPage = () => {
const navigate = useNavigate();
const [keyword, setKeyword] = useState<string>('');
const [currentPage, setCurrentPage] = useState<number>(0);
Expand Down Expand Up @@ -43,7 +43,10 @@ const ConnectionsPage = () => {
<S.TitleWrapper>
<p>친구 찾기</p>
</S.TitleWrapper>
<S.SecondaryTitleWrapper onClick={() => navigate(`/connections`)}>
<S.SecondaryTitleWrapper onClick={() => navigate(`/friends/recommend`)}>
<p>추천 친구</p>
</S.SecondaryTitleWrapper>
<S.SecondaryTitleWrapper onClick={() => navigate(`/friends`)}>
<p>친구 목록</p>
</S.SecondaryTitleWrapper>
</Flex>
Expand All @@ -54,7 +57,7 @@ const ConnectionsPage = () => {
<SearchIcon />

<S.InputWrapper
placeholder="이메일로 검색하기"
placeholder="이름이나 이메일로 검색하기"
type="text"
value={keyword}
name="keyword"
Expand All @@ -66,11 +69,11 @@ const ConnectionsPage = () => {
<S.SectionTitleWrapper>
<p>검색 결과</p>
</S.SectionTitleWrapper>
<S.ConnectionsWrapper>
<S.FriendsWrapper>
{followersList?.followInfoResDto.map((follower, index) => (
<Connection key={index} follower={follower} />
<Friend key={index} follower={follower} />
))}
</S.ConnectionsWrapper>
</S.FriendsWrapper>

{followersList?.followInfoResDto.length == 0 && (
<S.NoResultWrapper>
Expand All @@ -93,4 +96,4 @@ const ConnectionsPage = () => {
);
};

export default ConnectionsPage;
export default FriendsPage;
Loading

0 comments on commit 024bc3d

Please sign in to comment.