diff --git a/package.json b/package.json
index 5b1bad47..44840dcc 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"axios": "^1.6.5",
"eslint-plugin-react": "^7.33.2",
"grapheme-splitter": "^1.0.4",
+ "heic2any": "^0.0.4",
"lottie-react": "^2.4.0",
"postcss": "^8.4.33",
"postcss-styled-syntax": "^0.6.3",
diff --git a/src/LecueNote/components/SelectColor/index.tsx b/src/LecueNote/components/SelectColor/index.tsx
index eb2ccbc9..ee6cf943 100644
--- a/src/LecueNote/components/SelectColor/index.tsx
+++ b/src/LecueNote/components/SelectColor/index.tsx
@@ -18,8 +18,9 @@ function SelectColor({
handleColorFn,
handleIconFn,
selectedFile,
+ handleIsLoading,
}: SelectColorProps) {
- const { textColor, background, category } = lecueNoteState;
+ const { textColor, background, category, contents } = lecueNoteState;
return (
@@ -43,11 +44,13 @@ function SelectColor({
isIconClicked={isIconClicked}
colorChart={category === '텍스트색' ? TEXT_COLOR_CHART : BG_COLOR_CHART}
state={category === '텍스트색' ? textColor : background}
+ contents={contents}
handleTransformImgFile={handleTransformImgFile}
presignedUrlDispatch={presignedUrlDispatch}
selectedFile={selectedFile}
handleFn={handleColorFn}
handleIconFn={handleIconFn}
+ handleIsLoading={handleIsLoading}
/>
);
diff --git a/src/LecueNote/components/ShowColorChart/index.tsx b/src/LecueNote/components/ShowColorChart/index.tsx
index 452bb58e..d0785aff 100644
--- a/src/LecueNote/components/ShowColorChart/index.tsx
+++ b/src/LecueNote/components/ShowColorChart/index.tsx
@@ -4,44 +4,63 @@ import { IcCameraSmall } from '../../../assets';
import { BG_COLOR_CHART } from '../../constants/colorChart';
import useGetPresignedUrl from '../../hooks/useGetPresignedUrl';
import { ShowColorChartProps } from '../../type/lecueNoteType';
+import handleClickFiletoBinary from '../../util/handleClickFiletoBinary';
+import handleClickFiletoString from '../../util/handleClickFiletoString';
+import handleClickHeicToJpg from '../../util/handleClickHeicToJpg';
import * as S from './ShowColorChart.style';
function ShowColorChart({
isIconClicked,
colorChart,
state,
+ contents,
handleTransformImgFile,
presignedUrlDispatch,
selectedFile,
handleFn,
handleIconFn,
+ handleIsLoading,
}: ShowColorChartProps) {
const imgRef = useRef(null);
useGetPresignedUrl({ presignedUrlDispatch });
+ const handleChangeContents = () => {
+ sessionStorage.setItem('noteContents', contents ? contents : '');
+ };
+
+ const handleReaderOnloadend = (reader: FileReader, file: File) => {
+ handleTransformImgFile(reader);
+ selectedFile(file);
+ };
+
const handleImageUpload = () => {
const fileInput = imgRef.current;
if (fileInput && fileInput.files && fileInput.files.length > 0) {
const file = fileInput.files[0];
- // reader1: 파일을 base64로 읽어서 업로드
- const reader1 = new FileReader();
- reader1.readAsDataURL(file);
- reader1.onloadend = () => {
- if (reader1.result !== null) {
- handleTransformImgFile(reader1.result as string);
- }
- };
+ if (file.name.split('.')[1].toUpperCase() === 'HEIC') {
+ handleClickHeicToJpg({
+ file: file,
+ handleTransformImgFile,
+ handleReaderOnloadend,
+ handleIsLoading,
+ });
+ } else {
+ const reader1 = new FileReader();
+ handleClickFiletoString({
+ file: file,
+ reader: reader1,
+ handleTransformImgFile,
+ });
- // reader2: 파일을 ArrayBuffer로 읽어서 PUT 요청 수행
- const reader2 = new FileReader();
- reader2.readAsArrayBuffer(file);
- // reader1의 비동기 작업이 완료된 후 수행(onloadend() 활용)
- reader2.onloadend = () => {
- handleTransformImgFile(reader2);
- selectedFile(file);
- };
+ const reader2 = new FileReader();
+ handleClickFiletoBinary({
+ file: file,
+ reader: reader2,
+ handleReaderOnloadend,
+ });
+ }
}
};
@@ -51,7 +70,7 @@ function ShowColorChart({
<>
@@ -59,6 +78,7 @@ function ShowColorChart({
{
handleIconFn();
+ handleChangeContents();
imgRef.current?.click();
}}
$isIconClicked={isIconClicked}
diff --git a/src/LecueNote/components/WriteNote/NoteLoading/NoteLoading.style.ts b/src/LecueNote/components/WriteNote/NoteLoading/NoteLoading.style.ts
new file mode 100644
index 00000000..30277927
--- /dev/null
+++ b/src/LecueNote/components/WriteNote/NoteLoading/NoteLoading.style.ts
@@ -0,0 +1,21 @@
+import styled from '@emotion/styled';
+
+export const LoadingWrapper = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ top: 0;
+ left: 0;
+
+ width: 100%;
+ height: 100%;
+
+ border: 0.6rem;
+ background-color: ${({ theme }) => theme.colors.background};
+`;
+
+export const LottieWrapper = styled.div`
+ width: 10rem;
+ height: 10rem;
+`;
diff --git a/src/LecueNote/components/WriteNote/NoteLoading/index.tsx b/src/LecueNote/components/WriteNote/NoteLoading/index.tsx
new file mode 100644
index 00000000..e56ccde4
--- /dev/null
+++ b/src/LecueNote/components/WriteNote/NoteLoading/index.tsx
@@ -0,0 +1,24 @@
+import Lottie from 'lottie-react';
+import { useEffect } from 'react';
+
+import animationData from '../../../../../src/assets/lottie/spiner 120.json';
+import * as S from './NoteLoading.style';
+
+interface NoteLoadingProps {
+ handleResetPrevImg: () => void;
+}
+const NoteLoading = ({ handleResetPrevImg }: NoteLoadingProps) => {
+ useEffect(() => {
+ handleResetPrevImg();
+ }, []);
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default NoteLoading;
diff --git a/src/LecueNote/components/WriteNote/WriteNote.style.ts b/src/LecueNote/components/WriteNote/WriteNote.style.ts
index 8c6e0882..844138f2 100644
--- a/src/LecueNote/components/WriteNote/WriteNote.style.ts
+++ b/src/LecueNote/components/WriteNote/WriteNote.style.ts
@@ -14,6 +14,7 @@ export const LecueNote = styled.article<{
}>`
display: flex;
flex-direction: column;
+ position: relative;
width: 100%;
height: calc(100dvh - 33.2rem);
diff --git a/src/LecueNote/components/WriteNote/index.tsx b/src/LecueNote/components/WriteNote/index.tsx
index 774285cf..d2c95141 100644
--- a/src/LecueNote/components/WriteNote/index.tsx
+++ b/src/LecueNote/components/WriteNote/index.tsx
@@ -2,14 +2,17 @@ import GraphemeSplitter from 'grapheme-splitter';
import { useEffect, useState } from 'react';
import { WriteNoteProps } from '../../type/lecueNoteType';
+import NoteLoading from './NoteLoading';
import * as S from './WriteNote.style';
function WriteNote({
+ isLoading,
lecueNoteState,
imgFile,
isIconClicked,
contents,
handleChangeFn,
+ handleResetPrevImg,
}: WriteNoteProps) {
const nickname = localStorage.getItem('nickname');
const { textColor, background } = lecueNoteState;
@@ -30,6 +33,8 @@ function WriteNote({
$isIconClicked={isIconClicked}
$imgFile={imgFile}
>
+ {isLoading && }
+
{nickname}
{
+ setIsLoading(booleanStatus);
+ };
+
+ const handleResetPrevImg = () => {
+ dispatch({ type: 'RESET_PREV_IMG' });
+ };
+
const handleChangeContents = (e: React.ChangeEvent) => {
dispatch({ type: 'SET_CONTENTS', contents: e.target.value });
if (e.target.value.length > MAX_LENGTH) {
@@ -89,6 +99,8 @@ function LecueNotePage() {
isIconClicked: lecueNoteState.isIconClicked,
bookId: bookId,
});
+
+ sessionStorage.setItem('noteContents', '');
};
return putMutation.isLoading || postMutation.isLoading ? (
@@ -105,7 +117,10 @@ function LecueNotePage() {
{escapeModal && (
navigate(-1)}
+ handleFn={() => {
+ navigate(-1);
+ sessionStorage.setItem('noteContents', '');
+ }}
category="note_escape"
setModalOn={setEscapeModal}
/>
@@ -117,11 +132,13 @@ function LecueNotePage() {
dispatch({ type: 'CLICKED_IMG_ICON' })}
+ handleIsLoading={handleIsLoading}
/>
diff --git a/src/LecueNote/reducer/lecueNoteReducer.ts b/src/LecueNote/reducer/lecueNoteReducer.ts
index b7616fed..b4becec3 100644
--- a/src/LecueNote/reducer/lecueNoteReducer.ts
+++ b/src/LecueNote/reducer/lecueNoteReducer.ts
@@ -46,6 +46,9 @@ export const reducer = (state: State, action: Action): State => {
case 'IMG_TO_BINARY':
return { ...state, imgToBinary: action.imgFile };
+ case 'RESET_PREV_IMG':
+ return { ...state, imgToStr: '' };
+
default:
throw new Error('Unhandled action');
}
diff --git a/src/LecueNote/type/lecueNoteType.ts b/src/LecueNote/type/lecueNoteType.ts
index 787f67d1..0e9bb78d 100644
--- a/src/LecueNote/type/lecueNoteType.ts
+++ b/src/LecueNote/type/lecueNoteType.ts
@@ -4,6 +4,7 @@ export interface SelectColorProps {
textColor: string;
background: string;
category?: string;
+ contents?: string;
};
selectedFile: (file: File) => void;
presignedUrlDispatch: React.Dispatch<{
@@ -17,12 +18,14 @@ export interface SelectColorProps {
handleColorFn: (e: React.MouseEvent) => void;
handleIconFn: () => void;
handleTransformImgFile: (file: string | FileReader) => void;
+ handleIsLoading: (status: boolean) => void;
}
export interface ShowColorChartProps {
isIconClicked: boolean;
colorChart: string[];
state: string;
+ contents?: string;
handleTransformImgFile: (file: string | FileReader) => void;
selectedFile: (file: File) => void;
handleFn: (e: React.MouseEvent) => void;
@@ -32,9 +35,11 @@ export interface ShowColorChartProps {
presignedUrl: string;
filename: string;
}>;
+ handleIsLoading: (status: boolean) => void;
}
export interface WriteNoteProps {
+ isLoading: boolean;
imgFile: string;
isIconClicked: boolean;
lecueNoteState: {
@@ -44,6 +49,7 @@ export interface WriteNoteProps {
};
contents: string;
handleChangeFn: (e: React.ChangeEvent) => void;
+ handleResetPrevImg: () => void;
}
export interface FooterProps {
diff --git a/src/LecueNote/type/reducerType.ts b/src/LecueNote/type/reducerType.ts
index bd8a244d..e8e257f9 100644
--- a/src/LecueNote/type/reducerType.ts
+++ b/src/LecueNote/type/reducerType.ts
@@ -21,4 +21,5 @@ export type Action =
| { type: 'CLICKED_IMG_ICON' }
| { type: 'NOT_CLICKED_IMG_ICON' }
| { type: 'IMG_TO_STR'; imgFile: string }
- | { type: 'IMG_TO_BINARY'; imgFile: FileReader };
+ | { type: 'IMG_TO_BINARY'; imgFile: FileReader }
+ | { type: 'RESET_PREV_IMG'; };
diff --git a/src/LecueNote/util/handleClickFiletoBinary.ts b/src/LecueNote/util/handleClickFiletoBinary.ts
new file mode 100644
index 00000000..0db442be
--- /dev/null
+++ b/src/LecueNote/util/handleClickFiletoBinary.ts
@@ -0,0 +1,19 @@
+interface handleClickFiletoBinaryProps {
+ file: File;
+ reader: FileReader;
+ handleReaderOnloadend: (reader: FileReader, file: File) => void;
+}
+
+const handleClickFiletoBinary = ({
+ file,
+ reader,
+ handleReaderOnloadend,
+}: handleClickFiletoBinaryProps) => {
+ reader.readAsArrayBuffer(file);
+ // reader1의 비동기 작업이 완료된 후 수행(onloadend() 활용)
+ reader.onloadend = () => {
+ handleReaderOnloadend(reader, file);
+ };
+};
+
+export default handleClickFiletoBinary;
diff --git a/src/LecueNote/util/handleClickFiletoString.ts b/src/LecueNote/util/handleClickFiletoString.ts
new file mode 100644
index 00000000..68d02ca5
--- /dev/null
+++ b/src/LecueNote/util/handleClickFiletoString.ts
@@ -0,0 +1,20 @@
+interface handleClickFiletoStringProps {
+ file: File;
+ reader: FileReader;
+ handleTransformImgFile: (file: string | FileReader) => void;
+}
+
+const handleClickFiletoString = ({
+ file,
+ reader,
+ handleTransformImgFile,
+}: handleClickFiletoStringProps) => {
+ reader.readAsDataURL(file);
+ reader.onloadend = () => {
+ if (reader.result !== null) {
+ handleTransformImgFile(reader.result as string);
+ }
+ };
+};
+
+export default handleClickFiletoString;
diff --git a/src/LecueNote/util/handleClickHeicToJpg.ts b/src/LecueNote/util/handleClickHeicToJpg.ts
new file mode 100644
index 00000000..27799294
--- /dev/null
+++ b/src/LecueNote/util/handleClickHeicToJpg.ts
@@ -0,0 +1,49 @@
+import heic2any from 'heic2any';
+
+import handleClickFiletoBinary from './handleClickFiletoBinary';
+import handleClickFiletoString from './handleClickFiletoString';
+
+interface handleClickHeicToJpgProps {
+ file: File;
+ handleTransformImgFile: (file: string | FileReader) => void;
+ handleReaderOnloadend: (reader: FileReader, file: File) => void;
+ handleIsLoading: (status: boolean) => void;
+}
+
+const handleClickHeicToJpg = ({
+ file,
+ handleTransformImgFile,
+ handleReaderOnloadend,
+ handleIsLoading,
+}: handleClickHeicToJpgProps) => {
+ handleIsLoading(true);
+
+ heic2any({ blob: file, toType: 'image/jpeg' })
+ .then(function (resultBlob) {
+ // 변환된 Blob을 사용하여 새로운 File 생성
+ const jpg = new File(
+ Array.isArray(resultBlob) ? resultBlob : [resultBlob],
+ file.name.split('.')[0] + '.jpg',
+ { type: 'image/jpeg', lastModified: new Date().getTime() },
+ );
+
+ // reader1: 파일을 base64로 읽어서 업로드
+ const reader1 = new FileReader();
+ handleClickFiletoString({
+ file: jpg,
+ reader: reader1,
+ handleTransformImgFile,
+ });
+
+ // reader2: 파일을 ArrayBuffer로 읽어서 PUT 요청 수행
+ const reader2 = new FileReader();
+ handleClickFiletoBinary({
+ file: jpg,
+ reader: reader2,
+ handleReaderOnloadend,
+ });
+ })
+ .finally(() => handleIsLoading(false));
+};
+
+export default handleClickHeicToJpg;
diff --git a/yarn.lock b/yarn.lock
index 7ca484db..327d7b5d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2228,6 +2228,11 @@ hasown@^2.0.0:
dependencies:
function-bind "^1.1.2"
+heic2any@^0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/heic2any/-/heic2any-0.0.4.tgz#eddb8e6fec53c8583a6e18b65069bb5e8d19028a"
+ integrity sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==
+
hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"