본문 바로가기

트위터(React, TypeScript, Firebase, Vite)

19. TWEETING - Deleting tweets

1. 기존의 데이터를 모두 삭제한다.

 

 

 

 

 

2. 코드를 수정한다.

tweet.tsx

import styled from "styled-components";
import { ITweet } from "./timeline";
import { auth, db, storeage } from "../routes/firebase";
import { deleteDoc, doc } from "firebase/firestore";
import { deleteObject, ref } from "firebase/storage";

const Wrapper = styled.div`
   display: grid;
   grid-template-columns: 3fr 1fr;
   padding: 20px;
   border: 1px solid rgba(255, 255, 255, 0.5);
   border-radius: 15px;
 `;

const Column = styled.div``;
 
const Photo = styled.img`
  width: 100px;
  height: 100px;
  border-radius: 15px;
`;

const Username = styled.span`
  font-weight: 600;
  font-size: 15px;
`;

const Payload = styled.p`
  margin: 10px 0px;
  font-size: 18px;
`;

const DeleteButton = styled.button`
  background-color: tomato;
  color: white;
  font-weight: 600;
  border: 0;
  font-size: 12px;
  padding: 5px 10px;
  text-transform: uppercase;
  border-radius: 5px;
  cursor: pointer;
`;

export default function Tweet({username, photo, tweet, userId, id} : ITweet){
    const user = auth.currentUser;
    const onDelete = async() => {
      const ok = confirm("Are you sure you want to delete this tweet?");
      if(!ok || user?.uid !== userId){
        return;
      };

      try {
        await deleteDoc(doc(db, "tweets", id));
        if(photo){
          const photoRef = ref(storeage, `tweets/${user.uid}/${id}`); 
                                          //생성할때의 경로와 같다!!!
          await deleteObject(photoRef);
        };
      } catch (e) {
        
      }finally{

      }

    };
    return <Wrapper>

        <Column>
            <Username>{username}</Username>
            <Payload>{tweet}</Payload>
            {user?.uid === userId ? <DeleteButton onClick={onDelete}>Delete</DeleteButton> : null}
            
        </Column>  

        {photo ? (
        <Column>
            <Photo src = {photo}/>
        </Column>  
        ) : null}

    </Wrapper>
};

 

post-tweet-form.tsx

import { addDoc, collection, updateDoc } from "firebase/firestore";
import { useState } from "react";
import styled from "styled-components"
import { auth, db, storeage } from "../routes/firebase";
import { getDownloadURL, ref, uploadBytes } from "firebase/storage";

const Form = styled.form`
    display: flex;
    flex-direction: column;
    gap: 10px;
`;

const TextArea = styled.textarea`
   border: 2px solid white;
   padding: 20px;
   border-radius: 20px;
   font-size: 16px;
   color: white;
   background-color: black;
   width: 100%;
   resize: none;
   font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
     Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
   &::placeholder {
     font-size: 16px;
   }
   &:focus {
     outline: none;
     border-color: #1d9bf0;
   }
 `;

const AttachFileButton = styled.label`
   padding: 10px 0px;
   color: #1d9bf0;
   text-align: center;
   border-radius: 20px;
   border: 1px solid #1d9bf0;
   font-size: 14px;
   font-weight: 600;
   cursor: pointer;
 `;
 
 const AttachFileInput = styled.input`
   display: none;
 `;
 
 const SubmitBtn = styled.input`
   background-color: #1d9bf0;
   color: white;
   border: none;
   padding: 10px 0px;
   border-radius: 20px;
   font-size: 16px;
   cursor: pointer;
   &:hover,
   &:active {
     opacity: 0.9;
   }
 `;

export default function PostTweetForm(){
    const [isLoading, setLoading] = useState(false);
    const [tweet, setTweet] = useState("");
    const [file, setFile] = useState<File|null>(null);
    const onChange = (e : React.ChangeEvent<HTMLTextAreaElement>) => {
        setTweet(e.target.value);
    };
    const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        const {files} = e.target;
        if(files && files.length === 1){
            setFile(files[0]);
        }
    };

    const onSubmit = async (e:React.FormEvent<HTMLFormElement>) => {
      e.preventDefault();
      const user = auth.currentUser;
      if(!user || tweet === "" || tweet.length > 180){
        return;
      }
        
      try {
        setLoading(true);
        const doc = await addDoc(collection(db, "tweets"), {
          tweet,
          createAt : Date.now(),
          username : user.displayName || "Anonymous",
          userId : user.uid
        });
        if(file){
          const locationRef = ref(storeage, `tweets/${user.uid}/${doc.id}`);
          const result = await uploadBytes(locationRef, file);
          const url = await getDownloadURL(result.ref);
          await updateDoc(doc, {photo : url});
        }
        setTweet("");
        setFile(null);
        
      } catch (e) {
        console.log(e);
      }finally{
        setLoading(false);
      }

    };

    return <Form onSubmit={onSubmit}>
        <TextArea onChange={onChange} value={tweet} placeholder="What is happening?"/>
        <AttachFileButton htmlFor="file">{file ? "Photo added ✅" : "Add photo"}</AttachFileButton>
        <AttachFileInput onChange={onFileChange} type="file" id="file" accept="image/*"/>
        <SubmitBtn type="submit" value={isLoading ? "Posting..." : "Post Tweet"}/>
    </Form>
};

 

 

 

 

 

3. 테스트를 해본다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

nwitter-reloaded.z01
19.53MB
nwitter-reloaded.z02
19.53MB
nwitter-reloaded.z03
19.53MB
nwitter-reloaded.z04
19.53MB
nwitter-reloaded.z05
19.53MB
nwitter-reloaded.zip
15.59MB

 

'트위터(React, TypeScript, Firebase, Vite)' 카테고리의 다른 글

21. User's Timeline  (0) 2025.04.22
20. USER PROFILE - User Avartar  (0) 2025.04.21
18. TWEETING - Realtime [2]  (0) 2025.04.17
17. TWEETING - Realtime [1]  (0) 2025.04.17
16. TWEETING - Fetching Timeline  (0) 2025.04.17