Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(toks-main): 카테고리 기능 리팩토링 및 Bug Fixed #402

Merged
merged 6 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions public/img/icon/question.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/app/_context/Provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use client';

import React from 'react';
import { RecoilRoot } from 'recoil';

const Provider = ({ children }: StrictPropsWithChildren) => {
return <RecoilRoot>{children}</RecoilRoot>;
};
Comment on lines +6 to +8
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리코일 루트 위치가 에러와 연관이 있었군요..!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 몰랐네요 ㄷㄷ templates 시급하게 때야할듯요


export default Provider;
6 changes: 5 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { bgColor } from '@/common/foundation';
import QueryProvider from '@/common/providers/QueryProvider';
import * as gtag from '@/common/utils';

import Provider from './_context/Provider';

export const metadata: Metadata = {
viewport: {
width: 'device-width',
Expand Down Expand Up @@ -73,7 +75,9 @@ export default function RootLayout({
/>
<body className={clsx(pretendard.className, bgColor['mainLayout'])}>
<QueryProvider>
<StyledLayout>{children}</StyledLayout>
<Provider>
<StyledLayout>{children}</StyledLayout>
</Provider>
</QueryProvider>
</body>
</html>
Expand Down
6 changes: 3 additions & 3 deletions src/app/quiz/components/Comment/LikeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { useUnlikeCommentMutation } from '@/app/quiz/hooks/useUnlikeCommentMutat
import { Text, useAuth } from '@/common';
import { LOGIN_URL } from '@/common/constants';

import like_off from '../../../../../public/img/icon/like_off.svg';
import like_on from '../../../../../public/img/icon/like_on.svg';
import likeOff from '../../../../../public/img/icon/like_off.svg';
import likeOn from '../../../../../public/img/icon/like_on.svg';

interface LikeButtonProps
extends Omit<HTMLAttributes<HTMLSpanElement>, 'className'> {
Expand Down Expand Up @@ -46,7 +46,7 @@ function LikeButton({
}}
>
<Image
src={isLiked ? like_on : like_off}
src={isLiked ? likeOn : likeOff}
alt="좋아요 버튼"
width={18}
height={18}
Expand Down
31 changes: 14 additions & 17 deletions src/app/template.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client';

import { usePathname } from 'next/navigation';
import { RecoilRoot } from 'recoil';

import { Appbar, BackHeader, GlobalPortal } from '@/common';

Expand All @@ -13,21 +12,19 @@ export default function Template({ children }: StrictPropsWithChildren) {
const pathName = usePathname();

return (
<RecoilRoot>
<GlobalPortal.Provider>
{pathName === '/toks-main' ? (
<>
<Appbar />
{children}
<CategoryBottomSheet />
</>
) : (
<>
<BackHeader />
{children}
</>
)}
</GlobalPortal.Provider>
</RecoilRoot>
<GlobalPortal.Provider>
{pathName === '/toks-main' ? (
<>
<Appbar />
{children}
<CategoryBottomSheet />
</>
) : (
<>
<BackHeader />
{children}
</>
)}
</GlobalPortal.Provider>
);
}
56 changes: 42 additions & 14 deletions src/app/toks-main/components/CategoryBottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use client';

import Image from 'next/image';
import React, { useState } from 'react';
import { useRecoilState, useSetRecoilState } from 'recoil';
import React, { useEffect, useLayoutEffect, useState } from 'react';
import { useRecoilState } from 'recoil';

import { BottomSheet, Button, ICON_URL, Tab, Text } from '@/common';
import { useAuth, usePreventScroll } from '@/common/hooks';
Expand All @@ -16,27 +16,55 @@ import { CategoryButtonGroups } from './CategoryButtonGroups';
import { useCategoryUpdateMutation } from '../hooks/useCategoryUpdateMutation';

export const CategoryBottomSheet = () => {
const { isLogin } = useAuth();
const [isShow, setIsShow] = useRecoilState(isVisibleCategoryBottomSheetAtom);
const { mutate: updateCategories } = useCategoryUpdateMutation();
const { data: selectedCategory = [] } = useSelectedCategoriesQuery();
const { isLogin } = useAuth();

const { data: selectedLoginCategory = [] } = useSelectedCategoriesQuery();
const [selectedLocalCategory, setSelectedLocalCategories] = useState<
string[]
>(selectedCategory ?? []);
>([]);

const [selectedTemporaryCategory, setSelectedTemporaryCategory] =
useRecoilState(selectedTemporaryCategoryAtom);

const setSelectedTemporaryCategory = useSetRecoilState(
selectedTemporaryCategoryAtom
);
const { data: categoryQuery } = useCategoriesQuery();
const [isShow, setIsShow] = useRecoilState(isVisibleCategoryBottomSheetAtom);
const [selectedTab, setSelectedTab] = useState(0);
usePreventScroll(isShow);

const panel = categoryQuery?.panels[selectedTab];
const buttons = panel?.map(({ id, name }) => ({
const [selectedTab, setSelectedTab] = useState(0);
const { data: categoryQuery } = useCategoriesQuery();
const selectedPanel = categoryQuery?.panels[selectedTab];
const buttons = selectedPanel?.map(({ id, name }) => ({
label: name,
value: id,
}));

const tabs = categoryQuery?.tabs.map(({ name }) => name) ?? [];

/**
* useLayoutEffect(동기) -> useEffect(비동기) 순으로 렌더링된다.
* STEP1. BottomSheet가 열릴 때마다 selectedTab, selectedLocalCategories를 초기화한다.
* STEP2. 로그인 상태일 때, 로그인한 사용자의 관심 카테고리를 selectedLocalCategories에 저장한다.
* STEP3. 로그인 상태가 아니고, selectedTemporaryCategory가 존재할 때, selectedLocalCategories에 저장한다.
*
* 이를 통해 BottomSheet가 열릴 때마다 초기화되고, 저장한 카테고리/로그인 정보 카테고리를 불러온다.
*/
useLayoutEffect(() => {
setSelectedTab(0);
setSelectedLocalCategories([]);
}, [isShow]);

useEffect(() => {
if (isLogin && isShow && selectedLoginCategory.length > 0) {
setSelectedLocalCategories(selectedLoginCategory);
}
}, [isLogin, isShow, selectedLoginCategory]);

useEffect(() => {
if (selectedTemporaryCategory.length > 0 && isShow) {
setSelectedLocalCategories(selectedTemporaryCategory);
}
}, [isShow, selectedTemporaryCategory]);
Comment on lines +51 to +66
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

민석님 혹시 여기 각 useEffect가 어떤 목적으로 작성되었는지 간단한 주석 추가되면 어떨까요?
추후에 알아보기 쉽지 않을까해서 남겨봅니다

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

18411a2 반영했습니다!


return (
<BottomSheet
className="flex h-categoryBottomSheet flex-col "
Expand All @@ -55,7 +83,7 @@ export const CategoryBottomSheet = () => {
</div>
<Tab
activeIndex={selectedTab}
tabs={categoryQuery?.tabs ?? []}
tabs={tabs}
onTabChange={(index) => {
setSelectedTab(index);
}}
Expand Down
2 changes: 1 addition & 1 deletion src/app/toks-main/components/MainPageBottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BottomSheet } from '@/common';
import { BottomSheetProps } from '@/common/components/BottomSheet/types';
import { useAuth, usePreventScroll } from '@/common/hooks';
import { BottomSheetProps } from '@/types/bottomsheet';

import { OnboardingBottomSheet } from './OnboardingBottomsheet';
import { ProgressCheckBottomSheet } from './ProgressCheckBottomSheet';
Expand Down
55 changes: 55 additions & 0 deletions src/app/toks-main/components/QuizCategory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client';

import { getCookie } from 'cookies-next';
import React, { memo } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';

import { Categories } from '@/common/components/Categories';
import { useSelectedCategoriesQuery } from '@/queries';
import { useCategoriesQuery } from '@/queries/useCategoriesQuery';
import {
isVisibleCategoryBottomSheetAtom,
selectedTemporaryCategoryAtom,
} from '@/store';

import { isSameCategory } from '../utils';

const QuizCategory = () => {
const accessToken = getCookie('accessToken');
const { data: categoryQuery } = useCategoriesQuery();
const { data: selectedLoginCategory } = useSelectedCategoriesQuery();
const selectedTemporaryCategory = useRecoilValue(
selectedTemporaryCategoryAtom
);

const setIsOpenCategoryBottomSheet = useSetRecoilState(
isVisibleCategoryBottomSheetAtom
);

const selectedCategory = Boolean(accessToken)
? selectedLoginCategory
: selectedTemporaryCategory;

const categories = categoryQuery?.tabs.map(({ name, id }) => {
const isSelected =
selectedCategory?.filter((categoryId) =>
isSameCategory(categoryId, id)
) ?? [];

return {
categoryName: `${name} ${isSelected.length}`,
isSelected: isSelected.length > 0,
};
});

return (
<Categories
categories={categories ?? []}
onClick={() => {
setIsOpenCategoryBottomSheet(true);
}}
/>
);
};

export default memo(QuizCategory);
3 changes: 3 additions & 0 deletions src/app/toks-main/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const isSameCategory = (a: string, b: string) => {
return a.slice(0, 2) === b.slice(0, 2);
};
77 changes: 12 additions & 65 deletions src/common/components/Appbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,92 +3,38 @@
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';

import { ICON_URL, LOGIN_URL } from '@/common/constants';
import QuizCategory from '@/app/toks-main/components/QuizCategory';
import { GOOGLE_FORM_URL, ICON_URL, LOGIN_URL } from '@/common/constants';
import { useAuth } from '@/common/hooks';
import { useSelectedCategoriesQuery } from '@/queries';
import {
isVisibleCategoryBottomSheetAtom,
isVisibleFloatingButtonBottomSheetAtom,
selectedTemporaryCategoryAtom,
} from '@/store';

import questionSvg from '../../../../public/img/icon/question.svg';
import { SSRSuspense } from '../SSRSuspense';
import { Text } from '../Text';
import { Tooltip } from '../Tooltip';

export const Appbar = () => {
const router = useRouter();
const { isLogin, user } = useAuth();
const [isOpenCategoryBottomSheet, setIsOpenCategoryBottomSheet] =
useRecoilState(isVisibleCategoryBottomSheetAtom);
const isOpenFloatingButtonBottomSheet = useRecoilValue(
isVisibleFloatingButtonBottomSheetAtom
);
const localSelectedCategoryArray = useRecoilValue(
selectedTemporaryCategoryAtom
);
const { data: serverSelectedCategory = [] } = useSelectedCategoriesQuery();

const renderCategoryCountBadge = () => {
if (isLogin && serverSelectedCategory.length > 0) {
return serverSelectedCategory.length;
}
if (!isLogin && localSelectedCategoryArray.length > 0) {
return localSelectedCategoryArray.length;
}

return null;
};

// TODO: useAppbar hook 구현
return (
<SSRSuspense
fallback={<div className="h-54px bg-gray-120">로딩중입니다..</div>}
fallback={<div className="h-64px bg-gray-120">로딩중입니다..</div>}
>
<nav className="sticky left-0 right-0 top-0 z-50 h-54px bg-gray-120">
<div className="flex h-full w-full items-center justify-between">
<div
className="flex items-center gap-4px"
role="button"
onClick={() => {
setIsOpenCategoryBottomSheet(true);
}}
>
<nav className="sticky left-0 right-0 top-0 z-50 bg-gray-120 pb-[20px]">
<div className="flex w-full items-center justify-between pb-[20px] pt-[16px]">
<div className="flex items-center gap-4px" role="button">
<Image
layout="fixed"
width={60}
height={20}
src={ICON_URL.TOKS_LOGO}
alt="toks 로고"
/>
{/* TODO: POPOVER 구현 */}
<Tooltip
isFirstRender
message="관심있는 카테고리를 선택해보세요"
isVisibleTooltip={
!isOpenCategoryBottomSheet && !isOpenFloatingButtonBottomSheet
}
>
<button type="button" className="h-fit">
<Image
src={ICON_URL.CHEVRON_DOWN}
alt="카테고리 드롭다운"
width={24}
height={24}
/>
</button>
</Tooltip>
{renderCategoryCountBadge() && (
<div className="flex h-[26px] w-[26px] items-center justify-center rounded-6px bg-primary-default text-white">
<Text typo="subheadingBold" color="white">
{renderCategoryCountBadge()}
</Text>
</div>
)}
</div>
<button className="flex items-center">
<button className="flex items-center gap-[12px]">
<a href={GOOGLE_FORM_URL} target="_blank" rel="noreferrer">
<Image src={questionSvg} alt="질문하기" width={28} height={28} />
</a>
{/* TODO: 로그인 여부 분기 */}
{isLogin ? (
<div className="relative h-[30px] w-[30px]">
Expand Down Expand Up @@ -116,6 +62,7 @@ export const Appbar = () => {
)}
</button>
</div>
<QuizCategory />
</nav>
</SSRSuspense>
);
Expand Down
6 changes: 0 additions & 6 deletions src/common/components/BottomSheet/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import React, { PropsWithChildren } from 'react';

// import { useClickAway } from '@/common/hooks/useClickAway';
import { cn } from '@/common/utils';

import { BottomSheetProps } from './types';
Expand All @@ -14,11 +13,6 @@ export const BottomSheet = ({
onClose,
className,
}: PropsWithChildren<BottomSheetProps>) => {
// const bottomSheetContentRef = useClickAway({
// callback: () => {
// onClose();
// },
// });
return (
<GlobalPortal.Consumer>
<Dimmer isShow={isShow} onClose={() => onClose()} />
Expand Down
Loading
Loading