diff --git a/frontend/src/Router.tsx b/frontend/src/Router.tsx index 2021d52..5a57f59 100644 --- a/frontend/src/Router.tsx +++ b/frontend/src/Router.tsx @@ -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; +}; const Router = () => { const setUser = useSetRecoilState(userState); + const setFriends = useSetRecoilState(friendsState); useEffect(() => { const checkAuth = async () => { @@ -20,6 +27,24 @@ const Router = () => { nickname: data.nickname.trim(), hair: data.characterName.trim(), }); + + (async () => { + 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); + })(); } }; diff --git a/frontend/src/component/Sidebar/Friends/callingList.tsx b/frontend/src/component/Sidebar/Friends/callingList.tsx index c7242c1..f30f1ba 100644 --- a/frontend/src/component/Sidebar/Friends/callingList.tsx +++ b/frontend/src/component/Sidebar/Friends/callingList.tsx @@ -1,5 +1,5 @@ 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'; @@ -7,7 +7,7 @@ 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) => { @@ -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 ( diff --git a/frontend/src/component/Sidebar/Friends/friendItem.tsx b/frontend/src/component/Sidebar/Friends/friendItem.tsx index 81a70ea..1d6c951 100644 --- a/frontend/src/component/Sidebar/Friends/friendItem.tsx +++ b/frontend/src/component/Sidebar/Friends/friendItem.tsx @@ -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 ( - -
+ +
{name}
채팅하기 diff --git a/frontend/src/component/Sidebar/Friends/friendList.tsx b/frontend/src/component/Sidebar/Friends/friendList.tsx index a2d224d..8e942ae 100644 --- a/frontend/src/component/Sidebar/Friends/friendList.tsx +++ b/frontend/src/component/Sidebar/Friends/friendList.tsx @@ -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 (

친구 목록

@@ -25,9 +26,7 @@ const FriendList = () => { onDragEnd={handleDrag}> {friendList.map((friend: friendType) => FriendItem(friend))} -
- -
+
); }; diff --git a/frontend/src/component/Sidebar/Friends/friends.styled.ts b/frontend/src/component/Sidebar/Friends/friends.styled.ts index ce65925..fdf77cc 100644 --- a/frontend/src/component/Sidebar/Friends/friends.styled.ts +++ b/frontend/src/component/Sidebar/Friends/friends.styled.ts @@ -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; @@ -83,6 +84,36 @@ export const findFriend = css` background-color: rgba(255, 255, 255, 0.7); 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}); } diff --git a/frontend/src/component/Sidebar/Friends/friends.ts b/frontend/src/component/Sidebar/Friends/friends.ts index 0e8861b..ba3fb90 100644 --- a/frontend/src/component/Sidebar/Friends/friends.ts +++ b/frontend/src/component/Sidebar/Friends/friends.ts @@ -1,5 +1,4 @@ export type friendType = { - id: string; isCalling: boolean; isOnline: boolean; name: string; diff --git a/frontend/src/component/Sidebar/Friends/search.tsx b/frontend/src/component/Sidebar/Friends/search.tsx new file mode 100644 index 0000000..8bc651b --- /dev/null +++ b/frontend/src/component/Sidebar/Friends/search.tsx @@ -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(''); + const [nicknameList, setNicknameList] = useState([]); + + 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(); + + 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); + }; + + return ( +
+ {nicknameList.length ? ( +
    + {nicknameList.map(name => ( +
  • {name}
  • + ))} +
+ ) : ( + <> + )} + +
+ ); +}; + +export default Search; diff --git a/frontend/src/page/Main/index.tsx b/frontend/src/page/Main/index.tsx index 135cdf2..b0e57d2 100644 --- a/frontend/src/page/Main/index.tsx +++ b/frontend/src/page/Main/index.tsx @@ -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(';'); diff --git a/frontend/src/store/atom/friends.ts b/frontend/src/store/atom/friends.ts index 4250754..2ec06c0 100644 --- a/frontend/src/store/atom/friends.ts +++ b/frontend/src/store/atom/friends.ts @@ -2,7 +2,6 @@ import { atom } from 'recoil'; export interface friendsProps { [key: string]: { - id: string; name: string; isOnline: boolean; isCalling: boolean; @@ -12,52 +11,44 @@ export interface friendsProps { export const friendsState = atom({ 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, }, },