diff --git a/index.html b/index.html index 3b98a8df4..365a877b1 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + DummyGram diff --git a/src/App.jsx b/src/App.jsx index 6ae0c8bf0..9e8dd227f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,9 +1,11 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useMemo } from "react"; import Post from "./components/Post"; import { db, auth } from "./lib/firebase"; import { Modal, Button, Input } from "@mui/material"; import { makeStyles } from "@mui/styles"; import ImgUpload from "./components/ImgUpload"; +import Loader from "./components/Loader"; +import AnimatedButton from "./components/AnimatedButton"; function getModalStyle() { const top = 50; @@ -39,6 +41,12 @@ function App() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [user, setUser] = useState(null); + const [signingUp, setSigningUp] = useState(false); + const [logginIn, setLogginIn] = useState(false); + const processingAuth = useMemo( + () => logginIn || signingUp, + [logginIn, signingUp] + ); useEffect(() => { const unsubscribe = auth.onAuthStateChanged((authUser) => { @@ -73,6 +81,7 @@ function App() { const signUp = (e) => { e.preventDefault(); + setSigningUp(true); auth .createUserWithEmailAndPassword(email, password) .then((authUser) => { @@ -80,16 +89,35 @@ function App() { displayName: username, }); }) - .catch((error) => alert(error.message)); - setOpenSignUp(false); + .then(() => { + alert("Signup Successful!"); + setOpenSignUp(false); + }) + .catch((error) => alert(error.message)) + .finally(() => { + setSigningUp(false); + }); }; const signIn = (e) => { e.preventDefault(); + setLogginIn(true); auth .signInWithEmailAndPassword(email, password) - .catch((error) => alert(error.message)); - setOpenSignIn(false); + .then(() => { + alert("Login successful!"); + setOpenSignIn(false); + }) + .catch((error) => alert(error.message)) + .finally(() => { + setLogginIn(false); + }); + }; + + const signOut = () => { + if (confirm("Are you sure you want to logout?")) { + auth.signOut().finally(); + } }; return ( @@ -100,10 +128,11 @@ function App() { alt="instagram" className="app__header__img" /> - - {user ? ( + {processingAuth ? ( + + ) : user ? ( + @@ -193,14 +223,15 @@ function App() { value={password} onChange={(e) => setPassword(e.target.value)} /> - + diff --git a/src/components/AnimatedButton.jsx b/src/components/AnimatedButton.jsx new file mode 100644 index 000000000..330e3825d --- /dev/null +++ b/src/components/AnimatedButton.jsx @@ -0,0 +1,11 @@ +import { Button } from "@mui/material"; +import Loader from "./Loader"; + +export default function AnimatedButton(props) { + const { loading, children, ..._props } = props; + return ( + + ); +} diff --git a/src/components/ImgUpload.jsx b/src/components/ImgUpload.jsx index 4bc79555e..98210d40e 100644 --- a/src/components/ImgUpload.jsx +++ b/src/components/ImgUpload.jsx @@ -1,12 +1,13 @@ import React, { useState } from "react"; import { db, storage } from "../lib/firebase"; import firebase from "firebase/compat/app"; -import Button from "@mui/material/Button"; +import AnimatedButton from "./AnimatedButton"; function ImgUpload(props) { const [image, setImage] = useState(null); const [caption, setCaption] = useState(""); const [progress, setProgress] = useState(0); + const [uploadingPost, setUploadingPost] = useState(false); const handleChange = (e) => { if (e.target.files[0]) { @@ -14,6 +15,7 @@ function ImgUpload(props) { } }; const handleUpload = () => { + setUploadingPost(true); const uploadTask = storage.ref(`images/${image.name}`).put(image); uploadTask.on( "state_changed", @@ -27,8 +29,10 @@ function ImgUpload(props) { // error function ... console.log(error); alert(error.message); + setUploadingPost(false); }, () => { + setUploadingPost(false); // complete function ... storage .ref("images") @@ -62,7 +66,9 @@ function ImgUpload(props) { value={caption} /> - + + Upload + ); } diff --git a/src/components/Loader.css b/src/components/Loader.css new file mode 100644 index 000000000..7257285af --- /dev/null +++ b/src/components/Loader.css @@ -0,0 +1,93 @@ +/** + * Credits: https://codepen.io/feirer/pen/zovgae + * Patrick Feirer + * +*/ + +body { + background-color: #fbfbfb; +} + +.loader { + box-sizing: border-box; + display: flex; + position: relative; + justify-content: center; + perspective-origin: 60px 60px; + transform-origin: 60px 60px; + border: 0px solid #000000; + flex: 0 0 auto; + flex-flow: column nowrap; +} + +.loader:after, +.loader:before { + height: 100%; + width: 100%; + border-radius: 50%; + border-style: solid; + border-width: 2px; + box-sizing: border-box; + content: ""; + left: 0; + position: absolute; + top: 0; +} + +.loader:after { + -webkit-animation: rotate 1s infinite ease; + animation: rotate 1s infinite ease; + border-color: #3897f0 transparent transparent; + -webkit-transform-origin: 50%; + transform-origin: 50%; +} + +.loader:before { + border-color: #c7c7c7; +} + +.loader a { + display: block; + font-size: 14px; + margin: -60px 0; + padding: 60px 9px; + position: relative; + text-align: center; + vertical-align: middle; + z-index: 1; + color: #c7c7c7; + text-decoration: none; + font: normal normal 600 normal 14px / 14px proxima-nova, "Helvetica Neue", + Arial, Helvetica, sans-serif; +} + +@-webkit-keyframes rotate { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes rotate { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +.note { + color: #c7c7c7; + text-decoration: none; + font: normal normal 600 normal 12px / 12px proxima-nova, "Helvetica Neue", + Arial, Helvetica, sans-serif; + text-align: center; + width: 100%; +} diff --git a/src/components/Loader.jsx b/src/components/Loader.jsx new file mode 100644 index 000000000..d468d4c16 --- /dev/null +++ b/src/components/Loader.jsx @@ -0,0 +1,36 @@ +import "./Loader.css"; + +/** + * + * @param {{ + * width: number, + * height:number + * }} props + */ +export default function Loader(props) { + const { width, height } = Object.assign( + { + width: 30, + height: 30, + }, + props + ); + + return ( +
+
+
+ ); +}