Skip to content

Commit

Permalink
Merge branch 'release/2.1.0' into feat/#309-last_card
Browse files Browse the repository at this point in the history
  • Loading branch information
NYeonK committed Aug 5, 2023
2 parents 84fce0c + 0171398 commit 60c5ddc
Show file tree
Hide file tree
Showing 20 changed files with 355 additions and 43 deletions.
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
<!--
<script>
if("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("./pwaServiceWorker.js")
})
}
</script>
-->
</body>
</html>
18 changes: 11 additions & 7 deletions src/@components/@common/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface ModalContents {

export default function Modal(props: PropsWithChildren<ModalContents>) {
const { theme = "DEFAULT", closeHandler, closeOpacityClassName, closeBtnClassName, isNoCloseBtn, children } = props;
const outClickCloserRef = useOutClickCloser(closeHandler, true);
const outClickCloserRef = useOutClickCloser(closeHandler, true);

if (theme === "GRAY_CENTER")
return (
Expand Down Expand Up @@ -54,9 +54,11 @@ export default function Modal(props: PropsWithChildren<ModalContents>) {
<ModalPortal>
<St.WhiteRoot>
<St.WhiteModal ref={outClickCloserRef}>
<St.CloseBtn type="button" className={closeBtnClassName} onClick={closeHandler}>
<IcModalCloseBtn closeBtnClassName={closeBtnClassName} />
</St.CloseBtn>
{isNoCloseBtn || (
<St.CloseBtn type="button" className={closeBtnClassName} onClick={closeHandler}>
<IcModalCloseBtn closeBtnClassName={closeBtnClassName} />
</St.CloseBtn>
)}
<St.ModalContents>{children}</St.ModalContents>
</St.WhiteModal>
</St.WhiteRoot>
Expand All @@ -67,9 +69,11 @@ export default function Modal(props: PropsWithChildren<ModalContents>) {
<ModalPortal>
<St.DefaultRoot>
<St.DefaultModal ref={outClickCloserRef}>
<St.CloseBtn type="button" className={closeBtnClassName} onClick={closeHandler}>
<IcModalCloseBtn closeBtnClassName={closeBtnClassName} />
</St.CloseBtn>
{isNoCloseBtn || (
<St.CloseBtn type="button" className={closeBtnClassName} onClick={closeHandler}>
<IcModalCloseBtn closeBtnClassName={closeBtnClassName} />
</St.CloseBtn>
)}
<St.ModalContents>{children}</St.ModalContents>
</St.DefaultModal>
</St.DefaultRoot>
Expand Down
37 changes: 37 additions & 0 deletions src/@components/@common/Toast/ToastProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { PropsWithChildren, useCallback, useState } from "react";

import { ToastContext } from "./context";
import useTimeout from "./hooks/useTimeout";
import * as St from "./style";

export type ToastType = {
message: string;
duration: number;
};

export default function ToastProvider({ children }: PropsWithChildren) {
const [toast, setToast] = useState<ToastType | null>(null);
const toastTimeout = useTimeout();

const showToast = useCallback(
async ({ message, duration }: ToastType) => {
setToast({ message, duration });

toastTimeout.set(() => {
setToast(null);
}, duration * 1000);
},
[setToast, toastTimeout],
);

return (
<ToastContext.Provider value={{ showToast }}>
{children}
{toast && (
<St.ToastContainer>
<St.ToastMessage>{toast.message}</St.ToastMessage>
</St.ToastContainer>
)}
</ToastContext.Provider>
);
}
13 changes: 13 additions & 0 deletions src/@components/@common/Toast/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";

import { ToastType } from "./ToastProvider";

interface ToastController {
showToast: (toast: ToastType) => void;
}

export const ToastContext = React.createContext<ToastController>({
showToast: () => {
throw new Error("Function not implemented.");
},
});
16 changes: 16 additions & 0 deletions src/@components/@common/Toast/hooks/useTimeout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useRef } from "react";

import { ToastType } from "../ToastProvider";

export default function useTimeout() {
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

return {
set(handler: () => void, duration: number) {
if (timerRef.current !== null) {
clearTimeout(timerRef.current);
}
timerRef.current = setTimeout(handler, duration);
},
};
}
10 changes: 10 additions & 0 deletions src/@components/@common/Toast/hooks/useToast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useContext } from "react";

import { ToastContext } from "../context";
import { ToastType } from "../ToastProvider";

export default function useToast() {
const { showToast } = useContext(ToastContext);

return (toast: ToastType) => showToast(toast);
}
26 changes: 26 additions & 0 deletions src/@components/@common/Toast/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import styled from "styled-components";

export const ToastContainer = styled.div`
position: absolute;
bottom: 7.2rem;
width: 100%;
padding: 0 0.8rem;
z-index: 10;
`;

export const ToastMessage = styled.div`
display: flex;
height: 4.8rem;
padding: 0.8rem 1.6rem;
align-items: center;
border-radius: 0.6rem;
border: 0.1rem solid ${({ theme }) => theme.newColors.gray200};
background: ${({ theme }) => theme.newColors.white};
${({ theme }) => theme.newFonts.caption1}
color: ${({ theme }) => theme.newColors.gray900};
`;
42 changes: 42 additions & 0 deletions src/@components/CardCollectionPage/Card/CardMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import IcBookmarkCheck_23_28 from "../../../../asset/icon/IcBookmarkCheck_23_28";
import IcMenuBtn from "../../../../asset/icon/IcMenuBtn";
import IcShareBtn from "../../../../asset/icon/IcShareBtn";
import { GTM_CLASS_NAME } from "../../../../util/const/gtm";
import useCardBookmark from "../../hooks/useCardBookmark";
import * as St from "./style";

interface CardMenuProps {
_id: string;
isBookmark: boolean;
openLoginModalHandler: () => void;
toggleMenuModal: () => void;
}

export default function CardMenu(props: CardMenuProps) {
const { _id, isBookmark, openLoginModalHandler, toggleMenuModal } = props;

const { isBookmarked, handleClickBookmark } = useCardBookmark(isBookmark, openLoginModalHandler);
return (
<St.MenuContainer>
<St.ButtonWrapper onClick={toggleMenuModal}>
<IcMenuBtn />
</St.ButtonWrapper>
<St.ButtonWrapper>
<St.IconWrapper>
<IcShareBtn />
</St.IconWrapper>
<St.ButtonLabel>공유하기</St.ButtonLabel>
</St.ButtonWrapper>
<St.ButtonWrapper
className={GTM_CLASS_NAME.cardBookmark}
onClick={() => handleClickBookmark(_id)}
aria-label="북마크"
role="dialog">
<St.IconWrapper>
<IcBookmarkCheck_23_28 isChecked={isBookmarked} />
</St.IconWrapper>
<St.ButtonLabel>저장하기</St.ButtonLabel>
</St.ButtonWrapper>
</St.MenuContainer>
);
}
35 changes: 35 additions & 0 deletions src/@components/CardCollectionPage/Card/CardMenu/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import styled from "styled-components";

export const MenuContainer = styled.article`
display: flex;
flex-direction: column;
position: absolute;
bottom: 1.7rem;
right: 1.2rem;
gap: 2.4rem;
`;

export const ButtonWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
width: 4.1rem;
`;

export const IconWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: 4rem;
height: 4rem;
`;

export const ButtonLabel = styled.p`
margin-top: 0.6rem;
${({ theme }) => theme.newFonts.caption1};
color: ${({ theme }) => theme.newColors.gray600};
`;
60 changes: 60 additions & 0 deletions src/@components/CardCollectionPage/Card/MenuModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Modal from "../../../@common/Modal";
import useToast from "../../../@common/Toast/hooks/useToast";
import * as St from "./style";

interface MenuModalProps {
closeHandler: () => void;
}

type ModalItem = {
emoji: string;
title: string;
isNeedLogin?: boolean;
handleClickItem: () => void;
};

export default function MenuModal(props: MenuModalProps) {
const { closeHandler } = props;
const showToast = useToast();

const ModalItems: ModalItem[] = [
{
emoji: "🥲",
title: "이 주제 별로예요",
handleClickItem: () => {
closeHandler();
showToast({ message: "🥰 소중한 의견 주셔서 감사해요", duration: 2.5 });
},
},
{
emoji: "👀",
title: "주제 다시 안보기",
isNeedLogin: true,
handleClickItem: () => {
/* todo */
},
},
{
emoji: "❓",
title: "주제에 대한 다른 사람들의 의견이 궁금해요",
handleClickItem: () => {
closeHandler();
showToast({ message: "📢 다른 사람들의 의견을 모아서 들려드릴게요", duration: 2.5 });
},
},
];

return (
<Modal theme="WHITE_BOTTOM" closeHandler={closeHandler} isNoCloseBtn>
<St.ModalContainer>
{ModalItems.map(({ emoji, title, isNeedLogin, handleClickItem }, idx) => (
<St.ModalItemWrapper key={idx} onClick={handleClickItem}>
<St.EmojiWrapper>{emoji}</St.EmojiWrapper>
{title}
{isNeedLogin && <St.MessageWrapper>로그인 시 사용가능 합니다</St.MessageWrapper>}
</St.ModalItemWrapper>
))}
</St.ModalContainer>
</Modal>
);
}
39 changes: 39 additions & 0 deletions src/@components/CardCollectionPage/Card/MenuModal/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import styled from "styled-components";

export const ModalContainer = styled.aside`
position: relative;
display: flex;
flex-direction: column;
width: 36rem;
`;

export const ModalItemWrapper = styled.div`
display: flex;
width: 36rem;
padding: 1.6rem;
${({ theme }) => theme.newFonts.body4};
color: ${({ theme }) => theme.newColors.gray900};
&:first-child {
padding-top: 0;
border-bottom: 0.1rem solid ${({ theme }) => theme.newColors.gray200};
}
&:last-child {
border-top: 1.2rem solid ${({ theme }) => theme.newColors.gray100};
}
`;

export const EmojiWrapper = styled.span`
margin-right: 0.8rem;
`;

export const MessageWrapper = styled.span`
margin-left: 1.2rem;
${({ theme }) => theme.newFonts.caption1};
color: ${({ theme }) => theme.newColors.gray600};
`;
16 changes: 4 additions & 12 deletions src/@components/CardCollectionPage/Card/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from "react";

import IcBookmarkCheck from "../../../asset/icon/IcBookmarkCheck";
import { GTM_CLASS_NAME } from "../../../util/const/gtm";
import useCardBookmark from "../hooks/useCardBookmark";
import TagsSlider from "../TagsSlider";
import CardMenu from "./CardMenu";
import St from "./style";

interface LoginCheckProps {
Expand All @@ -12,12 +11,11 @@ interface LoginCheckProps {
content: string;
isBookmark: boolean;
tags: string[];
toggleMenuModal: () => void;
}

const Card = (props: LoginCheckProps) => {
const { _id, content, isBookmark, tags, openLoginModalHandler } = props;

const { isBookmarked, handleClickBookmark } = useCardBookmark(isBookmark, openLoginModalHandler);
const { content, tags } = props;

return (
<St.Card className={GTM_CLASS_NAME.cardSwipe}>
Expand All @@ -27,13 +25,7 @@ const Card = (props: LoginCheckProps) => {
<TagsSlider tags={tags} />
</St.TagsWrapper>
</St.Container>
<St.BookmarkWrapper
className={GTM_CLASS_NAME.cardBookmark}
onClick={() => handleClickBookmark(_id)}
aria-label="북마크"
role="dialog">
<IcBookmarkCheck isChecked={isBookmarked} />
</St.BookmarkWrapper>
<CardMenu {...props} />
</St.Card>
);
};
Expand Down
Loading

0 comments on commit 60c5ddc

Please sign in to comment.