<repo_name>mejdi5/chat-app<file_sep>/src/pages/auth/Auth.jsx
import React, {useState, useEffect} from 'react'
import './Auth.scss'
import { useNavigate } from 'react-router-dom'
import { createUserWithEmailAndPassword, updateProfile, signInWithEmailAndPassword } from 'firebase/auth'
import { firebaseAuth } from '../../firebase'
import { storage, db } from '../../firebase'
import { ref, uploadBytesResumable, getDownloadURL, deleteObject } from "firebase/storage";
import { doc, setDoc } from "firebase/firestore"; 


const Auth = ({setLoading}) => {

    const [auth, setAuth] = useState("login")
    const navigate = useNavigate()
    const [file, setFile] = useState(null)
    const [image, setImage] = useState(null)
    const [percentage, setPercentage] = useState(0)
    const [uploading, setUploading] = useState(null)
    const [error, setError] = useState(null)


    const handleDeleteImage = async () => {
        try {
        //delete image from firebase storage
        const desertRef = ref(storage, image);
        await deleteObject(desertRef)
        } catch (error) {
        console.log(error)
        }
        setImage(null)
    }

    
    const handleRegister = async (e) => {
        e.preventDefault();
        setLoading(true)
        const name = e.target[0].value
        const email = e.target[1].value
        const password = e.target[2].value
        try {
            setLoading(false)
            const res = await createUserWithEmailAndPassword(firebaseAuth, email, password)
            await updateProfile(res.user, {
                displayName: name,
                photoURL: file ? image : ""
            })
            await setDoc(doc(db, "users", res.user.uid), {
                name,
                email,
                image: file ? image : ""
            });
        } catch (err) {
            setLoading(false)
            setError(err.message)
            setTimeout(() => setError(null), 3000)
        }
    }

    useEffect(() => {
        const uploadImage = () => {
        const storageRef = ref(storage, file.name + Date.now());
        const uploadTask = uploadBytesResumable(storageRef, file);
        uploadTask.on('state_changed', 
        (snapshot) => {
            const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
            setPercentage(progress)
            switch (snapshot.state) {
                case 'paused': setUploading('Paused');
                break;
                case 'running': setUploading('Uploading');
                break;
            }
        }, 
        (error) => console.log(error), () => {
        getDownloadURL(uploadTask.snapshot.ref).then(downloadURL => {
            setImage(downloadURL)
            setPercentage(0)
            setUploading(null)
        })})
        }
        file && uploadImage()
    }, [file])


    const handleLogin = async (e) => {
        e.preventDefault();
        setLoading(true)
        const email = e.target[0].value
        const password = e.target[1].value
        try {
            setLoading(false)
            await signInWithEmailAndPassword(firebaseAuth, email, password)
            navigate("/")
        } catch (err) {
            setLoading(false)
            setError(err.message)
            setTimeout(() => setError(null), 3000) 
        }
    }


return (
<div className="auth">
    <div className="wrapper">
        <div className="left">
            {auth === "login"
            ?
            <div className="leftContainer">
                <h1>Chat App</h1>
                <div className="leftWrapper">
                    <span>Haven't account yet ?</span>
                    <button onClick={() => setAuth('register')}>
                        Sign Up
                    </button>
                </div>
            </div>
            :
            <div className="leftContainer">
                <h1>Chat App</h1>
                <div className="leftWrapper">
                    <span>Already signed up ?</span>
                    <button onClick={() => setAuth('login')}>
                        Sign In
                    </button>
                </div>
            </div>
            }
        </div>
        <div className="right">
            {auth === "login"
            ?
            <div className='rightContainer'>
                <div className="formWrapper">
                    <h2>SIGN IN</h2>
                    <form onSubmit={handleLogin}>
                        <input
                        type="email"
                        placeholder="Email.."
                        name="email"
                        required
                        />
                        <input
                        type="password"
                        placeholder="Password.."
                        name="password"
                        required
                        />
                        {error && <span className='error'>{error}</span>}
                        <button>
                            Login
                        </button>
                    </form>
                </div>
            </div>
            :
            <div className='rightContainer'>
                <div className="formWrapper">
                    <h2>SIGN UP</h2>
                    <form onSubmit={handleRegister}>
                        <input
                        type="text"
                        placeholder="Name.."
                        name="name"
                        required
                        />
                        <input
                        type="email"
                        placeholder="Email.."
                        name="email"
                        required
                        />
                        <input
                        type="password"
                        placeholder="Password.."
                        name="password"
                        required
                        />
                        <input
                        className='file'
                        type="file"
                        id="file"
                        onChange={e => setFile(e.target.files[0])}
                        />
                        <label htmlFor='file'>
                            <img src="/image.jpg" alt=""/>
                            <span>Add Image</span>
                        </label>
                        <div className='uploadProgress'>
                            {uploading && <div className='uploading'>{uploading}</div>}
                            {percentage > 0 && !image && 
                            <div className='progress'>
                                <div style={{width: `${percentage}%`}} className='percentage'>{percentage.toFixed(0)}%</div>
                            </div>}
                            {image && <img src={image} alt="" onClick={handleDeleteImage}/>}
                        </div>
                        {error && <span className='error'>{error}</span>}
                        <button>
                            Register
                        </button>
                    </form>
                </div>
            </div>
            }
        </div>
    </div>
</div>
)}

export default Auth<file_sep>/src/components/messages/Messages.jsx
import React, {useContext, useRef , useEffect} from 'react'
import './Messages.scss'
import {format} from 'timeago.js'
import { AuthContext } from '../../context/AuthContext'
import { arrayRemove, doc, updateDoc } from "firebase/firestore";
import { ref, deleteObject } from "firebase/storage";
import { db, storage } from '../../firebase';


const Messages = ({currentChat}) => {

    const user = useContext(AuthContext)
    const scroll = useRef()

    const handleDeleteMessage = async (messageId, messageText, messageDate, messageImage) => {
        try {
            //delete message image from firebase storage
            if(messageImage) {
                const desertRef = ref(storage, messageImage);
                await deleteObject(desertRef)
            }
            //delete message from firestore database
            await updateDoc(doc(db, "chats", currentChat?.chatId), {
                messages: arrayRemove({
                    id: messageId,
                    text: messageText,
                    sender: user,
                    date: messageDate,
                    image: messageImage
                })
            });
        } catch (error) {
            console.log(error)
        }
        
    }

    useEffect(() => {
        ref.current?.scrollIntoView({ behavior: "smooth" });
    }, [currentChat]);
    
return (
<div className='messages'>
    {currentChat?.messages?.map(message=> 
    <div 
    className={message?.sender?.id === user.id ? 'sentMessage' : 'receivedMessage'} 
    key={message?.id} 
    ref={scroll}
    >
        <img
        src={message?.sender?.image || "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAL8AAAEICAMAAAA5jNVNAAAAP1BMVEV1dXXAwMC+vr7CwsJycnK7u7urq6u3t7d2dnahoaGcnJyoqKh5eXm0tLSZmZl9fX2NjY2JiYmDg4OUlJRtbW0crqSPAAAGU0lEQVR4nO2d6XbbIBBGJRi0WtaW93/WApJjx/EmIHzQM/f0tP3T5oIGNKwqCo<KEY>bzB88KFsqiMAAAAASUVORK5CYII="} 
        alt=""
        />
        <div 
        className="content" 
        onClick={() => message?.sender?.id === user.id && handleDeleteMessage(message?.id, message?.text, message?.date, message?.image)}>
            {message?.text && <p>{message?.text}</p>}
            {message?.image && <img src={message.image} alt="" />}
        </div>
        <small>{format(message?.date)}</small>
    </div>
    )}
</div>
)}

export default Messages<file_sep>/src/pages/home/Home.jsx
import React, { useEffect, useState } from 'react'
import { signOut } from 'firebase/auth'
import { firebaseAuth } from '../../firebase'
import './Home.scss'
import Sidebar from '../../components/sidebar/Sidebar'
import Chat from '../../components/chat/Chat'
import { onSnapshot, doc } from "firebase/firestore"
import { db } from '../../firebase'


const Home = () => {

  const [currentChat, setCurrentChat] = useState(null)


  useEffect(() => {
    if (currentChat) {
    const unSub = onSnapshot(doc(db, "chats", currentChat?.chatId), (doc) => {
      doc.exists() && setCurrentChat(doc.data())
    });
  
    return () => {
      unSub();
    };}
  }, [currentChat?.chatId, currentChat?.messages]);


return (
<div className='home'>
  <div className='logout'>
    <button 
    onClick={() => signOut(firebaseAuth)}
    >Logout</button>
  </div>
  <div className="container">
    <div className="wrapper">
      <Sidebar
      currentChat={currentChat}
      setCurrentChat={setCurrentChat}
      />
      {currentChat
      ?
      <Chat currentChat={currentChat}/>
      :
      <div className="startChat">
        <span>Start a new conversation</span>
      </div>
      }
    </div>   
  </div>
</div>
)}

export default Home<file_sep>/src/context/AuthContext.js
import React, {createContext, useEffect, useState} from 'react';
import { onAuthStateChanged } from 'firebase/auth';
import { firebaseAuth } from '../firebase';

export const AuthContext = createContext()

export const AuthContextProvider = ({children}) => {
    const [user, setUser] = useState(null)
    
    useEffect(() => {
    const getAuth = onAuthStateChanged(firebaseAuth, currentUser => {
        if (currentUser) {
        setUser({
            id: currentUser.uid,
            name: currentUser.displayName,
            email: currentUser.email,
            image: currentUser.photoURL
        })
        } else {
        setUser(null)
        }
    })
    return () => {
        getAuth()
    }
    }, [])

return (
    <AuthContext.Provider value={user} >
        {children}
    </AuthContext.Provider>
)}<file_sep>/src/App.jsx
import React, { useContext, useState, useEffect } from "react";
import {BrowserRouter, Routes, Route, Navigate} from 'react-router-dom'
import Home from "./pages/home/Home";
import Auth from './pages/auth/Auth'
import Loading from './pages/loading/Loading'
import {AuthContext} from './context/AuthContext'
import { UserContextProvider } from "./context/UserContext";
import {UserChatContextProvider} from './context/ChatContext'

function App() {

  const user = useContext(AuthContext)
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    setLoading(true)
    setTimeout(() => setLoading(false), 1000)
  }, [])
  

return (
<BrowserRouter>
    <Routes>
      <Route index path="/" element={
        user 
        ? 
        <UserContextProvider>
          <UserChatContextProvider>
            <Home/>
          </UserChatContextProvider>
        </UserContextProvider> 
        : loading
        ? <Loading/>
        : <Navigate to="/auth"/>
        }/>
      <Route path="/auth" element={
        loading
        ? <Loading/>
        : !user 
        ? <Auth setLoading={setLoading}/>
        : <Navigate to="/"/>
        }/>
    </Routes>
</BrowserRouter>
  );
}

export default App;
<file_sep>/src/firebase.js
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAuth } from 'firebase/auth'
import { getStorage } from 'firebase/storage'
import { getFirestore} from 'firebase/firestore'


const firebaseConfig = {
  apiKey: "<KEY>",
  authDomain: "chat-app-91c16.firebaseapp.com",
  projectId: "chat-app-91c16",
  storageBucket: "chat-app-91c16.appspot.com",
  messagingSenderId: "830816280664",
  appId: "1:830816280664:web:2f3ec2f6f42dbbd65cea2d",
  measurementId: "G-4D35S5DB1F"
};

// Initialize Firebase
export const app = initializeApp(firebaseConfig);
export const firebaseAuth = getAuth()
export const storage = getStorage()
export const db = getFirestore()
