Skip to content

Commit

Permalink
Merge pull request #291 from TeamPiickle/feat/#228-suspense
Browse files Browse the repository at this point in the history
Feat/#228 suspense
  • Loading branch information
joohaem committed Jun 17, 2023
2 parents ce9d395 + 9e6f546 commit 802fa24
Show file tree
Hide file tree
Showing 29 changed files with 259 additions and 176 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"qs": "^6.11.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-error-boundary": "^4.0.9",
"react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0",
"react-mobile-datepicker": "^4.0.2",
Expand Down
6 changes: 5 additions & 1 deletion src/@components/@common/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { lazy } from "react";

import { IcHamburger, IcLogo } from "../../../asset/icon";
import { routePaths } from "../../../core/routes/path";
import { GTM_CLASS_NAME } from "../../../util/const/gtm";
import useModal from "../hooks/useModal";
import MenuBar from "../MenuBar";
import * as St from "./style";

const MenuBar = lazy(() => import("../MenuBar"));

export default function Header() {
const { isModalOpen, toggleModal } = useModal();

Expand All @@ -16,6 +19,7 @@ export default function Header() {
<St.HamburgerContainer className={GTM_CLASS_NAME.mainMenuBtn} isClicked={isModalOpen}>
<IcHamburger aria-label="메뉴" className={GTM_CLASS_NAME.mainMenuBtn} onClick={toggleModal} />
</St.HamburgerContainer>

{isModalOpen && <MenuBar closeMenuBar={toggleModal} />}
</St.HeaderWrapper>
);
Expand Down
2 changes: 1 addition & 1 deletion src/@components/@common/MenuBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function MenuBar(props: MenuBarProps) {

const { isLogin } = useAuth();

const { ballotLists } = useBallotLists();
const { ballotLists } = useBallotLists(true);
const navigateCardCollection = useNavigateCardCollection(LocationType.ALL) as NavigateCardCollectionAllType;

const moveCardCollection = () => {
Expand Down
19 changes: 19 additions & 0 deletions src/@components/@common/SuspenseBoundary/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { PropsWithChildren, Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";

import Error404Page from "../../Error404Page";
import Loading from "../Loading";

interface SuspenseBoundaryProps {
bgColor?: string;
}

const SuspenseBoundary = ({ children, bgColor }: PropsWithChildren<SuspenseBoundaryProps>) => {
return (
<ErrorBoundary fallback={<Error404Page />}>
<Suspense fallback={<Loading backgroundColor={bgColor || "transparent"} />}>{children}</Suspense>
</ErrorBoundary>
);
};

export default SuspenseBoundary;
3 changes: 1 addition & 2 deletions src/@components/BookmarkPage/hooks/useUserBookmarks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import { PiickleSWRResponse } from "../../../types/remote/swr";
import { MyPiickle } from "../../../types/users";

export function useUserBookmarks() {
const { data, error } = useSWR<PiickleSWRResponse<MyPiickle[]>>(`${PATH.USERS_}/bookmarks`, realReq.GET_SWR);
const { data } = useSWR<PiickleSWRResponse<MyPiickle[]>>(`${PATH.USERS_}/bookmarks`, realReq.GET_SWR);

return {
userBookmarks: data?.data,
isLoading: !error && !data,
};
}
41 changes: 5 additions & 36 deletions src/@components/CardCollectionPage/hooks/useCardLists.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,30 @@
import qs from "qs";
import { useEffect } from "react";
import { useRecoilState } from "recoil";
import useSWR from "swr";

import { realReq } from "../../../core/api/common/axios";
import { PATH } from "../../../core/api/common/constants";
import { filterTagsState } from "../../../core/atom/slider";
import { CardList, CardsTypeLocation, LocationType } from "../../../types/cardCollection";
import { PiickleSWRResponse } from "../../../types/remote/swr";
import { intimacyTags } from "../../../util/cardCollection/filter";
import useNavigateCardCollection, {
NavigateCardCollectionFilterType,
} from "../../@common/hooks/useNavigateCardCollection";
import useCardListsFilter from "./useCardListsFilter";

interface ExtendedCardList extends Array<CardList> {
cardList?: CardList[]; // with category id
cards?: CardList[]; // with medly id
}

export function useCardLists(cardsTypeLocation: CardsTypeLocation) {
const [filterTags, setFilterTags] = useRecoilState(filterTagsState);
const navigateCardCollection = useNavigateCardCollection(LocationType.FILTER) as NavigateCardCollectionFilterType;

const fetchingKeyByLocation = getSWRFetchingKeyByLocation(cardsTypeLocation);
const optionsByLocation = getSWROptionsByLocation(cardsTypeLocation);
const { data, error } = useSWR<PiickleSWRResponse<ExtendedCardList>>(
const { data } = useSWR<PiickleSWRResponse<ExtendedCardList>>(
fetchingKeyByLocation,
realReq.GET_SWR,
optionsByLocation,
);

useEffect(() => {
if (cardsTypeLocation.type !== LocationType.FILTER) setFilterTags((prev) => ({ ...prev, isActive: false }));
}, [cardsTypeLocation, setFilterTags]);

const fetchCardListsWithFilter = () => {
// 남 -> 남자, 여 -> 여자
const _fetchingCheckedTags = new Set([...filterTags.tags, intimacyTags[filterTags.intimacy[0]]]);
if (_fetchingCheckedTags.has("남")) {
_fetchingCheckedTags.delete("남");
_fetchingCheckedTags.add("남자");
}
if (_fetchingCheckedTags.has("여")) {
_fetchingCheckedTags.delete("여");
_fetchingCheckedTags.add("여자");
}

setFilterTags((prevFilterTags) => {
return { ...prevFilterTags, isActive: true };
});

navigateCardCollection([..._fetchingCheckedTags]);
};
const { fetchCardListsWithFilter } = useCardListsFilter(cardsTypeLocation.type !== LocationType.FILTER);

return {
cardLists: getReturnCardLists(data, cardsTypeLocation) ?? [],
isLoading: !error && !data,
fetchCardListsWithFilter,
};
}
Expand Down Expand Up @@ -119,8 +88,8 @@ function getSWROptionsByLocation(cardsTypeLocation: CardsTypeLocation) {
case LocationType.BEST:
case LocationType.BOOKMARK:
case LocationType.MEDLEY:
return {};
return { suspense: true };
default:
return { revalidateOnMount: true, dedupingInterval: 700 };
return { suspense: true, revalidateOnMount: true, dedupingInterval: 700 };
}
}
42 changes: 42 additions & 0 deletions src/@components/CardCollectionPage/hooks/useCardListsFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useEffect } from "react";
import { useRecoilState } from "recoil";

import { filterTagsState } from "../../../core/atom/slider";
import { LocationType } from "../../../types/cardCollection";
import { intimacyTags } from "../../../util/cardCollection/filter";
import useNavigateCardCollection, {
NavigateCardCollectionFilterType,
} from "../../@common/hooks/useNavigateCardCollection";

const useCardListsFilter = (isFilterTypeLocation: boolean) => {
const navigateCardCollection = useNavigateCardCollection(LocationType.FILTER) as NavigateCardCollectionFilterType;

const [filterTags, setFilterTags] = useRecoilState(filterTagsState);

useEffect(() => {
if (isFilterTypeLocation) setFilterTags((prev) => ({ ...prev, isActive: false }));
}, [isFilterTypeLocation, setFilterTags]);

const fetchCardListsWithFilter = () => {
// 남 -> 남자, 여 -> 여자
const _fetchingCheckedTags = new Set([...filterTags.tags, intimacyTags[filterTags.intimacy[0]]]);
if (_fetchingCheckedTags.has("남")) {
_fetchingCheckedTags.delete("남");
_fetchingCheckedTags.add("남자");
}
if (_fetchingCheckedTags.has("여")) {
_fetchingCheckedTags.delete("여");
_fetchingCheckedTags.add("여자");
}

setFilterTags((prevFilterTags) => {
return { ...prevFilterTags, isActive: true };
});

navigateCardCollection([..._fetchingCheckedTags]);
};

return { fetchCardListsWithFilter };
};

export default useCardListsFilter;
18 changes: 11 additions & 7 deletions src/@components/CardCollectionPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,29 @@ import HeaderMinVer from "../@common/Header/HeaderMinVer";
import useGTMPage from "../@common/hooks/useGTMPage";
import useModal from "../@common/hooks/useModal";
import useScroll from "../@common/hooks/useScrollToTop";
import Loading from "../@common/Loading";
import LoginModal from "../@common/LoginModal";
import SuspenseBoundary from "../@common/SuspenseBoundary";
import CardSlider from "./CardSlider";
import FilterModal from "./FilterModal";
import { useCardLists } from "./hooks/useCardLists";
import useCTAFilter from "./hooks/useCTAFilter";
import * as St from "./style";

export default function CardCollectionPage() {
return (
<SuspenseBoundary>
<CardCollectionContent />
</SuspenseBoundary>
);
}

function CardCollectionContent() {
useGTMPage();
useScroll();

const location = useLocation();
const cardsTypeLoaction = location.state as CardsTypeLocation;
const { cardLists, isLoading, fetchCardListsWithFilter } = useCardLists(cardsTypeLoaction);
const { cardLists, fetchCardListsWithFilter } = useCardLists(cardsTypeLoaction);

const { isVisibleCTAButton, intersectionObserverRef: lastCardObsvRef } = useCTAFilter();

Expand All @@ -37,11 +45,7 @@ export default function CardCollectionPage() {
<St.MainPage>
{isSliderDown ? <HeaderMinVer /> : <Header />}

{!isLoading ? (
<CardSlider openLoginModalHandler={toggleLoginModal} cardLists={cardLists} lastCardObsvRef={lastCardObsvRef} />
) : (
<Loading backgroundColor="transparent" />
)}
<CardSlider openLoginModalHandler={toggleLoginModal} cardLists={cardLists} lastCardObsvRef={lastCardObsvRef} />

{isVisibleCTAButton && (
<HeadlessCTAButton
Expand Down
41 changes: 41 additions & 0 deletions src/@components/CategoryPage/CategoryBanner/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Helmet } from "react-helmet";

import { IcSmallRightArrow } from "../../../asset/icon";
import { ImgCategoryBanner } from "../../../asset/image";
import { OriginImgCategoryBanner } from "../../../asset/image/origin";
import { LocationType } from "../../../types/cardCollection";
import { GTM_CLASS_NAME } from "../../../util/const/gtm";
import useNavigateCardCollection, {
NavigateCardCollectionBestType,
} from "../../@common/hooks/useNavigateCardCollection";
import St from "./style";

const CategoryBanner = () => {
const navigateCardCollection = useNavigateCardCollection(LocationType.BEST) as NavigateCardCollectionBestType;

return (
<>
<Helmet>
<link rel="preload" as="image" href={ImgCategoryBanner} />
</Helmet>

<St.CategoryBanner>
<picture>
<source srcSet={ImgCategoryBanner} type="image/webp" />
<St.BgImg src={OriginImgCategoryBanner} alt="" />
</picture>
<St.BannerTitle>베스트 카드들만 모아서 보기</St.BannerTitle>
<St.BannerSubTitle>북마크를 가장 많이 달성한 핫한 대화 주제 30선</St.BannerSubTitle>
<St.GoBestPiickleBtn
type="button"
className={GTM_CLASS_NAME.moodShowCard}
onClick={() => navigateCardCollection()}>
<St.BtnTitle className={GTM_CLASS_NAME.moodShowCard}>카드보기</St.BtnTitle>
<IcSmallRightArrow />
</St.GoBestPiickleBtn>
</St.CategoryBanner>
</>
);
};

export default CategoryBanner;
60 changes: 60 additions & 0 deletions src/@components/CategoryPage/CategoryBanner/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import styled from "styled-components";

const CategoryBanner = styled.section`
height: 17.9rem;
padding: 2rem 0 0 1.6rem;
position: relative;
`;

const BgImg = styled.img`
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 17.9rem;
object-fit: cover;
z-index: -10;
`;

const BannerTitle = styled.h2`
${({ theme }) => theme.newFonts.graphic1};
color: ${({ theme }) => theme.colors.darkblue};
`;

const BannerSubTitle = styled.p`
${({ theme }) => theme.newFonts.h2};
color: ${({ theme }) => theme.colors.gray800};
`;

const GoBestPiickleBtn = styled.button`
position: absolute;
height: 1.5rem;
right: 1.6rem;
bottom: 1.2rem;
color: ${({ theme }) => theme.colors.gray900};
${({ theme }) => theme.newFonts.btn2};
display: flex;
justify-content: center;
align-items: center;
`;

const BtnTitle = styled.p`
margin-right: 0.8rem;
`;

const St = {
CategoryBanner,
BgImg,
BannerTitle,
BannerSubTitle,
GoBestPiickleBtn,
BtnTitle,
};
export default St;
2 changes: 0 additions & 2 deletions src/@components/CategoryPage/CategoryContents/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import { GTM_CLASS_NAME } from "../../../util/const/gtm";
import useNavigateCardCollection, {
NavigateCardCollectionCategoryType,
} from "../../@common/hooks/useNavigateCardCollection";
import Loading from "../../@common/Loading";
import { useCategoryLists } from "../../MainPage/hooks/useCategoryLists";
import { St } from "./style";

export default function CategoryContents() {
const { categoryLists } = useCategoryLists();
const navigateCardCollection = useNavigateCardCollection(LocationType.CATEGORY) as NavigateCardCollectionCategoryType;

if (categoryLists?.data === undefined) return <Loading backgroundColor="transparent" />;
return (
<St.FlexContainer>
<St.CategoryItemContainer>
Expand Down
Loading

0 comments on commit 802fa24

Please sign in to comment.