├── src
├── App.css
├── assets
│ └── img
│ │ ├── Loading.gif
│ │ └── profile.jpg
├── Components
│ ├── pages
│ │ ├── Home
│ │ │ ├── Home.jsx
│ │ │ ├── Search.jsx
│ │ │ └── Header
│ │ │ │ ├── UserModal.jsx
│ │ │ │ └── HomeHeader.jsx
│ │ ├── Demo
│ │ │ ├── Auth
│ │ │ │ ├── Trending.jsx
│ │ │ │ ├── Banner.jsx
│ │ │ │ ├── Discover.jsx
│ │ │ │ ├── SignIn.jsx
│ │ │ │ ├── SignUp.jsx
│ │ │ │ └── Auth.jsx
│ │ │ ├── Demo.jsx
│ │ │ └── DemoHeader.jsx
│ │ └── Profile
│ │ │ ├── Activites
│ │ │ ├── ProfileList.jsx
│ │ │ ├── ProfileAbout.jsx
│ │ │ └── ProfileHome.jsx
│ │ │ ├── Profile.jsx
│ │ │ └── EditProfile.jsx
│ ├── Loading
│ │ └── Loading.jsx
│ ├── common
│ │ └── Posts
│ │ │ └── Posts.jsx
│ └── Context
│ │ └── Context.jsx
├── utils
│ ├── Modal.jsx
│ ├── Input.jsx
│ └── Helper.js
├── index.css
├── main.jsx
├── App.jsx
├── Firebase
│ └── Firebase-config.js
└── data.js
├── postcss.config.js
├── vite.config.js
├── .gitignore
├── index.html
├── README.md
├── .eslintrc.cjs
├── tailwind.config.js
├── package.json
└── public
└── vite.svg
/src/App.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/img/Loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Alirazaramejo/meduim-blog/HEAD/src/assets/img/Loading.gif
--------------------------------------------------------------------------------
/src/assets/img/profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Alirazaramejo/meduim-blog/HEAD/src/assets/img/profile.jpg
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/src/Components/pages/Home/Home.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Home() {
4 | return (
5 |
setModal(false)}
8 | className={`bg-white/50 fixed inset-0 z-10
9 | ${
10 | modal ? "visible opacity-100" : "invisible opacity-0"
11 | } transition-all duration-500`}
12 | />
13 | {children}
14 | >
15 | );
16 | };
17 |
18 | export default Modal;
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | *{
6 | @apply m-0 p-0 box-border;
7 | }
8 | body{
9 | @apply font-texts;
10 | }
11 | .size{
12 | @apply w-[95%] md:w-[90%] mx-auto;
13 | }
14 | .shadows{
15 | box-shadow:0px 0px 3px 0.3px rgb(182,182,182);
16 | }
17 | .btn{
18 | @apply px-3 p-2 text-sm font-medium;
19 | }
20 | .center{
21 | position: fixed;
22 | top: 50%;
23 | left: 50%;
24 | transform: translate(-50%,-50%);
25 | }
26 |
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App.jsx';
4 | import './index.css';
5 | import 'react-toastify/dist/ReactToastify.css';
6 | import { BrowserRouter } from 'react-router-dom';
7 | import Context from './Components/Context/Context.jsx';
8 |
9 | ReactDOM.createRoot(document.getElementById('root')).render(
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/src/utils/Input.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function Input({ type, title, form, setForm }) {
4 | const handleChange = (e) => {
5 | setForm({ ...form, [e.target.name]: e.target.value });
6 | };
7 |
8 | return (
9 |
10 | {title}
11 |
17 |
18 | );
19 | }
20 |
21 | export default Input;
22 |
--------------------------------------------------------------------------------
/src/Components/pages/Profile/Activites/ProfileAbout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function ProfileAbout({getAllUsersData,setEditModal}) {
4 | return (
5 |
6 |
7 | {getAllUsersData?.bio ? getAllUsersData.username : "Hasn't added a bio yet."}
8 |
9 |
10 | setEditModal(true)}
12 | className='border border-black py-2 px-5 rounded-full text-black mt-[3rem]'>Edit
13 |
14 |
15 | )
16 | }
17 |
18 | export default ProfileAbout
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:react/recommended',
7 | 'plugin:react/jsx-runtime',
8 | 'plugin:react-hooks/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12 | settings: { react: { version: '18.2' } },
13 | plugins: ['react-refresh'],
14 | rules: {
15 | 'react/jsx-no-target-blank': 'off',
16 | 'react-refresh/only-export-components': [
17 | 'warn',
18 | { allowConstantExport: true },
19 | ],
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/src/Components/pages/Demo/Demo.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Banner from './Auth/Banner';
3 | import Trending from './Auth/Trending';
4 | import Posts from '../../common/Posts/Posts';
5 | import Discover from './Auth/Discover';
6 |
7 | function Demo() {
8 | return (
9 | <>
10 |
11 |
12 |
20 | >
21 | );
22 | }
23 |
24 | export default Demo;
25 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | /** @type {import('tailwindcss').Config} */
3 | export default {
4 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
5 | theme: {
6 | extend: {
7 | colors: {
8 | black1: "rgba(0,0,0,0.8)",
9 | banner: "rgb(255, 192, 23)",
10 | },
11 | fontFamily: {
12 | title: `gt-super, Georgia, Cambria,Times New Roman, Times, serif;`,
13 | texts: `sohne, Helvetica Neue, Helvetica, Arial, sans-serif`,
14 | },
15 | gridTemplateColumns: {
16 | card: "repeat(auto-fit, minmax(280px, 1fr))",
17 | },
18 | },
19 | },
20 | plugins: [],
21 | };
--------------------------------------------------------------------------------
/src/Components/pages/Demo/Auth/Banner.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import reactImg from '../../../../assets/react.svg';
3 |
4 | function Banner() {
5 | return (
6 |
7 |
8 | {/* Main heading */}
9 |
Stay curious.
10 | {/* Subheading */}
11 |
12 | Discover stories, thinking, and expertise from writers on any topic.
13 |
14 | {/* Button */}
15 |
16 | Start Reading
17 |
18 |
19 |
20 | );
21 | }
22 |
23 | export default Banner;
24 |
--------------------------------------------------------------------------------
/src/utils/Helper.js:
--------------------------------------------------------------------------------
1 | export const secretEmail = (email) => {
2 | const [username, domain] = email.split("@");
3 | const secretUser = username.substring(0, 2) + "*".repeat(username.length - 2);
4 | return `${secretUser}@${domain}`;
5 | };
6 |
7 | export const readTime = (desc) => {
8 | const averageReading = 225;
9 |
10 | const div = document.createElement("div");
11 | div.innerHTML = desc.__html;
12 |
13 | const textContext = div.textContent || div.innerHTML;
14 | const words = textContext.trim().split(/\s+/);
15 | return Math.ceil(words.length / averageReading);
16 | };
17 |
18 | export const formatNum = (num) => {
19 | if (num >= 1e9) {
20 | return (num / 1e9).toFixed(1) + "B";
21 | } else if (num >= 1e6) {
22 | return (num / 1e6).toFixed(1) + "M";
23 | } else if (num >= 1e3) {
24 | return (num / 1e3).toFixed(1) + "K";
25 | } else {
26 | return num.toString();
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "medium",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "firebase": "^10.8.0",
14 | "react": "^18.2.0",
15 | "react-dom": "^18.2.0",
16 | "react-icons": "^5.0.1",
17 | "react-router-dom": "^6.22.0",
18 | "react-toastify": "^10.0.4"
19 | },
20 | "devDependencies": {
21 | "@types/react": "^18.2.55",
22 | "@types/react-dom": "^18.2.19",
23 | "@vitejs/plugin-react": "^4.2.1",
24 | "autoprefixer": "^10.4.17",
25 | "eslint": "^8.56.0",
26 | "eslint-plugin-react": "^7.33.2",
27 | "eslint-plugin-react-hooks": "^4.6.0",
28 | "eslint-plugin-react-refresh": "^0.4.5",
29 | "postcss": "^8.4.35",
30 | "tailwindcss": "^3.4.1",
31 | "vite": "^5.1.0"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Navigate, Route, Routes } from 'react-router-dom';
3 | import Home from './Components/pages/Home/Home';
4 | import Demo from './Components/pages/Demo/Demo';
5 | import HomeHeader from './Components/pages/Home/Header/HomeHeader';
6 | import DemoHeader from './Components/pages/Demo/DemoHeader';
7 | import { ToastContainer } from 'react-toastify';
8 | import { Blog } from './Components/Context/Context';
9 | import Profile from './Components/pages/Profile/Profile';
10 | function App() {
11 | const { currentUser } = Blog();
12 |
13 | return (
14 | <>
15 | {currentUser ?
:
}
16 |
17 |
18 | {currentUser && } />}
19 | {!currentUser && } />}
20 | } />
21 | } />
22 |
23 | >
24 | );
25 | }
26 |
27 | export default App;
28 |
--------------------------------------------------------------------------------
/src/Components/pages/Home/Search.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Modal from "../../../utils/Modal";
3 | import { CiSearch } from "react-icons/ci";
4 | function Search({ modal, setModal }) {
5 | return (
6 | <>
7 |
8 |
15 |
16 |
17 |
18 |
19 |
24 |
25 |
26 |
27 | >
28 | );
29 | }
30 |
31 | export default Search;
32 |
--------------------------------------------------------------------------------
/src/Firebase/Firebase-config.js:
--------------------------------------------------------------------------------
1 | // Import the functions you need from the SDKs you need
2 | import { initializeApp } from "firebase/app";
3 | import { getAuth,GoogleAuthProvider } from "firebase/auth";
4 | import { getStorage, ref, uploadBytes, getDownloadURL } from "firebase/storage"; // Import getDownloadURL
5 | import { getFirestore, doc } from "firebase/firestore";
6 | const firebaseConfig = {
7 | apiKey: "AIzaSyAQjlwhuwWYJ-OdhhPP4XRxEgWSfQ5d3-o",
8 | authDomain: "ali-raza-518df.firebaseapp.com",
9 | databaseURL: "https://ali-raza-518df-default-rtdb.firebaseio.com",
10 | projectId: "ali-raza-518df",
11 | storageBucket: "ali-raza-518df.appspot.com",
12 | messagingSenderId: "308309137877",
13 | appId: "1:308309137877:web:ac0be37dd3c2bb9544055c",
14 | measurementId: "G-82NM2JZS9D"
15 | };
16 |
17 | // Initialize Firebase
18 | const app = initializeApp(firebaseConfig);
19 | export const auth = getAuth();
20 | export const provider = new GoogleAuthProvider();
21 | export const storage = getStorage();
22 | export const db = getFirestore(app);
23 | export { doc };
24 | export { ref, uploadBytes, getDownloadURL };
25 |
--------------------------------------------------------------------------------
/src/Components/pages/Profile/Activites/ProfileHome.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function ProfileHome() {
4 | return (
5 |
6 |
aaa
7 |
aaa
8 |
aaa
9 |
aaa
10 |
aaa
11 |
aaa
12 |
aaa
13 |
aaa
14 |
aaa
15 |
aaa
16 |
aaa
17 |
aaa
18 |
aaa
19 |
aaa
20 |
aaa
21 |
aaa
22 |
aaa
23 |
aaa
24 |
aaa
25 |
aaa
26 |
aaa
27 |
aaa
28 |
aaa
29 |
aaa
30 |
aaa
31 |
aaa
32 |
aaa
33 |
aaa
34 |
aaa
35 |
aaa
36 |
aaa
37 |
aaa
38 |
aaa
39 |
aaa
40 |
aaa
41 |
aaa
42 |
aaa
43 |
aaa
44 |
aaa
45 |
aaa
46 |
aaa
47 |
aaa
48 |
aaa
49 |
aaa
50 |
aaa
51 |
aaa
52 |
aaa
53 |
aaa
54 |
55 | )
56 | }
57 |
58 | export default ProfileHome
--------------------------------------------------------------------------------
/src/Components/pages/Demo/Auth/Discover.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { discover, discoverActions } from '../../../../data';
3 |
4 | function Discover() {
5 | return (
6 |
7 | {/* Discover topics section */}
8 |
9 |
Discover more of what matters to you
10 |
11 | {/* Mapping over discover topics */}
12 | {discover.map((item, i) => (
13 |
14 | {item}
15 |
16 | ))}
17 |
18 | {/* Button to see more topics */}
19 |
See more topics
20 |
21 | {/* Discover actions section */}
22 |
23 | {/* Mapping over discover actions */}
24 | {discoverActions.map((item, i) => (
25 | {item}
26 | ))}
27 |
28 |
29 | );
30 | }
31 |
32 | export default Discover;
33 |
--------------------------------------------------------------------------------
/src/Components/common/Posts/Posts.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Posts() {
4 | return (
5 |
6 |
post
7 |
post
8 |
post
9 |
post
10 |
post
11 |
post
12 |
post
13 |
post
14 |
post
15 |
post
16 |
post
17 |
post
18 |
post
19 |
post
20 |
post
21 |
post
22 |
post
23 |
post
24 |
post
25 |
post
26 |
post
27 |
post
28 |
post
29 |
post
30 |
post
31 |
post
32 |
post
33 |
post
34 |
post
35 |
post
36 |
post
37 |
post
38 |
post
39 |
post
40 |
post
41 |
post
42 |
post
43 |
post
44 |
post
45 |
post
46 |
post
47 |
post
48 |
post
49 |
post
50 |
post
51 |
post
52 |
post
53 |
post
54 |
post
55 |
post
56 |
post
57 |
post
58 |
59 | )
60 | }
61 |
62 | export default Posts
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Components/Context/Context.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext, useEffect, useState } from 'react';
2 | import { auth, db } from '../../Firebase/Firebase-config'; // Assuming db is your Firestore instance
3 | import LoadingIndicator from '../Loading/Loading';
4 | import { onAuthStateChanged } from 'firebase/auth';
5 | import { collection, query, onSnapshot } from 'firebase/firestore';
6 |
7 | const BlogContext = createContext();
8 |
9 | function Context({ children }) {
10 | const [currentUser, setCurrentUser] = useState(null);
11 | const [loading, setLoading] = useState(true);
12 | const [allUsers, setAllUsers] = useState([]);
13 | const [userLoading, setUserLoading] = useState(true);
14 | useEffect(() => {
15 | const unsubscribe = onAuthStateChanged(auth, (user) => {
16 | setCurrentUser(user);
17 | setLoading(false);
18 | });
19 |
20 | return unsubscribe; // return the unsubscribe function to clean up
21 | }, []); // no need to include currentUser in dependencies
22 |
23 | useEffect(() => {
24 | const getUsers = () => {
25 | const usersRef = collection(db, 'users');
26 | const q = query(usersRef);
27 |
28 | const unsubscribe = onSnapshot(q, (snapshot) => {
29 | setAllUsers(snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
30 | setUserLoading(false);
31 | });
32 |
33 | return unsubscribe; // return the unsubscribe function to clean up
34 | };
35 |
36 | getUsers();
37 | }, []); // empty dependency array to run only once when component mounts
38 |
39 | // console.log(allUsers);
40 |
41 | return (
42 |
43 | {loading ? : children}
44 |
45 | );
46 | }
47 |
48 | export default Context;
49 |
50 | export const Blog = () => useContext(BlogContext);
51 |
--------------------------------------------------------------------------------
/src/Components/pages/Demo/DemoHeader.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { Link } from 'react-router-dom';
3 | import { nav } from "../../../data.js";
4 | import Auth from './Auth/Auth.jsx';
5 |
6 | function DemoHeader() {
7 | const [colorActive, setColorActive] = useState(false);
8 | const [modal, setModal] = useState(false);
9 |
10 | useEffect(() => {
11 | const scrollHandler = () => {
12 | window.scrollY > 480 ? setColorActive(true) : setColorActive(false);
13 | }
14 |
15 | window.addEventListener('scroll', scrollHandler);
16 |
17 | // Cleanup function
18 | return () => {
19 | window.removeEventListener('scroll', scrollHandler);
20 | };
21 | }, []);
22 |
23 | return (
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {nav.map((link, i) => (
32 |
{link.title}
33 | ))}
34 |
35 |
setModal(true)} className='hidden text-sm sm:flex items-center gap-5'>Sign in
36 |
37 |
38 |
39 |
40 | Get Started
41 |
42 |
43 |
44 |
45 | );
46 | }
47 |
48 | export default DemoHeader;
49 |
--------------------------------------------------------------------------------
/src/Components/pages/Demo/Auth/SignIn.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import Input from "../../../../utils/Input";
3 | import { toast } from "react-toastify";
4 | import { MdKeyboardArrowLeft } from "react-icons/md";
5 | import { signInWithEmailAndPassword } from "firebase/auth";
6 | import { auth } from "../../../../Firebase/Firebase-config";
7 | import { useNavigate } from "react-router-dom";
8 |
9 | function SignIn({ setSignReq }) {
10 | const Navigate = useNavigate();
11 | const [form, setForm] = useState({
12 | email: "",
13 | password: "",
14 | });
15 | const [loading, setLoading] = useState(false);
16 |
17 | const handleSubmit = async (e) => {
18 | e.preventDefault();
19 | if (form.email === "" || form.password === "") {
20 | toast.error("All fields are required!!");
21 | return;
22 | }
23 | try {
24 | setLoading(true);
25 | await signInWithEmailAndPassword(auth, form.email, form.password);
26 | Navigate("/");
27 | toast.success("User has been logged in successfully");
28 | } catch (error) {
29 | toast.error(error.message);
30 | } finally {
31 | setLoading(false);
32 | }
33 | };
34 |
35 | return (
36 |
37 |
Sign In with email
38 |
39 | Enter the email address associated with your account, and we’ll send a
40 | magic link to your inbox.
41 |
42 |
52 |
setSignReq("")}
54 | className="mt-5 text-sm text-green-600 hover:text-green-700 flex items-center mx-auto"
55 | >
56 | All Sign In Options
57 |
58 |
59 | );
60 | }
61 |
62 | export default SignIn;
63 |
--------------------------------------------------------------------------------
/src/Components/pages/Home/Header/UserModal.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { LiaUserSolid } from "react-icons/lia";
3 | import { MdLocalLibrary } from "react-icons/md"; // Changed from MdOutlineLocalLibrary
4 | import { BiSpreadsheet } from "react-icons/bi";
5 | import { HiOutlineChartBar } from "react-icons/hi";
6 | import { LiaEditSolid } from "react-icons/lia";
7 | import { Blog } from "../../../Context/Context";
8 | import { Link } from "react-router-dom";
9 | import { secretEmail } from "../../../../utils/Helper";
10 | import { signOut } from "firebase/auth";
11 |
12 | import { toast } from "react-toastify";
13 |
14 | function UserModal({ setModal }) {
15 | const { currentUser } = Blog();
16 | const userModal = [
17 | {
18 | title: "Profile",
19 | icon:
,
20 | path: `/profile/${currentUser?.uid}`,
21 | },
22 | {
23 | title: "Library",
24 | icon:
, // Changed from MdOutlineLocalLibrary
25 | path: "/library",
26 | },
27 | {
28 | title: "Stories",
29 | icon:
,
30 | path: "/stories",
31 | },
32 | {
33 | title: "Stats",
34 | icon:
,
35 | path: "/stats",
36 | },
37 | ];
38 |
39 | const logout = () => {
40 | signOut().then(() => {
41 | toast.success("Logged out successfully!");
42 | setModal(false);
43 | }).catch(error => {
44 | toast.error(error.message);
45 | });
46 | };
47 |
48 | return (
49 |
52 |
55 |
56 |
57 |
58 | Write
59 |
60 |
61 | {userModal.map((link, i) => (
62 | setModal(false)}
64 | className="flex items-center gap-2 text-gray-500 hover:text-black/70"
65 | key={i}
66 | to={link.path}>
67 | {link.icon}
68 |
{link.title}
69 |
70 | ))}
71 |
72 |
75 | Sign Out
76 | {secretEmail(currentUser?.email)}
77 |
78 |
79 | );
80 | }
81 |
82 | export default UserModal;
83 |
--------------------------------------------------------------------------------
/src/Components/pages/Home/Header/HomeHeader.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { BsMedium } from "react-icons/bs";
3 | import { CiSearch } from "react-icons/ci";
4 | import { RiEditCircleLine } from "react-icons/ri";
5 | import { IoMdNotificationsOutline } from "react-icons/io";
6 | import { MdKeyboardArrowDown } from "react-icons/md";
7 | import { Link } from 'react-router-dom';
8 | import Modal from "../../../../utils/Modal";
9 | import Search from '../Search';
10 | import UserModal from './UserModal';
11 | import Profile from "../../../../assets/img/profile.jpg";
12 | import { Blog } from '../../../Context/Context';
13 | import Loading from '../../../Loading/Loading';
14 |
15 | function HomeHeader() {
16 | const { currentUser, allUsers, userLoading } = Blog(); // Destructuring Blog context
17 | const [modal, setModal] = useState(false);
18 | const [search, setSearch] = useState(false);
19 | const [userData, setUserData] = useState(null);
20 |
21 | useEffect(() => {
22 | if (!userLoading && currentUser && allUsers.length > 0) {
23 | const getUserData = allUsers.find((user) => user.id === currentUser.uid);
24 | setUserData(getUserData);
25 | }
26 | }, [userLoading, currentUser, allUsers]);
27 |
28 | return (
29 |
64 | );
65 | }
66 |
67 | export default HomeHeader;
68 |
--------------------------------------------------------------------------------
/src/Components/pages/Demo/Auth/SignUp.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import Input from "../../../../utils/Input";
3 | import { MdKeyboardArrowLeft } from "react-icons/md";
4 | import { toast } from "react-toastify";
5 | import { createUserWithEmailAndPassword } from "firebase/auth";
6 | import { auth, db } from "../../../../Firebase/Firebase-config";
7 | import { useNavigate } from "react-router-dom";
8 | import { doc, getDoc, setDoc } from "firebase/firestore";
9 |
10 | const SignUp = ({ setSignReq, setModal }) => {
11 | const navigate = useNavigate();
12 | const [loading, setLoading] = useState(false);
13 | const [form, setForm] = useState({
14 | username: "",
15 | email: "",
16 | password: "",
17 | rePassword: "",
18 | });
19 |
20 | const handleSubmit = async (e) => {
21 | e.preventDefault();
22 | if (form[("username", "email", "password", "rePassword")] === "") {
23 | toast.error("All fields are required");
24 | } else if (form["password"] !== form["rePassword"]) {
25 | toast.error("Your passwords are not matching!!");
26 | return;
27 | } else {
28 | setLoading(true);
29 | const { user } = await createUserWithEmailAndPassword(
30 | auth,
31 | form.email,
32 | form.password
33 | );
34 |
35 | const ref = doc(db, "users", user.uid);
36 | const userDoc = await getDoc(ref);
37 |
38 | if (!userDoc.exists()) {
39 | await setDoc(ref, {
40 | userId: user.uid,
41 | username: form.username,
42 | email: form.email,
43 | userImg: "",
44 | bio: "",
45 | created: Date.now(),
46 | });
47 | navigate("/");
48 | toast.success("New Account has been Created");
49 | setModal(false);
50 | setLoading(false);
51 | }
52 | }
53 | };
54 |
55 | return (
56 |
57 |
Sign up with email
58 |
59 | Enter the email address associated with your account, and we’ll send a
60 | magic link to your inbox.
61 |
62 |
79 |
setSignReq("")}
81 | className="mt-5 text-sm text-green-600 hover:text-green-700
82 | flex items-center mx-auto">
83 |
84 | All sign Up Options
85 |
86 |
87 | );
88 | };
89 |
90 | export default SignUp;
91 |
--------------------------------------------------------------------------------
/src/Components/pages/Profile/Profile.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import ProfileAbout from "./Activites/ProfileAbout";
3 | import ProfileList from "./Activites/ProfileList";
4 | import ProfileHome from "./Activites/ProfileHome";
5 | import Modal from "../../../utils/Modal";
6 | import { LiaTimesSolid } from "react-icons/lia";
7 | import { IoSettingsSharp } from "react-icons/io5";
8 | import profileImg from "../../../assets/img/profile.jpg";
9 | import { discoverActions } from "../../../data";
10 |
11 | import EditProfile from "./EditProfile";
12 | import { Blog } from "../../Context/Context";
13 | import { useParams } from "react-router-dom";
14 |
15 | function Profile() {
16 | const {allUsers} = Blog();
17 | const {userId} = useParams();
18 | const activity = [
19 | { title: "Home", comp: ProfileHome },
20 | { title: "Lists", comp: ProfileList },
21 | { title: "About", comp: ProfileAbout },
22 | ];
23 |
24 | const [currentActive, setCurrentActive] = useState(activity[0]);
25 | const [modal, setModal] = useState(false);
26 |
27 | const [editModal, setEditModal] = useState(false);
28 | const getAllUsersData = allUsers.find((user) => user.id === userId);
29 | console.log("all data", getAllUsersData);
30 | return (
31 |
32 | {/* User activity */}
33 |
34 |
35 |
36 | {getAllUsersData?.username || "Ali Raza"}
37 |
38 |
Followers(2)
39 |
Following(2)
40 |
41 |
42 | {activity.map((item, index) => (
43 |
51 | setCurrentActive(item)}>
52 | {item.title}
53 |
54 |
55 | ))}
56 |
57 |
58 |
59 | {/* btn add to close and open side bar */}
60 | setModal(true)}
62 | className="fixed top-[8rem] right-0 w-[2rem] h-[2rem] bg-black text-white grid place-items-center md:hidden"
63 | >
64 |
65 |
66 | {/* User Full details here */}
67 |
68 |
74 | {/* Icons make to close the profile */}
75 |
76 | setModal(false)}
78 | className="inline-block md:hidden "
79 | >
80 |
81 |
82 |
83 | {/* Profile details make */}
84 |
85 |
90 |
Ali Raza
91 |
92 | I am Ali Raza
93 |
94 |
95 |
setEditModal(true)}
98 | >
99 | {" "}
100 | Edit Profile
101 |
102 | {/* Nav data from datajs file get */}
103 |
104 | {discoverActions.map((item, index) => (
105 |
106 | {item}
107 |
108 | ))}
109 |
110 |
111 |
112 |
113 | {editModal && (
114 |
115 | )}
116 |
117 | );
118 | }
119 |
120 | export default Profile;
121 |
--------------------------------------------------------------------------------
/src/Components/pages/Demo/Auth/Auth.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { LiaTimesSolid } from "react-icons/lia";
3 | import { FcGoogle } from "react-icons/fc";
4 | import { MdFacebook } from "react-icons/md";
5 | import { AiOutlineMail } from "react-icons/ai";
6 | import { FaApple } from "react-icons/fa";
7 | import { BsTwitterX } from "react-icons/bs";
8 | import { signInWithPopup } from "firebase/auth";
9 | import { doc, getDoc, setDoc } from "firebase/firestore";
10 | import { toast } from "react-toastify";
11 | import { Navigate } from "react-router-dom";
12 | import SignUp from "../Auth/SignUp"
13 | import SignIn from "../Auth/SignIn"
14 |
15 | import Modal from "../../../../utils/Modal";
16 | import { auth, db, provider } from "../../../../Firebase/Firebase-config";
17 |
18 | function Auth({ modal, setModal }) {
19 | const [createUser, setCreateUser] = useState(false);
20 | const [signReq, setSignReq] = useState("");
21 |
22 | // Function to handle Google authentication
23 | const GoogleAuth = async () => {
24 | try {
25 | const userCredential = await signInWithPopup(auth, provider);
26 | const newUser = userCredential.user;
27 | const ref = doc(db, "users", newUser.uid);
28 | const userDoc = await getDoc(ref);
29 | if (!userDoc.exists()) {
30 | await setDoc(ref, {
31 | userId: newUser.uid,
32 | username: newUser.displayName,
33 | email: newUser.email,
34 | userImg: newUser.photoURL,
35 | bio: "",
36 | });
37 | Navigate("/"); // Redirect to home page
38 | setModal(false); // Close the modal
39 | toast.success("Sign in with Google successful");
40 | }
41 | } catch (error) {
42 | toast.error("Error signing in with Google: " + error.message);
43 | }
44 | };
45 |
46 | // CSS class to control visibility of the modal
47 | const hidden = modal ? "visible opacity-100" : "invisible opacity-0";
48 |
49 | return (
50 |
51 |
52 | setModal(false)} className="absolute top-8 right-8 text-2xl hover:opacity-50">
53 |
54 |
55 |
56 | {signReq === "" ? (
57 | // Show sign-in/sign-up options
58 | <>
59 |
{createUser ? "Join Medium" : "Welcome Back"}
60 |
61 |
} text={`${createUser ? "Sign Up " : "Sign In"} With Google`} />
62 |
} text={`${createUser ? "Sign Up " : "Sign In"} With Facebook`} />
63 |
setSignReq(createUser ? "sign-In" : "sign-up")} icon={ } text={`${createUser ? "Sign Up " : "Sign In"} With Email`} />
64 | {!createUser && (
65 |
66 |
67 | } text="Sign In With Apple" />
68 |
69 |
70 | } text="Sign In With Twitter" />
71 |
72 |
73 | )}
74 |
75 |
76 | {createUser ? "Already have an account?" : "No Account?"}
77 | setCreateUser(!createUser)}>
78 | {createUser ? "Create One" : "Sign In"}
79 |
80 |
81 | >
82 | ) : signReq === "sign-In" ? (
83 | // Show sign-in form
84 |
85 | ) : signReq === "sign-up" ? (
86 | // Show sign-up form
87 |
88 | ) : null}
89 | {!createUser && (
90 | // Forgot password or sign-in trouble message
91 |
92 | Forgot email or trouble signing in? Get help.
93 |
94 | )}
95 |
96 | {`Click ${createUser ? "Sign In" : "Sign Up"} to agree to Medium’s Terms of Service and acknowledge that Medium’s Privacy Policy applies to you.`}
97 |
98 |
99 |
100 |
101 | );
102 | }
103 |
104 | // Button component
105 | const Button = ({ icon, text, click }) => {
106 | return (
107 |
108 | {icon} {text}
109 |
110 | );
111 | };
112 |
113 | export default Auth;
114 |
--------------------------------------------------------------------------------
/src/Components/pages/Profile/EditProfile.jsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState, useEffect } from "react";
2 | import Modal from "../../../utils/Modal";
3 | import { FaTimes } from "react-icons/fa";
4 | import profileImg from "../../../assets/img/profile.jpg";
5 | import { storage, db, uploadBytes, getDownloadURL, ref, doc } from "../../../Firebase/Firebase-config.js";
6 | import { toast } from "react-toastify";
7 | import { updateDoc } from "firebase/firestore";
8 |
9 | function EditProfile({ editModal, setEditModal, getAllUsersData }) {
10 | const imgReference = useRef(null);
11 | const [imgURL, setImgURL] = useState("");
12 | const [loading, setLoading] = useState(false);
13 | const [form, setForm] = useState({
14 | username: "",
15 | bio: "",
16 | userImg: ""
17 | });
18 |
19 | const openFile = () => {
20 | imgReference.current.click();
21 | };
22 |
23 | const buttonStyle = "border border-gray-600 py-2 px-5 rounded-full text-green-600";
24 |
25 | useEffect(() => {
26 | if (getAllUsersData) {
27 | setForm({
28 | username: getAllUsersData.username,
29 | bio: getAllUsersData.bio,
30 | userImg: getAllUsersData.userImg,
31 | });
32 | }
33 | }, [getAllUsersData]);
34 |
35 | const saveForm = async () => {
36 | if (form.username.length < 1 || form.bio.length < 1) {
37 | toast.error("Please fill all the fields");
38 | return;
39 | }
40 | setLoading(true);
41 | const storageRef = ref(storage, `usersImg/${form.username}`);
42 | await uploadBytes(storageRef, form?.userImg);
43 | const imageURL = await getDownloadURL(storageRef);
44 | try {
45 | const uploadDocRef = doc(db, "users", getAllUsersData.id);
46 | await updateDoc(uploadDocRef, {
47 | username: form.username,
48 | bio: form.bio,
49 | userImg: imgURL ? imageURL : form.userImg
50 | });
51 | toast.success("Profile Updated");
52 | setLoading(false);
53 | setEditModal(false);
54 | } catch (error) {
55 | toast.error("Something went wrong");
56 | }
57 | };
58 |
59 | return (
60 |
61 |
62 |
63 |
Profile Information
64 | setEditModal(false)} className="text-xl">
65 |
66 |
67 |
68 |
69 | Photo
70 |
71 |
72 |
77 |
{
79 | setImgURL(URL.createObjectURL(e.target.files[0]));
80 | setForm({ ...form, userImg: e.target.files[0] });
81 | }}
82 | accept="image/jpg, image/png, image/jpeg"
83 | ref={imgReference}
84 | type="file"
85 | hidden
86 | />
87 |
88 |
89 |
90 |
91 | Update
92 |
93 | Remove
94 |
95 |
96 | Recommended: Square JPG, PNG, or GIF, at least 1000 pixels per side
97 |
98 |
99 |
100 |
101 |
133 |
134 | setEditModal(false)} className={buttonStyle}>
135 | Cancel
136 |
137 |
138 | Save
139 |
140 |
141 |
142 |
143 | );
144 | }
145 |
146 | export default EditProfile;
147 |
--------------------------------------------------------------------------------
/src/data.js:
--------------------------------------------------------------------------------
1 | export const nav = [
2 | {
3 | title: "Our story",
4 | path: "/",
5 | },
6 | {
7 | title: "Membership",
8 | path: "/",
9 | },
10 | {
11 | title: "Write",
12 | path: "/",
13 | },
14 | ];
15 |
16 | export const discover = [
17 | "Technology",
18 | "Study",
19 | "Programming",
20 | "Sport",
21 | "Knowledge",
22 | "Self Improvement",
23 | "Relationships",
24 | "Machine Learning",
25 | "Politics",
26 | ];
27 |
28 | export const discoverActions = [
29 | "Help",
30 | "Status",
31 | "Writers",
32 | "Blog",
33 | "Careers",
34 | "Privacy",
35 | "Terms",
36 | "About",
37 | "Text to speech",
38 | "Teams",
39 | ];
40 |
41 | export const recommendedPosts = [
42 | {
43 | postId: 1,
44 | user: "Tara Haelle",
45 | title:
46 | "We’re starting to understand more of what causes long COVID brain fog",
47 | subtitle: "Do this instead",
48 | desc: `Not only did a new study identify two blood proteins linked to cognitive difficulties a year
49 | after COVID-19 infection, but the What is Lorem Ipsum?
50 | Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
51 | industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type
52 | and scrambled it to make a type specimen book. It has survived not only five centuries, but also
53 | the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the
54 | 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with
55 | desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
56 | Why do we use it?
57 | It is a long established fact that a reader will be distracted by the readable content of a page
58 | when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal
59 | distribution of letters, as opposed to using 'Content here, content here', making it look like
60 | readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as
61 | their default model text, and a search for 'lorem ipsum' will uncover many web sites still in
62 | their infancy. Various versions have evolved over the years, sometimes by accident, sometimes
63 | on purpose (injected humour and the like).
64 | Where does it come from?
65 | Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of
66 | classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a
67 | Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin
68 | words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in
69 | classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10
70 | .32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero,
71 | written in 45 BC. This book is a treatise on the theory of ethics, very popular during the
72 | Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a
73 | line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested.
74 | Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced
75 | in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.`,
76 | date: `Sep 13`,
77 | readTime: "7 min read",
78 | postImg:
79 | "https://miro.medium.com/v2/resize:fill:300:201/1*6nZUT6CkYE1frUF8eAVphw.jpeg",
80 | userImg:
81 | "https://fiverr-res.cloudinary.com/images/t_main1,q_auto,f_auto,q_auto,f_auto/gigs/142819271/original/09dafa4104fa6aeca4e62f33326be4933ae7ccac/create-cartoon-profile-picture-abd7.jpg",
82 | },
83 | {
84 | postId: 2,
85 | user: "Tara Haelle",
86 | title:
87 | "We’re starting to understand more of what causes long COVID brain fog",
88 | subtitle: "Do this instead",
89 | desc: `Not only did a new study identify two blood proteins linked to cognitive difficulties a year
90 | after COVID-19 infection, but the What is Lorem Ipsum?
91 | Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
92 | industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type
93 | and scrambled it to make a type specimen book. It has survived not only five centuries, but also
94 | the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the
95 | 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with
96 | desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
97 | Why do we use it?
98 | It is a long established fact that a reader will be distracted by the readable content of a page
99 | when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal
100 | distribution of letters, as opposed to using 'Content here, content here', making it look like
101 | readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as
102 | their default model text, and a search for 'lorem ipsum' will uncover many web sites still in
103 | their infancy. Various versions have evolved over the years, sometimes by accident, sometimes
104 | on purpose (injected humour and the like).
105 | Where does it come from?
106 | Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of
107 | classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a
108 | Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin
109 | words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in
110 | classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10
111 | .32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero,
112 | written in 45 BC. This book is a treatise on the theory of ethics, very popular during the
113 | Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a
114 | line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested.
115 | Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced
116 | in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.`,
117 | date: `Sep 13`,
118 | readTime: "7 min read",
119 | postImg:
120 | "https://miro.medium.com/v2/resize:fill:300:201/1*6nZUT6CkYE1frUF8eAVphw.jpeg",
121 | userImg:
122 | "https://fiverr-res.cloudinary.com/images/t_main1,q_auto,f_auto,q_auto,f_auto/gigs/142819271/original/09dafa4104fa6aeca4e62f33326be4933ae7ccac/create-cartoon-profile-picture-abd7.jpg",
123 | },
124 | ];
125 |
126 | export const users = [
127 | {
128 | userId: 1,
129 | username: "Benjamin Marie",
130 | bio: "Ph.D, research scientist in NLP/AI",
131 | userImg:
132 | "https://i.pinimg.com/736x/72/ea/af/72eaaf5c70436356bce53862c75c7eeb.jpg",
133 | },
134 | {
135 | userId: 2,
136 | username: "Benjamin Marie",
137 | bio: "Ph.D, research scientist in NLP/AI",
138 | userImg:
139 | "https://i.pinimg.com/736x/72/ea/af/72eaaf5c70436356bce53862c75c7eeb.jpg",
140 | },
141 | {
142 | userId: 3,
143 | username: "Benjamin Marie",
144 | bio: "Ph.D, research scientist in NLP/AI",
145 | userImg:
146 | "https://i.pinimg.com/736x/72/ea/af/72eaaf5c70436356bce53862c75c7eeb.jpg",
147 | },
148 | ];
149 |
150 | export const comments = [
151 | {
152 | id: 1,
153 | username: "username",
154 | userImg:
155 | "https://i.pinimg.com/736x/72/ea/af/72eaaf5c70436356bce53862c75c7eeb.jpg",
156 | text: "Nice post",
157 | created: new Date(),
158 | },
159 | {
160 | id: 2,
161 | username: "username",
162 | userImg:
163 | "https://i.pinimg.com/736x/72/ea/af/72eaaf5c70436356bce53862c75c7eeb.jpg",
164 | text: "Nice post Lorem ipsum, dolor sit amet consectetur adipisicing elit. Exercitationem obcaecati totam officia doloremque est reprehenderit ipsam nam optio quisquam velit mollitia aliquid rem, eos repellat natus aliquam voluptatibus laborum, ratione assumenda earum. Beatae ratione recusandae laboriosam quo! Error quaerat esse tempore voluptatibus, aliquid possimus consequuntur doloremque mollitia dicta! Voluptate iure ut fugiat, animi quibusdam mollitia, necessitatibus laborum magnam facere cupiditate amet culpa aspernatur libero dolore porro expedita quia? Tempora modi perferendis natus impedit architecto, odio vitae! Impedit reiciendis, soluta laudantium non aut aliquid dolor. Omnis dolore eaque reiciendis eveniet, eius, facere eligendi ipsum atque iusto magnam deleniti blanditiis, corporis repudiandae?",
165 | created: new Date(),
166 | },
167 | ];
168 |
--------------------------------------------------------------------------------