Skip to content

Commit

Permalink
Add: Modal about Chat
Browse files Browse the repository at this point in the history
  • Loading branch information
14KGun committed Aug 2, 2023
1 parent 8ad841a commit bc14689
Show file tree
Hide file tree
Showing 14 changed files with 515 additions and 41 deletions.
51 changes: 42 additions & 9 deletions src/components/Chat/Header/SideMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import dayjs from "dayjs";
import { memo, useState } from "react";
import { memo, useCallback, useState } from "react";

import useIsTimeOver from "hooks/useIsTimeOver";

import DottedLine from "components/DottedLine";
import { ModalRoomShare } from "components/ModalPopup";
import { ModalChatCancel, ModalRoomShare } from "components/ModalPopup";
import User from "components/User";

import { day2str } from "tools/day";
import alertAtom from "atoms/alert";
import { useSetRecoilState } from "recoil";

import { day2str, dayServerToClient } from "tools/day";
import theme from "tools/theme";

import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded";
Expand Down Expand Up @@ -65,11 +70,23 @@ const SideMenuButton = ({ type, onClick }: SideMenuButtonProps) => {

const SideMenu = ({
roomInfo,
fetchRoomInfo,
fetchRoomInfo, // @fixme, @todo: ? 안쓰는 파라미터 인것 같은데?
isOpen,
setIsOpen,
}: SideMenuProps) => {
const setAlert = useSetRecoilState(alertAtom);
const [isOpenShare, setIsOpenShare] = useState<boolean>(false);
const [isOpenCancel, setIsOpenCancel] = useState<boolean>(false);
const isDepart = useIsTimeOver(dayServerToClient(roomInfo.time)); // 방 출발 여부

const onClikcShare = useCallback(() => setIsOpenShare(true), []);
const onClickCancel = useCallback(
() =>
isDepart
? setAlert("출발 시각이 이전인 방은 탑승 취소를 할 수 없습니다.")
: setIsOpenCancel(true),
[isDepart]
);

const styleBackground = {
position: "absolute" as any,
Expand Down Expand Up @@ -171,19 +188,30 @@ const SideMenu = ({
</div>
</div>
<DottedLine />
<SideMenuButton type="share" onClick={() => setIsOpenShare(true)} />
<SideMenuButton type="share" onClick={onClikcShare} />
{/* <DottedLine />
<SideMenuButton type="report" />
<DottedLine />
<SideMenuButton type="taxi" /> */}
</div>
<DottedLine />
<div css={styleNameSection}>
<div css={styleNameSection} onClick={onClickCancel}>
<LogoutOutlinedIcon
style={{ fontSize: "24px", fill: theme.purple, cursor: "pointer" }}
onClick={() => setIsOpen(false)}
style={{
fontSize: "24px",
fill: isDepart ? theme.gray_text : theme.purple,
...theme.cursor(isDepart),
}}
/>
<div css={{ color: theme.purple, ...theme.font18 }}>탑승 취소</div>
<div
css={{
color: isDepart ? theme.gray_text : theme.purple,
...theme.font18,
...theme.cursor(isDepart),
}}
>
탑승 취소
</div>
</div>
<div css={{ height: "env(safe-area-inset-bottom)" }} />
</div>
Expand All @@ -192,6 +220,11 @@ const SideMenu = ({
onChangeIsOpen={setIsOpenShare}
roomInfo={roomInfo}
/>
<ModalChatCancel
roomId={roomInfo._id}
isOpen={isOpenCancel}
onChangeIsOpen={setIsOpenCancel}
/>
</>
);
};
Expand Down
82 changes: 76 additions & 6 deletions src/components/Chat/MessageForm/ToolSheet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
import { memo, useCallback, useRef } from "react";
import { memo, useCallback, useMemo, useRef, useState } from "react";

import { useValueRecoilState } from "hooks/useFetchRecoilState";
import useIsTimeOver from "hooks/useIsTimeOver";

import AdaptiveDiv from "components/AdaptiveDiv";
import { ModalChatPayement, ModalChatSettlement } from "components/ModalPopup";

import ToolButton from "./ToolButton";

import alertAtom from "atoms/alert";
import { useSetRecoilState } from "recoil";

import { dayNowClient, dayServerToClient } from "tools/day";
import theme from "tools/theme";

type ToolSheetProps = {
roomInfo: Nullable<Room>;
isOpen: boolean;
onChangeIsOpen?: (x: boolean) => void;
onChangeUploadedImage?: (x: Nullable<File>) => void;
};

const ToolSheet = ({
roomInfo,
isOpen,
onChangeIsOpen,
onChangeUploadedImage,
}: ToolSheetProps) => {
const setAlert = useSetRecoilState(alertAtom);
const { oid: userOid } = useValueRecoilState("loginInfo") || {};
const [isOpenSettlement, setIsOpenSettlement] = useState<boolean>(false);
const [isOpenPayment, setIsOpenPayment] = useState<boolean>(false);
const isDepart = useIsTimeOver(
roomInfo ? dayServerToClient(roomInfo.time) : dayNowClient()
); // 방 출발 여부
const settlementStatusForMe = useMemo(
() =>
roomInfo &&
roomInfo.part.filter((user) => user._id === userOid)?.[0]?.isSettlement,
[userOid, roomInfo]
);

const inputImageRef = useRef<HTMLInputElement>(null);
const onChangeImage = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target?.files?.[0];
onChangeUploadedImage && onChangeUploadedImage(file);
onChangeIsOpen && onChangeIsOpen(false);
onChangeIsOpen?.(false);
e.target.value = "";
},
[onChangeUploadedImage]
Expand All @@ -31,8 +55,26 @@ const ToolSheet = ({
() => inputImageRef.current?.click(),
[onChangeIsOpen]
);
const onClickSettlement = useCallback(() => {}, [onChangeIsOpen]);
const onClickPayment = useCallback(() => {}, [onChangeIsOpen]);
const onClickSettlement = useCallback(() => {
if (!isDepart) setAlert("출발 시각 이후에 정산하기 요청을 보내주세요.");
else if (settlementStatusForMe === "paid")
setAlert("정산하기 요청은 중복하여 보낼 수 없습니다.");
else if (roomInfo?.settlementTotal)
setAlert(
"정산하기 요청을 한 사용자가 이미 있습니다." +
"만약 결제하지 않은 사용자가 정산하기 요청을 보냈다면 신고해주세요."
);
else setIsOpenSettlement(true);
}, [isDepart, settlementStatusForMe]);
const onClickPayment = useCallback(() => {
if (!isDepart) setAlert("출발 시각 이후에 송금하기 요청을 보내주세요.");
else if (settlementStatusForMe === "sent")
setAlert("송금하기 요청은 중복하여 보낼 수 없습니다.");
else if (!roomInfo?.settlementTotal)
setAlert("정산하기 요청을 보낸 사용자가 없어 송금하기가 불가능합니다.");
else setIsOpenPayment(true);
}, [isDepart, settlementStatusForMe]);
const onRecallSettlePayment = useCallback(() => onChangeIsOpen?.(false), []);

const styleWrap = {
position: "absolute" as any,
Expand Down Expand Up @@ -61,10 +103,38 @@ const ToolSheet = ({
<AdaptiveDiv type="center">
<div css={style}>
<ToolButton type="image" onClick={onClickImage} />
<ToolButton type="settlement" onClick={onClickSettlement} />
<ToolButton type="payment" onClick={onClickPayment} />
<ToolButton
type="settlement"
onClick={onClickSettlement}
isVaild={isDepart && !roomInfo?.settlementTotal}
/>
<ToolButton
type="payment"
onClick={onClickPayment}
isVaild={
isDepart &&
!!roomInfo?.settlementTotal &&
settlementStatusForMe === "send-required"
}
/>
</div>
</AdaptiveDiv>
{roomInfo && (
<>
<ModalChatSettlement
isOpen={isOpenSettlement}
onChangeIsOpen={setIsOpenSettlement}
roomId={roomInfo._id}
onRecall={onRecallSettlePayment}
/>
<ModalChatPayement
isOpen={isOpenPayment}
onChangeIsOpen={setIsOpenPayment}
roomId={roomInfo._id}
onRecall={onRecallSettlePayment}
/>
</>
)}
</div>
);
};
Expand Down
3 changes: 3 additions & 0 deletions src/components/Chat/MessageForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import theme from "tools/theme";

type MessageFormProps = {
layoutType: LayoutType;
roomInfo: Nullable<Room>;
isDisplayNewMessage: boolean;
isOpenToolSheet: boolean;
onChangeIsOpenToolSheet: (x: boolean) => void;
Expand All @@ -27,6 +28,7 @@ type MessageFormProps = {

const MessageForm = ({
layoutType,
roomInfo,
isDisplayNewMessage,
isOpenToolSheet,
onChangeIsOpenToolSheet,
Expand Down Expand Up @@ -68,6 +70,7 @@ const MessageForm = ({
onClick={onClickNewMessage}
/>
<ToolSheet
roomInfo={roomInfo}
isOpen={isOpenToolSheet}
onChangeIsOpen={onChangeIsOpenToolSheet}
onChangeUploadedImage={setUploadedImage}
Expand Down
2 changes: 2 additions & 0 deletions src/components/Chat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const Chat = ({ roomId, layoutType }: ChatProps) => {
// socket.io를 통해 채팅 전송 및 수신
useSocketChatEffect(
roomId,
fetchRoomInfo,
setChats,
setDisplayNewMessage,
messageBodyRef,
Expand Down Expand Up @@ -70,6 +71,7 @@ const Chat = ({ roomId, layoutType }: ChatProps) => {
/>
<MessageForm
layoutType={layoutType}
roomInfo={roomInfo}
isDisplayNewMessage={isDisplayNewMessage}
isOpenToolSheet={isOpenToolSheet}
onChangeIsOpenToolSheet={setIsOpenToolSheet}
Expand Down
6 changes: 3 additions & 3 deletions src/components/ModalPopup/Body/BodyRoomSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
useFetchRecoilState,
useValueRecoilState,
} from "hooks/useFetchRecoilState";
import useIsTimeOver from "hooks/useIsTimeOver";
import { useAxios } from "hooks/useTaxiAPI";

import Button from "components/Button";
Expand All @@ -18,7 +19,7 @@ import { MAX_PARTICIPATION } from "pages/Myroom";
import alertAtom from "atoms/alert";
import { useSetRecoilState } from "recoil";

import { dayNowClient, dayServerToClient } from "tools/day";
import { dayServerToClient } from "tools/day";
import { date2str } from "tools/moment";
import theme from "tools/theme";
import { getLocationName } from "tools/trans";
Expand Down Expand Up @@ -121,8 +122,7 @@ const BodyRoomSelection = ({ roomInfo }: BodyRoomSelectionProps) => {
true); // 이미 참여 중인지 여부
const isMaxPart =
isLogin && myRooms && myRooms.ongoing.length >= MAX_PARTICIPATION; // 최대 참여 가능한 방 개수를 초과했는지 여부
const isDepart =
roomInfo && dayServerToClient(roomInfo.time) <= dayNowClient(); // 방 출발 여부
const isDepart = useIsTimeOver(dayServerToClient(roomInfo.time)); // 방 출발 여부

const requestJoin = useCallback(async () => {
if (onCall.current) return;
Expand Down
113 changes: 113 additions & 0 deletions src/components/ModalPopup/ModalChatCancel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { useCallback } from "react";
import { useHistory } from "react-router-dom";

import { useFetchRecoilState } from "hooks/useFetchRecoilState";
import { useAxios } from "hooks/useTaxiAPI";

import Button from "components/Button";
import Modal from "components/Modal";

import alertAtom from "atoms/alert";
import { useSetRecoilState } from "recoil";

import theme from "tools/theme";

type ModalChatCancelProps = Omit<
Parameters<typeof Modal>[0],
"padding" | "children" | "onEnter"
> & { roomId: Room["_id"] };

const ModalChatCancel = ({ roomId, ...modalProps }: ModalChatCancelProps) => {
const axios = useAxios();
const history = useHistory();
const setAlert = useSetRecoilState(alertAtom);
const fetchMyrooms = useFetchRecoilState("myRooms");

const onClickOk = useCallback(
() =>
axios({
url: "/rooms/abort",
method: "post",
data: { roomId },
onSuccess: () => {
fetchMyrooms();
history.replace("/myroom");
},
onError: () => setAlert("탑승 취소를 실패하였습니다."),
}),
[roomId]
);

const styleTextCont = {
textAlign: "center" as any,
};
const styleTextCont2 = {
textAlign: "center" as any,
lineHieght: "12px",
paddingTop: "6px",
fontSize: "10px",
color: "888888",
};
const styleTxt1 = {
fontSize: "16px",
fontWeight: "bold",
};
const styleTxt2 = {
fontSize: "16px",
fontWeight: 300,
};
const styleTxt3 = {
fontSize: "16px",
fontWeight: "bold",
color: "#6E3678",
};

return (
<Modal {...modalProps} padding="10px" onEnter={onClickOk}>
<div css={{ margin: "26px 0 24px" }}>
<div css={styleTextCont}>
<span css={styleTxt1}>탑승</span>
<span css={styleTxt2}></span>
<span css={styleTxt3}>취소</span>
<span css={styleTxt2}>하시겠습니까?</span>
</div>
<div css={styleTextCont2}>
취소 후 재탑승이 가능합니다.
<br />
다만, 혼자 탑승 중인 경우에는 방이 사라집니다.
</div>
</div>
<div
css={{
position: "relative",
display: "flex",
justifyContent: "space-between",
gap: "10px",
}}
>
<Button
type="gray"
width="calc(50% - 5px)"
padding="10px 0 9px"
radius={8}
font={theme.font14}
onClick={() => modalProps?.onChangeIsOpen?.(false)}
>
돌아가기
</Button>
<Button
type="purple_inset"
width="calc(50% - 5px)"
padding="10px 0 9px"
radius={8}
font={theme.font14_bold}
onClick={onClickOk}
>
취소하기
</Button>
</div>
</Modal>
);
};

export default ModalChatCancel;
Loading

0 comments on commit bc14689

Please sign in to comment.