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" ? (
) : (
);