├── .eslintrc.json
├── .gitignore
├── README.md
├── components
├── Banner.js
├── BlogCard.js
├── EditModal.js
├── Footer.js
├── Header.js
└── Layout.js
├── context
└── AuthContext.js
├── firebase.js
├── images
├── Blogify.png
├── Me_pic.jpg
├── glogo.png
├── robot.png
├── sample.jpg
└── shape.png
├── next.config.js
├── package-lock.json
├── package.json
├── pages
├── _app.js
├── _document.js
├── api
│ └── hello.js
├── blog
│ └── [blogId].js
├── create.js
├── index.js
├── login.js
├── userDashboard.js
└── yourBlogs.js
├── postcss.config.js
├── public
├── favicon.ico
└── vercel.svg
├── recoil
└── modalAtom.js
├── styles
└── globals.css
└── tailwind.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
16 |
17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
18 |
19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/components/Banner.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import React from "react";
3 | import { HiUserCircle } from "react-icons/hi";
4 | import Link from "next/link";
5 | import { userAuth } from "../context/AuthContext";
6 |
7 | const Banner = () => {
8 | const { user } = userAuth();
9 | return (
10 | <>
11 |
12 |
19 | {user ? (
20 |
21 |
28 |
29 | ) : (
30 |
31 |
32 |
33 | )}
34 |
35 | >
36 | );
37 | };
38 |
39 | export default Banner;
40 |
--------------------------------------------------------------------------------
/components/BlogCard.js:
--------------------------------------------------------------------------------
1 | import { BsThreeDots } from "react-icons/bs";
2 | import Image from "next/image";
3 | import { useState } from "react";
4 | import { userAuth } from "../context/AuthContext";
5 | import { deleteDoc, doc } from "firebase/firestore";
6 | import { db } from "../firebase";
7 | import { useRouter } from "next/router";
8 | import { useRecoilState } from "recoil";
9 | import { modalState } from "../recoil/modalAtom";
10 |
11 | const BlogCard = ({
12 | blogId,
13 | userId,
14 | username,
15 | content,
16 | image,
17 | profileImg,
18 | title,
19 | timestamp,
20 | }) => {
21 | const { user } = userAuth();
22 | const [showOptions, setShowOptions] = useState(false);
23 | const router = useRouter();
24 | const [openModal, setOpenModal] = useRecoilState(modalState);
25 |
26 | async function deleteBlog() {
27 | try {
28 | await deleteDoc(doc(db, "blogs", blogId));
29 | } catch (error) {
30 | console.log(error);
31 | }
32 | }
33 |
34 | function handleClick() {
35 | router.push({
36 | pathname: `/blog/${blogId}`,
37 | query: {
38 | title,
39 | content,
40 | username,
41 | profileImg,
42 | image,
43 | timestamp,
44 | },
45 | });
46 | }
47 |
48 | return (
49 | <>
50 |
51 | {/* TOP SECTION */}
52 |
53 |
54 |
61 | {/* TO BE REPLACED WITH USERNAME FROM USER OBJECT */}
62 | {username}
63 |
64 |
65 | {userId === user?.uid && (
66 |
setShowOptions(!showOptions)}
69 | />
70 | )}
71 | {showOptions && (
72 |
73 | {
75 | // console.log(blogId);
76 | setOpenModal({ isOpen: true, blogId: blogId });
77 | }}
78 | className="flex items-center space-x-1 active:scale-105 transition transform duration-150 ease-in-out"
79 | >
80 |
81 | Edit
82 |
83 |
87 |
88 | Delete
89 |
90 |
91 | )}
92 |
93 |
94 | {/* IMAGES SECTION */}
95 |
96 |
97 |

102 |
103 |
104 |
105 |
106 | {title}
107 |
108 |
109 |
110 |
111 |
112 | >
113 | );
114 | };
115 |
116 | export default BlogCard;
117 |
--------------------------------------------------------------------------------
/components/EditModal.js:
--------------------------------------------------------------------------------
1 | import { doc, getDoc, updateDoc, serverTimestamp } from "firebase/firestore";
2 | import { getDownloadURL, ref, uploadString } from "firebase/storage";
3 | import React, { useEffect, useRef, useState } from "react";
4 | import { useRecoilState } from "recoil";
5 | import { db, storage } from "../firebase";
6 | import { modalState } from "../recoil/modalAtom";
7 | import { AiFillCamera } from "react-icons/ai";
8 |
9 | const EditModal = ({ blogId }) => {
10 | const [openModal, setOpenModal] = useRecoilState(modalState);
11 | const imagePickerRef = useRef(null);
12 | const [loading, setLoading] = useState(false);
13 |
14 | const [title, setTitle] = useState("");
15 | const [newTitle, setNewTitle] = useState("");
16 |
17 | const [content, setContent] = useState("");
18 | const [newContent, setNewContent] = useState("");
19 |
20 | const [selectedFile, setSelectedFile] = useState(null);
21 | const [newSelectedFile, setNewSelectedFile] = useState(null);
22 |
23 | const [message, setMessage] = useState("");
24 | const [showCancel, setShowCancel] = useState(false);
25 |
26 | const handleClose = (e) => {
27 | if (
28 | e.target.className ===
29 | "h-full fixed top-0 left-0 w-full flex flex-col items-center justify-center bg-black bg-opacity-50"
30 | ) {
31 | setOpenModal(false);
32 | }
33 | };
34 |
35 | // ! MAIN FUNCTION
36 | async function updatePost(e) {
37 | e.preventDefault();
38 | if (
39 | title === newTitle &&
40 | content === newContent &&
41 | selectedFile === newSelectedFile
42 | ) {
43 | setOpenModal(false);
44 | return;
45 | }
46 | try {
47 | setMessage("Updating Post");
48 | if (loading) return;
49 | setLoading(true);
50 | // * UPDATE BLOG IN FIREBASE
51 | await updateDoc(doc(db, "blogs", blogId), {
52 | title: newTitle
53 | .split(" ")
54 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
55 | .join(" "),
56 | content: newContent,
57 | timestamp: serverTimestamp(),
58 | });
59 | if (newSelectedFile !== selectedFile) {
60 | // * getting the image ref for the new image
61 | const imageRef = ref(storage, `blogs/${blogId}/image`);
62 | // * uploading new image to the imageRef
63 | await uploadString(imageRef, newSelectedFile, "data_url").then(
64 | async (snapshot) => {
65 | const downloadUrl = await getDownloadURL(imageRef);
66 | await updateDoc(doc(db, "blogs", blogId), {
67 | image: downloadUrl,
68 | });
69 | }
70 | );
71 | } else {
72 | await updateDoc(doc(db, "blogs", blogId), {
73 | image: selectedFile,
74 | });
75 | }
76 | setLoading(false);
77 | setNewSelectedFile(null);
78 | setNewTitle("");
79 | setNewContent("");
80 | setOpenModal(false);
81 | } catch (error) {
82 | console.log(error);
83 | }
84 | }
85 |
86 | const addImageToBlog = (e) => {
87 | const reader = new FileReader();
88 | if (e.target.files[0]) {
89 | reader.readAsDataURL(e.target.files[0]);
90 | }
91 | reader.onload = (readerEvent) => {
92 | setNewSelectedFile(readerEvent.target.result);
93 | };
94 | };
95 |
96 | useEffect(() => {
97 | async function getBlog() {
98 | const blogSnap = await getDoc(doc(db, "blogs", blogId));
99 | const data = blogSnap.data();
100 | setTitle(data.title); // ? original data
101 | setNewTitle(data.title);
102 | setSelectedFile(data.image); // ? original data
103 | setNewSelectedFile(data.image);
104 | setContent(data.content); // ? original data
105 | setNewContent(data.content);
106 | }
107 | getBlog();
108 | }, []);
109 |
110 | return (
111 | <>
112 |
116 |
117 | Edit Blog
118 |
204 |
205 |
206 | >
207 | );
208 | };
209 |
210 | export default EditModal;
211 |
--------------------------------------------------------------------------------
/components/Footer.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import React from "react";
3 |
4 | function Footer() {
5 | return (
6 |
7 |
11 | {" "}
12 |
13 |
14 |
15 |
16 |
17 | {" "}
18 |
19 |
20 | );
21 | }
22 |
23 | export default Footer;
24 |
--------------------------------------------------------------------------------
/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import brand from "../images/Blogify.png";
3 | import Link from "next/link";
4 | import Image from "next/image";
5 |
6 | const Header = () => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 | Home
19 |
20 |
24 | Your Blogs
25 |
26 |
30 | Create
31 |
32 |
33 |
34 |
35 | );
36 | };
37 |
38 | export default Header;
39 |
--------------------------------------------------------------------------------
/components/Layout.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Footer from "./Footer";
3 | import Header from "./Header";
4 |
5 | const Layout = ({ children }) => {
6 | return (
7 | <>
8 |
9 |
10 |
11 |
12 |
13 | >
14 | );
15 | };
16 | export default Layout;
17 |
--------------------------------------------------------------------------------
/context/AuthContext.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useContext, createContext } from "react";
3 | import {
4 | GoogleAuthProvider,
5 | signInWithRedirect,
6 | signOut,
7 | onAuthStateChanged,
8 | } from "firebase/auth"; // ? the logic for implementing login and signup feature is written in this context file
9 | import { auth } from "../firebase";
10 |
11 | const AuthContext = createContext();
12 |
13 | export const AuthContextProvider = ({ children }) => {
14 | const [user, setUser] = useState({});
15 |
16 | // ? checking the login state of user via useeffect and onAuthStateChanged
17 | useEffect(() => {
18 | const unsubscribe = onAuthStateChanged(auth, async (currentUser) => {
19 | setUser(currentUser);
20 | });
21 | return unsubscribe;
22 | }, []);
23 |
24 | // ? FUNCTION TO SIGN IN WITH GOOGLE
25 | const signIn = async () => {
26 | const provider = new GoogleAuthProvider();
27 | await signInWithRedirect(auth, provider);
28 | };
29 |
30 | // ? FUNCTION TO LOG OUT THE USER
31 | const logOut = async () => {
32 | await signOut(auth);
33 | };
34 |
35 | return (
36 |
37 | {children}
38 |
39 | );
40 | };
41 |
42 | export const userAuth = () => {
43 | return useContext(AuthContext);
44 | };
45 |
--------------------------------------------------------------------------------
/firebase.js:
--------------------------------------------------------------------------------
1 | // Import the functions you need from the SDKs you need
2 | import { initializeApp } from "firebase/app";
3 | import { getAuth } from "firebase/auth";
4 | import { getFirestore } from "firebase/firestore";
5 | import { getStorage } from "firebase/storage";
6 |
7 | const firebaseConfig = {
8 | apiKey: process.env.NEXT_PUBLIC_APIKEY,
9 | authDomain: process.env.NEXT_PUBLIC_AUTHDOMAIN,
10 | projectId: process.env.NEXT_PUBLIC_PROJECTID,
11 | storageBucket: process.env.NEXT_PUBLIC_STORAGEBUCKET,
12 | messagingSenderId: process.env.NEXT_PUBLIC_MESSAGINGSENDERID,
13 | appId: process.env.NEXT_PUBLIC_APPID,
14 | };
15 |
16 | // Initialize Firebase
17 | const app = initializeApp(firebaseConfig);
18 | export const auth = getAuth(app);
19 | // db
20 | export const db = getFirestore();
21 | export const storage = getStorage();
22 |
--------------------------------------------------------------------------------
/images/Blogify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-ansh-007/Blogify/5ef9e9e48f85e72de622ff5b09bef398d9538830/images/Blogify.png
--------------------------------------------------------------------------------
/images/Me_pic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-ansh-007/Blogify/5ef9e9e48f85e72de622ff5b09bef398d9538830/images/Me_pic.jpg
--------------------------------------------------------------------------------
/images/glogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-ansh-007/Blogify/5ef9e9e48f85e72de622ff5b09bef398d9538830/images/glogo.png
--------------------------------------------------------------------------------
/images/robot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-ansh-007/Blogify/5ef9e9e48f85e72de622ff5b09bef398d9538830/images/robot.png
--------------------------------------------------------------------------------
/images/sample.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-ansh-007/Blogify/5ef9e9e48f85e72de622ff5b09bef398d9538830/images/sample.jpg
--------------------------------------------------------------------------------
/images/shape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-ansh-007/Blogify/5ef9e9e48f85e72de622ff5b09bef398d9538830/images/shape.png
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | swcMinify: true,
5 | eslint: {
6 | ignoreDuringBuilds: true,
7 | },
8 | images: {
9 | domains: ["lh3.googleusercontent.com", "firebasestorage.googleapis.com"],
10 | },
11 | };
12 |
13 | module.exports = nextConfig;
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "blogify",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "firebase": "^9.17.1",
13 | "next": "13.1.6",
14 | "react": "18.2.0",
15 | "react-dom": "18.2.0",
16 | "react-icons": "^4.7.1",
17 | "recoil": "^0.7.6"
18 | },
19 | "devDependencies": {
20 | "autoprefixer": "^10.4.13",
21 | "eslint": "8.34.0",
22 | "eslint-config-next": "13.1.6",
23 | "postcss": "^8.4.21",
24 | "tailwind-scrollbar": "^2.1.0",
25 | "tailwindcss": "^3.2.7"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import Layout from "../components/Layout";
2 | import { AuthContextProvider } from "../context/AuthContext";
3 | import "../styles/globals.css";
4 | import { RecoilRoot } from "recoil";
5 |
6 | function MyApp({ Component, pageProps }) {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
18 | export default MyApp;
19 |
--------------------------------------------------------------------------------
/pages/_document.js:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from "next/document";
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
7 |
8 |
13 |
17 | {/* font awesome */}
18 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/pages/api/hello.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 |
3 | export default function handler(req, res) {
4 | res.status(200).json({ name: 'John Doe' })
5 | }
6 |
--------------------------------------------------------------------------------
/pages/blog/[blogId].js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import React from "react";
3 | import { useRouter } from "next/router";
4 |
5 | const BlogDetail = () => {
6 | const router = useRouter();
7 | const { title, content, username, profileImg, image, timestamp } =
8 | router.query;
9 | return (
10 | <>
11 |
12 |
13 |
20 | {username}
21 |
22 |
23 |
24 |

29 |
30 |
31 |
32 | {title}
33 |
34 | {content}
35 |
36 |
37 |
38 | >
39 | );
40 | };
41 |
42 | export default BlogDetail;
43 |
--------------------------------------------------------------------------------
/pages/create.js:
--------------------------------------------------------------------------------
1 | import {
2 | addDoc,
3 | collection,
4 | doc,
5 | serverTimestamp,
6 | updateDoc,
7 | } from "firebase/firestore";
8 | import { getDownloadURL, ref, uploadString } from "firebase/storage";
9 | import Head from "next/head";
10 | import { useRouter } from "next/router";
11 | import React, { useEffect, useRef, useState } from "react";
12 | import { AiFillCamera } from "react-icons/ai";
13 | import { userAuth } from "../context/AuthContext";
14 | import { db, storage } from "../firebase";
15 |
16 | const create = () => {
17 | const router = useRouter();
18 | const imagePickerRef = useRef(null);
19 | const [loading, setLoading] = useState(false);
20 | const { user } = userAuth();
21 | const [title, setTitle] = useState("");
22 | const [content, setContent] = useState("");
23 | const [selectedFile, setSelectedFile] = useState(null);
24 | const [message, setMessage] = useState("");
25 |
26 | const uploadPost = async (e) => {
27 | e.preventDefault();
28 | try {
29 | setMessage("Uploading Post");
30 | if (loading) return;
31 | setLoading(true); // ? setting loading state to true the first time user clicks the post button
32 | // * create blog in firestore
33 | const docRef = await addDoc(collection(db, "blogs"), {
34 | uid: user.uid,
35 | username: user.displayName.toLowerCase().replace(/\s/g, ""),
36 | title: title
37 | .split(" ")
38 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
39 | .join(" "),
40 | content: content,
41 | profileImg: user.photoURL,
42 | timestamp: serverTimestamp(),
43 | });
44 | // * get the image ref from the storage by using firebase url
45 | const imageRef = ref(storage, `blogs/${docRef.id}/image`);
46 | // * 3) upload the image to the firebase storage with the blog id
47 | // * 4) get a download URL from the fb storage and update original blog with the image
48 | await uploadString(imageRef, selectedFile, "data_url").then(
49 | async (snapshot) => {
50 | const downloadUrl = await getDownloadURL(imageRef);
51 | await updateDoc(doc(db, "blogs", docRef.id), {
52 | image: downloadUrl,
53 | });
54 | }
55 | );
56 | setLoading(false);
57 | setSelectedFile(null);
58 | setTitle("");
59 | setContent("");
60 | setMessage("Post Uploaded Successfully");
61 | } catch (error) {
62 | console.log(error.message);
63 | setMessage("Post could not be uploaded!");
64 | }
65 | };
66 |
67 | const addImageToBlog = (e) => {
68 | const reader = new FileReader();
69 | if (e.target.files[0]) {
70 | reader.readAsDataURL(e.target.files[0]);
71 | }
72 | reader.onload = (readerEvent) => {
73 | setSelectedFile(readerEvent.target.result);
74 | };
75 | };
76 |
77 | useEffect(() => {
78 | let timeoutId;
79 | timeoutId = setTimeout(() => {
80 | setMessage("Slow Internet Detected!");
81 | }, 7000);
82 | return () => {
83 | clearTimeout(timeoutId);
84 | };
85 | }, [message]);
86 |
87 | useEffect(() => {
88 | if (user) {
89 | return;
90 | }
91 | router.push("/login");
92 | }, []);
93 |
94 | return (
95 | <>
96 |
97 | Blogify - Create Blog
98 |
99 |
100 | Create Blog
101 |
179 |
180 | >
181 | );
182 | };
183 |
184 | export default create;
185 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import { collection, onSnapshot, orderBy, query } from "firebase/firestore";
2 | import Head from "next/head";
3 | import { useEffect, useState } from "react";
4 | import { useRecoilValue } from "recoil";
5 | import Banner from "../components/Banner";
6 | import BlogCard from "../components/BlogCard";
7 | import EditModal from "../components/EditModal";
8 | import { db } from "../firebase";
9 | import { modalState } from "../recoil/modalAtom";
10 |
11 | export default function Home() {
12 | const [blogs, setBlogs] = useState([]);
13 | const [loading, setLoading] = useState(false);
14 | const { isOpen, blogId } = useRecoilValue(modalState);
15 |
16 | useEffect(() => {
17 | setLoading(true);
18 | const unsubscribe = onSnapshot(
19 | query(collection(db, "blogs"), orderBy("timestamp", "desc")),
20 | (snapshot) => {
21 | setBlogs(snapshot.docs);
22 | setLoading(false);
23 | }
24 | );
25 | return unsubscribe;
26 | }, [db]);
27 |
28 | return (
29 | <>
30 |
31 | Blogify
32 |
33 |
34 |
35 |
36 |
37 |
38 |
45 | {loading ? (
46 |
47 | ) : (
48 | blogs?.map((blog) => {
49 | return (
50 |
62 | );
63 | })
64 | )}
65 |
66 | {isOpen && }
67 | >
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/pages/login.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import Image from "next/image";
3 | import { useRouter } from "next/router";
4 | import brand from "../images/Blogify.png";
5 | import glogo from "../images/glogo.png";
6 | import { userAuth } from "../context/AuthContext";
7 |
8 | function Login() {
9 | const router = useRouter();
10 | const { signIn } = userAuth();
11 |
12 | const handleSignIn = async () => {
13 | try {
14 | await signIn();
15 | } catch (error) {
16 | console.log(error.message);
17 | }
18 | };
19 |
20 | const { user } = userAuth();
21 | useEffect(() => {
22 | if (user) {
23 | router.push("/");
24 | }
25 | }, [user]);
26 |
27 | return (
28 | <>
29 |
30 |
31 |
router.push("/")}>
32 |
33 |
34 |
38 |
41 |
42 |
43 |
44 |
45 | >
46 | );
47 | }
48 |
49 | export default Login;
50 |
--------------------------------------------------------------------------------
/pages/userDashboard.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { userAuth } from "../context/AuthContext";
3 | import { useRouter } from "next/router";
4 | import shape from "../images/shape.png";
5 | import Image from "next/image";
6 |
7 | const UserDashboard = () => {
8 | const router = useRouter();
9 | const { logOut, user } = userAuth();
10 | useEffect(() => {
11 | if (user) {
12 | return;
13 | }
14 | router.push("/");
15 | }, [user]);
16 | return (
17 | <>
18 |
19 |
20 |
21 |

26 |
27 | {user?.email}
28 |
34 |
35 | >
36 | );
37 | };
38 |
39 | export default UserDashboard;
40 |
--------------------------------------------------------------------------------
/pages/yourBlogs.js:
--------------------------------------------------------------------------------
1 | import {
2 | collection,
3 | onSnapshot,
4 | orderBy,
5 | query,
6 | where,
7 | } from "firebase/firestore";
8 | import Head from "next/head";
9 | import Image from "next/image";
10 | import { useRouter } from "next/router";
11 | import React, { useEffect, useState } from "react";
12 | import BlogCard from "../components/BlogCard";
13 | import { userAuth } from "../context/AuthContext";
14 | import { db } from "../firebase";
15 | import robot from "../images/robot.png";
16 | import EditModal from "../components/EditModal";
17 | import { useRecoilState, useRecoilValue } from "recoil";
18 | import { modalState } from "../recoil/modalAtom";
19 |
20 | const YourBlogs = () => {
21 | const { user } = userAuth();
22 | const [blogs, setBlogs] = useState([]);
23 | const [loading, setLoading] = useState(false);
24 | const { isOpen, blogId } = useRecoilValue(modalState);
25 | // ! authentication code
26 | const router = useRouter();
27 | useEffect(() => {
28 | if (user) {
29 | return;
30 | }
31 | router.push("/login");
32 | }, []);
33 |
34 | useEffect(() => {
35 | if (!user) {
36 | return;
37 | }
38 | setLoading(true);
39 | const unsubscribe = onSnapshot(
40 | query(
41 | collection(db, "blogs"),
42 | where("uid", "==", user.uid || ""),
43 | orderBy("timestamp", "desc")
44 | ),
45 | (snapshot) => {
46 | setBlogs(snapshot.docs);
47 | setLoading(false);
48 | }
49 | );
50 | return unsubscribe;
51 | }, [db, user]);
52 |
53 | return (
54 | <>
55 |
56 | Blogify - Your Blogs
57 |
58 | 0
61 | ? "flex flex-col items-center justify-center"
62 | : "flex flex-col items-center justify-center h-full"
63 | }
64 | >
65 | {blogs.length > 0 ? (
66 | blogs.map((blog) => (
67 |
78 | ))
79 | ) : loading ? (
80 |
81 | ) : (
82 |
83 |
84 |
85 | No Blogs Yet!
86 |
87 |
88 | )}
89 |
90 | {isOpen && }
91 | >
92 | );
93 | };
94 |
95 | export default YourBlogs;
96 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-ansh-007/Blogify/5ef9e9e48f85e72de622ff5b09bef398d9538830/public/favicon.ico
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/recoil/modalAtom.js:
--------------------------------------------------------------------------------
1 | import { atom } from "recoil";
2 |
3 | export const modalState = atom({
4 | key: "modalState",
5 | default: {
6 | isOpen: false,
7 | blogId: "",
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | font-family: "Montserrat", sans-serif;
7 | }
8 |
9 | * {
10 | padding: 0 1px 0 1px;
11 | }
12 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./app/**/*.{js,ts,jsx,tsx}",
5 | "./pages/**/*.{js,ts,jsx,tsx}",
6 | "./components/**/*.{js,ts,jsx,tsx}",
7 |
8 | // Or if using `src` directory:
9 | "./src/**/*.{js,ts,jsx,tsx}",
10 | ],
11 | theme: {
12 | extend: {},
13 | },
14 | plugins: [
15 | // ...
16 | require("tailwind-scrollbar"),
17 | ],
18 | };
19 |
--------------------------------------------------------------------------------