From c065077c405e10bd45deee1b8b7ff6492ec8df84 Mon Sep 17 00:00:00 2001 From: ybmin Date: Tue, 26 Mar 2024 20:26:42 +0900 Subject: [PATCH 01/17] Add: channelTalk openChat with given message --- .../useChannelTalkEffect/channelService.js | 3 +++ .../hooks/skeleton/useChannelTalkEffect/index.tsx | 11 +++++++++++ packages/web/src/pages/Mypage/Menu.tsx | 3 +++ packages/web/src/pages/Mypage/index.tsx | 15 ++++++++++++--- packages/web/src/pages/Mypage/langs/en.json | 1 + packages/web/src/pages/Mypage/langs/ko.json | 1 + 6 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/web/src/hooks/skeleton/useChannelTalkEffect/channelService.js b/packages/web/src/hooks/skeleton/useChannelTalkEffect/channelService.js index c6a7638b4..ea3b249a9 100644 --- a/packages/web/src/hooks/skeleton/useChannelTalkEffect/channelService.js +++ b/packages/web/src/hooks/skeleton/useChannelTalkEffect/channelService.js @@ -42,6 +42,9 @@ class ChannelService { updateUser(options) { window.ChannelIO("updateUser", options); } + openChat(message) { + window.ChannelIO("openChat", undefined, message); + } shutdown() { window.ChannelIO("shutdown"); } diff --git a/packages/web/src/hooks/skeleton/useChannelTalkEffect/index.tsx b/packages/web/src/hooks/skeleton/useChannelTalkEffect/index.tsx index 3f2dec5b8..21e8ed292 100644 --- a/packages/web/src/hooks/skeleton/useChannelTalkEffect/index.tsx +++ b/packages/web/src/hooks/skeleton/useChannelTalkEffect/index.tsx @@ -36,4 +36,15 @@ export default () => { }); } }, [pathname, error]); + + const openWithdraw = () => { + if (channelTalkPluginKey) { + ChannelService.openChat({ + chatId: undefined, + message: + "스팍스 택시 서비스의 계정 탈퇴를 신청하고 싶습니다.\n신청 사유는 다음과 같습니다: ", + }); + } + }; + return { openWithdraw }; }; diff --git a/packages/web/src/pages/Mypage/Menu.tsx b/packages/web/src/pages/Mypage/Menu.tsx index fcc0dddf1..d63d2d08a 100644 --- a/packages/web/src/pages/Mypage/Menu.tsx +++ b/packages/web/src/pages/Mypage/Menu.tsx @@ -12,6 +12,7 @@ import ExitToAppRoundedIcon from "@mui/icons-material/ExitToAppRounded"; import HelpOutlineRoundedIcon from "@mui/icons-material/HelpOutlineRounded"; import KeyboardArrowLeftRoundedIcon from "@mui/icons-material/KeyboardArrowLeftRounded"; import LanguageRoundedIcon from "@mui/icons-material/LanguageRounded"; +import PersonRemoveRoundedIcon from "@mui/icons-material/PersonRemoveRounded"; import PortraitRoundedIcon from "@mui/icons-material/PortraitRounded"; import StarRoundedIcon from "@mui/icons-material/StarRounded"; @@ -46,6 +47,8 @@ const getIcon = (icon: string) => { return ; case "logout": return ; + case "withdraw": + return ; case "beta": return ; } diff --git a/packages/web/src/pages/Mypage/index.tsx b/packages/web/src/pages/Mypage/index.tsx index f2d4e3005..725d82647 100644 --- a/packages/web/src/pages/Mypage/index.tsx +++ b/packages/web/src/pages/Mypage/index.tsx @@ -1,6 +1,7 @@ import { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; +import useChannelTalkEffect from "@/hooks/skeleton/useChannelTalkEffect"; import { useValueRecoilState } from "@/hooks/useFetchRecoilState"; import AdaptiveDiv from "@/components/AdaptiveDiv"; @@ -63,6 +64,9 @@ const Mypage = () => { ); const onClickEventPolicy = useCallback(() => setIsOpenEventPolicy(true), []); const onClickMembers = useCallback(() => setOpenIsMembers(true), []); + const onClickWithdraw = useCallback(() => { + useChannelTalkEffect().openWithdraw(); + }, []); const styleProfImg = { width: "50px", @@ -194,9 +198,14 @@ const Mypage = () => { {t("credit")} {userId && ( - - {t("logout")} - + <> + + {t("withdraw")} + + + {t("logout")} + + )} diff --git a/packages/web/src/pages/Mypage/langs/en.json b/packages/web/src/pages/Mypage/langs/en.json index 365791c93..fe6dddb8d 100644 --- a/packages/web/src/pages/Mypage/langs/en.json +++ b/packages/web/src/pages/Mypage/langs/en.json @@ -16,6 +16,7 @@ "terms_privacy": "Collection and Use of Personal Information", "privacy_policy": "Privacy Policy", "credit": "Credit", + "withdraw": "Withdraw Account", "logout": "Logout", "page_modify": { "cancel": "Cancel", diff --git a/packages/web/src/pages/Mypage/langs/ko.json b/packages/web/src/pages/Mypage/langs/ko.json index 99afcaebe..e6c52dbab 100644 --- a/packages/web/src/pages/Mypage/langs/ko.json +++ b/packages/web/src/pages/Mypage/langs/ko.json @@ -16,6 +16,7 @@ "terms_privacy": "개인정보 수집 및 이용", "privacy_policy": "개인정보 처리방침", "credit": "만든 사람들", + "withdraw": "회원 탈퇴하기", "logout": "로그아웃", "page_modify": { "cancel": "취소", From 2c67424934f61c2864b7134464c813583bc80de6 Mon Sep 17 00:00:00 2001 From: ybmin Date: Tue, 26 Mar 2024 20:39:33 +0900 Subject: [PATCH 02/17] Fix: conflict with dev --- .../src/hooks/skeleton/useChannelTalkEffect/index.tsx | 11 ----------- packages/web/src/pages/Mypage/index.tsx | 8 ++++++-- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/web/src/hooks/skeleton/useChannelTalkEffect/index.tsx b/packages/web/src/hooks/skeleton/useChannelTalkEffect/index.tsx index 21e8ed292..3f2dec5b8 100644 --- a/packages/web/src/hooks/skeleton/useChannelTalkEffect/index.tsx +++ b/packages/web/src/hooks/skeleton/useChannelTalkEffect/index.tsx @@ -36,15 +36,4 @@ export default () => { }); } }, [pathname, error]); - - const openWithdraw = () => { - if (channelTalkPluginKey) { - ChannelService.openChat({ - chatId: undefined, - message: - "스팍스 택시 서비스의 계정 탈퇴를 신청하고 싶습니다.\n신청 사유는 다음과 같습니다: ", - }); - } - }; - return { openWithdraw }; }; diff --git a/packages/web/src/pages/Mypage/index.tsx b/packages/web/src/pages/Mypage/index.tsx index 725d82647..4937a2d83 100644 --- a/packages/web/src/pages/Mypage/index.tsx +++ b/packages/web/src/pages/Mypage/index.tsx @@ -1,7 +1,7 @@ import { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; -import useChannelTalkEffect from "@/hooks/skeleton/useChannelTalkEffect"; +import channelService from "@/hooks/skeleton/useChannelTalkEffect/channelService"; import { useValueRecoilState } from "@/hooks/useFetchRecoilState"; import AdaptiveDiv from "@/components/AdaptiveDiv"; @@ -65,7 +65,11 @@ const Mypage = () => { const onClickEventPolicy = useCallback(() => setIsOpenEventPolicy(true), []); const onClickMembers = useCallback(() => setOpenIsMembers(true), []); const onClickWithdraw = useCallback(() => { - useChannelTalkEffect().openWithdraw(); + channelService.openChat({ + chatId: undefined, + message: + "스팍스 택시 서비스의 계정 탈퇴를 신청하고 싶습니다.\n신청 사유는 다음과 같습니다:\n", + }); }, []); const styleProfImg = { From e60e8f434f7b8175f0f402856f2e86a835334149 Mon Sep 17 00:00:00 2001 From: andyye <63383967+0ev@users.noreply.github.com> Date: Tue, 26 Mar 2024 20:45:46 +0900 Subject: [PATCH 03/17] Add: room diff calculation --- packages/web/src/pages/Home/RoomList.tsx | 38 +++++++++++++++++++++ packages/web/src/pages/Home/RoomSection.tsx | 4 +-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/web/src/pages/Home/RoomList.tsx b/packages/web/src/pages/Home/RoomList.tsx index 532dc7ab1..f3c77dd7a 100644 --- a/packages/web/src/pages/Home/RoomList.tsx +++ b/packages/web/src/pages/Home/RoomList.tsx @@ -1,3 +1,4 @@ +import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import usePageFromSearchParams from "@/hooks/usePageFromSearchParams"; @@ -13,6 +14,43 @@ type RoomListProps = { const RoomList = (props: RoomListProps) => { const totalPages = Math.ceil((props.rooms ?? []).length / PAGE_MAX_ITEMS); const currentPage = usePageFromSearchParams(totalPages); + const [localRooms, setLocalRooms] = useState(props.rooms); + + // calculates the difference between two arrays location of the difference. Only works when before is subset of after + const calculateDiffAndLocation = (before: Array, after: Array) => { + let beforeIndex = 0; + let afterIndex = 0; + let diffs: { + diff: any; + location: number; + }[] = []; + while (beforeIndex < before.length && afterIndex < after.length) { + if (before[beforeIndex] !== after[afterIndex]) { + diffs.push({ + diff: after[afterIndex], + location: afterIndex, + }); + } else { + beforeIndex++; + } + afterIndex++; + } + if (beforeIndex < before.length) { + return null; + } + return diffs; + }; + + useEffect(() => { + if (props.rooms && localRooms && props.rooms.length !== localRooms.length) { + const diffs = calculateDiffAndLocation(localRooms, props.rooms); + if (diffs) { + setLocalRooms(props.rooms); + } + } else { + setLocalRooms(props.rooms); + } + }, [props.rooms]); return ( <> diff --git a/packages/web/src/pages/Home/RoomSection.tsx b/packages/web/src/pages/Home/RoomSection.tsx index 172c30d3e..9c84239f7 100644 --- a/packages/web/src/pages/Home/RoomSection.tsx +++ b/packages/web/src/pages/Home/RoomSection.tsx @@ -47,9 +47,9 @@ const RoomSection = ({ roomId }: RoomSectionProps) => { 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); }, []); From 4cdb2803f870cc55b74fd56c05726d7849909e02 Mon Sep 17 00:00:00 2001 From: andyye <63383967+0ev@users.noreply.github.com> Date: Tue, 26 Mar 2024 22:56:04 +0900 Subject: [PATCH 04/17] Add: diff animation --- packages/web/src/pages/Home/NewRoom.tsx | 47 ++++++++++ packages/web/src/pages/Home/RoomList.tsx | 107 ++++++++++++++++------- 2 files changed, 123 insertions(+), 31 deletions(-) create mode 100644 packages/web/src/pages/Home/NewRoom.tsx 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 f3c77dd7a..b081534d8 100644 --- a/packages/web/src/pages/Home/RoomList.tsx +++ b/packages/web/src/pages/Home/RoomList.tsx @@ -7,6 +7,8 @@ import Empty from "@/components/Empty"; import Pagination, { PAGE_MAX_ITEMS } from "@/components/Pagination"; import Room from "@/components/Room"; +import NewRoom from "./NewRoom"; + type RoomListProps = { rooms: Nullable>; }; @@ -16,37 +18,76 @@ const RoomList = (props: RoomListProps) => { const currentPage = usePageFromSearchParams(totalPages); const [localRooms, setLocalRooms] = useState(props.rooms); - // calculates the difference between two arrays location of the difference. Only works when before is subset of after - const calculateDiffAndLocation = (before: Array, after: Array) => { - let beforeIndex = 0; - let afterIndex = 0; - let diffs: { - diff: any; - location: number; - }[] = []; - while (beforeIndex < before.length && afterIndex < after.length) { - if (before[beforeIndex] !== after[afterIndex]) { - diffs.push({ - diff: after[afterIndex], - location: afterIndex, - }); - } else { - beforeIndex++; + const [state, setState] = useState< + "default" | "additionAnimate" | "addition" | "deletionAnimate" | "deletion" + >("default"); + + const animateDeletion = () => { + const deletionRooms = localRooms?.map((localRoom) => { + if (!props.rooms?.find((room) => room._id === localRoom._id)) { + return { ...localRoom, animating: true, type: "deletion" }; } - afterIndex++; - } - if (beforeIndex < before.length) { - return null; - } - return diffs; + return localRoom; + }); + setLocalRooms(deletionRooms); + + const timer = setTimeout(() => { + setState("deletion"); + }, 500); + return () => clearTimeout(timer); }; - useEffect(() => { - if (props.rooms && localRooms && props.rooms.length !== localRooms.length) { - const diffs = calculateDiffAndLocation(localRooms, props.rooms); - if (diffs) { - setLocalRooms(props.rooms); + const deletion = () => { + setLocalRooms(localRooms?.filter((room) => room.type !== "deletion")); + setState("additionAnimate"); + }; + + const animateAddition = () => { + let additionRooms = props.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(props.rooms); + setState("default"); + }; + + useEffect(() => { + switch (state) { + case "deletionAnimate": + animateDeletion(); + break; + case "deletion": + deletion(); + break; + case "additionAnimate": + animateAddition(); + break; + case "addition": + addition(); + break; + default: + break; + } + }, [state]); + + useEffect(() => { + if ( + props.rooms && + localRooms?.length && + (props.rooms.length !== localRooms.length || + JSON.stringify(props.rooms) !== JSON.stringify(localRooms.length)) + ) { + setState("deletionAnimate"); } else { setLocalRooms(props.rooms); } @@ -54,9 +95,9 @@ const RoomList = (props: RoomListProps) => { return ( <> - {props.rooms?.length ? ( + {localRooms?.length ? ( <> - {props.rooms + {localRooms ?.slice( PAGE_MAX_ITEMS * (currentPage - 1), PAGE_MAX_ITEMS * currentPage @@ -68,10 +109,14 @@ const RoomList = (props: RoomListProps) => { replace style={{ textDecoration: "none" }} > - + {room.animating ? ( + + ) : ( + + )} ))} - {props.rooms.length > PAGE_MAX_ITEMS && ( + {localRooms.length > PAGE_MAX_ITEMS && ( Date: Tue, 2 Apr 2024 22:08:33 +0900 Subject: [PATCH 05/17] =?UTF-8?q?Style:=20=EC=88=9C=EC=84=9C,=20=ED=83=9D?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web/src/pages/Mypage/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web/src/pages/Mypage/index.tsx b/packages/web/src/pages/Mypage/index.tsx index 9054174cd..0b7a4c77d 100644 --- a/packages/web/src/pages/Mypage/index.tsx +++ b/packages/web/src/pages/Mypage/index.tsx @@ -78,7 +78,7 @@ const Mypage = () => { channelService.openChat({ chatId: undefined, message: - "스팍스 택시 서비스의 계정 탈퇴를 신청하고 싶습니다.\n신청 사유는 다음과 같습니다:\n", + "SPARCS Taxi 서비스의 계정 탈퇴를 신청하고 싶습니다.\n신청 사유는 다음과 같습니다:\n", }); }, []); @@ -213,12 +213,12 @@ const Mypage = () => { {userId && ( <> - - {t("withdraw")} - {t("logout")} + + {t("withdraw")} + )} From c106ec1f9ebbb9688a22b30ef58485b371e8a3df Mon Sep 17 00:00:00 2001 From: jinhyeonkwon Date: Tue, 2 Apr 2024 23:04:30 +0900 Subject: [PATCH 06/17] Add: shortcut button in the modal of already joined room --- .../src/components/ModalPopup/Body/BodyRoomSelection.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/web/src/components/ModalPopup/Body/BodyRoomSelection.tsx b/packages/web/src/components/ModalPopup/Body/BodyRoomSelection.tsx index 306f1a2cc..edf92ce34 100644 --- a/packages/web/src/components/ModalPopup/Body/BodyRoomSelection.tsx +++ b/packages/web/src/components/ModalPopup/Body/BodyRoomSelection.tsx @@ -125,6 +125,11 @@ const BodyRoomSelection = ({ roomInfo }: BodyRoomSelectionProps) => { const isDepart = useIsTimeOver(dayServerToClient(roomInfo.time)); // 방 출발 여부 const requestJoin = useCallback(async () => { + if (isAlreadyPart) { + // 이미 참여 중인 방에서 버튼을 누르면 API 호출 관련 로직을 건너뛰고 해당 방으로 이동합니다. + history.push(`/myroom/${roomInfo._id}`); + return; + } if (onCall.current) return; onCall.current = true; await axios({ @@ -204,7 +209,7 @@ const BodyRoomSelection = ({ roomInfo }: BodyRoomSelectionProps) => { {isLogin || isRoomFull || isDepart ? (