diff --git a/src/common/libs/firebase/firebaseManager.js b/src/common/libs/firebase/firebaseManager.ts
similarity index 60%
rename from src/common/libs/firebase/firebaseManager.js
rename to src/common/libs/firebase/firebaseManager.ts
index 8007773b..088ff2fa 100644
--- a/src/common/libs/firebase/firebaseManager.js
+++ b/src/common/libs/firebase/firebaseManager.ts
@@ -1,12 +1,16 @@
import {
ref as firebaseRef,
getDownloadURL,
- uploadString,
+ uploadBytes,
} from 'firebase/storage';
+import { uniqueId } from 'lodash';
import { firebaseStorage } from './firebaseConfig';
-export const uploadFirebase = async (userId, file, dir) => {
- let storageRef;
+export const uploadFirebase = async (
+ userId: number,
+ file: File,
+ dir = 'posting',
+) => {
const date = new Date();
const year = date.getFullYear();
const month = ('0' + (1 + date.getMonth())).slice(-2);
@@ -16,11 +20,10 @@ export const uploadFirebase = async (userId, file, dir) => {
const seconds = ('0' + date.getSeconds()).slice(-2);
const now = year + month + day + hours + minutes + seconds;
- storageRef = firebaseRef(firebaseStorage, `${dir}/${userId}_${now}`);
- try {
- const snapshot = await uploadString(storageRef, file, 'data_url');
- return getDownloadURL(snapshot.ref);
- } catch (e) {
- return '';
- }
+ const storageRef = firebaseRef(
+ firebaseStorage,
+ `${dir}/${userId}_${now}_${uniqueId()}`,
+ );
+ const snapshot = await uploadBytes(storageRef, file);
+ return getDownloadURL(snapshot.ref);
};
diff --git a/src/features/posts/components/post-form/ImagePreview.tsx b/src/features/posts/components/post-form/ImagePreview.tsx
index a3f04f19..53811296 100644
--- a/src/features/posts/components/post-form/ImagePreview.tsx
+++ b/src/features/posts/components/post-form/ImagePreview.tsx
@@ -1,7 +1,7 @@
import DeleteIcon from '@/common/assets/images/write/delete-icon.svg';
interface ImagePreviewProps {
- images: { id?: number; url: string }[];
+ images: string[];
onDelete: (index: number) => () => void;
}
@@ -9,12 +9,9 @@ export function ImagePreview({ images, onDelete }: ImagePreviewProps) {
return (
{images.map((image, index) => (
-
+
diff --git a/src/features/posts/components/post-form/PostForm.tsx b/src/features/posts/components/post-form/PostForm.tsx
index 0e75977f..9228f21c 100644
--- a/src/features/posts/components/post-form/PostForm.tsx
+++ b/src/features/posts/components/post-form/PostForm.tsx
@@ -1,3 +1,4 @@
+import { useMemo } from 'react';
import { DockedButton, EditButton } from '@/common/components/ui/buttons';
import { Select } from '@/common/components/ui/selections';
import { CATEGORY_OPTIONS, DEADLINE_OPTIONS } from '@/common/constants/options';
@@ -49,7 +50,7 @@ export function PostForm({
defaultValue={formData.title}
/>
image.url)}
onDelete={handlers.imageDelete}
/>
void;
onChange: (data: PostFormData) => void;
}) {
- const { user } = useUser();
const [formData, setFormData] = useState(
initialData || {
title: '',
@@ -49,20 +45,10 @@ export function usePostForm({
};
const handleMainImageUpload = (file: File) => {
- const reader = new FileReader();
- reader.onload = (e) => {
- if (!e.target?.result || !user) {
- return;
- }
- resizeImage(e.target.result.toString()).then(async (result) => {
- const imgUrl = await uploadFirebase(user.id, result, 'posting');
- setFormData({
- ...formData,
- images: [...formData.images, { url: imgUrl }],
- });
- });
- };
- reader.readAsDataURL(file);
+ setFormData({
+ ...formData,
+ images: [...formData.images, { file, url: URL.createObjectURL(file) }],
+ });
};
const handleTitleChange = (title: string) => {
@@ -84,21 +70,15 @@ export function usePostForm({
};
const handleVoteOptionImageUpload = (index: number) => (file: File) => {
- const reader = new FileReader();
- reader.onload = (e) => {
- if (!e.target?.result || !user) {
- return;
- }
- resizeImage(e.target.result.toString()).then(async (result) => {
- const imgUrl = await uploadFirebase(user.id, result, 'posting');
- const newVoteOptions = formData?.voteOptions
- ? [...formData.voteOptions]
- : [];
- newVoteOptions[index].image = imgUrl;
- setFormData({ ...formData, voteOptions: newVoteOptions });
- });
- };
- reader.readAsDataURL(file);
+ const newVoteOptions = formData?.voteOptions
+ ? [...formData.voteOptions]
+ : [];
+ newVoteOptions[index].image = { file, url: URL.createObjectURL(file) };
+
+ setFormData({
+ ...formData,
+ voteOptions: newVoteOptions,
+ });
};
const handleVoteOptionImageDelete = (index: number) => () => {
diff --git a/src/features/posts/types/post-form.ts b/src/features/posts/types/post-form.ts
index a147fd38..3e7fd014 100644
--- a/src/features/posts/types/post-form.ts
+++ b/src/features/posts/types/post-form.ts
@@ -3,12 +3,16 @@ export interface PostFormData {
content: string;
images: {
id?: number;
+ file: File;
url: string;
}[];
categoryId?: number;
deadline?: number;
voteOptions: {
label: string;
- image: string | null;
+ image: {
+ file: File;
+ url: string;
+ } | null;
}[];
}
diff --git a/src/pages/write/WritePage.tsx b/src/pages/write/WritePage.tsx
index f013b656..b5a69bdb 100644
--- a/src/pages/write/WritePage.tsx
+++ b/src/pages/write/WritePage.tsx
@@ -3,6 +3,7 @@ import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { TopAppBar } from '@/common/components/layout';
import { Popup } from '@/common/components/ui/modal';
+import { uploadFirebase } from '@/common/libs/firebase/firebaseManager';
import { scrollRestorationAtom } from '@/common/states/scroll-restoration';
import getFutureDateTime from '@/common/utils/getFutureDateTime';
import { PostForm } from '@/features/posts/components/post-form/PostForm';
@@ -17,6 +18,7 @@ export default function WritePage() {
const navigate = useNavigate();
const { mutate: addPost, isLoading, isSuccess, data } = useAddPost();
const [hasChanges, setHasChanges] = useState(false);
+ const [uploadLoading, setUploadLoading] = useState(false);
const { user } = useUser();
const handleChange = ({
@@ -48,10 +50,25 @@ export default function WritePage() {
return;
}
+ setUploadLoading(true);
+ const uploadPromises = [
+ ...images.map((image) => uploadFirebase(user.id, image.file)),
+ ...voteOptions.map(
+ (option) => option.image && uploadFirebase(user.id, option.image.file),
+ ),
+ ];
+ const result = (await Promise.all(uploadPromises)).filter(
+ (url) => !!url,
+ ) as string[];
+ setUploadLoading(false);
+ const numOfMainImages = images.length;
+
addPost({
title: title.trim(),
content: content.trim(),
- files: images.map((image) => ({ url: image.url, contentType: 'image' })),
+ files: result
+ .slice(0, numOfMainImages)
+ .map((url) => ({ url, contentType: 'image' })),
userId: user?.id,
expirationTime: getFutureDateTime(deadline),
choices: voteOptions
@@ -59,7 +76,7 @@ export default function WritePage() {
.map((option, i) => ({
sequenceNumber: i,
label: option.label.trim(),
- url: option.image || null,
+ url: result[numOfMainImages + i],
})),
worryCategoryId: categoryId,
});
@@ -81,7 +98,7 @@ export default function WritePage() {
}
}, [isSuccess, data, navigate]);
- if (isLoading) {
+ if (isLoading || uploadLoading) {
return ;
}