diff --git a/packages/web/src/components/Room/AnimatedRoom.tsx b/packages/web/src/components/Room/AnimatedRoom.tsx new file mode 100644 index 000000000..8082b3ac1 --- /dev/null +++ b/packages/web/src/components/Room/AnimatedRoom.tsx @@ -0,0 +1,65 @@ +import { keyframes } from "@emotion/react"; + +import Room from "."; + +type AnimatedRoomProps = { + data: any; + selected?: boolean; + onClick?: () => void; + marginTop?: string; + marginBottom?: string; + theme?: string; + type?: "addition" | "deletion"; +}; + +const growHeight = keyframes` + from { + height: 0px; + } + to { + height: 125px; + } +`; + +const shrinkHeight = keyframes` + from { + height: 125px; + } + to { + height: 0px; + } +`; + +const AnimatedRoom = ({ + data, + selected = false, + onClick = () => {}, + marginTop = "0px", + marginBottom = "0px", + theme, + type, +}: AnimatedRoomProps) => { + const props = { + data, + selected, + onClick, + marginTop, + marginBottom, + theme, + type, + }; + + return !data.animating ? ( + + ) : ( +
+ ); +}; + +export default AnimatedRoom; diff --git a/packages/web/src/hooks/useRoomListAnimationState.tsx b/packages/web/src/hooks/useRoomListAnimationState.tsx new file mode 100644 index 000000000..4160001df --- /dev/null +++ b/packages/web/src/hooks/useRoomListAnimationState.tsx @@ -0,0 +1,79 @@ +import { useEffect, useState } from "react"; + +const useRoomListAnimationState = (rooms: Nullable>) => { + const [state, setState] = useState< + "default" | "additionAnimate" | "addition" | "deletionAnimate" | "deletion" + >("default"); + + const [localRooms, setLocalRooms] = useState(rooms); + + const animateDeletion = () => { + const deletionRooms = localRooms?.map((localRoom) => { + if (!rooms?.find((room) => room._id === localRoom._id)) { + return { ...localRoom, animating: true, type: "deletion" }; + } + return localRoom; + }); + setLocalRooms(deletionRooms); + const timer = setTimeout(() => { + setState("deletion"); + }, 500); + + return () => clearTimeout(timer); + }; + + const deletion = () => { + setLocalRooms(localRooms?.filter((room) => room.type !== "deletion")); + setState("additionAnimate"); + }; + + const animateAddition = () => { + let additionRooms = rooms?.map((room, index) => { + if (!localRooms?.find((localRoom) => localRoom._id === room._id)) { + return { ...room, animating: true, type: "addition" }; + } + return room; + }); + setLocalRooms(additionRooms); + const timer = setTimeout(() => { + setState("addition"); + }, 500); + return () => clearTimeout(timer); + }; + + const addition = () => { + setLocalRooms(rooms); + setState("default"); + }; + + useEffect(() => { + if (rooms && localRooms?.length && rooms.length !== localRooms.length) { + setState("deletionAnimate"); + } else { + setLocalRooms(rooms); + } + }, [rooms]); + + useEffect(() => { + switch (state) { + case "deletionAnimate": + animateDeletion(); + break; + case "deletion": + deletion(); + break; + case "additionAnimate": + animateAddition(); + break; + case "addition": + addition(); + break; + default: + break; + } + }, [state]); + + return { localRooms, state }; +}; + +export default useRoomListAnimationState; diff --git a/packages/web/src/pages/Home/NewRoom.tsx b/packages/web/src/pages/Home/NewRoom.tsx new file mode 100644 index 000000000..51afba082 --- /dev/null +++ b/packages/web/src/pages/Home/NewRoom.tsx @@ -0,0 +1,47 @@ +import { keyframes } from "@emotion/react"; +import { useEffect, useState } from "react"; + +import Room from "@/components/Room"; + +const DiffRoom = (props: any) => { + const [animating, setAnimating] = useState(true); + + useEffect(() => { + const timer = setTimeout(() => { + setAnimating(false); + }, 500); + return () => clearTimeout(timer); + }, []); + + const growHeight = keyframes` + from { + height: 0px; + } + to { + height: 125px; + } + `; + + const shrinkHeight = keyframes` + from { + height: 125px; + } + to { + height: 0px; + } + `; + + const animation = props.type === "addition" ? growHeight : shrinkHeight; + + return animating ? ( +
+ ) : props.type === "addition" ? ( + + ) : null; +}; + +export default DiffRoom; diff --git a/packages/web/src/pages/Home/RoomList.tsx b/packages/web/src/pages/Home/RoomList.tsx index 532dc7ab1..b52cf9574 100644 --- a/packages/web/src/pages/Home/RoomList.tsx +++ b/packages/web/src/pages/Home/RoomList.tsx @@ -1,10 +1,11 @@ import { Link } from "react-router-dom"; import usePageFromSearchParams from "@/hooks/usePageFromSearchParams"; +import useRoomListAnimationState from "@/hooks/useRoomListAnimationState"; import Empty from "@/components/Empty"; import Pagination, { PAGE_MAX_ITEMS } from "@/components/Pagination"; -import Room from "@/components/Room"; +import AnimatedRoom from "@/components/Room/AnimatedRoom"; type RoomListProps = { rooms: Nullable>; @@ -13,12 +14,13 @@ type RoomListProps = { const RoomList = (props: RoomListProps) => { const totalPages = Math.ceil((props.rooms ?? []).length / PAGE_MAX_ITEMS); const currentPage = usePageFromSearchParams(totalPages); + const { localRooms } = useRoomListAnimationState(props.rooms); return ( <> - {props.rooms?.length ? ( + {localRooms?.length ? ( <> - {props.rooms + {localRooms ?.slice( PAGE_MAX_ITEMS * (currentPage - 1), PAGE_MAX_ITEMS * currentPage @@ -30,10 +32,14 @@ const RoomList = (props: RoomListProps) => { replace style={{ textDecoration: "none" }} > - + ))} - {props.rooms.length > PAGE_MAX_ITEMS && ( + {localRooms.length > PAGE_MAX_ITEMS && ( { const [roomInfo, setRoomInfo] = useState>(null); - // 5분 간격으로 allRoms(요일 별 출발하는 방)을 갱신합니다. + // 10초 간격으로 allRoms(요일 별 출발하는 방)을 갱신합니다. useEffect(() => { - const interval = setInterval(fetchAllRooms, 1000 * 60 * 5); + const interval = setInterval(fetchAllRooms, 1000 * 10); return () => clearInterval(interval); }, []); diff --git a/packages/web/src/pages/Myroom/R1Myroom.tsx b/packages/web/src/pages/Myroom/R1Myroom.tsx index f370da6db..43ad069f0 100644 --- a/packages/web/src/pages/Myroom/R1Myroom.tsx +++ b/packages/web/src/pages/Myroom/R1Myroom.tsx @@ -3,7 +3,7 @@ import { Link } from "react-router-dom"; import AdaptiveDiv from "@/components/AdaptiveDiv"; import Empty from "@/components/Empty"; import Pagination, { PAGE_MAX_ITEMS } from "@/components/Pagination"; -import Room from "@/components/Room"; +import AnimatedRoom from "@/components/Room/AnimatedRoom"; import Title from "@/components/Title"; /** @@ -23,63 +23,67 @@ const R1Myroom = ({ ongoing = [], done = [], donePageInfo, -}: R1MyroomProps) => ( - - - 참여 중인 방 - - {ongoing.length === 0 ? ( - 참여 중인 방이 없습니다 - ) : ( - ongoing.map((item) => ( - - { + return ( + + + 참여 중인 방 + + {ongoing?.length === 0 ? ( + 참여 중인 방이 없습니다 + ) : ( + ongoing.map((item) => ( + + + + )) + )} + + 과거 참여 방 + + {done?.length === 0 ? ( + 과거 참여했던 방이 없습니다 + ) : ( + <> + {done + ?.slice( + PAGE_MAX_ITEMS * (donePageInfo.currentPage - 1), + PAGE_MAX_ITEMS * donePageInfo.currentPage + ) + .map((item) => ( + + + + ))} + - - )) - )} - - 과거 참여 방 - - {done.length === 0 ? ( - 과거 참여했던 방이 없습니다 - ) : ( - <> - {done - .slice( - PAGE_MAX_ITEMS * (donePageInfo.currentPage - 1), - PAGE_MAX_ITEMS * donePageInfo.currentPage - ) - .map((item) => ( - - - - ))} - - - )} - -); + + )} + + ); +}; export default R1Myroom; diff --git a/packages/web/src/pages/Myroom/R2Myroom.jsx b/packages/web/src/pages/Myroom/R2Myroom.jsx index e64524732..82618053b 100644 --- a/packages/web/src/pages/Myroom/R2Myroom.jsx +++ b/packages/web/src/pages/Myroom/R2Myroom.jsx @@ -7,6 +7,7 @@ import DottedLine from "@/components/DottedLine"; import Empty from "@/components/Empty"; import Pagination, { PAGE_MAX_ITEMS } from "@/components/Pagination"; import Room from "@/components/Room"; +import AnimatedRoom from "@/components/Room/AnimatedRoom"; import Title from "@/components/Title"; import WhiteContainer from "@/components/WhiteContainer"; @@ -60,11 +61,12 @@ const R2Myroom = (props) => { currentId={props.roomId} id={item._id} > - )) @@ -91,11 +93,12 @@ const R2Myroom = (props) => { currentId={props.roomId} id={item._id} > - ))} diff --git a/packages/web/src/pages/Myroom/index.jsx b/packages/web/src/pages/Myroom/index.jsx index 74addd835..2fba017e4 100644 --- a/packages/web/src/pages/Myroom/index.jsx +++ b/packages/web/src/pages/Myroom/index.jsx @@ -2,8 +2,13 @@ import { useEffect } from "react"; import { useHistory, useParams } from "react-router-dom"; import useButterflyState from "@/hooks/useButterflyState"; -import { useIsLogin, useValueRecoilState } from "@/hooks/useFetchRecoilState"; +import { + useFetchRecoilState, + useIsLogin, + useValueRecoilState, +} from "@/hooks/useFetchRecoilState"; import usePageFromSearchParams from "@/hooks/usePageFromSearchParams"; +import useRoomListAnimationState from "@/hooks/useRoomListAnimationState"; import AdaptiveDiv from "@/components/AdaptiveDiv"; import { PAGE_MAX_ITEMS } from "@/components/Pagination"; @@ -20,10 +25,22 @@ const Myroom = () => { const { roomId } = useParams(); const butterflyState = useButterflyState(); const myRooms = useValueRecoilState("myRooms"); + const fetchMyRooms = useFetchRecoilState("myRooms"); const totalPages = Math.ceil((myRooms?.done?.length ?? 0) / PAGE_MAX_ITEMS); const currentPage = usePageFromSearchParams(totalPages); const isLogin = useIsLogin(); + const { localRooms: ongoing } = useRoomListAnimationState(myRooms?.ongoing); + const { localRooms: done } = useRoomListAnimationState(myRooms?.done); + + useEffect(() => { + const interval = setInterval(() => { + fetchMyRooms(); + }, 1000 * 10); + + return () => clearInterval(interval); + }, []); + useEffect(() => { if (butterflyState === "fold" && roomId) { history.replace(`/chatting/${roomId}`); @@ -40,15 +57,15 @@ const Myroom = () => { ) : butterflyState === "fold" ? ( ) : ( );