Skip to content

Commit

Permalink
[FEAT] 테스트 결과 이미지 저장 기능 구현 (#131)
Browse files Browse the repository at this point in the history
  • Loading branch information
CodyMan0 authored Feb 20, 2024
1 parent ccc8c64 commit b315c26
Show file tree
Hide file tree
Showing 16 changed files with 198 additions and 15 deletions.
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]">
<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

0 comments on commit b315c26

Please sign in to comment.