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 17, 2023
2 parents 000ebf3 + fb58837 commit 0263138
Show file tree
Hide file tree
Showing 18 changed files with 227 additions and 42 deletions.
14 changes: 10 additions & 4 deletions src/@components/@common/Toast/ToastProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import * as St from "./style";
export type ToastType = {
message: string;
duration: number;
handleClickCancel?: () => void;
};

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 });
async ({ message, duration, handleClickCancel }: ToastType) => {
setToast({ message, duration, handleClickCancel });

toastTimeout.set(() => {
setToast(null);
Expand All @@ -24,12 +25,17 @@ export default function ToastProvider({ children }: PropsWithChildren) {
[setToast, toastTimeout],
);

const blackoutToast = useCallback(() => setToast(null), [setToast]);

return (
<ToastContext.Provider value={{ showToast }}>
<ToastContext.Provider value={{ showToast, blackoutToast }}>
{children}
{toast && (
<St.ToastContainer>
<St.ToastMessage>{toast.message}</St.ToastMessage>
<St.ToastMessage>
{toast.message}
{toast.handleClickCancel && <St.CancelButton onClick={toast.handleClickCancel}>취소</St.CancelButton>}
</St.ToastMessage>
</St.ToastContainer>
)}
</ToastContext.Provider>
Expand Down
4 changes: 4 additions & 0 deletions src/@components/@common/Toast/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import { ToastType } from "./ToastProvider";

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

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

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

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

Expand Down
4 changes: 2 additions & 2 deletions src/@components/@common/Toast/hooks/useToast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ToastContext } from "../context";
import { ToastType } from "../ToastProvider";

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

return (toast: ToastType) => showToast(toast);
return { showToast: (toast: ToastType) => showToast(toast), blackoutToast };
}
6 changes: 6 additions & 0 deletions src/@components/@common/Toast/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const ToastContainer = styled.div`

export const ToastMessage = styled.div`
display: flex;
justify-content: space-between;
height: 4.8rem;
padding: 0.8rem 1.6rem;
Expand All @@ -24,3 +25,8 @@ export const ToastMessage = styled.div`
${({ theme }) => theme.newFonts.caption1}
color: ${({ theme }) => theme.newColors.gray900};
`;

export const CancelButton = styled.button`
${({ theme }) => theme.newFonts.caption1}
color: ${({ theme }) => theme.newColors.gray600};
`;
15 changes: 15 additions & 0 deletions src/@components/@common/hooks/useShowByQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useEffect, useState } from "react";

import { LocationType } from "../../../types/cardCollection";

// 파라미터로 보이지 않아야할 locationType 전달
export default function useShowByCardType(locationTypes: LocationType[]) {
const [isShow, setIsShow] = useState<boolean>(false);

useEffect(() => {
const cardType = new URLSearchParams(window.location.search.split("?")[1]).get("type");
setIsShow(!locationTypes.includes((cardType as LocationType) || ""));
}, [locationTypes]);

return { isShow };
}
54 changes: 44 additions & 10 deletions src/@components/CardCollectionPage/Card/MenuModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import { useEffect, useState } from "react";

import { LocationType } from "../../../../types/cardCollection";
import useShowByCardType from "../../../@common/hooks/useShowByQuery";
import Modal from "../../../@common/Modal";
import useToast from "../../../@common/Toast/hooks/useToast";
import { handleClickBlacklistType } from "../../hooks/useBlacklist";
import { autoSlideType } from "../../hooks/useCardSwiper";
import * as St from "./style";

interface MenuModalProps {
currentCardId: string;
closeHandler: () => void;
autoSlide: autoSlideType;
handleClickAddBlacklist: handleClickBlacklistType;
handleClickCancelBlacklist: handleClickBlacklistType;
}

type ModalItem = {
Expand All @@ -14,8 +24,23 @@ type ModalItem = {
};

export default function MenuModal(props: MenuModalProps) {
const { closeHandler } = props;
const showToast = useToast();
const { currentCardId, closeHandler, autoSlide, handleClickAddBlacklist, handleClickCancelBlacklist } = props;
const { showToast, blackoutToast } = useToast();

const { isShow: isBlockShow } = useShowByCardType([LocationType.BEST, LocationType.MEDLEY]);

const onSuccessAddBlacklist = () => {
closeHandler();
showToast({
message: "🚫 해당 대화주제가 더 이상 추천되지 않아요",
duration: 3.5,
handleClickCancel: () => {
handleClickCancelBlacklist({ _id: currentCardId, onSuccess: blackoutToast });
autoSlide.slideUp();
},
});
autoSlide.slideDown();
};

const ModalItems: ModalItem[] = [
{
Expand All @@ -31,7 +56,10 @@ export default function MenuModal(props: MenuModalProps) {
title: "주제 다시 안보기",
isNeedLogin: true,
handleClickItem: () => {
/* todo */
handleClickAddBlacklist({
_id: currentCardId,
onSuccess: onSuccessAddBlacklist,
});
},
},
{
Expand All @@ -47,13 +75,19 @@ export default function MenuModal(props: MenuModalProps) {
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>
))}
{ModalItems.map(({ emoji, title, isNeedLogin, handleClickItem }, idx) => {
if (idx === 1 && !isBlockShow) {
return null;
} else {
return (
<St.ModalItemWrapper key={idx} onClick={handleClickItem}>
<St.EmojiWrapper>{emoji}</St.EmojiWrapper>
{title}
{isNeedLogin && <St.MessageWrapper>로그인 시 사용가능 합니다</St.MessageWrapper>}
</St.ModalItemWrapper>
);
}
})}
</St.ModalContainer>
</Modal>
);
Expand Down
3 changes: 1 addition & 2 deletions src/@components/CardCollectionPage/Card/MenuModal/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ export const ModalContainer = styled.aside`
display: flex;
flex-direction: column;
width: 36rem;
width: 100%;
`;

export const ModalItemWrapper = styled.div`
display: flex;
width: 36rem;
padding: 1.6rem;
${({ theme }) => theme.newFonts.body4};
Expand Down
31 changes: 27 additions & 4 deletions src/@components/CardCollectionPage/Card/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import React from "react";

import { GTM_CLASS_NAME } from "../../../util/const/gtm";
import useModal from "../../@common/hooks/useModal";
import useBlacklist from "../hooks/useBlacklist";
import { autoSlideType } from "../hooks/useCardSwiper";
import TagsSlider from "../TagsSlider";
import CardMenu from "./CardMenu";
import MenuModal from "./MenuModal";
import St from "./style";

interface LoginCheckProps {
openLoginModalHandler: () => void;
autoSlide: autoSlideType;
_id: string;
content: string;
isBookmark: boolean;
tags: string[];
toggleMenuModal: () => void;
openLoginModalHandler: () => void;
}

const Card = (props: LoginCheckProps) => {
const { content, tags } = props;
const { _id, content, tags, autoSlide, openLoginModalHandler } = props;
const { isModalOpen: isMenuModalOpen, toggleModal: toggleMenuModal } = useModal();
const { getIsBlacklist, handleClickAddBlacklist, handleClickCancelBlacklist } = useBlacklist(openLoginModalHandler);

return (
<St.Card className={GTM_CLASS_NAME.cardSwipe}>
Expand All @@ -25,7 +31,24 @@ const Card = (props: LoginCheckProps) => {
<TagsSlider tags={tags} />
</St.TagsWrapper>
</St.Container>
<CardMenu {...props} />
<CardMenu {...props} toggleMenuModal={toggleMenuModal} />

{getIsBlacklist(_id) && (
<St.BlockCardWrapper>
<St.BlockCardText>다시 안보기를 설정한 주제입니다</St.BlockCardText>
<St.BlockCardButton onClick={() => handleClickCancelBlacklist({ _id })}>취소하기</St.BlockCardButton>
</St.BlockCardWrapper>
)}

{isMenuModalOpen && (
<MenuModal
currentCardId={_id}
closeHandler={toggleMenuModal}
autoSlide={autoSlide}
handleClickAddBlacklist={handleClickAddBlacklist}
handleClickCancelBlacklist={handleClickCancelBlacklist}
/>
)}
</St.Card>
);
};
Expand Down
32 changes: 32 additions & 0 deletions src/@components/CardCollectionPage/Card/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,42 @@ const TagsWrapper = styled.div`
margin-top: 1.6rem;
`;

const BlockCardWrapper = styled.div`
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
gap: 0.8rem;
border-radius: 0.8rem;
background: var(--blackblur, rgba(0, 0, 0, 0.6));
backdrop-filter: blur(1.2rem);
`;

const BlockCardText = styled.p`
color: ${({ theme }) => theme.newColors.white};
${({ theme }) => theme.newFonts.body4};
`;

const BlockCardButton = styled.button`
color: ${({ theme }) => theme.newColors.white};
${({ theme }) => theme.newFonts.caption1};
text-decoration: underline;
`;

const St = {
Card,
Container,
ContentWrapper,
TagsWrapper,
BlockCardWrapper,
BlockCardText,
BlockCardButton,
};

export default St;
10 changes: 5 additions & 5 deletions src/@components/CardCollectionPage/CardSlider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@ import { CardList } from "../../../types/cardCollection";
import { externalLinks } from "../../../util/const/externalLinks";
import Card from "../Card";
import LastCard from "../Card/LastCard";
import useBlacklist from "../hooks/useBlacklist";
import useCardSwiper from "../hooks/useCardSwiper";
import * as St from "./style";

interface CardSliderProps {
openLoginModalHandler: () => void;
cardLists: CardList[];
lastCardObsvRef: React.RefObject<HTMLDivElement>;
toggleMenuModal: () => void;
}

const CardSlider = (props: CardSliderProps) => {
const { openLoginModalHandler, cardLists, lastCardObsvRef, toggleMenuModal } = props;
const { swiperSettings } = useCardSwiper();
const { openLoginModalHandler, cardLists, lastCardObsvRef } = props;
const { swiperSettings, swiperRef, autoSlide } = useCardSwiper();

return (
<St.Wrapper>
<Swiper {...swiperSettings}>
<Swiper {...swiperSettings} ref={swiperRef}>
{cardLists.map((cardList) => (
<SwiperSlide key={cardList._id}>
<Card toggleMenuModal={toggleMenuModal} openLoginModalHandler={openLoginModalHandler} {...cardList} />
<Card autoSlide={autoSlide} openLoginModalHandler={openLoginModalHandler} {...cardList} />
</SwiperSlide>
))}
<SwiperSlide>
Expand Down
48 changes: 48 additions & 0 deletions src/@components/CardCollectionPage/hooks/useBlacklist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useCallback, useState } from "react";

import { cardCollectionApi } from "../../../core/api/cardCollection";
import useAuth from "../../../core/hooks/useAuth";

interface handleClickParams {
_id: string;
onSuccess?: () => void;
}

export type handleClickBlacklistType = (params: handleClickParams) => void;

const useBlacklist = (handleClickBeforeLogin: () => void) => {
const { isLogin } = useAuth();
const [blacklist, setBlacklist] = useState<string[]>([]);

const handleClickAddBlacklist: handleClickBlacklistType = useCallback(
({ _id, onSuccess: onSuccessAdd }) => {
switch (isLogin) {
case true:
cardCollectionApi.addBlacklist(_id);
onSuccessAdd && onSuccessAdd();
setBlacklist((prev) => [...prev, _id]);
break;
case false:
handleClickBeforeLogin();
break;
}
},
[isLogin, handleClickBeforeLogin],
);

const handleClickCancelBlacklist: handleClickBlacklistType = useCallback(({ _id, onSuccess: onSuccessDelete }) => {
cardCollectionApi.deleteBlacklist(_id);
setBlacklist((prev) => prev.filter((id) => id !== _id));
onSuccessDelete && onSuccessDelete();
}, []);

const getIsBlacklist = useCallback((_id: string) => blacklist.includes(_id), [blacklist]);

return {
getIsBlacklist,
handleClickAddBlacklist,
handleClickCancelBlacklist,
};
};

export default useBlacklist;
Loading

0 comments on commit 0263138

Please sign in to comment.