Skip to content

Commit

Permalink
Merge pull request #20 from dnd-side-project/OZ-70-F-pick
Browse files Browse the repository at this point in the history
Feature : Pick, Talk페이지 애니메이션 구현
  • Loading branch information
guesung authored Aug 20, 2023
2 parents 8d607a0 + ad7c95d commit cca0dde
Show file tree
Hide file tree
Showing 23 changed files with 3,084 additions and 1,750 deletions.
856 changes: 856 additions & 0 deletions public/lotties/pick.json

Large diffs are not rendered by default.

1,681 changes: 0 additions & 1,681 deletions public/lotties/posepicker.json

This file was deleted.

537 changes: 537 additions & 0 deletions public/lotties/talk_after_click.json

Large diffs are not rendered by default.

Binary file added public/lotties/talk_after_loading.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,540 changes: 1,540 additions & 0 deletions public/lotties/talk_before_click.json

Large diffs are not rendered by default.

32 changes: 4 additions & 28 deletions src/apis/apis.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query';

import { PosePickResponse, PoseTalkResponse } from '.';
import publicApi from './config/publicApi';

export const getPoseTalk = () => publicApi.get('/pose/talk');
export const usePoseTalkQuery = () =>
useQuery(['poseTalk'], getPoseTalk, {
enabled: false,
});

export const getPoseDetail = (poseId: number) => publicApi.get(`/pose/${poseId}`);
export const usePoseDetailQuery = (poseId: number) =>
useQuery(['poseId', poseId], () => getPoseDetail(poseId));

export interface PosePickResponse {
poseInfo: {
createdAt: string;
frameCount: number;
imageKey: string;
peopleCount: number;
poseId: number;
source: string;
sourceUrl: string;
tagAttributes: string;
updatedAt: string;
};
}

export const getPosePick = (peopleCount: number) =>
publicApi.get<PosePickResponse>(`/pose/pick/${peopleCount}`);

export const usePosePickQuery = (
peopleCount: number,
options?: UseQueryOptions<PosePickResponse>
) => useQuery<PosePickResponse>(['posePick', peopleCount], () => getPosePick(peopleCount), options);
export const getPoseDetail = (poseId: number) => publicApi.get(`/pose/${poseId}`);

export const getPoseTalk = () => publicApi.get<PoseTalkResponse>('/pose/talk');
3 changes: 3 additions & 0 deletions src/apis/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './apis';
export * from './queries';
export * from './type';
24 changes: 24 additions & 0 deletions src/apis/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query';

import {
type PosePickResponse,
type PoseTalkResponse,
getPoseDetail,
getPosePick,
getPoseTalk,
} from '.';

export const usePoseDetailQuery = (poseId: number) =>
useQuery(['poseId', poseId], () => getPoseDetail(poseId));

export const usePosePickQuery = (
peopleCount: number,
options?: UseQueryOptions<PosePickResponse>
) =>
useQuery<PosePickResponse>(['posePick', peopleCount], () => getPosePick(peopleCount), {
enabled: false,
...options,
});

export const usePoseTalkQuery = (options?: UseQueryOptions<PoseTalkResponse>) =>
useQuery<PoseTalkResponse>(['poseTalk'], getPoseTalk, { enabled: false, ...options });
22 changes: 22 additions & 0 deletions src/apis/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export interface PosePickResponse {
poseInfo: {
createdAt: string;
frameCount: number;
imageKey: string;
peopleCount: number;
poseId: number;
source: string;
sourceUrl: string;
tagAttributes: string;
updatedAt: string;
};
}

export interface PoseTalkResponse {
poseWord: {
content: string;
createdAt: string;
updateAt: string;
wordId: number;
};
}
13 changes: 6 additions & 7 deletions src/app/(Main)/components/MainHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import { Header } from '@/components/Header';

export default function MainHeader() {
return (
<div>
<Header
leftNode={<h4>PosePicker</h4>}
rightNode={<Image src="/icons/menu.svg" width={24} height={24} alt="24" />}
headerDownNode={<Tab />}
/>
</div>
<Header
leftNode={<h4>PosePicker</h4>}
rightNode={<Image src="/icons/menu.svg" width={24} height={24} alt="24" />}
headerDownNode={<Tab />}
className="px-20"
/>
);
}
28 changes: 16 additions & 12 deletions src/app/(Main)/pick/components/PickSection.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
'use client';

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

import lottieJson from '../../../../../public/lotties/posepicker.json';
import { usePosePickQuery } from '@/apis/apis';
import { usePosePickQuery } from '@/apis';
import { BottomFixedButton } from '@/components/Button';
import { Spacing } from '@/components/Spacing';

import lottiePick from '#/lotties/pick.json';

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

export default function PickSection() {
const [countState, setCountState] = useState<string>('1인');
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [image, setImage] = useState<string>('');
const { refetch, data } = usePosePickQuery(+countState[0], {
enabled: false,
const { refetch } = usePosePickQuery(+countState[0], {
onSuccess: (data) => {
if (!data) return;
setImage(data.poseInfo.imageKey);
},
});

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

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

return (
Expand All @@ -37,7 +41,7 @@ export default function PickSection() {
{countList.map((count) => (
<CountItem
key={count}
onClick={() => setCountState(count)}
onClick={() => !isLoading && setCountState(count)}
isSelected={count === countState}
count={count}
/>
Expand All @@ -47,9 +51,9 @@ export default function PickSection() {
<Spacing size={13} />
<div className="relative h-520">
{isLoading ? (
<Lottie loop animationData={lottieJson} play style={{ width: '100%', height: '100%' }} />
<Lottie loop animationData={lottiePick} play style={{ width: '100%', height: '100%' }} />
) : (
<Image src={image || '/images/sample.png'} fill priority alt="image" />
<Image src={image || '/images/sample.png'} fill priority alt="sample" />
)}
</div>
<BottomFixedButton className="bg-main-violet text-white" onClick={handlePickClick}>
Expand Down
54 changes: 53 additions & 1 deletion src/app/(Main)/talk/components/TalkSection.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,60 @@
'use client';

import Image from 'next/image';
import { useEffect, 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';

interface TalkSectionProps {}
export default function TalkSection() {
return <section></section>;
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 handleTalkClick = () => {
refetch();
};

return (
<section className="flex flex-col items-center">
<h1>{talkWord}</h1>
{isFirstLoading && (
<Lottie
loop
animationData={lottieTalkBeforeClick}
play
style={{ width: '100%', height: '100%' }}
/>
)}
{isLoading && (
<Lottie
loop
animationData={lottieTalkAfterClick}
play
style={{ width: '100%', height: '100%' }}
/>
)}
{!isFirstLoading && !isLoading && (
<Image src="/lotties/talk_after_loading.png" width={360} height={10} alt="lottie" />
)}
<BottomFixedButton className="bg-main-violet text-white" onClick={handleTalkClick}>
제시어 뽑기
</BottomFixedButton>
</section>
);
}
4 changes: 2 additions & 2 deletions src/app/(Main)/talk/components/TitleSection.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use client';
import { Spacing } from '@/components/Spacing';
import Image from 'next/image';
import { useState } from 'react';
import { Tooltip } from 'react-tooltip';

import { Spacing } from '@/components/Spacing';

export default function TitleSection() {
const [tooltipOpened, setTooltipOpened] = useState<boolean>(false);
console.log(tooltipOpened);
Expand All @@ -24,7 +25,6 @@ export default function TitleSection() {
</div>
<Tooltip id="my-tooltip" style={{ fontSize: '1rem', fontWeight: 400 }} />
<Spacing size={8} />
<h1>포즈로 말해요</h1>
</section>
);
}
5 changes: 3 additions & 2 deletions src/app/(Sub)/menu/components/MakerSection.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import BottomFixedDiv from '@/components/BottomFixedDiv';
import { Spacing } from '@/components/Spacing';
import Image from 'next/image';
import Link from 'next/link';

import BottomFixedDiv from '@/components/BottomFixedDiv';
import { Spacing } from '@/components/Spacing';

export default function MakerSection() {
return (
<BottomFixedDiv>
Expand Down
3 changes: 2 additions & 1 deletion src/app/(Sub)/menu/components/MenuListSection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Image from 'next/image';

import { Header } from '@/components/Header';
import { Spacing } from '@/components/Spacing';
import Image from 'next/image';

export default function MenuHeader() {
const LeftNode = (
Expand Down
13 changes: 6 additions & 7 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Header } from '@/components/Header';
import './globals.css';
import '../../styles/font.css';
import '../../styles/typography.css';
import './globals.css';

import type { Metadata } from 'next';
import QueryProvider from './QueryProvider';
import { OverlayProvider } from '@/components/Overlay/OverlayProvider';

import QueryProvider from './QueryProvider';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'PosePicker',
Expand Down Expand Up @@ -51,9 +50,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<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>
<QueryProvider>
<OverlayProvider>{children}</OverlayProvider>
</QueryProvider>
<div id="portal" />
</div>
</body>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { StrictPropsWithChildren } from '@/types';

import type { ButtonHTMLAttributes, HTMLAttributes, PropsWithChildren } from 'react';
import type { ButtonHTMLAttributes } from 'react';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
className?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function Header({
...props
}: HeaderProps) {
return (
<div className="fixed inset-x-0 top-8 bg-white px-20">
<div className="fixed inset-x-0 top-8 bg-white">
<header
className={`flex h-48 items-center justify-between ${className ? className : ''}`}
{...props}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Modal/Popup.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { StrictPropsWithChildren } from '@/types';
import ModalWrapper from './ModalWrapper';
import { StrictPropsWithChildren } from '@/types';

interface PopupProps {
className?: string;
Expand Down
3 changes: 1 addition & 2 deletions src/components/Overlay/OverlayController.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/** @tossdocs-ignore */
import { Ref, useImperativeHandle, forwardRef, useEffect, useState, useCallback } from 'react';
import { Ref, forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react';

import { CreateOverlayElement } from './type';

Expand Down
4 changes: 2 additions & 2 deletions src/components/Overlay/OverlayProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use client';

import React, {
createContext,
PropsWithChildren,
ReactNode,
createContext,
useCallback,
useMemo,
useState,
Expand Down Expand Up @@ -41,7 +41,7 @@ export function OverlayProvider({ children }: PropsWithChildren) {
return (
<OverlayContext.Provider value={context}>
{children}
{[...overlayById.entries()].map(([id, element]) => (
{Array.from(overlayById.entries()).map(([id, element]) => (
<React.Fragment key={id}>{element}</React.Fragment>
))}
</OverlayContext.Provider>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Overlay/useOverlay.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useContext, useEffect, useMemo, useRef, useState } from 'react';

import { OverlayControlRef, OverlayController } from './OverlayController';
import { OverlayContext } from './OverlayProvider';
import { OverlayController, OverlayControlRef } from './OverlayController';
import { CreateOverlayElement } from './type';

let elementId = 1;
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"jsx": "preserve",
"incremental": true,
"paths": {
"@/*": ["./src/*"]
"@/*": ["./src/*"],
"#/*": ["./public/*"]
},
"plugins": [
{
Expand Down

0 comments on commit cca0dde

Please sign in to comment.