Driving force
As a starter in web development, many times I find it hard to get the information I want surfing the net. It is not because of a lack of write-ups about the subject matter, but having read a lot of bulky write-ups, I seemed not to get what I wanted. That can be frustrating, 'gash'. So, I decided to build a blog site based on the AltSchool exam project, where developers can contribute - in the simplest form. And I also made the web app an open-source project for further improvement on the app.
Introduction
Code miller is a web application that allows a user to write a blog and save it on a firebase database. These saved blogs can be accessed by any user who successfully logged into the application. In this blog, I will show you how I built the web application from start to finish.
Overview of the application
Starting the app, the first page to be displayed is the home page with the navbar which consists of the links for the main pages across the web application, and also the sidebar which helps to display the titles of the existing blogs fetched from the firebase. The both navbar and the sidebar cut across the application. Other pages of the web application include a sign-up page that allows users to sign in to the application and a blog page that helps display the blogs available for the user with pagination implemented.
React part of the application
What is React?
The React.js framework is an open-source JavaScript framework and library developed by Facebook. It’s used for building interactive user interfaces and web applications quickly and efficiently with significantly less code than you would with vanilla JavaScript.
In React, you develop your applications by creating reusable components that you can think of as independent Lego blocks. These components are individual pieces of a final interface, which, when assembled, form the application’s entire user interface.
Getting started
To get started using React, you must have Node installed on your local development as a tool. You can click here to get Node installed on your local development.
How to create a React application?
You can create a React application using the Command Line Interface (CLI), by following this command:
npx create-react-app [app name]
Running the above command will create a React app with the preferred app name you used.
After that, you can move into the file you just created using this command:
cd [app name]
npm start or npm run start
After starting up the app, a default React app is displayed on the browser and you can start cleaning the app by deleting the unwanted files in your local development and start creating your own files.
This is the overview of the default app created:
To achieve the aim of building the app, some React libraries were installed such as:
React Error Boundary
React Router
React Helmet Async
All of the above libraries can be installed using this command:
npm install react-router-dom react-helmet-async react-error-boundary @mui/material @emotion/react @emotion/styled @mui/icons-material
Firebase part of the application
What is Firebase?
Firebase is a Google-backed application development software that enables developers to develop iOS, Android, and Web applications.
Used for in the application:
The first thing I utilized firebase for is to store the contribution blogs (data) made by the users of the application. From which those data can be later retrieved by any authenticated user.
It was also used for the authentication of a user so that the user experience can be personal. It is also responsible for handling whether a user would have access to the available blogs or not.
Getting started
Getting started using firebase requires a few steps. And to start using it, you can check this link. Sign up on the web and check its documentation.
How to install firebase on CLI
Just run this command:
npm install -g firebase-tools
For details, you can check this link. Though following the link under 'Getting started' will take you through the process of signing up, adding a project, and installing firebase.
How to add Firebase Database and Authentication
For adding the firebase database you can watch this video:
For adding the authentication you can watch this video:
Important Codes responsible for the application
This is what the index.js file looks like:
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter as Router } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>
);
For the App.js file, I have this code:
import React, { useState, useEffect, createContext } from "react";
import "./App.css";
import { Navbar } from "./components/LayoutComponent";
import { Sidebar } from "./components/LayoutComponent";
import AppRouter from "./components/routes/Index";
import { ErrorBoundary, useErrorHandler } from "react-error-boundary";
import { HelmetProvider } from "react-helmet-async";
import {
ArrowUpward,
ArrowCircleLeft,
ArrowCircleRight,
} from "@mui/icons-material";
import {
auth,
provider,
signInWithPopup,
getRedirectResult,
onAuthStateChanged,
db,
} from "./firebase";
// create context
export const DbContext = createContext();
export const UserContext = createContext();
// ErrorBoundary
const ErrorBoundaryComponent = ({ error }) => {
return (
<div role="alert" className="errorBoundary">
<h1 className="errorBoundary-title">Something went wrong!</h1>
<p className="errorBoundary-message">{error.message}</p>
</div>
);
};
function App() {
const handleError = useErrorHandler();
// ErrorBoundary useEffect
useEffect(() => {
handleError();
}, [handleError]);
//Set state for the darkmode
const [darkMode, setDarkMode] = useState(false);
const [user, setUser] = useState(null);
const [sidebar, setSidebar] = useState(false);
// Handle sidebar
const handleSidebar = () => {
setSidebar((prev) => !prev);
};
//Handle the darkmode
const handleDarkMode = (e) => {
e.preventDefault();
setDarkMode((prev) => !prev);
};
//Styles
const darkModeStyle = {
light: {
foreground: "#000000",
background: "#ffffff",
},
dark: {
foreground: "#737737",
background: "#000000",
},
};
// Auth
const handleAuth = () => {
signInWithPopup(auth, provider)
.then((result) => {})
.catch((error) => {
const errorMessage = error.message;
console.log(errorMessage);
});
};
useEffect(() => {
getRedirectResult(auth)
.then((result) => {})
.catch((error) => {
// Handle Errors here.
const errorMessage = error.message;
console.log(errorMessage);
});
}, []);
useEffect(() => {
//Get signedIn user
onAuthStateChanged(auth, (user) => {
if (user) {
const { displayName, photoURL, email } = user;
setUser({ displayName, photoURL, email });
} else {
console.log("user signed out");
setUser(null);
}
});
}, []);
return (
<DbContext.Provider value={db}>
<div
className="App"
style={
darkMode
? {
backgroundColor: darkModeStyle.dark.background,
color: darkModeStyle.dark.foreground,
}
: {
backgroundColor: darkModeStyle.light.background,
color: darkModeStyle.light.foreground,
}
}
>
<div id="topRegion-locator"></div>
<div id="circleArrow">
{sidebar ? (
<ArrowCircleRight className="arrows" onClick={handleSidebar} />
) : (
<ArrowCircleLeft className="arrows" onClick={handleSidebar} />
)}
</div>
<ErrorBoundary FallbackComponent={ErrorBoundaryComponent}>
<HelmetProvider>
<UserContext.Provider value={user}>
<Navbar toggle={handleDarkMode} darkMode={darkMode} />
<div className="sidebarAppDiv">
<Sidebar sidebar={sidebar} />
<a href="#topRegion-locator" id="topRegion">
<ArrowUpward />
</a>
<AppRouter handleAuth={handleAuth} />
</div>
</UserContext.Provider>
</HelmetProvider>
</ErrorBoundary>
</div>
</DbContext.Provider>
);
}
export default App;
This is the code responsible for the firebase integration:
import { initializeApp } from "firebase/app";
import { getFirestore } from "@firebase/firestore";
import {
getAuth,
signInWithPopup,
GoogleAuthProvider,
getRedirectResult,
signOut,
createUserWithEmailAndPassword,
onAuthStateChanged,
} from "firebase/auth";
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "AIzaSyDfHMkuB4P4KoccHyM_6E1kHUYVVTKFAO4",
authDomain: "miller16.firebaseapp.com",
projectId: "miller16",
storageBucket: "miller16.appspot.com",
messagingSenderId: "914222060299",
appId: "1:914222060299:web:9d8beeec83e30659ce080e"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);
const provider = new GoogleAuthProvider();
const auth = getAuth();
export {
provider,
auth,
signInWithPopup,
getRedirectResult,
onAuthStateChanged,
signOut,
createUserWithEmailAndPassword,
};
This code is responsible for both the Navbar and the Sidebar which cut across the whole web application:
import React, { useState, useContext, useEffect } from "react";
import { NavLink, Link } from "react-router-dom";
import { UserContext } from "../App";
import "./LayoutComponent.css";
import appLogo from "../images/logoImg.png";
import { collection, getDocs } from "@firebase/firestore";
import { DbContext } from "../App";
import {
DarkMode,
LightMode,
HomeOutlined,
Login,
MenuOutlined,
CancelOutlined,
ExpandCircleDownSharp,
ExpandLessRounded,
} from "@mui/icons-material";
import { auth } from "../firebase";
import CodingSchools from "./pages/courses/CodingSchools";
export const Navbar = (props) => {
const user = useContext(UserContext);
// States
const [expandCourses, setExpandCourses] = useState(false);
const [userDisplay, setUserDisplay] = useState(false);
const [menu, setMenu] = useState(false);
//Handle the dropdown menu
const handleCoursesExpand = (e) => {
e.preventDefault();
setExpandCourses(true);
};
const handleCoursesClose = (e) => {
e.preventDefault();
setExpandCourses(false);
};
const handleMenu = () => {
setMenu((prev) => !prev);
};
//Handle signout
const handleSignout = (e) => {
e.preventDefault();
auth.signOut();
};
const handleUser = () => {
setUserDisplay((prev) => !prev);
};
const handleUserHid = () => {
setUserDisplay(false);
};
const CustomNavbarLink = ({ to, ...props }) => {
let activeStyle = {
color: "rgb(167, 52, 52)",
transition: "all 0.3s",
};
return (
<NavLink
style={({ isActive }) =>
isActive ? activeStyle : { textDecoration: "none" }
}
to={to}
end
{...props}
/>
);
};
//Styles
const style = {
display: expandCourses ? "block" : "none",
};
return (
<nav className="layoutNavbar">
<ul>
<li>
<Link to="/">
<img src={appLogo} alt="logo" className="logo" />
</Link>
<h1 className="logoText">Codemiller</h1>
<CustomNavbarLink to="/" className="layoutNavbar-links">
<HomeOutlined id="home" />
</CustomNavbarLink>
<CustomNavbarLink to="/blog" className="layoutNavbar-links" id="blog">
<h1>Blogs</h1>
</CustomNavbarLink>
<div onClick={props.toggle}>
{props.darkMode ? (
<LightMode className="layoutNavbar-links" id="lightMode" />
) : (
<DarkMode className="layoutNavbar-links" id="darkMode" />
)}
</div>
<div className="coursesDiv" onMouseLeave={handleCoursesClose}>
<div className="expand" onMouseOver={handleCoursesExpand}>
<h1 to="/courses" id="courses">
Courses
</h1>
</div>
<div className="coursesDisplayDiv">
{user ? (
<div id="coursesContents" style={style}>
<CodingSchools />
</div>
) : (
<div
style={style}
className="layoutNavbar-links"
id="loginFirst"
>
Login to view courses!
</div>
)}
</div>
</div>
<div>
{user ? (
<img
src={user.photoURL}
alt="img"
className="layoutNavbar-links"
id="userImg"
onMouseOver={handleUser}
/>
) : (
<CustomNavbarLink
to="/sign"
className="layoutNavbar-links"
id="login1"
>
<Login id="login" />
</CustomNavbarLink>
)}
</div>
<div
className="layoutNavbar-user_div"
style={{ display: userDisplay ? "block" : "none" }}
onMouseLeave={handleUserHid}
>
<p>Signed in as:</p>
<p>{user?.email}</p>
<h2 onClick={handleSignout}>Sign out</h2>
</div>
{menu ? (
<CancelOutlined className="menuIcon" onClick={handleMenu} />
) : (
<MenuOutlined className="menuIcon" onClick={handleMenu} />
)}
</li>
<div
className="s-navbar"
style={{ display: menu ? "block" : "none" }}
onMouseLeave={handleMenu}
>
<div className="s-navbarFlex">
<CustomNavbarLink
to="/blog"
className="s-layoutNavbar-links"
id="s-blog"
>
<h1>Blogs</h1>
</CustomNavbarLink>
<div onClick={props.toggle}>
{props.darkMode ? (
<LightMode className="s-layoutNavbar-links" id="s-lightMode" />
) : (
<DarkMode className="s-layoutNavbar-links" id="s-darkMode" />
)}
</div>
<div className="s-coursesDiv">
<div
className="s-expand"
onMouseOver={handleCoursesExpand}
onClick={handleCoursesClose}
>
<h1 to="/courses" id="s-courses">
Courses
</h1>
</div>
<div
className="s-coursesDisplayDiv"
onMouseLeave={handleCoursesClose}
>
{user ? (
<div id="s-coursesContents" style={style}>
<CodingSchools />
</div>
) : (
<div
style={style}
className="s-layoutNavbar-links"
id="s-loginFirst"
>
Login to view courses!
</div>
)}
</div>
</div>
<div>
{user ? (
<img
src={user.photoURL}
alt="img"
className="s-layoutNavbar-links"
id="s-userImg"
onMouseOver={handleUser}
/>
) : (
<CustomNavbarLink to="/sign" className="s-layoutNavbar-links">
<Login id="s-login" />
</CustomNavbarLink>
)}
</div>
<div
className="s-layoutNavbar-user_div"
style={{ display: userDisplay ? "block" : "none" }}
onMouseLeave={handleUserHid}
>
<p>Signed in as:</p>
<p>{user?.email}</p>
<h2 onClick={handleSignout}>Sign out</h2>
</div>
</div>
</div>
</ul>
</nav>
);
};
export const Sidebar = ({ sidebar }) => {
// useContext
const db = useContext(DbContext);
const dbRef = collection(db, "contribution");
const user = useContext(UserContext);
// States
const [openSource, setOpenSource] = useState(false);
const [AllContributions, setAllContributions] = useState([]);
//Handle open-source
const handleOpenSource = () => {
setOpenSource((prev) => !prev);
};
async function getAllContributions() {
const contributions = await getDocs(dbRef);
setAllContributions(
contributions.docs.map((doc) => ({ ...doc.data(), id: doc.id }))
);
}
// useEffect
useEffect(() => {
getAllContributions();
});
const blogs = AllContributions.map((blog) => {
return (
<div key={blog.id} className="sidebarBlog-title_lists">
<h2>{blog.contributionTitle}</h2>
</div>
);
});
return (
<div
className="sidebarWrapper"
style={{
marginLeft: sidebar ? "-350px" : "0",
transition: "all 0.3s",
position: sidebar ? "fixed" : "",
}}
>
<h2 className="sidebarTitle">Blogs:</h2>
<div className="sidebarHid">
<h4 className="sidebarBlog-title_list">{blogs}</h4>
</div>
<div className="sidebarBlog-title_div"></div>
<div className="sidebarFooterDiv">
<div onClick={handleOpenSource}>
<h2 className="sidebarFooter-heading">Open source</h2>
<div className="sidebarFooter">
<div>
{openSource ? (
<ExpandLessRounded className="sidebarFooterExpand" />
) : (
<ExpandCircleDownSharp className="sidebarFooterExpand" />
)}
</div>
</div>
</div>
<a
href="https://github.com/michado2019/altschool-second-semester-exam"
className="sidebarFooterBtn"
style={{
visibility: openSource ? "visible" : "hidden",
opacity: openSource ? "1" : "0",
transition: "all 0.5s",
}}
>
Github Link
</a>
</div>
<div className="appUser">
{user ? (
<h2 className="sidebarFooterUser">User: {user?.displayName}</h2>
) : (
""
)}
</div>
</div>
);
};
This is the code that controls the routing of the pages of the application:
import React, { lazy, Suspense, useState } from "react";
import { Routes, Route } from "react-router-dom";
const Loading = () => {
return <div>Loading....</div>;
};
export default function AppRouter({ handleAuth }) {
const Home = lazy(() => import("../pages/home/Home"));
const Contribute = lazy(() => import("../pages/contribute/Contribute"));
const Sign = lazy(() => import("../pages/sign/Sign"));
const CodingSchools = lazy(() => import("../pages/courses/CodingSchools"));
const ErrorPage = lazy(() => import("../pages/errorPage/ErrorPage"));
const SignIn = lazy(() => import("../pages/signIn/SignIn"));
const Blog = lazy(() => import("../pages/blog/Blog"));
// State
const [contribute, setContribute] = useState("");
return (
<div className="appRouter">
<Suspense fallback={Loading}>
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/contribute"
element={
<Contribute
contribute={contribute}
setContribute={setContribute}
handleAuth={handleAuth}
/>
}
/>
<Route path="/blog" element={<Blog />} />
<Route path="/sign" element={<Sign handleAuth={handleAuth} />} />
<Route path="/signIn" element={<SignIn />} />
<Route path="/courses" element={<CodingSchools />} />
<Route path="*" element={<ErrorPage />} />
</Routes>
</Suspense>
</div>
);
}
This is the code for the sign page:
import React, { useEffect, useContext } from "react";
import "./Sign.css";
import { Helmet } from "react-helmet-async";
import { Link } from "react-router-dom";
import useForm from "../../hooks/useForm";
import { useNavigate } from "react-router-dom";
import googleImg from "../sign/assets/googleImg.png";
import { UserContext } from "../../../App";
export default function Sign({ handleAuth }) {
// usecontext
const userData = useContext(UserContext);
//Navigation
const navigate = useNavigate();
//useForm data
const { inputs, handleChange } = useForm({
firstName: "",
lastName: "",
email: "",
password: "",
});
// Handlers
const handleSubmit = (e) => {
e.preventDefault();
};
// useEffect
useEffect(() => {
if (userData) {
navigate("/");
}
}, [navigate, userData]);
return (
<div className="signWrapper">
<Helmet>
<title>Sign in page</title>
<meta name="description" content="Sign in here" />
<link rel="canonical" href="/sign" />
</Helmet>
<div>
<form className="signForm" onSubmit={handleSubmit}>
<label htmlFor="first_name" className="signForm-labels">
First name
</label>
<input
type="text"
placeholder="Enter your first name"
className="signInput"
id="first_name"
name="firstName"
value={inputs.firstName}
onChange={handleChange}
/>
<label htmlFor="Last_name" className="signForm-labels">
Last name
</label>
<input
type="text"
placeholder="Enter your last name"
className="signInput"
id="last_name"
name="lastName"
value={inputs.lastName}
onChange={handleChange}
/>
<label htmlFor="email" className="signForm-labels">
Email
</label>
<input
type="text"
placeholder="yourname@gmail.com"
className="signInput"
id="email"
name="email"
value={inputs.email}
onChange={handleChange}
required
/>
<label htmlFor="email" className="signForm-labels">
Password
</label>
<input
type="password"
placeholder="Choose a password"
className="signInput"
id="password"
name="password"
value={inputs.password}
onChange={handleChange}
required
/>
<button className="signInput" id="btn__submit">
Sign up
</button>
</form>
<div className="signGoogle">
<p>Sign in here:</p>
<Link className="signLink">Sign in</Link>
<img
src={googleImg}
alt="img"
className="signGoogleImg"
onClick={handleAuth}
/>
</div>
</div>
</div>
);
}
This is the code for the homepage:
import React, { useState } from "react";
import "./Home.css";
import { ExpandMore, ExpandLess } from "@mui/icons-material";
import { Link } from "react-router-dom";
import { Helmet } from "react-helmet-async";
import AppGuide from "../../AppGuide";
export default function Home() {
// State
const [expandFAQs, setExpandFAQs] = useState(false);
const [guide, setGuide] = useState(false);
//Handle the dropdown menu
const handleFAQsExpand = (e) => {
e.preventDefault();
setExpandFAQs((prev) => !prev);
};
// Handle guide display
const handleGuide = () => {
setGuide((prev) => !prev);
};
//Styles
const faqsDisplayStyles = {
display: expandFAQs ? "block" : "none",
};
return (
<div className="homeWrapper">
<Helmet>
<title>Homepage</title>
<meta name="description" content="Welcome to my Homepage" />
<link rel="canonical" href="/" />
</Helmet>
<div className="faqContactDiv">
<div className="homeAbout-div">
<div className="homeAbout">
<h1 className="About-title">About</h1>
<p className="About-details">
<span className="codeMiller-span">Code miller</span> is an app
that permits coders break codes complexity, such that starters
would understand. And share via our{" "}
<Link to="contribute" className="contributeLink">
'contribute platform'
</Link>
.
</p>
</div>
<div>
<img src="aboutImg.jpg" alt="aboutImg" className="homeAbout-img" />
</div>
</div>
<AppGuide guide={guide} />
<div className="homeBigBtnsDiv">
<div className="homeBigBtns">
<Link to="contribute" className="contributeLink">
<button className="homeBigBtn">Get Started</button>
</Link>
<button
className="homeBigBtn"
id="homeBigBtn"
onClick={handleGuide}
>
Guide
</button>
</div>
</div>
<div className="homeFAQs">
<h1 className="homeFAQs-gen_title">FAQs</h1>
<div className="homeFAQs-main_flex">
<div className="homeFAQs-flex">
<div className="homeFAQs-div">
<h4 className="homeFAQs-heading" onClick={handleFAQsExpand}>
HTML freguently asked questions
</h4>
<div className="homeFAQs-expands">
{expandFAQs ? <ExpandLess /> : <ExpandMore />}
</div>
</div>
<p className="homeFAQs-click" style={faqsDisplayStyles}>
<a
href="https://www.w3.org/html/wiki/FAQs"
target="_blank"
rel="noreferrer"
>
Click me
</a>
</p>
<div className="homeFAQs-div">
<h4 className="homeFAQs-heading" onClick={handleFAQsExpand}>
CSS freguently asked questions
</h4>
<div className="homeFAQs-expands">
{expandFAQs ? <ExpandLess /> : <ExpandMore />}
</div>
</div>
<p className="homeFAQs-click" style={faqsDisplayStyles}>
<a
href="https://www.hwg.org/resources/faqs/cssFAQ.html"
target="_blank"
rel="noreferrer"
>
Click me
</a>
</p>
<div className="homeFAQs-div">
<h4 className="homeFAQs-heading" onClick={handleFAQsExpand}>
JAVASCRIPT freguently asked questions
</h4>
<div className="homeFAQs-expands">
{expandFAQs ? <ExpandLess /> : <ExpandMore />}
</div>
</div>
<p className="homeFAQs-click" style={faqsDisplayStyles}>
<a
href="https://www.johnsmiley.com/jscript/faqs.htm"
target="_blank"
rel="noreferrer"
>
Click me
</a>
</p>
</div>
<img src="qs.jpg" alt="qsImg" className="qsImg" />
<img src="faqs.jpg" alt="faqImg" className="homeFAQs-img" />
</div>
</div>
<div className="homeContacts">
<h1 className="homeContacts-gen_title">Contact Us</h1>
<div className="homeContacts-flex">
<div className="homeContact-newsletter">
<h3 className="homeContact-newsletter-title">
Subscribe for our newsletter:
</h3>
<form className="subForm">
<input
type="email"
placeholder="Enter your email"
className="homeContact-sub"
/>
<button className="homeContact-sub_btn">Subscribe</button>
</form>
<form className="textarea">
<textarea
placeholder="Enter your comment"
className=" homeContact-msg"
cols="20"
rows="5"
/>
<button className="homeContact-comment_btn">Send</button>
</form>
<img
src="contact2.webp"
alt="contactImg"
className="homeContacts-img2"
/>
</div>
</div>
</div>
</div>
</div>
);
}
The contribute page- the page that controls where a contributor can write his contribution. This is its code below:
import React, { useState, useContext, useEffect } from "react";
import AppGuide from "../../AppGuide";
import "./Contribute.css";
import { Link } from "react-router-dom";
import { addDoc, collection } from "@firebase/firestore";
import { DbContext } from "../../../App";
import { auth, signOut } from "../../../firebase";
import { UserContext } from "../../../App";
function Contribute({ contribute, setContribute, handleAuth }) {
//useContext
const db = useContext(DbContext);
const dbRef = collection(db, "contribution");
const user = useContext(UserContext);
// State
const [guide, setGuide] = useState(false);
const [showForm, setShowForm] = useState(false);
const [form, setForm] = useState({
contributionTitle: "",
contributionText: "",
contributor: "",
email: "",
twitter: "",
});
// Handlers
const handleGuide = () => {
setGuide((prev) => !prev);
};
const handleFormShow = () => {
setShowForm((prev) => !prev);
};
const handleChange = (e) => {
e.preventDefault();
const { name, value } = e.target;
setForm({
...form,
[name]: value,
});
};
const handleContributionSubmit = async (e) => {
e.preventDefault();
if (contribute !== "") {
await addDoc(dbRef, contribute);
alert("Your contribution successfully saved! Thanks");
}
};
const handleSignOut = () => {
signOut(auth)
.then(() => {
// Sign-out successful.
})
.catch((error) => {
// An error happened.
});
};
const handleFormReset = () => {
setForm({
contributionTitle: "",
contributionText: "",
contributor: "",
email: "",
twitter: "",
});
};
// useEffect
useEffect(() => {
if (form.twitter.length > 7) setContribute(form);
}, [form, setContribute]);
return (
<div className="contributeWrapper">
<div className="contributeFlex-1">
<div className="contributeGuide-div">
<h2 className="contributeGuide-heading">
Click the 'Guide' button before contributing
</h2>
<div className="contributeGuideFlex">
<button
className="homeBigBtn"
id="homeBigBtn"
onClick={handleGuide}
>
Guide
</button>
{user ? (
<div className="contributeFlex-2">
<button
className="contributeGoogleBtn"
id="contributeGoogleBtn"
onClick={handleSignOut}
>
Sign out
</button>
</div>
) : (
<div className="contributeFlex-2">
<button className="contributeGoogleBtn" onClick={handleAuth}>
Sign with Google
</button>
</div>
)}
</div>
</div>
<div
style={{ display: guide ? "block" : "none" }}
className="contributeGuide"
>
<AppGuide guide={guide} />
</div>
</div>
{user ? (
<h2 className="contributeAuthorizedUser">
Hi, <span className="displayName">{user.displayName}</span>. You are
now authorized to contribute!
</h2>
) : (
<h2 className="contributeAuthorizedUser">
Sign in with google to be able to make your contribution
</h2>
)}
<div className="contributeGuideDiv">
<div className="contributeFlex-3">
<button
onClick={handleFormShow}
style={{ display: user ? "block" : "none" }}
>
Contribute
</button>
<Link to="/">
<button>Home</button>
</Link>
<button
onClick={handleFormReset}
style={{ display: user ? "block" : "none" }}
>
Reset form
</button>
</div>
<div
className="contributeFormDiv"
style={{ display: showForm ? "none" : "block" }}
>
<form
className="contributeForm"
onSubmit={handleContributionSubmit}
style={{ display: user ? "block" : "none" }}
>
<label className="contributeLabel">Title:</label>
<input
type="text"
placeholder="Contribution title"
className="contributeFormInput"
onChange={handleChange}
name="contributionTitle"
value={form.contributionTitle}
required
/>
<label className="contributeLabel">Contribution:</label>
<textarea
className="contributeTextarea"
placeholder="Contribute here...."
onChange={handleChange}
name="contributionText"
value={form.contributionText}
required
/>
<label className="contributeLabel">Email:</label>
<input
type="email"
placeholder="Enter your email"
className="contributeFormInput"
onChange={handleChange}
name="email"
value={form.email}
required
/>
<label className="contributeLabel">Contributor's name:</label>
<input
type="text"
placeholder="Enter contributor's name"
className="contributeFormInput"
onChange={handleChange}
name="contributor"
value={form.contributor}
required
/>
<label className="contributeLabel">Twitter username:</label>
<input
type="text"
placeholder="Enter twitter username"
className="contributeFormInput"
onChange={handleChange}
name="twitter"
value={form.twitter}
required
/>
<button id="contributeSubmitBtn" className="contributeFormInput">
Contribute
</button>
</form>
</div>
</div>
</div>
);
}
export default Contribute;
And lastly, here is the code for Blog. js - responsible for the display of the blog data fetch from the database:
import React, { useState, useContext, useEffect } from "react";
import "./Blog.css";
import { Helmet } from "react-helmet-async";
import { collection, getDocs } from "@firebase/firestore";
import { DbContext, UserContext } from "../../../App";
import { NoBlog } from "../../Loading";
import twitterLogo from "../blog/assets/logo-3491390.png";
export default function Blog() {
//useContext
const db = useContext(DbContext);
const dbRef = collection(db, "contribution");
const user = useContext(UserContext);
// State
const [AllContributions, setAllContributions] = useState([]);
const [page, setPage] = useState(1);
// useEffect
useEffect(() => {
async function getAllContributions() {
const contributions = await getDocs(dbRef);
if (user)
setAllContributions(
contributions.docs.map((doc) => ({ ...doc.data(), id: doc.id }))
);
}
getAllContributions();
});
// Pagination
const perPage = 1;
const pages = Math.ceil(AllContributions.length / perPage);
const skip = page * perPage - perPage;
return (
<div
className="blogWrapper"
style={{ height: user ? "fit-content" : "130vh" }}
>
<Helmet>
<title>Blogpage</title>
<meta name="description" content="Welcome to my Blogpage" />
<link rel="canonical" href="/blog" />
</Helmet>
<div className="blogContributeWrapper">
{user ? (
<div>
{AllContributions.slice(skip, skip + perPage).map((doc) => {
return (
<div key={doc.id} className="blogDiv">
<h2 className="blogTitle">{doc.contributionTitle}</h2>
<p className="blogText">{doc.contributionText}</p>
<p className="blogContributor">
Contributor: <span id="contributor">{doc.contributor}</span>
</p>
<a
href={`https://twitter.com/${doc.twitter}`}
className="blogTwitter"
>
Follow me on twitter:{" "}
<span>
<img
src={twitterLogo}
alt="img"
className="blogTwitterImg"
/>
</span>
</a>
<p className="pagination">
Pages: {page} of {pages}
</p>
<div className="blogBtnsDiv">
<button
className="blogBtns"
disabled={page <= 1}
onClick={() => setPage((prev) => prev - 1)}
>
Prev
</button>
<button
className="blogBtns"
disabled={page >= pages}
aria-disabled={page >= pages}
onClick={() => setPage((prev) => prev + 1)}
>
Next
</button>
</div>
</div>
);
})}
</div>
) : (
<NoBlog />
)}
</div>
</div>
);
}
This is what the front page of the application looks like:
Conclusion
In conclusion, we have been able to create the Code miller app - which has the ability to take a contribution and store it in the firebase database, and retrieve the same when needed by a signed-up user.
This is the link to the source code: GitHub
Live link: https://altschool-second-semester-exam.vercel.app