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 6 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: ['velog.velcdn.com'], // 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
38 changes: 38 additions & 0 deletions src/app/test/result/_components/ImageBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use client';

import Image from 'next/image';
import { useRef } from 'react';

import { Icon } from '@/components/common/icon';
import { Typography } from '@/foundations/typography';
import { useDownloadImage } from '@/hooks/useDownloadImage';

const ImageBox = () => {
const imageRef = useRef<HTMLButtonElement>(null);
const { onDownloadImage } = useDownloadImage({ imageRef });

const handleDownloadImage = async () => {
await onDownloadImage();
};
return (
<article className="flex flex-col items-center justify-center px-2xs">
<button onClick={handleDownloadImage} ref={imageRef}>
<Image
src="https://velog.velcdn.com/images/sharphand1/post/18fa9fdc-5078-4477-82ed-0a4f9f5587a6/image.png"
width={335}
height={487}
alt="result-image"
/>
</button>
<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>
);
};

export default ImageBox;
18 changes: 4 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,9 @@ const ResultContents = () => {
<Header>
<Header.Previous />
</Header>
<ImageBox />
<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';

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 }>;
54 changes: 54 additions & 0 deletions src/hooks/useDownloadImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@

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);
} catch (error) {
toast({message:'IMAGE_SAVE_FAIL'})
} finally {
setIsDownloading(false);
}
};

return {
isDownloading,
onDownloadImage,
};
};
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