Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

친구 추가 로직 구현 #116

Merged
merged 9 commits into from
Dec 1, 2022
27 changes: 26 additions & 1 deletion frontend/src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@ import Signin from './page/Signin';
import Signup from './page/Signup';
import { useEffect } from 'react';
import axios from 'axios';
import { useSetRecoilState } from 'recoil';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { userState } from './store/atom/user';
import { friendsState } from './store/atom/friends';

type friendList = {
nickname: string;
characterName: string;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 타입 any로 많이 해줬는데 반성이 됩ㄴ디ㅏ....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저희가....anyscript를....사용했던가요.......? 🙃🙃🙃
라고 하지만 저도....


const Router = () => {
const setUser = useSetRecoilState(userState);
const setFriends = useSetRecoilState(friendsState);

useEffect(() => {
const checkAuth = async () => {
Expand All @@ -20,6 +27,24 @@ const Router = () => {
nickname: data.nickname.trim(),
hair: data.characterName.trim(),
});

(async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try catch 문 사용하면 더 좋을 것 같아요!!!!😀

const response = await axios.get('/api/friendship');

const friendList: friendList[] = response.data;
if (!friendList.length) return;

const initialList: any = {};
friendList.forEach(({ nickname }) => {
initialList[nickname] = {
isOnline: false,
name: nickname,
isCalling: false,
};
});

setFriends(initialList);
})();
}
};

Expand Down
14 changes: 11 additions & 3 deletions frontend/src/component/Sidebar/Friends/callingList.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { MouseEvent } from 'react';
import { useRecoilValue } from 'recoil';
import { useRecoilState } from 'recoil';
import { friendsState } from '../../../store/atom/friends';
import Content from '../Content';
import FriendItem from './friendItem';
import { friendType } from './friends';
import { callingList } from './friends.styled';

const CallingList = () => {
const friends = useRecoilValue(friendsState);
const [friends, setFriends] = useRecoilState(friendsState);
const friendList = Object.values(friends).filter(value => value.isCalling);

const handleDragOver = (e: MouseEvent) => {
Expand All @@ -19,7 +19,15 @@ const CallingList = () => {

if (target.tagName !== 'UL' || !draggingElement) return;

target.appendChild(draggingElement);
const id = draggingElement.children[0].id;

setFriends({
...friends,
[id]: {
...friends[id],
isCalling: true,
},
});
};

return (
Expand Down
21 changes: 16 additions & 5 deletions frontend/src/component/Sidebar/Friends/friendItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,32 @@ import { friendType } from './friends';
import { friendItemWrapper, userName } from './friends.styled';
import message from '../../../assets/icon/messageIcon.svg';
import unfollow from '../../../assets/icon/unfollowIcon.svg';
import axios from 'axios';

const FriendItem = (data: friendType) => {
const { id, isOnline, name } = data;
const { isOnline, name } = data;

const sendChatting = () => {
alert(`${name}님과 채팅하기`);
};

const unfollowing = () => {
alert(`${name}님을 언팔로우 하시겠습니까?`);
const unfollowing = async () => {
const isConfirm = confirm(`${name}님을 언팔로우 하시겠습니까?`);

if (isConfirm) {
try {
await axios.delete(`/api/friendship/${name}`);

alert(`${name}님을 언팔로우 하였습니다.`);
} catch {
alert('언팔로우 실패');
}
}
};

return (
<Content draggable={isOnline} key={id}>
<section id={id} css={friendItemWrapper(isOnline)}>
<Content draggable={isOnline} key={name}>
<section id={name} css={friendItemWrapper(isOnline)}>
<div css={userName(isOnline)}>{name}</div>
<div>
<img src={message} alt="채팅하기" onClick={sendChatting}></img>
Expand Down
13 changes: 6 additions & 7 deletions frontend/src/component/Sidebar/Friends/friendList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import { useRecoilValue } from 'recoil';
import Content from '../Content';
import FriendItem from './friendItem';
import { friendType } from './friends';
import { findFriend, friendListWrapper } from './friends.styled';
import { friendListWrapper } from './friends.styled';
import { friendsState } from '../../../store/atom/friends';
import Search from './search';

const FriendList = () => {
const friends = useRecoilValue(friendsState);
const friendList = Object.values(friends).filter(value => !value.isCalling);

const handleDrag = (e: MouseEvent) => {
const target = e.target as HTMLElement;

target.classList.toggle('dragging');
};

const friends = useRecoilValue(friendsState);
const friendList = Object.values(friends).filter(value => !value.isCalling);

return (
<Content>
<h2 className="srOnly">친구 목록</h2>
Expand All @@ -25,9 +26,7 @@ const FriendList = () => {
onDragEnd={handleDrag}>
{friendList.map((friend: friendType) => FriendItem(friend))}
</ul>
<div css={findFriend}>
<input type="text" placeholder="추가할 친구 이름" />
</div>
<Search />
</Content>
);
};
Expand Down
31 changes: 31 additions & 0 deletions frontend/src/component/Sidebar/Friends/friends.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const userName = (state: boolean) => css`
`;

export const findFriend = css`
position: relative;
display: flex;
justify-content: space-evenly;
align-items: center;
Expand All @@ -83,6 +84,36 @@ export const findFriend = css`
background-color: rgba(255, 255, 255, 0.7);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 지금 rgba 많이 사용하는데 이거 같이 theme으로 정리할까요??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넴 좋은 것 같아요~~
저희 서비스에서는 white랑 black만 사용하니까 두 개에(아님 하나에 색상도 넘겨주는 식으로?) opacity만 받아오면 될 것 같네용

margin-top: 10px;

ul {
position: absolute;
top: 0;
left: 0;

display: flex;
flex-flow: column nowrap;
gap: 8px;

width: 180px;
transform: translate(50px, -100%);
background-color: rgba(245, 245, 245, 0.9);
border-radius: 10px;
padding: 5px;
margin-top: -5px;

li {
border-radius: 5px;
font-weight: 700;
padding: 5px;

cursor: pointer;

:hover {
color: ${theme.green};
background-color: rgba(255, 255, 255, 0.9);
}
}
}

::before {
content: url(${addFriendIcon});
}
Expand Down
1 change: 0 additions & 1 deletion frontend/src/component/Sidebar/Friends/friends.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export type friendType = {
id: string;
isCalling: boolean;
isOnline: boolean;
name: string;
Expand Down
73 changes: 73 additions & 0 deletions frontend/src/component/Sidebar/Friends/search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import axios from 'axios';
import { FormEvent, MouseEvent, useState } from 'react';
import { findFriend } from './friends.styled';

const testSearchWord = (word: string) =>
['hj', 'ktmihs22', 'jongbin', 'kt', 'ktm', 'ktmi', 'ktmih', 'ktmihs'].filter(
name => name.indexOf(word) !== -1
);

const Search = () => {
const [searchWord, setSearchWord] = useState<string>('');
const [nicknameList, setNicknameList] = useState<string[]>([]);

const addToFriend = async (e: MouseEvent) => {
const target = e.target as HTMLUListElement;

const selectedWord = target.innerText;

setSearchWord('');
setNicknameList([]);

const response = confirm(`${selectedWord}를 친구추가 하시겠습니까?`);
if (response) {
try {
await axios.put(`/api/friendship/${selectedWord}`);

alert('팔로우 되었습니다.');
} catch {
alert('팔로우 실패');
}
}
};

const [timer, setTimer] = useState<NodeJS.Timeout>();

const handleSearchNickname = (e: FormEvent) => {
const target = e.target as HTMLInputElement;

const value = target.value;
setSearchWord(value);

if (timer) clearTimeout(timer);
const newTimer = setTimeout(() => {
// 서버로 리스트 받아오는 요청 보내기
const findList = testSearchWord(value);
setNicknameList(findList);
}, 500);

setTimer(newTimer);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

디바운스 구현을 이런 식으로 할 수 있네요.. 배워갑니당!

};

return (
<section css={findFriend}>
{nicknameList.length ? (
<ul onClick={addToFriend}>
{nicknameList.map(name => (
<li key={name}>{name}</li>
))}
</ul>
) : (
<></>
)}
<input
type="text"
value={searchWord}
placeholder="추가할 친구 이름"
onInput={handleSearchNickname}
/>
</section>
);
};

export default Search;
4 changes: 0 additions & 4 deletions frontend/src/page/Main/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import axios from 'axios';
import { useEffect, useState } from 'react';
import { useRecoilState } from 'recoil';
import Background from '../../component/Background';
import MainContent from '../../component/MainContent';
import { userState } from '../../store/atom/user';

export const getCookieValue = (key: string) => {
const cookieArr = document.cookie.split(';');
Expand Down
35 changes: 13 additions & 22 deletions frontend/src/store/atom/friends.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { atom } from 'recoil';

export interface friendsProps {
[key: string]: {
id: string;
name: string;
isOnline: boolean;
isCalling: boolean;
Expand All @@ -12,52 +11,44 @@ export interface friendsProps {
export const friendsState = atom<friendsProps>({
key: 'friendsState',
default: {
'1': {
id: '1',
isOnline: false,
안현서: {
isOnline: true,
name: '안현서',
isCalling: false,
},
'2': {
id: '2',
원종빈: {
isOnline: true,
name: '원종빈',
isCalling: true,
},
'3': {
id: '3',
강성준: {
isOnline: true,
name: '강성준',
isCalling: true,
},
'4': {
id: '4',
이형진: {
isOnline: true,
name: '이형진',
isCalling: false,
},
'5': {
id: '5',
안현서2: {
isOnline: false,
name: '안현서',
name: '안현서2',
isCalling: false,
},
'6': {
id: '6',
원종빈2: {
isOnline: false,
name: '원종빈',
name: '원종빈2',
isCalling: false,
},
'7': {
id: '7',
강성준2: {
isOnline: true,
name: '강성준',
name: '강성준2',
isCalling: false,
},
'8': {
id: '8',
이형진2: {
isOnline: true,
name: '이형진',
name: '이형진2',
isCalling: false,
},
},
Expand Down