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

[FEAT] 테스트 결과 이미지 저장 기능 구현 #131

Merged
merged 11 commits into from
Feb 20, 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
3 changes: 3 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: [], // mock image로 테스트 하기 위한 config입니다.
},
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"framer-motion": "^10.18.0",
"github-label-sync": "^2.3.1",
"lodash.compact": "^3.0.1",
"modern-screenshot": "^4.4.38",
"next": "14.1.0",
"react": "^18",
"react-dom": "^18",
Expand Down
43 changes: 43 additions & 0 deletions src/app/test/result/_components/ImageBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client';

import { useRef } from 'react';

import FirstResultType from '@/assets/images/result/firstResultType.svg';
import ForthResultType from '@/assets/images/result/forthResultType.svg';
import SecondResultType from '@/assets/images/result/secondResultType.svg';
import ThirdResultType from '@/assets/images/result/thirdResultType.svg';
import { Button } from '@/components/common/button';
import { Typography } from '@/foundations/typography';
import { useDownloadImage } from '@/hooks/useDownloadImage';
import { TestResultType } from '@/types/test';

// TODO : 백엔드와 논의하여 resultTypeId 프로퍼티 결정
const ImageBox = ({ resultTypeId = 100 }: { resultTypeId: TestResultType['ResultTypeid'] }) => {
const imageRef = useRef<HTMLDivElement>(null);
const { onDownloadImage } = useDownloadImage({ imageRef });

const handleDownloadImage = async () => {
await onDownloadImage();
};
return (
<>
<div ref={imageRef}>{resultTypeMap[resultTypeId]}</div>
<div className="flex items-center gap-5xs py-xs">
<Button variant="empty" onClick={handleDownloadImage} className="h-[30px]">
Copy link
Member

Choose a reason for hiding this comment

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

button에 isLoading 프롭이 있긴해요! 필요하심 사용하셔도 되고 아니면 지금 유지하셔도 됩니닷

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

디자인이 변경돼서 로딩하지 않기로 이야기했어요! disable 처리하려고 하는데 isLoading props 사용해서 구현할게요~

<Typography type="body2" className=" border-b-2 border-[#5382FF] text-[#5382FF]">
이미지 저장하기
</Typography>
</Button>
</div>
</>
);
};

export default ImageBox;

const resultTypeMap = {
0: <FirstResultType />,
36: <SecondResultType />,
70: <ThirdResultType />,
100: <ForthResultType />,
};
21 changes: 7 additions & 14 deletions src/app/test/result/_components/ResultContents.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import Image from 'next/image';
'use client';

import ResultType from '@/assets/images/resultType.png';
import { Button } from '@/components/common/button';
import { Icon } from '@/components/common/icon';
import { VoteCard, VoteItem } from '@/components/features/vote';
import { Header } from '@/components/layout/header';
import { Typography } from '@/foundations/typography';

import ImageBox from './ImageBox';
import TempertaureBox from './TempertaureBox';

const ResultContents = () => {
Expand All @@ -15,18 +14,12 @@ const ResultContents = () => {
<Header>
<Header.Previous />
</Header>

<article className="flex flex-col items-center justify-center px-2xs">
<ImageBox resultTypeId={100} />
</article>
<main className="pb-10">
<section className="flex flex-col items-center justify-center">
<article className="flex flex-col items-center justify-center px-2xs ">
<Image src={ResultType} width={335} height={487} alt="result-image" />
<div className="flex items-center gap-5xs py-3xs">
<Icon icon="caretUp" color="gray-300" size={12} />
<Typography type="body2" className="text-gray-500">
이미지 꾹 - 눌러서 저장하기
</Typography>
<Icon icon="caretUp" color="gray-300" size={12} />
</div>
</article>
<section className="flex flex-col items-center justify-center">
{/* TODO : 컴포넌트 분리 예정 */}
<article className="my-4xs flex w-full px-2xs ">
<div className="flex w-full flex-col items-center justify-center rounded-xl border border-gray-100 py-xs">
Expand Down
2 changes: 1 addition & 1 deletion src/app/test/result/_components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { default as ImageBox } from './ImageBox';
export { default as ResultContents } from './ResultContents';
export { default as TempertaureBox } from './TempertaureBox';

12 changes: 12 additions & 0 deletions src/assets/images/result/firstResultType.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/assets/images/result/forthResultType.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/assets/images/result/secondResultType.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/assets/images/result/thirdResultType.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added src/constants/test/result.tsx
Empty file.
1 change: 1 addition & 0 deletions src/constants/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const TOAST_MESSAGES = Object.freeze({
LINK_COPIED: { type: 'warning', text: '링크가 복사되었습니다' },

IMAGE_FILE_ONLY: { type: 'warning', text: '이미지 파일만 업로드 가능합니다' },
IMAGE_SAVE_FAIL: { type: 'warning', text: '유형 이미지 저장이 실패했습니다' },

ERROR: { type: 'warning', text: '잠시 후에 다시 시도해 주세요' },
}) satisfies Record<string, { type: 'default' | 'warning'; text: string }>;
57 changes: 57 additions & 0 deletions src/hooks/useDownloadImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

import { domToJpeg } from 'modern-screenshot';
import { RefObject, useState } from 'react';

import { downloadFile } from '@/utils/image';

import useToast from './useToast';

type DownloadImageOption = {
imageRef: RefObject<HTMLElement>;
}

// TODO : 필요한 옵션 알아봐서 추가할 예정
const defaultDownloadOption = {
scale: 2,
};

export const useDownloadImage = ({ imageRef }: DownloadImageOption) => {

const [isDownloading, setIsDownloading] = useState(false);
const toast = useToast();

const downloadOption = {
height: 700,
...defaultDownloadOption,
};

const onDownloadImage = async () => {
const image = imageRef.current;

if (!image) return;

try {
setIsDownloading(true);

const imageOption = downloadOption
const imageUrl = await domToJpeg(image, imageOption);

// TODO: 동적으로 4개의 유형의 이름을 할당할 예정
const IMAGE_FILE_NAME = '돈워리_결과_사진';


downloadFile(imageUrl, IMAGE_FILE_NAME);
toast({ type: 'default', message: '이미지를 저장하였습니다.' })
} catch (error) {
console.log(error,'error')
toast({message:'IMAGE_SAVE_FAIL'})
} finally {
setIsDownloading(false);
}
};

return {
isDownloading,
onDownloadImage,
};
};
1 change: 1 addition & 0 deletions src/types/test/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './result';
export * from './test';
19 changes: 19 additions & 0 deletions src/types/test/result.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@


// TODO : 엄격한 타입 설정하기
export type TestResultType = {
id: number
age: '20대' | '30대' | '40대'
gender: '남자' | '여자'
buddy: string
trust: number
love : number
talk : number
temperature: number
ResultTypeid : 0 | 36| 70 | 100
imageUrl: string
description : string
title: string
createdAt : string
}

9 changes: 9 additions & 0 deletions src/utils/image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

export const downloadFile = (url: string, filename: string) => {
const link = document.createElement('a');

link.download = filename;
link.href = url;

link.click();
};
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8452,6 +8452,7 @@ __metadata:
husky: "npm:^8.0.0"
jest: "npm:^29.7.0"
lodash.compact: "npm:^3.0.1"
modern-screenshot: "npm:^4.4.38"
next: "npm:14.1.0"
postcss: "npm:^8"
prettier: "npm:^3.2.4"
Expand Down Expand Up @@ -13180,6 +13181,13 @@ __metadata:
languageName: node
linkType: hard

"modern-screenshot@npm:^4.4.38":
version: 4.4.38
resolution: "modern-screenshot@npm:4.4.38"
checksum: db72a42a6f9d54bda8391f1d4f42c53639320bf994290f51387acdae3864a32133db147ddc17d0ba4b74b22407160b53d555648811bf41da1f2154b934b888bd
languageName: node
linkType: hard

"ms@npm:2.0.0":
version: 2.0.0
resolution: "ms@npm:2.0.0"
Expand Down
Loading