From d8b1974c058cda4685c20a31a4cc46370ad83944 Mon Sep 17 00:00:00 2001 From: Yonghun Date: Thu, 9 Nov 2023 14:04:52 +0900 Subject: [PATCH] Feat: Add invite and alert --- src/components/Main/CreateGameModal/index.tsx | 350 +++++++++++++++++- src/components/common/UserCard/index.tsx | 94 +++++ src/pages/Example/index.tsx | 105 +++++- src/pages/Login/index.tsx | 2 +- 4 files changed, 539 insertions(+), 12 deletions(-) create mode 100644 src/components/common/UserCard/index.tsx diff --git a/src/components/Main/CreateGameModal/index.tsx b/src/components/Main/CreateGameModal/index.tsx index 51d185d8..1807078e 100644 --- a/src/components/Main/CreateGameModal/index.tsx +++ b/src/components/Main/CreateGameModal/index.tsx @@ -1,5 +1,351 @@ -const CreateGameModal = () => { - return
create game modal
; +import { Button, Input } from "@chakra-ui/react"; +import { ChangeEvent, useEffect, useState } from "react"; +import { io } from "socket.io-client"; +import styled from "styled-components"; +import useFetch from "../../../hooks/useFetch"; +import useInput from "../../../hooks/useInput"; +import UserCard from "../../common/UserCard"; +import useFireFetch from "../../../hooks/useFireFetch"; + +const Container = styled.div` + position: absolute; + top: 0; + left: 0; + + width: 100vw; + height: 100vh; + + background-color: rgba(0, 0, 0, 0.7); +`; + +const Wrap = styled.div` + border-radius: 0.5rem; + + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + width: 45rem; + height: 30rem; + + background-color: #fff; + + display: flex; + + & > div:first-child { + padding: 3rem 1.5rem 3rem 5rem; + } + & > div:last-child { + padding: 3rem 5rem 3rem 1.5rem; + } + & > div:last-child > div { + border: 1px solid #c6c6c6; + border-radius: 0.5rem; + padding: 1rem; + } +`; + +const Section = styled.div` + width: 50%; + padding: 3rem; + + & > div { + width: 100%; + height: 100%; + + display: flex; + flex-direction: column; + align-items: center; + } +`; + +const ImgBox = styled.div` + border: 1px solid #c6c6c6; + border-radius: 0.5rem; + + width: 7rem; + height: 7rem; + + display: flex; + flex-direction: column; + justify-content: center; + + text-align: center; + + font-size: 3rem; + + margin-bottom: 1.5rem; +`; + +const Empty = styled.div` + flex-grow: 1; +`; + +const Radio = styled.div` + border: 1px solid #c6c6c6; + border-radius: 0.5rem; + + position: relative; + + width: 100%; + + padding: 1rem; + + display: flex; + flex-wrap: wrap; + + & > div { + display: inline-block; + margin: 0.4rem; + } + & > div > label { + display: inline-block; + margin-left: 0.2rem; + } + + & > div.p { + padding: 0; + margin: 0; + + position: absolute; + top: -0.7rem; + left: 50%; + transform: translateX(-50%); + + background-color: #fff; + } +`; + +const ButtonWrap = styled.div` + display: flex; +`; + +interface ChatRoom { + name: string; + users: string[]; + isPrivate?: boolean; + num?: number; + bg?: string; +} + +interface Props { + setModal: React.Dispatch>; +} + +const CreateGameModal = ({ setModal }: Props) => { + const token = JSON.parse(localStorage.getItem("token") as string); + + const socket = io( + `https://fastcampus-chat.net/chat?chatId=9fe8a1af-9c60-4937-82dd-21d6da5b9cd9`, + { + extraHeaders: { + Authorization: `Bearer ${token.accessToken}`, + serverId: import.meta.env.VITE_APP_SERVER_ID, + }, + }, + ); + + const fireFetch = useFireFetch(); + const [roomData, setRoomData] = useState({ + name: "", + users: [], + isPrivate: false, + num: 1, + }); + + const users = useFetch({ + url: "https://fastcampus-chat.net/users", + method: "GET", + start: true, + }); + + const createGame = useFetch({ + url: "https://fastcampus-chat.net/chat", + method: "POST", + data: { + name: roomData.name, + users: roomData.users, + }, + start: false, + }); + + const titleInput = useInput(""); + + useEffect(() => { + const copy = { ...roomData }; + copy.name = titleInput.value; + setRoomData(copy); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [titleInput.value, titleInput.value]); + + const handleMakeRoom = () => { + createGame.refresh(); + + console.log(roomData); + }; + + const handleRadioChange = (e: ChangeEvent) => { + const copy = { ...roomData }; + copy.num = ~~e.target.value; + + setRoomData(copy); + }; + + useEffect(() => { + if (createGame.result) { + const newData = { + ...roomData, + id: createGame.result.id, + host: token.id, + }; + + fireFetch.usePostData("game", createGame.result.id, { + ...newData, + bg: "⭐", + }); + + // const roomText = [...JSON.stringify(newData)]; + + const inviteUser: (string | ChatRoom | string[])[] = [...roomData.users]; + + inviteUser.push(newData); + inviteUser.push("*&^"); + + // const text = inviteUser.toString(); + const text = JSON.stringify(inviteUser); + + socket.emit("message-to-server", text); + console.log("완료"); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [createGame.result]); + + return ( + + +
+
+ + + + +
인원 선택
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + + + +
+
+
+
+ + {users.result && + users.result.map((value: any) => { + return ( + + ); + })} +
+
+
+
+ ); }; export default CreateGameModal; diff --git a/src/components/common/UserCard/index.tsx b/src/components/common/UserCard/index.tsx new file mode 100644 index 00000000..254b5696 --- /dev/null +++ b/src/components/common/UserCard/index.tsx @@ -0,0 +1,94 @@ +import styled from "styled-components"; +import { Button } from "@chakra-ui/react"; +import { useState } from "react"; + +const Profile = styled.div` + border: 1px solid #c6c6c6; + border-radius: 5px; + + width: 100%; + + padding: 0.4rem 1rem; + margin-bottom: 0.5rem; + + display: flex; + align-items: center; + + & > img { + width: 100%; + } +`; + +const ImageWrap = styled.div` + border-radius: 50%; + + width: 30px; + + overflow: hidden; + + margin-right: 1rem; +`; + +const Empty = styled.div` + flex-grow: 1; +`; + +interface ChatRoom { + name: string; + users: string[]; + isPrivate?: boolean; +} + +interface Props { + id: string; + name: string; + picture: string; + roomData: ChatRoom; + setRoomData: React.Dispatch>; +} + +const UserCard = ({ id, name, picture, roomData, setRoomData }: Props) => { + const [status, setStatue] = useState(false); + + const handleInvite = () => { + const copy = { ...roomData }; + if (status) { + copy.users = copy.users.filter((v) => v !== id); + } else { + copy.users.push(id); + } + setRoomData(copy); + setStatue((prev) => !prev); + }; + + return ( + + + profile + + {name} + + {status ? ( + + ) : ( + + )} + + ); +}; + +export default UserCard; diff --git a/src/pages/Example/index.tsx b/src/pages/Example/index.tsx index 55f19f72..f4a500cf 100644 --- a/src/pages/Example/index.tsx +++ b/src/pages/Example/index.tsx @@ -2,10 +2,25 @@ import { Button, Input } from "@chakra-ui/react"; import { serverTimestamp } from "firebase/firestore"; import { useEffect, useState } from "react"; import { io } from "socket.io-client"; +import styled from "styled-components"; +import CreateGameModal from "../../components/Main/CreateGameModal"; import useFetch from "../../hooks/useFetch"; import useFireFetch from "../../hooks/useFireFetch"; import useInput from "../../hooks/useInput"; +const Toast = styled.div` + border-radius: 16px; + + position: absolute; + top: 2rem; + left: 2rem; + + background: #cdcdcd; + + width: 400px; + height: 150px; +`; + const Example = () => { const token = JSON.parse(localStorage.getItem("token") as string); @@ -35,6 +50,20 @@ const Example = () => { id: "", text: "", }); + // 초대방 정보 데이터 + const [roomData, setRoomData] = useState({ + id: "", + name: "", + host: "", + users: [""], + }); + + // 팝업 데이터 + const [toastUser, setToastUser] = useState([""]); + + // 토스트 모달 + const [toast, setToast] = useState(false); + const [modal, setModal] = useState(false); // 파이어베이스 커스텀훅 선언 const fireFetch = useFireFetch(); @@ -49,18 +78,42 @@ const Example = () => { start: true, }); + const live = useFetch({ + url: "https://fastcampus-chat.net/chat/leave", + method: "PATCH", + data: { + chatId: "1598e7f6-ab51-43f8-b70a-67f7c57dce00", + }, + start: false, + }); + // 메시지 input value 저장 const messageValue = useInput(""); // 소켓 통신 시 메시지 데이터 저장 useEffect(() => { socket.on("message-to-client", (messageObject) => { + // 일반 채팅인지 초대 메시지인지 구별 + + if (messageObject.text.slice(-5, -2) === "*&^") { + // 초대 상태 저장 + const usersArr = JSON.parse(messageObject.text); + const users = [...usersArr]; + users.pop(); + users.pop(); + const room = usersArr[usersArr.length - 2]; + + setToastUser(users); + setRoomData(room); + } else { + // 메시지 데이터, 작성 유저 상태 저장 + const copy = { ...message }; + copy.id = messageObject.userId; + copy.text = messageObject.text; + setMessage(copy); + } + // console.log(messageObject); - // 메시지 데이터, 작성 유저 상태 저장 - const copy = { ...message }; - copy.id = messageObject.userId; - copy.text = messageObject.text; - setMessage(copy); }); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -72,6 +125,17 @@ const Example = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [message.text]); + //팝업 변화 감지 + useEffect(() => { + if (toastUser[0] !== "") { + if (toastUser.includes(token.id)) { + console.log(roomData); + setToast(true); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [toastUser]); + // 파이어 베이스에 저장할 데이터 const noticeData = { id: "asdasdasdasdasd", @@ -124,7 +188,7 @@ const Example = () => { }; const deleteData_A = () => { - console.log(2); + live.refresh(); }; // api get 요청으로 가져온 데이터 출력 @@ -152,13 +216,36 @@ const Example = () => { {/* 벡엔드 rest api 통신 */}
- +
{/* 메시지 소켓 통신 */} - - +
+ + +
+ + + {modal ? : null} + {toast && roomData ? ( + +
+
{roomData.host}님이 초대하였습니다.
+
{roomData.name}
+
+
+ + +
+
+ ) : null} ); }; diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx index 1e5ae422..670a2ff5 100644 --- a/src/pages/Login/index.tsx +++ b/src/pages/Login/index.tsx @@ -39,7 +39,7 @@ const Login = () => { useEffect(() => { if (login.result) { - const text = JSON.stringify(login.result); + const text = JSON.stringify({ ...login.result, id: idInput.value }); localStorage.setItem("token", text); } console.log(login.result, login.loading, login.statusCode);