Skip to content

Commit

Permalink
refactor: 이스터에그 토스트 사용성 개선 및 hook 디렉토리 이동 (#527)
Browse files Browse the repository at this point in the history
* feat: 페이지 이동 시 렌더링중인 토스트 제거

* refactor: 토스트 발생 높이 재조정

* refactor: easterEgg 로직과 컴포넌트 분리

* refactor: hooks/common > hooks/@common으로 이동
  • Loading branch information
n0eyes committed Oct 20, 2023
1 parent 89397b8 commit ef95a6f
Show file tree
Hide file tree
Showing 15 changed files with 98 additions and 67 deletions.
4 changes: 2 additions & 2 deletions frontend/src/components/@common/Toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ const fadeIn = keyframes`
}
to {
opacity: 1;
transform: translateY(-150%);
transform: translateY(0%);
}
`;

const fadeOut = keyframes`
from {
opacity: 1;
transform: translateY(-150%);
transform: translateY(-0%);
}
to {
opacity: 0;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/PetProfile/PetInfoInForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect } from 'react';
import { styled } from 'styled-components';

import CameraIcon from '@/assets/svg/camera_icon.svg';
import { useImageUpload } from '@/hooks/common/useImageUpload';
import { useImageUpload } from '@/hooks/@common/useImageUpload';
import { PetProfile } from '@/types/petProfile/client';

import { getGenderImage, getPetAge } from './PetItem';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { styled } from 'styled-components';
import CameraIcon from '@/assets/svg/camera_icon.svg';
import DefaultDogIcon from '@/assets/svg/dog_icon.svg';
import { usePetAdditionContext } from '@/context/petProfile/PetAdditionContext';
import { useImageUpload } from '@/hooks/common/useImageUpload';
import { useImageUpload } from '@/hooks/@common/useImageUpload';

const PetProfileImageUploader = () => {
const { petProfile, updatePetProfile } = usePetAdditionContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import styled, { css } from 'styled-components';

import { REVIEW_ALIGN_QUERY } from '@/constants/review';
import useEasyNavigate from '@/hooks/@common/useEasyNavigate';
import useValidQueryString from '@/hooks/common/useValidQueryString';
import useValidQueryString from '@/hooks/@common/useValidQueryString';
import { useReviewListAlignMeta } from '@/hooks/query/review';
import { generateQueryString } from '@/router/routes';
import { StyledProps } from '@/types/common/utility';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { styled } from 'styled-components';

import WriteIcon from '@/assets/svg/write_btn.svg';
import { useValidParams } from '@/hooks/@common/useValidParams';
import useValidQueryString from '@/hooks/common/useValidQueryString';
import useValidQueryString from '@/hooks/@common/useValidQueryString';
import { useReviewListQuery } from '@/hooks/query/review';
import { routerPath } from '@/router/routes';
import { zipgoLocalStorage } from '@/utils/localStorage';
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/context/Toast/ToastContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import {
PropsWithChildren,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { createPortal } from 'react-dom';
import { useLocation } from 'react-router-dom';
import { styled } from 'styled-components';

import ToastWarningIcon from '@/assets/svg/toast_warning_icon.svg';
Expand Down Expand Up @@ -60,6 +62,8 @@ const Portal = ({ children }: PropsWithChildren) =>
const ToastProvider = (props: PropsWithChildren) => {
const { children } = props;

const location = useLocation();

const [toastStates, setToastStates] = useState<ToastInterface[]>([]);
const currentToast = useRef<ToastInterface[]>([]);

Expand Down Expand Up @@ -90,6 +94,13 @@ const ToastProvider = (props: PropsWithChildren) => {
timers.current.delete(toastId);
};

const deleteAllToast = () => {
currentToast.current = [];

setToastStates(currentToast.current);
timers.current = new Map();
};

const generateToastId = (): ToastId => toastId.current;

const getToastId = useCallback(
Expand Down Expand Up @@ -150,6 +161,8 @@ const ToastProvider = (props: PropsWithChildren) => {
[toast, toastStates],
);

useEffect(deleteAllToast, [location.key]);

return (
<ToastContext.Provider value={memoizedValue}>
<PrefetchImg srcList={[ToastWarningIcon]} />
Expand Down
38 changes: 38 additions & 0 deletions frontend/src/hooks/@common/useEasterEgg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useState } from 'react';

import { useToast } from '@/context/Toast/ToastContext';

const AWAKE_NUMBER = 10;
const DANGER_NUMBER = AWAKE_NUMBER - 1;

let startTime: Date | undefined;
let endTime: Date | undefined;

const calcDuration = () => (Number(endTime) - Number(startTime)) / 1000;

const useEasterEgg = () => {
const { toast } = useToast();

const [dogTouchCount, setDogTouchCount] = useState(1);
const isDogAwake = dogTouchCount > AWAKE_NUMBER;

const onTouchDog = () => {
if (dogTouchCount === AWAKE_NUMBER && startTime) {
endTime = new Date();

toast.success(`${calcDuration()}초 만에 강아지를 깨웠어요!`);
} else if (dogTouchCount === DANGER_NUMBER) {
toast.warning('강아지를 깨우지 않게 조심하세요!');
} else if (dogTouchCount < AWAKE_NUMBER) {
startTime = startTime ?? new Date();

toast.info(`강아지를 ${dogTouchCount}번 쓰다듬었어요.`);
}

if (dogTouchCount <= AWAKE_NUMBER) setDogTouchCount(prev => prev + 1);
};

return { onTouchDog, isDogAwake };
};

export default useEasterEgg;
File renamed without changes.
2 changes: 1 addition & 1 deletion frontend/src/hooks/food/useFilterSelectionDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { generateQueryString } from '@/router/routes';
import { KeywordEn } from '@/types/food/client';

import useEasyNavigate from '../@common/useEasyNavigate';
import useValidQueryString from '../common/useValidQueryString';
import useValidQueryString from '../@common/useValidQueryString';

export const useFilterSelectionDisplay = () => {
const { replaceQueryString } = useEasyNavigate();
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/food/useInfiniteFoodListScroll.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef } from 'react';

import { KEYWORD_EN } from '@/constants/food';
import useValidQueryString from '@/hooks/common/useValidQueryString';
import useValidQueryString from '@/hooks/@common/useValidQueryString';
import { useFoodListInfiniteQuery } from '@/hooks/query/food';
import type { KeywordEn } from '@/types/food/client';

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/review/useCustomReview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { usePetProfile } from '@/context/petProfile/PetProfileContext';
import { generateQueryString } from '@/router/routes';

import useEasyNavigate from '../@common/useEasyNavigate';
import useValidQueryString from '../common/useValidQueryString';
import useValidQueryString from '../@common/useValidQueryString';

export const useCustomReview = () => {
const { replaceQueryString } = useEasyNavigate();
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/review/useReviewFilterList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FilterControlsMeta } from '@/types/review/client';
import { invariantOf } from '@/utils/invariantOf';
import { parseCheckList } from '@/utils/parseCheckList';

import useValidQueryString from '../common/useValidQueryString';
import useValidQueryString from '../@common/useValidQueryString';

const initialFilterList: Record<keyof FilterControlsMeta, Set<number>> = {
petSizes: new Set(),
Expand Down
92 changes: 36 additions & 56 deletions frontend/src/pages/Landing/Landing.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useState } from 'react';
import styled from 'styled-components';

import ZipgoBanner from '@/assets/webp/landing_banner.webp';
Expand All @@ -8,66 +7,47 @@ import Template from '@/components/@common/Template';
import FilterBottomSheet from '@/components/Food/FilterBottomSheet/FilterBottomSheet';
import FoodList from '@/components/Food/FoodList/FoodList';
import FoodSelectionGuideBanner from '@/components/FoodSelectionGuideBanner/FoodSelectionGuideBanner';
import { useToast } from '@/context/Toast/ToastContext';

const TARGET_NUMBER = 100;

let startTime: Date | undefined;
let endTime: Date | undefined;

const Landing = () => {
const { toast } = useToast();
const [dogTouchCount, setDogTouchCount] = useState<number>(1);
const isDogAwake = dogTouchCount > TARGET_NUMBER;

const onTouchDog = () => {
if (dogTouchCount === 1) {
startTime = new Date();
toast.info(`강아지를 ${dogTouchCount}번 쓰다듬었어요.`);
} else if (dogTouchCount === TARGET_NUMBER && startTime) {
endTime = new Date();
toast.success(`${(+endTime - +startTime) / 1000}초 만에 강아지를 깨웠어요!`);
} else if (TARGET_NUMBER - dogTouchCount === 10) {
toast.warning('강아지를 깨우지 않게 조심하세요!');
} else if (dogTouchCount < TARGET_NUMBER) {
toast.info(`강아지를 ${dogTouchCount}번 쓰다듬었어요.`);
}

if (dogTouchCount <= TARGET_NUMBER) setDogTouchCount(prev => prev + 1);
};
import useEasterEgg from '@/hooks/@common/useEasterEgg';

const Landing = () => (
<Template staticHeader={Header}>
<FoodSelectionGuideBanner />
<Layout>
<BannerSection>
<BannerText>
<TitleContainer>
<BannerSubTitle>
사료 선택이 어려운
<br />
초보 집사들을 위해
</BannerSubTitle>
<BannerTitle>집사의고민</BannerTitle>
</TitleContainer>
</BannerText>
<EasterEggBanner />
</BannerSection>
<ListSection>
<FilterBottomSheet />
<FoodList />
</ListSection>
</Layout>
</Template>
);

export default Landing;

const EasterEggBanner = () => {
const { isDogAwake, onTouchDog } = useEasterEgg();

return (
<Template staticHeader={Header}>
<FoodSelectionGuideBanner />
<Layout>
<BannerSection>
<BannerText>
<TitleContainer>
<BannerSubTitle>
사료 선택이 어려운
<br />
초보 집사들을 위해
</BannerSubTitle>
<BannerTitle>집사의고민</BannerTitle>
</TitleContainer>
</BannerText>
<BannerImg
src={isDogAwake ? ZipgoBannerAwake : ZipgoBanner}
onClick={onTouchDog}
alt="집사의고민 배너 이미지"
/>
</BannerSection>
<ListSection>
<FilterBottomSheet />
<FoodList />
</ListSection>
</Layout>
</Template>
<BannerImg
src={isDogAwake ? ZipgoBannerAwake : ZipgoBanner}
onClick={onTouchDog}
alt="집사의고민 배너 이미지"
/>
);
};

export default Landing;

const Layout = styled.div`
width: 100%;
height: 100%;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import ZipgoLogo from '@/assets/svg/zipgo_logo_dark.svg';
import ZipgoBanner from '@/assets/webp/landing_banner.webp';
import Template from '@/components/@common/Template';
import { KAKAO_HREF } from '@/constants/auth';
import useValidQueryString from '@/hooks/@common/useValidQueryString';
import { useAuth } from '@/hooks/auth';
import useValidQueryString from '@/hooks/common/useValidQueryString';
import { RuntimeError } from '@/utils/errors';

const Login = () => {
Expand Down

0 comments on commit ef95a6f

Please sign in to comment.