diff --git a/src/App.tsx b/src/App.tsx
index 8bc3121..2155651 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,7 +7,7 @@ import Profile from './routes/profile.tsx';
import SearchResult from './routes/search-result.tsx';
import Auth from './routes/auth.tsx';
import Layout from './components/layout.tsx';
-import LoadingScreen from './components/loading-screen.tsx';
+import LoadingSpinner from './components/loading-spinner.tsx';
import * as S from './styles/global.ts';
const router = createBrowserRouter([
@@ -51,7 +51,7 @@ function App() {
return (
<>
- {isLoading ? : }
+ {isLoading ? : }
>
);
}
diff --git a/src/assets/images/i-edit.svg b/src/assets/images/i-edit.svg
new file mode 100644
index 0000000..dc4d356
--- /dev/null
+++ b/src/assets/images/i-edit.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/images/loading-spinner-mini.svg b/src/assets/images/loading-spinner-mini.svg
new file mode 100644
index 0000000..976b5bd
--- /dev/null
+++ b/src/assets/images/loading-spinner-mini.svg
@@ -0,0 +1,40 @@
+
+
\ No newline at end of file
diff --git a/src/assets/images/loading-spinner.svg b/src/assets/images/loading-spinner.svg
new file mode 100644
index 0000000..d55d18e
--- /dev/null
+++ b/src/assets/images/loading-spinner.svg
@@ -0,0 +1,64 @@
+
+
\ No newline at end of file
diff --git a/src/components/edit-tweet-form.tsx b/src/components/edit-tweet-form.tsx
new file mode 100644
index 0000000..8f1103c
--- /dev/null
+++ b/src/components/edit-tweet-form.tsx
@@ -0,0 +1,110 @@
+import React, { useState } from 'react';
+import { deleteField, doc, updateDoc } from 'firebase/firestore';
+import {
+ deleteObject,
+ getDownloadURL,
+ ref,
+ uploadBytes,
+} from 'firebase/storage';
+import { auth, db, storage } from '../firebase.ts';
+import ITweet from '../interfaces/ITweet.ts';
+import * as S from '../styles/tweet-form.ts';
+import { ReactComponent as IconPhoto } from '../assets/images/i-photo.svg';
+import { ReactComponent as LoadingSpinner } from '../assets/images/loading-spinner-mini.svg';
+
+interface IEditTweetForm extends Pick {
+ onClose: () => void;
+}
+
+export default function EditTweetForm({
+ id,
+ tweet: initialTweet,
+ photo: initialPhoto,
+ onClose,
+}: IEditTweetForm) {
+ const [isLoading, setLoading] = useState(false);
+ const [tweet, setTweet] = useState(initialTweet);
+ const [image, setImage] = useState(null);
+ const [imagePreview, setImagePreview] = useState(initialPhoto);
+
+ const onChange = (e: React.ChangeEvent) => {
+ setTweet(e.target.value);
+ };
+ const onImageChange = (e: React.ChangeEvent) => {
+ const images = e.target.files;
+ if (images && images.length === 1) {
+ setImage(images[0]);
+ const previewUrl = URL.createObjectURL(images[0]);
+ setImagePreview(previewUrl);
+ }
+ };
+ const onImageDelete = () => {
+ setImage(null);
+ setImagePreview('');
+ };
+ const onSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ const user = auth.currentUser;
+ if (!user || isLoading || tweet === '' || tweet.length > 180) return;
+ try {
+ setLoading(true);
+ const tweetDocRef = doc(db, 'tweets', id);
+ await updateDoc(tweetDocRef, {
+ tweet,
+ });
+ if (image) {
+ const locationRef = ref(
+ storage,
+ initialPhoto || `tweets/${user.uid}/${id}`,
+ );
+ const result = await uploadBytes(locationRef, image);
+ const url = await getDownloadURL(result.ref);
+ await updateDoc(tweetDocRef, {
+ photo: url,
+ });
+ } else if (!image && initialPhoto && initialPhoto !== imagePreview) {
+ const locationRef = ref(storage, initialPhoto);
+ await deleteObject(locationRef);
+ await updateDoc(tweetDocRef, {
+ photo: deleteField(),
+ });
+ }
+ setImage(null);
+ } catch (error) {
+ console.log(error);
+ } finally {
+ setLoading(false);
+ onClose();
+ }
+ };
+ return (
+
+
+ {imagePreview ? (
+ <>
+
+
+ >
+ ) : (
+
+
+
+ )}
+
+
+ {isLoading ? : '수정'}
+
+
+ );
+}
diff --git a/src/components/loading-screen.tsx b/src/components/loading-screen.tsx
deleted file mode 100644
index be4175e..0000000
--- a/src/components/loading-screen.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import * as S from '../styles/components/loading-screen.ts';
-
-export default function LoadingScreen() {
- return (
-
- Loading...
-
- );
-}
diff --git a/src/components/loading-spinner.tsx b/src/components/loading-spinner.tsx
new file mode 100644
index 0000000..1ed6dd3
--- /dev/null
+++ b/src/components/loading-spinner.tsx
@@ -0,0 +1,10 @@
+import Wrapper from '../styles/loading-spinner.ts';
+import { ReactComponent as Spinner } from '../assets/images/loading-spinner.svg';
+
+export default function LoadingSpinner() {
+ return (
+
+
+
+ );
+}
diff --git a/src/components/post-tweet-form.tsx b/src/components/post-tweet-form.tsx
index f8ce76e..dd905b9 100644
--- a/src/components/post-tweet-form.tsx
+++ b/src/components/post-tweet-form.tsx
@@ -2,8 +2,9 @@ import React, { useState } from 'react';
import { addDoc, collection, updateDoc } from 'firebase/firestore';
import { getDownloadURL, ref, uploadBytes } from 'firebase/storage';
import { auth, db, storage } from '../firebase.ts';
-import * as S from '../styles/post-tweet-form.ts';
+import * as S from '../styles/tweet-form.ts';
import { ReactComponent as IconPhoto } from '../assets/images/i-photo.svg';
+import { ReactComponent as LoadingSpinner } from '../assets/images/loading-spinner-mini.svg';
export default function PostTweetForm() {
const [isLoading, setLoading] = useState(false);
@@ -56,7 +57,7 @@ export default function PostTweetForm() {
}
};
return (
-
+
- {isLoading ? '포스팅 중...' : '포스팅'}
+ {isLoading ? : '포스팅'}
-
+
);
}
diff --git a/src/components/signIn.tsx b/src/components/sign-in.tsx
similarity index 95%
rename from src/components/signIn.tsx
rename to src/components/sign-in.tsx
index 3856e39..39c4242 100644
--- a/src/components/signIn.tsx
+++ b/src/components/sign-in.tsx
@@ -6,6 +6,7 @@ import { auth } from '../firebase.ts';
import * as S from '../styles/auth.ts';
import * as P from '../styles/popup.ts';
import ImageComputer from '../assets/images/logo-small.png';
+import { ReactComponent as LoadingSpinner } from '../assets/images/loading-spinner-mini.svg';
interface ISignInProps {
onClose: () => void;
@@ -90,7 +91,7 @@ export default function SignIn({ onClose }: ISignInProps) {
required
/>
- {isLoading ? '로딩...' : '로그인하기'}
+ {isLoading ? : '로그인하기'}
{firebaseError !== '' ? {firebaseError} : null}
diff --git a/src/components/signUp.tsx b/src/components/sign-up.tsx
similarity index 96%
rename from src/components/signUp.tsx
rename to src/components/sign-up.tsx
index 4007a09..0cdd9b1 100644
--- a/src/components/signUp.tsx
+++ b/src/components/sign-up.tsx
@@ -7,6 +7,7 @@ import { auth, db } from '../firebase.ts';
import * as S from '../styles/auth.ts';
import * as P from '../styles/popup.ts';
import ImageComputer from '../assets/images/logo-small.png';
+import { ReactComponent as LoadingSpinner } from '../assets/images/loading-spinner-mini.svg';
interface ISignUpProps {
onClose: () => void;
@@ -116,7 +117,7 @@ export default function SignUp({ onClose }: ISignUpProps) {
required
/>
- {isLoading ? '로딩...' : '가입하기'}
+ {isLoading ? : '가입하기'}
{firebaseError !== '' ? {firebaseError} : null}
diff --git a/src/components/socialSignIn.tsx b/src/components/social-sign-in.tsx
similarity index 100%
rename from src/components/socialSignIn.tsx
rename to src/components/social-sign-in.tsx
diff --git a/src/components/tweet-form.tsx b/src/components/tweet-form.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/tweet.tsx b/src/components/tweet.tsx
index d6117d3..9da3043 100644
--- a/src/components/tweet.tsx
+++ b/src/components/tweet.tsx
@@ -4,9 +4,11 @@ import { deleteObject, ref } from 'firebase/storage';
import { auth, db, storage } from '../firebase.ts';
import ITweet from '../interfaces/ITweet.ts';
import FormattedDate from '../utils/formattedDate.tsx';
+import EditTweetForm from './edit-tweet-form.tsx';
import * as S from '../styles/tweet.ts';
import * as P from '../styles/popup.ts';
import { ReactComponent as IconUser } from '../assets/images/i-user.svg';
+import { ReactComponent as IconEdit } from '../assets/images/i-edit.svg';
export default function Tweet({
id,
@@ -23,6 +25,10 @@ export default function Tweet({
const userData = userDoc.data();
setUserAvatar(userData?.userAvatar || null);
};
+ const [editPopup, setEditPopup] = useState(false);
+ const toggleEditPopup = () => {
+ setEditPopup(!editPopup);
+ };
const [deletePopup, setDeletePopup] = useState(false);
const toggleDeletePopup = () => {
setDeletePopup(!deletePopup);
@@ -43,7 +49,7 @@ export default function Tweet({
};
useEffect(() => {
fetchUserAvatar();
- }, [userId, userAvatar]);
+ }, [userName, userAvatar]);
return (
@@ -57,11 +63,28 @@ export default function Tweet({
{photo ? : null}
{user?.uid === userId ? (
<>
+
+ 포스팅 수정하기
+
+
포스팅 삭제하기
>
) : null}
+ {editPopup ? (
+
+
+
+ setEditPopup(false)}
+ />
+
+
+ ) : null}
{deletePopup ? (
diff --git a/src/styles/button.ts b/src/styles/button.ts
index d52540c..5aee82a 100644
--- a/src/styles/button.ts
+++ b/src/styles/button.ts
@@ -4,9 +4,14 @@ import { blackColor, grayColor, primaryColor, whiteColor } from './global.ts';
const Button = styled.button`
width: 100%;
margin: 8px 0;
- padding: 10px 20px;
+ padding: 0 20px;
border-radius: 50px;
font-size: 16px;
+ line-height: 36px;
+ svg {
+ width: 36px;
+ height: 36px;
+ }
`;
export const LineButton = styled(Button)`
diff --git a/src/styles/components/loading-screen.ts b/src/styles/components/loading-screen.ts
deleted file mode 100644
index bd6848b..0000000
--- a/src/styles/components/loading-screen.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import styled from 'styled-components';
-
-export const Wrapper = styled.div`
- display: flex;
- justify-content: center;
- align-items: center;
- height: 100vh;
-`;
-
-export const Text = styled.span`
- font-size: 24px;
-`;
diff --git a/src/styles/loading-spinner.ts b/src/styles/loading-spinner.ts
new file mode 100644
index 0000000..c246a75
--- /dev/null
+++ b/src/styles/loading-spinner.ts
@@ -0,0 +1,22 @@
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+ z-index: 100;
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ svg {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ }
+`;
+
+export default Wrapper;
diff --git a/src/styles/popup.ts b/src/styles/popup.ts
index d663660..6402733 100644
--- a/src/styles/popup.ts
+++ b/src/styles/popup.ts
@@ -27,15 +27,14 @@ export const PopupBox = styled.div`
align-items: center;
justify-content: center;
width: calc(100vw - 100px);
- padding: 30px;
+ padding: 50px 30px 30px 30px;
background-color: ${whiteColor};
border: 3px solid ${blackColor};
`;
export const Popup = styled(PopupBox)`
- max-width: 757px;
- height: calc(100vh - 100px);
- max-height: 728px;
+ max-width: 600px;
+ max-height: calc(100vh - 100px);
border-radius: 10px;
`;
diff --git a/src/styles/post-tweet-form.ts b/src/styles/tweet-form.ts
similarity index 93%
rename from src/styles/post-tweet-form.ts
rename to src/styles/tweet-form.ts
index 2fa8165..a82b072 100644
--- a/src/styles/post-tweet-form.ts
+++ b/src/styles/tweet-form.ts
@@ -9,6 +9,9 @@ export const Form = styled.form`
gap: 10px;
width: 100%;
padding-bottom: 20px;
+`;
+
+export const PostForm = styled(Form)`
&::after {
content: '';
position: absolute;
@@ -21,6 +24,8 @@ export const Form = styled.form`
}
`;
+export const EditForm = styled(Form)``;
+
export const TextArea = styled.textarea`
width: calc(100% - 120px);
padding: 20px;
@@ -108,10 +113,14 @@ export const AttachImageInput = styled.input`
`;
export const SubmitButton = styled.button`
- padding: 10px 0px;
background-color: ${primaryColor};
border: none;
border-radius: 20px;
color: white;
font-size: 16px;
+ line-height: 36px;
+ svg {
+ width: 36px;
+ height: 36px;
+ }
`;
diff --git a/src/styles/tweet.ts b/src/styles/tweet.ts
index 98f4a64..11175aa 100644
--- a/src/styles/tweet.ts
+++ b/src/styles/tweet.ts
@@ -110,5 +110,6 @@ export const EditButton = styled.button`
width: 16px;
height: 16px;
stroke: ${primaryColor};
+ stroke-width: 2;
}
`;