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

Refactoring : Pick, Talk 페이지 리팩토링 #22

Merged
merged 11 commits into from
Aug 23, 2023
2 changes: 1 addition & 1 deletion .github/workflows/cd-feature-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CD-feature branch
on:
push:
branches:
- 'feature/**'
- 'OZ-**'

jobs:
build:
Expand Down
Binary file removed public/lotties/talk_after_loading.png
Binary file not shown.
35 changes: 18 additions & 17 deletions src/app/(Main)/pick/components/PickSection.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
'use client';

import Image from 'next/image';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import Lottie from 'react-lottie-player';

import lottiePick from '#/lotties/pick.json';
import { usePosePickQuery } from '@/apis';
import { BottomFixedButton } from '@/components/Button';
import { Spacing } from '@/components/Spacing';

import lottiePick from '#/lotties/pick.json';
import useLoading from '@/hooks/useLoading';

const countList = ['1인', '2인', '3인', '4인', '5인+'];

export default function PickSection() {
const [countState, setCountState] = useState<string>('1인');
const [isLoading, setIsLoading] = useState<boolean>(true);
const { isLoading, startLoading } = useLoading({ loadingDelay: 3000 });
const [image, setImage] = useState<string>('');
const { refetch } = usePosePickQuery(+countState[0], {
onSuccess: (data) => {
Expand All @@ -23,15 +23,8 @@ export default function PickSection() {
},
});

useEffect(() => {
if (!isLoading) return;
setTimeout(() => {
setIsLoading(false);
}, 3000);
}, [isLoading]);

const handlePickClick = () => {
setIsLoading(true);
startLoading();
refetch();
};

Expand All @@ -41,23 +34,31 @@ export default function PickSection() {
{countList.map((count) => (
<CountItem
key={count}
onClick={() => !isLoading && setCountState(count)}
onClick={() => setCountState(count)}
isSelected={count === countState}
count={count}
/>
))}
</div>

<Spacing size={13} />

<div className="relative h-520">
{isLoading ? (
{true && (
<Lottie loop animationData={lottiePick} play style={{ width: '100%', height: '100%' }} />
) : (
<Image src={image || '/images/sample.png'} fill priority alt="sample" />
)}
<Image
src={image || '/images/sample.png'}
fill
alt="sample"
priority
loading="eager"
className={isLoading ? 'hidden' : ''}
/>
</div>

<BottomFixedButton className="bg-main-violet text-white" onClick={handlePickClick}>
포즈 pick!
{!!image ? '포즈 pick!' : '인원수 선택하고 포즈 pick!'}
</BottomFixedButton>
</section>
);
Expand Down
49 changes: 29 additions & 20 deletions src/app/(Main)/talk/components/TalkSection.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,50 @@
'use client';

import Image from 'next/image';
import { useEffect, useRef, useState } from 'react';
import { useRef, useState } from 'react';
import Lottie from 'react-lottie-player';

import { usePoseTalkQuery } from '@/apis';
import { BottomFixedButton } from '@/components/Button';

import lottieTalkAfterClick from '#/lotties/talk_after_click.json';
import lottieTalkBeforeClick from '#/lotties/talk_before_click.json';
import { usePoseTalkQuery } from '@/apis';
import { BottomFixedButton } from '@/components/Button';
import useLoading from '@/hooks/useLoading';

interface TalkSectionProps {}
export default function TalkSection() {
const [isFirstLoading, setIsFirstLoading] = useState<boolean>(true);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [talkWord, setTalkWord] = useState<string>('포즈로 말해요');
const { refetch } = usePoseTalkQuery({
onSuccess: (data) => {
setIsFirstLoading(false);
setIsLoading(true);
setTimeout(() => {
setIsLoading(false);
setTalkWord(data.poseWord.content);
}, 3000);
},
const { isLoading: isFirstLoading, stopLoading: stopFirstLoading } = useLoading({
loadingDelay: 3000,
});
const ref = useRef(null);

const { refetch, data } = usePoseTalkQuery();

const { isLoading, startLoading } = useLoading({
loadingDelay: 3000,
onStopLoading: () => data && setTalkWord(data.poseWord.content),
});
const [talkWord, setTalkWord] = useState<string>('포즈로 말해요');

const handleTalkClick = () => {
refetch();
stopFirstLoading();
startLoading();
};

return (
<section className="flex flex-col items-center">
<h1>{talkWord}</h1>
<h1 className="text-center">{talkWord}</h1>

{isFirstLoading && (
<Lottie
loop
animationData={lottieTalkBeforeClick}
play
style={{ width: '100%', height: '100%' }}
ref={ref}
onComplete={() => console.log('complete')}
/>
)}
{isLoading && (
{isLoading && !isFirstLoading && (
<Lottie
loop
animationData={lottieTalkAfterClick}
Expand All @@ -50,7 +53,13 @@ export default function TalkSection() {
/>
)}
{!isFirstLoading && !isLoading && (
<Image src="/lotties/talk_after_loading.png" width={360} height={10} alt="lottie" />
<Lottie
loop
animationData={lottieTalkAfterClick}
play
style={{ width: '100%', height: '100%' }}
speed={0}
/>
)}
<BottomFixedButton className="bg-main-violet text-white" onClick={handleTalkClick}>
제시어 뽑기
Expand Down
26 changes: 18 additions & 8 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StrictPropsWithChildren } from '@/types';
import '../../styles/font.css';
import '../../styles/typography.css';
import './globals.css';
Expand Down Expand Up @@ -47,14 +48,23 @@ export const metadata: Metadata = {

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body className="flex h-[100dvh] w-screen touch-none justify-center bg-slate-100 py-px">
<div className="h-full w-full max-w-440 bg-white text-primary drop-shadow-2xl">
<QueryProvider>
<OverlayProvider>{children}</OverlayProvider>
</QueryProvider>
<div id="portal" />
</div>
<Layout>
<QueryProvider>
<OverlayProvider>{children}</OverlayProvider>
</QueryProvider>
<div id="portal" />
</Layout>
);
}

function Layout({ children }: StrictPropsWithChildren) {
return (
<html
lang="ko"
className="flex h-[100dvh] w-screen touch-none justify-center bg-slate-100 py-px"
>
<body className="h-full w-full max-w-440 bg-white text-primary drop-shadow-2xl">
{children}
</body>
</html>
);
Expand Down
5 changes: 4 additions & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { redirect } from 'next/navigation';

export default function Home() {
return <div>ssss</div>;
redirect('/pick');
return <div></div>;
}
2 changes: 1 addition & 1 deletion src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ModalWrapper from './ModalWrapper';
import ModalWrapper from './PortalWrapper';
import { StrictPropsWithChildren } from '@/types';

interface PopupProps {
Expand Down
35 changes: 0 additions & 35 deletions src/components/Modal/ModalWrapper.tsx

This file was deleted.

44 changes: 44 additions & 0 deletions src/components/Modal/PortalWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client';
import clsx from 'clsx';
import { useRef } from 'react';
import { twMerge } from 'tailwind-merge';

import { AnimatedPortal } from '../Portal';
import { useOnClickOutside } from '@/hooks/useOnClickOutside';

import type { StrictPropsWithChildren } from '@/types';

interface PortalWrapperProps {
onClose?: () => void;
isBackGroundBlur?: boolean;
className?: string;
}

export default function PortalWrapper({
onClose = () => {},
children,
isBackGroundBlur = true,
className,
}: StrictPropsWithChildren<PortalWrapperProps>) {
const portalRef = useRef<HTMLDivElement>(null);

useOnClickOutside(portalRef, onClose);

return (
<AnimatedPortal
motionProps={{ initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 } }}
>
<div
className={clsx(
'fixed left-1/2 top-0 z-10 h-full w-full max-w-440 -translate-x-1/2',
{
'bg-neutral-900 bg-opacity-90': isBackGroundBlur,
},
className
)}
>
{children}
</div>
</AnimatedPortal>
);
}
39 changes: 39 additions & 0 deletions src/hooks/useLoading.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useCallback, useState } from 'react';

import useIsMounted from './useIsMounted';

interface UseLoadingProps {
initialState?: boolean;
loadingDelay?: number;
onStopLoading?: () => void;
}
export default function useLoading({
initialState = true,
loadingDelay,
onStopLoading,
}: UseLoadingProps = {}) {
const [isLoading, setIsLoading] = useState(initialState ?? false);
const isMounted = useIsMounted();

const stopLoading = useCallback(() => {
setIsLoading(false);
}, []);

const startLoading = useCallback(() => {
setIsLoading(true);
setTimeout(() => {
stopLoading();
}, loadingDelay);
}, [loadingDelay, stopLoading]);

if (isMounted) {
if (loadingDelay) {
setTimeout(() => {
stopLoading();
onStopLoading && onStopLoading();
}, loadingDelay);
}
}

return { isLoading, stopLoading, startLoading };
}