├── .firebase
└── hosting.ZGlzdA.cache
├── .firebaserc
├── .gitignore
├── Readme.md
├── firebase.json
├── index.html
├── package-lock.json
├── package.json
├── public
├── 404.html
└── vite.svg
├── src
├── Pages
│ ├── Connections.jsx
│ ├── Home.jsx
│ ├── Login.jsx
│ ├── Profile.jsx
│ └── Register.jsx
├── Routes
│ └── index.jsx
├── Sass
│ ├── ConnectionsComponent.scss
│ ├── HomeComponent.scss
│ └── LoginComponent.scss
├── api
│ ├── AuthAPI.jsx
│ ├── FirestoreAPI.jsx
│ └── ImageUpload.jsx
├── assets
│ ├── linkedinLogo.png
│ ├── react.svg
│ └── user.png
├── components
│ ├── ConnectionsComponent.jsx
│ ├── HomeComponent.jsx
│ ├── LoginComponent.jsx
│ ├── ProfileComponent.jsx
│ ├── RegisterComponent.jsx
│ └── common
│ │ ├── Button
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── ConnectedUsers
│ │ └── index.jsx
│ │ ├── FileUploadModal
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── LikeButton
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── Loader
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── Modal
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── PostUpdate
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── PostsCard
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── ProfileCard
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── ProfileEdit
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── ProfilePopup
│ │ ├── index.jsx
│ │ └── index.scss
│ │ ├── SearchUsers
│ │ ├── index.jsx
│ │ └── index.scss
│ │ └── Topbar
│ │ ├── index.jsx
│ │ └── index.scss
├── firebaseConfig.js
├── helpers
│ ├── getUniqueId.jsx
│ ├── useMoment.jsx
│ └── useNavigate.jsx
├── index.scss
├── layouts
│ ├── ConnectionLayout.jsx
│ ├── HomeLayout.jsx
│ └── ProfileLayout.jsx
└── main.jsx
└── vite.config.js
/.firebase/hosting.ZGlzdA.cache:
--------------------------------------------------------------------------------
1 | 404.html,1677956389330,daa499dd96d8229e73235345702ba32f0793f0c8e5c0d30e40e37a5872be57aa
2 | vite.svg,1675669638367,59ec4b6085a0cb1bf712a5e48dd5f35b08e34830d49c2026c18241be04e05d5a
3 | index.html,1677959444529,6cc23cb4c31261f47e3da8ad7c836f3c4225f527ee46ba89167c073010c3e605
4 | assets/index-738cf444.css,1677959444529,caae3544d9ac5d202a3867858e84eb4b66a8311ec436c27d675633a8f50d4d20
5 | assets/linkedinLogo-412a7a29.png,1677959444529,be9cdcfd85ba97a250fdf3772952753ddef2508aa5c9071636e2fb4bd2becd90
6 | assets/index-28c6a808.js,1677959444530,3194428b72113e1064cd1759e4ffdc201ab5b0ba5b3d3d5146c0686a85c814ce
7 |
--------------------------------------------------------------------------------
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "linkedin-clone-a077e"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 | firebaseConfig.js*
10 |
11 | node_modules
12 | dist
13 |
14 | dist-ssr
15 | *.local
16 |
17 | # Editor directories and files
18 | .vscode/*
19 | !.vscode/extensions.json
20 | .idea
21 | .DS_Store
22 | *.suo
23 | *.ntvs*
24 | *.njsproj
25 | *.sln
26 | *.sw?
27 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Features
2 |
3 | Link: https://linkedin-clone-a077e.web.app
4 |
5 | 1. Authentication
6 | 2. Start a Post
7 | 3. Update a Post
8 | 4. Delete a Post
9 | 5. Add Connections
10 | 6. Like and Comment on a Post
11 | 7. Update Profile
12 | 8. See Other Profiles
13 | 9. Add a Profile Picture
14 | 10. Add Post Images
15 | 11. Search For Other Users
16 |
17 | If you want to learn how to build this, follow my tutorial here:
18 |
19 | Linkedin Clone - React and Firebase Full Tutorial.
20 |
21 | Check the playlist for the videos: https://lnkd.in/d6JvCm-t
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "public": "dist",
4 | "ignore": [
5 | "firebase.json",
6 | "**/.*",
7 | "**/node_modules/**"
8 | ]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Linkedin Clone!
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "linkedin-clone",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "antd": "^5.2.1",
13 | "firebase": "^9.17.1",
14 | "firebase-tools": "^11.24.0",
15 | "localforage": "^1.10.0",
16 | "match-sorter": "^6.3.1",
17 | "moment": "^2.29.4",
18 | "quill-emoji": "^0.2.0",
19 | "react": "^18.2.0",
20 | "react-dom": "^18.2.0",
21 | "react-google-button": "^0.7.2",
22 | "react-icons": "^4.7.1",
23 | "react-quill": "^2.0.0",
24 | "react-router-dom": "^6.8.1",
25 | "react-toastify": "^9.1.1",
26 | "react-uuid": "^2.0.0",
27 | "sass": "^1.58.2",
28 | "sort-by": "^1.2.0"
29 | },
30 | "devDependencies": {
31 | "@types/react": "^18.0.27",
32 | "@types/react-dom": "^18.0.10",
33 | "@vitejs/plugin-react": "^3.1.0",
34 | "vite": "^4.1.0"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Not Found
7 |
8 |
23 |
24 |
25 |
26 |
404
27 |
Page Not Found
28 |
The specified file was not found on this website. Please check the URL for mistakes and try again.
29 |
Why am I seeing this?
30 |
This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html
file in your project's configured public
directory.
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Pages/Connections.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import ConnectionsComponent from "../components/ConnectionsComponent";
3 | import { onAuthStateChanged } from "firebase/auth";
4 | import { useNavigate } from "react-router-dom";
5 | import { auth } from "../firebaseConfig";
6 | import Loader from "../components/common/Loader";
7 |
8 | export default function Connections({ currentUser }) {
9 | const [loading, setLoading] = useState(true);
10 | let navigate = useNavigate();
11 | useEffect(() => {
12 | onAuthStateChanged(auth, (res) => {
13 | if (!res?.accessToken) {
14 | navigate("/");
15 | } else {
16 | setLoading(false);
17 | }
18 | });
19 | }, []);
20 | return loading ? (
21 |
22 | ) : (
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/Pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import HomeComponent from "../components/HomeComponent";
3 | import { onAuthStateChanged } from "firebase/auth";
4 | import { useNavigate } from "react-router-dom";
5 | import { auth } from "../firebaseConfig";
6 | import Loader from "../components/common/Loader";
7 |
8 | export default function Home({ currentUser }) {
9 | const [loading, setLoading] = useState(true);
10 | let navigate = useNavigate();
11 | useEffect(() => {
12 | onAuthStateChanged(auth, (res) => {
13 | if (!res?.accessToken) {
14 | navigate("/");
15 | } else {
16 | setLoading(false);
17 | }
18 | });
19 | }, []);
20 | return loading ? : ;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import LoginComponent from "../components/LoginComponent";
3 | import { onAuthStateChanged } from "firebase/auth";
4 | import { useNavigate } from "react-router-dom";
5 | import { auth } from "../firebaseConfig";
6 | import Loader from "../components/common/Loader";
7 |
8 | export default function Login() {
9 | const [loading, setLoading] = useState(true);
10 | let navigate = useNavigate();
11 | useEffect(() => {
12 | onAuthStateChanged(auth, (res) => {
13 | if (res?.accessToken) {
14 | navigate("/home");
15 | } else {
16 | setLoading(false);
17 | }
18 | });
19 | }, []);
20 | return loading ? : ;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pages/Profile.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import ProfileComponent from "../components/ProfileComponent";
3 | import { onAuthStateChanged } from "firebase/auth";
4 | import { useNavigate } from "react-router-dom";
5 | import { auth } from "../firebaseConfig";
6 | import Loader from "../components/common/Loader";
7 |
8 | export default function Profile({ currentUser }) {
9 | const [loading, setLoading] = useState(true);
10 | let navigate = useNavigate();
11 | useEffect(() => {
12 | onAuthStateChanged(auth, (res) => {
13 | if (!res?.accessToken) {
14 | navigate("/");
15 | } else {
16 | setLoading(false);
17 | }
18 | });
19 | }, []);
20 | return loading ? : ;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pages/Register.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import RegisterComponent from "../components/RegisterComponent";
3 |
4 | export default function Register() {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/src/Routes/index.jsx:
--------------------------------------------------------------------------------
1 | import { createBrowserRouter } from "react-router-dom";
2 | import Login from "../Pages/Login";
3 | import Register from "../Pages/Register";
4 | import HomeLayout from "../layouts/HomeLayout";
5 | import ProfileLayout from "../layouts/ProfileLayout";
6 | import ConnectionLayout from "../layouts/ConnectionLayout";
7 |
8 | export const router = createBrowserRouter([
9 | {
10 | path: "/",
11 | element: ,
12 | },
13 | {
14 | path: "/register",
15 | element: ,
16 | },
17 | {
18 | path: "/home",
19 | element: ,
20 | },
21 | {
22 | path: "/profile",
23 | element: ,
24 | },
25 | {
26 | path: "/connections",
27 | element: ,
28 | },
29 | ]);
30 |
--------------------------------------------------------------------------------
/src/Sass/ConnectionsComponent.scss:
--------------------------------------------------------------------------------
1 | .connections-main {
2 | display: grid;
3 | grid-template-columns: auto auto;
4 | gap: 10px;
5 | justify-content: center;
6 | align-items: center;
7 | padding: 10px;
8 | text-align: center;
9 | margin: 30px;
10 | border: 1px solid #bbbbbb;
11 | background-color: white;
12 | border-radius: 10px;
13 | .grid-child {
14 | border: 1px solid #bbbbbb;
15 | width: 250px;
16 | height: 330px;
17 | margin: 10px;
18 | padding: 10px;
19 | display: flex;
20 |
21 | align-items: center;
22 | flex-direction: column;
23 | border-radius: 10px;
24 | position: relative;
25 | cursor: pointer;
26 |
27 | img {
28 | width: 150px;
29 | height: 150px;
30 | border-radius: 50%;
31 | object-fit: cover;
32 | margin-top: 20px;
33 | }
34 |
35 | .name {
36 | font-family: system-ui;
37 | font-size: 16px;
38 | font-weight: 600;
39 | }
40 |
41 | .headline {
42 | margin-top: -15px;
43 | font-family: system-ui;
44 | font-size: 15px;
45 | font-weight: 400;
46 | }
47 |
48 | button {
49 | width: 90%;
50 | height: 40px;
51 | position: absolute;
52 |
53 | bottom: 10px;
54 | cursor: pointer;
55 | background-color: white;
56 | color: #004284;
57 | border: 1px solid #004284;
58 | font-size: 16px;
59 | font-family: system-ui;
60 | border-radius: 30px;
61 | font-weight: 600;
62 | display: flex;
63 | justify-content: center;
64 | align-items: center;
65 | gap: 10px;
66 | }
67 |
68 | button:hover {
69 | border: 2px solid #004284;
70 | background-color: #bbdefb;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Sass/HomeComponent.scss:
--------------------------------------------------------------------------------
1 | .home-component {
2 | }
3 |
--------------------------------------------------------------------------------
/src/Sass/LoginComponent.scss:
--------------------------------------------------------------------------------
1 | $linkedinBlue1: #0072b1;
2 | $linkedinBlue2: #0073b1;
3 | $linkedinBlue3: #004c75;
4 |
5 | body {
6 | margin: 0;
7 | padding: 0;
8 | }
9 |
10 | .login-wrapper {
11 | background-color: white !important;
12 | height: 100vh;
13 | .login-wrapper-inner {
14 | display: flex;
15 | flex-direction: column;
16 | justify-content: center;
17 | align-items: center;
18 | }
19 | .heading {
20 | font-family: system-ui;
21 | font-weight: 500;
22 | color: rgba(0, 0, 0, 0.9);
23 | }
24 |
25 | .sub-heading {
26 | margin-top: -20px;
27 | font-family: system-ui;
28 | }
29 | }
30 |
31 | .login-btn {
32 | width: 300px;
33 | height: 50px;
34 | cursor: pointer;
35 | background-color: $linkedinBlue2;
36 | border-radius: 30px;
37 | outline: none;
38 | border: none;
39 | font-family: system-ui;
40 | font-weight: 600;
41 | color: #ffffff;
42 | font-size: 18px;
43 | margin-top: 20px;
44 | }
45 |
46 | .login-btn:hover {
47 | background-color: $linkedinBlue3;
48 | }
49 |
50 | .auth-inputs {
51 | display: flex;
52 | flex-direction: column;
53 | gap: 10px;
54 | width: 300px;
55 | }
56 |
57 | .linkedinLogo {
58 | width: 80px;
59 | }
60 |
61 | .hr-text {
62 | line-height: 1em;
63 | position: relative;
64 | outline: 0;
65 | border: 0;
66 | color: black;
67 | text-align: center;
68 | height: 1.5em;
69 | opacity: 0.5;
70 | &:before {
71 | content: "";
72 | // use the linear-gradient for the fading effect
73 | // use a solid background color for a solid bar
74 | background: linear-gradient(to right, transparent, #818078, transparent);
75 | position: absolute;
76 | left: 0;
77 | top: 50%;
78 | width: 100%;
79 | height: 1px;
80 | }
81 | &:after {
82 | content: attr(data-content);
83 | position: relative;
84 | display: inline-block;
85 | color: black;
86 |
87 | padding: 0 0.5em;
88 | line-height: 1.5em;
89 | // this is really the only tricky part, you need to specify the background color of the container element...
90 | color: #818078;
91 | background-color: #fcfcfa;
92 | }
93 | }
94 |
95 | .google-btn-container {
96 | display: flex;
97 | flex-direction: column;
98 | justify-content: center;
99 | align-items: center;
100 |
101 | .google-btn {
102 | width: 300px !important;
103 | }
104 |
105 | .go-to-signup {
106 | font-size: 18px;
107 | }
108 |
109 | .join-now {
110 | color: $linkedinBlue1;
111 | font-family: system-ui;
112 | cursor: pointer;
113 | font-size: 18px;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/api/AuthAPI.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | signInWithEmailAndPassword,
3 | createUserWithEmailAndPassword,
4 | GoogleAuthProvider,
5 | signInWithPopup,
6 | signOut,
7 | } from "firebase/auth";
8 | import { auth } from "../firebaseConfig";
9 |
10 | export const LoginAPI = (email, password) => {
11 | try {
12 | let response = signInWithEmailAndPassword(auth, email, password);
13 | return response;
14 | } catch (err) {
15 | return err;
16 | }
17 | };
18 |
19 | export const RegisterAPI = (email, password) => {
20 | try {
21 | let response = createUserWithEmailAndPassword(auth, email, password);
22 | return response;
23 | } catch (err) {
24 | return err;
25 | }
26 | };
27 |
28 | export const GoogleSignInAPI = () => {
29 | try {
30 | let googleProvider = new GoogleAuthProvider();
31 | let res = signInWithPopup(auth, googleProvider);
32 | return res;
33 | } catch (err) {
34 | return err;
35 | }
36 | };
37 |
38 | export const onLogout = () => {
39 | try {
40 | signOut(auth);
41 | } catch (err) {
42 | return err;
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/src/api/FirestoreAPI.jsx:
--------------------------------------------------------------------------------
1 | import { firestore } from "../firebaseConfig";
2 | import {
3 | addDoc,
4 | collection,
5 | onSnapshot,
6 | doc,
7 | updateDoc,
8 | query,
9 | where,
10 | setDoc,
11 | deleteDoc,
12 | orderBy,
13 | serverTimestamp,
14 | } from "firebase/firestore";
15 | import { toast } from "react-toastify";
16 |
17 | let postsRef = collection(firestore, "posts");
18 | let userRef = collection(firestore, "users");
19 | let likeRef = collection(firestore, "likes");
20 | let commentsRef = collection(firestore, "comments");
21 | let connectionRef = collection(firestore, "connections");
22 |
23 | export const postStatus = (object) => {
24 | addDoc(postsRef, object)
25 | .then(() => {
26 | toast.success("Post has been added successfully");
27 | })
28 | .catch((err) => {
29 | console.log(err);
30 | });
31 | };
32 |
33 | export const getStatus = (setAllStatus) => {
34 | const q = query(postsRef, orderBy("timeStamp"));
35 | onSnapshot(q, (response) => {
36 | setAllStatus(
37 | response.docs.map((docs) => {
38 | return { ...docs.data(), id: docs.id };
39 | })
40 | );
41 | });
42 | };
43 |
44 | export const getAllUsers = (setAllUsers) => {
45 | onSnapshot(userRef, (response) => {
46 | setAllUsers(
47 | response.docs.map((docs) => {
48 | return { ...docs.data(), id: docs.id };
49 | })
50 | );
51 | });
52 | };
53 |
54 | export const getSingleStatus = (setAllStatus, id) => {
55 | const singlePostQuery = query(postsRef, where("userID", "==", id));
56 | onSnapshot(singlePostQuery, (response) => {
57 | setAllStatus(
58 | response.docs.map((docs) => {
59 | return { ...docs.data(), id: docs.id };
60 | })
61 | );
62 | });
63 | };
64 |
65 | export const getSingleUser = (setCurrentUser, email) => {
66 | const singleUserQuery = query(userRef, where("email", "==", email));
67 | onSnapshot(singleUserQuery, (response) => {
68 | setCurrentUser(
69 | response.docs.map((docs) => {
70 | return { ...docs.data(), id: docs.id };
71 | })[0]
72 | );
73 | });
74 | };
75 |
76 | export const postUserData = (object) => {
77 | addDoc(userRef, object)
78 | .then(() => {})
79 | .catch((err) => {
80 | console.log(err);
81 | });
82 | };
83 |
84 | export const getCurrentUser = (setCurrentUser) => {
85 | onSnapshot(userRef, (response) => {
86 | setCurrentUser(
87 | response.docs
88 | .map((docs) => {
89 | return { ...docs.data(), id: docs.id };
90 | })
91 | .filter((item) => {
92 | return item.email === localStorage.getItem("userEmail");
93 | })[0]
94 | );
95 | });
96 | };
97 |
98 | export const editProfile = (userID, payload) => {
99 | let userToEdit = doc(userRef, userID);
100 |
101 | updateDoc(userToEdit, payload)
102 | .then(() => {
103 | toast.success("Profile has been updated successfully");
104 | })
105 | .catch((err) => {
106 | console.log(err);
107 | });
108 | };
109 |
110 | export const likePost = (userId, postId, liked) => {
111 | try {
112 | let docToLike = doc(likeRef, `${userId}_${postId}`);
113 | if (liked) {
114 | deleteDoc(docToLike);
115 | } else {
116 | setDoc(docToLike, { userId, postId });
117 | }
118 | } catch (err) {
119 | console.log(err);
120 | }
121 | };
122 |
123 | export const getLikesByUser = (userId, postId, setLiked, setLikesCount) => {
124 | try {
125 | let likeQuery = query(likeRef, where("postId", "==", postId));
126 |
127 | onSnapshot(likeQuery, (response) => {
128 | let likes = response.docs.map((doc) => doc.data());
129 | let likesCount = likes?.length;
130 |
131 | const isLiked = likes.some((like) => like.userId === userId);
132 |
133 | setLikesCount(likesCount);
134 | setLiked(isLiked);
135 | });
136 | } catch (err) {
137 | console.log(err);
138 | }
139 | };
140 |
141 | export const postComment = (postId, comment, timeStamp, name) => {
142 | try {
143 | addDoc(commentsRef, {
144 | postId,
145 | comment,
146 | timeStamp,
147 | name,
148 | });
149 | } catch (err) {
150 | console.log(err);
151 | }
152 | };
153 |
154 | export const getComments = (postId, setComments) => {
155 | try {
156 | let singlePostQuery = query(commentsRef, where("postId", "==", postId));
157 |
158 | onSnapshot(singlePostQuery, (response) => {
159 | const comments = response.docs.map((doc) => {
160 | return {
161 | id: doc.id,
162 | ...doc.data(),
163 | };
164 | });
165 |
166 | setComments(comments);
167 | });
168 | } catch (err) {
169 | console.log(err);
170 | }
171 | };
172 |
173 | export const updatePost = (id, status, postImage) => {
174 | let docToUpdate = doc(postsRef, id);
175 | try {
176 | updateDoc(docToUpdate, { status, postImage });
177 | toast.success("Post has been updated!");
178 | } catch (err) {
179 | console.log(err);
180 | }
181 | };
182 |
183 | export const deletePost = (id) => {
184 | let docToDelete = doc(postsRef, id);
185 | try {
186 | deleteDoc(docToDelete);
187 | toast.success("Post has been Deleted!");
188 | } catch (err) {
189 | console.log(err);
190 | }
191 | };
192 |
193 | export const addConnection = (userId, targetId) => {
194 | try {
195 | let connectionToAdd = doc(connectionRef, `${userId}_${targetId}`);
196 |
197 | setDoc(connectionToAdd, { userId, targetId });
198 |
199 | toast.success("Connection Added!");
200 | } catch (err) {
201 | console.log(err);
202 | }
203 | };
204 |
205 | export const getConnections = (userId, targetId, setIsConnected) => {
206 | try {
207 | let connectionsQuery = query(
208 | connectionRef,
209 | where("targetId", "==", targetId)
210 | );
211 |
212 | onSnapshot(connectionsQuery, (response) => {
213 | let connections = response.docs.map((doc) => doc.data());
214 |
215 | const isConnected = connections.some(
216 | (connection) => connection.userId === userId
217 | );
218 |
219 | setIsConnected(isConnected);
220 | });
221 | } catch (err) {
222 | console.log(err);
223 | }
224 | };
225 |
--------------------------------------------------------------------------------
/src/api/ImageUpload.jsx:
--------------------------------------------------------------------------------
1 | import { storage } from "../firebaseConfig";
2 | import { ref, getDownloadURL, uploadBytesResumable } from "firebase/storage";
3 | import { editProfile } from "./FirestoreAPI";
4 |
5 | export const uploadImage = (
6 | file,
7 | id,
8 | setModalOpen,
9 | setProgress,
10 | setCurrentImage
11 | ) => {
12 | const profilePicsRef = ref(storage, `profileImages/${file.name}`);
13 | const uploadTask = uploadBytesResumable(profilePicsRef, file);
14 |
15 | uploadTask.on(
16 | "state_changed",
17 | (snapshot) => {
18 | const progress = Math.round(
19 | (snapshot.bytesTransferred / snapshot.totalBytes) * 100
20 | );
21 |
22 | setProgress(progress);
23 | },
24 | (error) => {
25 | console.error(err);
26 | },
27 | () => {
28 | getDownloadURL(uploadTask.snapshot.ref).then((response) => {
29 | editProfile(id, { imageLink: response });
30 | setModalOpen(false);
31 | setCurrentImage({});
32 | setProgress(0);
33 | });
34 | }
35 | );
36 | };
37 |
38 | export const uploadPostImage = (file, setPostImage, setProgress) => {
39 | const postPicsRef = ref(storage, `postImages/${file.name}`);
40 | const uploadTask = uploadBytesResumable(postPicsRef, file);
41 |
42 | uploadTask.on(
43 | "state_changed",
44 | (snapshot) => {
45 | const progress = Math.round(
46 | (snapshot.bytesTransferred / snapshot.totalBytes) * 100
47 | );
48 |
49 | setProgress(progress);
50 | },
51 | (error) => {
52 | console.error(err);
53 | },
54 | () => {
55 | getDownloadURL(uploadTask.snapshot.ref).then((response) => {
56 | setPostImage(response);
57 | });
58 | }
59 | );
60 | };
61 |
--------------------------------------------------------------------------------
/src/assets/linkedinLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishant-666/Linkedin-clone-using-React-and-Firebase-9/bd687ceea3813d6bae07e8332210d09db9c534ee/src/assets/linkedinLogo.png
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nishant-666/Linkedin-clone-using-React-and-Firebase-9/bd687ceea3813d6bae07e8332210d09db9c534ee/src/assets/user.png
--------------------------------------------------------------------------------
/src/components/ConnectionsComponent.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { getAllUsers, addConnection } from "../api/FirestoreAPI";
3 | import ConnectedUsers from "./common/ConnectedUsers";
4 | import "../Sass/ConnectionsComponent.scss";
5 |
6 | export default function ConnectionsComponent({ currentUser }) {
7 | const [users, setUsers] = useState([]);
8 | const getCurrentUser = (id) => {
9 | addConnection(currentUser.id, id);
10 | };
11 | useEffect(() => {
12 | getAllUsers(setUsers);
13 | }, []);
14 |
15 | return users.length > 1 ? (
16 |
17 | {users.map((user) => {
18 | return user.id === currentUser.id ? (
19 | <>>
20 | ) : (
21 |
26 | );
27 | })}
28 |
29 | ) : (
30 | No Connections to Add!
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/HomeComponent.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "../Sass/HomeComponent.scss";
3 | import PostStatus from "./common/PostUpdate";
4 |
5 | export default function HomeComponent({ currentUser }) {
6 | return (
7 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/LoginComponent.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { LoginAPI } from "../api/AuthAPI";
3 | import LinkedinLogo from "../assets/linkedinLogo.png";
4 | import { useNavigate } from "react-router-dom";
5 | import "../Sass/LoginComponent.scss";
6 | import { toast } from "react-toastify";
7 |
8 | export default function LoginComponent() {
9 | let navigate = useNavigate();
10 | const [credentails, setCredentials] = useState({});
11 | const login = async () => {
12 | try {
13 | let res = await LoginAPI(credentails.email, credentails.password);
14 | toast.success("Signed In to Linkedin!");
15 | localStorage.setItem("userEmail", res.user.email);
16 | navigate("/home");
17 | } catch (err) {
18 | console.log(err);
19 | toast.error("Please Check your Credentials");
20 | }
21 | };
22 |
23 | return (
24 |
25 |

26 |
27 |
28 |
Sign in
29 |
Stay updated on your professional world
30 |
31 |
32 |
34 | setCredentials({ ...credentails, email: event.target.value })
35 | }
36 | type="email"
37 | className="common-input"
38 | placeholder="Email or Phone"
39 | />
40 |
42 | setCredentials({ ...credentails, password: event.target.value })
43 | }
44 | type="password"
45 | className="common-input"
46 | placeholder="Password"
47 | />
48 |
49 |
52 |
53 |
54 |
55 |
56 | New to LinkedIn?{" "}
57 | navigate("/register")}>
58 | Join now
59 |
60 |
61 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/ProfileComponent.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import ProfileCard from "./common/ProfileCard";
3 | import ProfileEdit from "./common/ProfileEdit";
4 |
5 | export default function ProfileComponent({ currentUser }) {
6 | const [isEdit, setisEdit] = useState(false);
7 |
8 | const onEdit = () => {
9 | setisEdit(!isEdit);
10 | };
11 | return (
12 |
13 | {isEdit ? (
14 |
15 | ) : (
16 |
17 | )}
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/RegisterComponent.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { RegisterAPI } from "../api/AuthAPI";
3 | import { postUserData } from "../api/FirestoreAPI";
4 | import LinkedinLogo from "../assets/linkedinLogo.png";
5 | import { useNavigate } from "react-router-dom";
6 | import { getUniqueID } from "../helpers/getUniqueId";
7 | import "../Sass/LoginComponent.scss";
8 | import { toast } from "react-toastify";
9 |
10 | export default function RegisterComponent() {
11 | let navigate = useNavigate();
12 | const [credentails, setCredentials] = useState({});
13 | const register = async () => {
14 | try {
15 | let res = await RegisterAPI(credentails.email, credentails.password);
16 | toast.success("Account Created!");
17 | postUserData({
18 | userID: getUniqueID(),
19 | name: credentails.name,
20 | email: credentails.email,
21 | imageLink:
22 | "https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=880&q=80",
23 | });
24 | navigate("/home");
25 | localStorage.setItem("userEmail", res.user.email);
26 | } catch (err) {
27 | console.log(err);
28 | toast.error("Cannot Create your Account");
29 | }
30 | };
31 |
32 | return (
33 |
34 |

35 |
36 |
69 |
70 |
71 |
72 | Already on LinkedIn?{" "}
73 | navigate("/")}>
74 | Sign in
75 |
76 |
77 |
78 |
79 | );
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/common/Button/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./index.scss";
3 |
4 | export default function Button({ title, onClick }) {
5 | return (
6 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/common/Button/index.scss:
--------------------------------------------------------------------------------
1 | .common-btn {
2 | width: 200px;
3 | height: 30px;
4 | cursor: pointer;
5 | background-color: whitesmoke;
6 | border: 2px solid #003365;
7 | color: #014488;
8 | border-radius: 30px;
9 | font-family: system-ui;
10 | font-weight: 600;
11 | font-size: 14px;
12 | margin: 7px 0;
13 | }
14 |
15 | .common-btn:hover {
16 | background-color: rgb(230, 230, 230);
17 | border: 2px solid #014488;
18 | color: #014488;
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/common/ConnectedUsers/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { AiOutlineUsergroupAdd } from "react-icons/ai";
3 | import { getConnections } from "../../../api/FirestoreAPI";
4 |
5 | export default function ConnectedUsers({ user, getCurrentUser, currentUser }) {
6 | const [isConnected, setIsConnected] = useState(false);
7 | useEffect(() => {
8 | getConnections(currentUser.id, user.id, setIsConnected);
9 | }, [currentUser.id, user.id]);
10 | return isConnected ? (
11 | <>>
12 | ) : (
13 |
14 |

15 |
{user.name}
16 |
{user.headline}
17 |
18 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/common/FileUploadModal/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button, Modal, Progress } from "antd";
3 | import "./index.scss";
4 |
5 | export default function FileUploadModal({
6 | modalOpen,
7 | setModalOpen,
8 | getImage,
9 | uploadImage,
10 | currentImage,
11 | progress,
12 | }) {
13 | return (
14 |
15 |
setModalOpen(false)}
20 | onCancel={() => setModalOpen(false)}
21 | footer={[
22 | ,
30 | ]}
31 | >
32 |
33 |
{currentImage.name}
34 |
37 | {progress === 0 ? (
38 | <>>
39 | ) : (
40 |
43 | )}
44 |
45 |
46 |
47 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/common/FileUploadModal/index.scss:
--------------------------------------------------------------------------------
1 | .image-upload-main {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | height: auto;
6 | flex-direction: column;
7 | label {
8 | border: 1px solid #212121;
9 | padding: 10px;
10 | cursor: pointer;
11 | font-family: system-ui;
12 | }
13 |
14 | .progress-bar {
15 | padding: 20px;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/common/LikeButton/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useState } from "react";
2 | import {
3 | likePost,
4 | getLikesByUser,
5 | postComment,
6 | getComments,
7 | } from "../../../api/FirestoreAPI";
8 | import { getCurrentTimeStamp } from "../../../helpers/useMoment";
9 | import "./index.scss";
10 | import { AiOutlineHeart, AiFillHeart, AiOutlineComment } from "react-icons/ai";
11 | import { BsFillHandThumbsUpFill, BsHandThumbsUp } from "react-icons/bs";
12 |
13 | export default function LikeButton({ userId, postId, currentUser }) {
14 | const [likesCount, setLikesCount] = useState(0);
15 | const [showCommentBox, setShowCommentBox] = useState(false);
16 | const [liked, setLiked] = useState(false);
17 | const [comment, setComment] = useState("");
18 | const [comments, setComments] = useState([]);
19 | const handleLike = () => {
20 | likePost(userId, postId, liked);
21 | };
22 | const getComment = (event) => {
23 | setComment(event.target.value);
24 | };
25 |
26 | const addComment = () => {
27 | postComment(postId, comment, getCurrentTimeStamp("LLL"), currentUser?.name);
28 | setComment("");
29 | };
30 | useMemo(() => {
31 | getLikesByUser(userId, postId, setLiked, setLikesCount);
32 | getComments(postId, setComments);
33 | }, [userId, postId]);
34 | return (
35 |
36 |
{likesCount} People Like this Post
37 |
38 |
39 |
40 |
41 |
42 | {liked ? (
43 |
44 | ) : (
45 |
46 | )}
47 |
48 |
Like
49 |
50 |
setShowCommentBox(!showCommentBox)}
53 | >
54 | {
55 |
59 | }
60 |
61 |
Comments
62 |
63 |
64 | {showCommentBox ? (
65 | <>
66 |
73 |
76 |
77 | {comments.length > 0 ? (
78 | comments.map((comment) => {
79 | return (
80 |
81 |
{comment.name}
82 |
{comment.comment}
83 |
84 |
{comment.timeStamp}
85 | {/*
86 |
•
87 | */}
88 |
89 | );
90 | })
91 | ) : (
92 | <>>
93 | )}
94 | >
95 | ) : (
96 | <>>
97 | )}
98 |
99 | );
100 | }
101 |
--------------------------------------------------------------------------------
/src/components/common/LikeButton/index.scss:
--------------------------------------------------------------------------------
1 | .like-container {
2 | display: flex;
3 | justify-content: center;
4 |
5 | flex-direction: column;
6 | gap: 5px;
7 | padding: 7px 10px;
8 | cursor: pointer;
9 | margin-bottom: -20px;
10 |
11 | .like-comment {
12 | display: grid;
13 | grid-template-columns: auto auto auto auto;
14 | margin-top: 5px;
15 | }
16 |
17 | .comment-input {
18 | height: 40px;
19 | background-color: whitesmoke;
20 | padding-left: 10px;
21 | border-radius: 30px;
22 | border: 1px solid #737373;
23 | font-family: system-ui;
24 | color: #4b4b4b;
25 | font-size: 16px;
26 | }
27 |
28 | .comment-input:focus {
29 | outline: 1px solid #4b4b4b;
30 | color: #4b4b4b;
31 | }
32 | .add-comment-btn {
33 | width: 150px;
34 | height: 35px;
35 | background-color: #0a66c2;
36 | color: white;
37 | outline: none;
38 | border: none;
39 | border-radius: 30px;
40 | cursor: pointer;
41 | margin-top: 15px;
42 | margin-bottom: 15px;
43 | }
44 | .all-comments {
45 | display: flex;
46 | justify-content: center;
47 | flex-direction: column;
48 | background-color: rgb(227, 227, 227);
49 | border-radius: 10px;
50 | position: relative;
51 | margin: 10px;
52 | .name {
53 | color: #212121;
54 | text-decoration: none;
55 | margin-left: 10px;
56 | margin-top: 10px;
57 | }
58 |
59 | .comment {
60 | margin-left: 10px;
61 | margin-top: 0px;
62 | }
63 |
64 | .timestamp {
65 | position: absolute;
66 | right: 10px;
67 | top: 10px;
68 | }
69 | }
70 | .hr-line {
71 | margin-top: -17px;
72 | hr {
73 | border-top: 1px solid rgb(181, 181, 181);
74 | }
75 | }
76 | p {
77 | font-family: Inter, sans-serif;
78 | font-weight: 400;
79 | color: rgba(58, 58, 58, 0.835);
80 | font-size: 14px;
81 | margin-left: 5px;
82 | }
83 |
84 | .likes-comment-inner {
85 | display: flex;
86 | align-items: center;
87 | margin-top: -20px;
88 | .blue {
89 | color: #0a66c2;
90 | }
91 |
92 | .black {
93 | color: rgba(58, 58, 58, 0.835);
94 | }
95 | p {
96 | font-family: Inter, sans-serif;
97 | font-weight: 600;
98 | color: rgba(58, 58, 58, 0.835);
99 | font-size: 16px;
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/components/common/Loader/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Space, Spin } from "antd";
3 | import "./index.scss";
4 |
5 | export default function Loader() {
6 | return (
7 |
8 |
Loading..Please Wait..
9 |
10 |
11 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/common/Loader/index.scss:
--------------------------------------------------------------------------------
1 | .loader {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | height: 100vh;
6 | flex-direction: column;
7 | gap: 10px;
8 |
9 | p {
10 | font-family: system-ui;
11 | font-weight: 500;
12 | color: rgba(0, 0, 0, 0.9);
13 | font-size: 20px;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/common/Modal/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Button, Modal, Progress } from "antd";
3 | import { AiOutlinePicture } from "react-icons/ai";
4 | import ReactQuill from "react-quill";
5 | import "./index.scss";
6 |
7 | const ModalComponent = ({
8 | modalOpen,
9 | setModalOpen,
10 | sendStatus,
11 | setStatus,
12 | status,
13 | isEdit,
14 | updateStatus,
15 | uploadPostImage,
16 | setPostImage,
17 | postImage,
18 | currentPost,
19 | setCurrentPost,
20 | }) => {
21 | const [progress, setProgress] = useState(0);
22 | return (
23 | <>
24 | {
29 | setStatus("");
30 | setModalOpen(false);
31 | setPostImage("");
32 | setCurrentPost({});
33 | }}
34 | onCancel={() => {
35 | setStatus("");
36 | setModalOpen(false);
37 | setPostImage("");
38 | setCurrentPost({});
39 | }}
40 | footer={[
41 | ,
49 | ]}
50 | >
51 |
52 |
59 | {progress === 0 || progress === 100 ? (
60 | <>>
61 | ) : (
62 |
65 | )}
66 | {postImage?.length > 0 || currentPost?.postImage?.length ? (
67 |

72 | ) : (
73 | <>>
74 | )}
75 |
76 |
79 |
84 | uploadPostImage(event.target.files[0], setPostImage, setProgress)
85 | }
86 | />
87 |
88 | >
89 | );
90 | };
91 |
92 | export default ModalComponent;
93 |
--------------------------------------------------------------------------------
/src/components/common/Modal/index.scss:
--------------------------------------------------------------------------------
1 | .modal-input {
2 | border: none !important;
3 | background-color: white;
4 | outline: none;
5 | color: black;
6 | font-size: 16px;
7 | font-family: system-ui;
8 | width: 100%;
9 | resize: none;
10 | }
11 |
12 | .ql-container.ql-snow,
13 | .ql-toolbar.ql-snow {
14 | border: none !important;
15 | }
16 |
17 | .ql-container.ql-snow {
18 | font-size: 20px;
19 | font-family: system-ui;
20 | }
21 |
22 | .picture-icon {
23 | color: #0073b1;
24 | cursor: pointer;
25 | position: absolute;
26 | bottom: 20px;
27 | }
28 |
29 | .preview-image {
30 | width: 100%;
31 | margin-top: 20px;
32 | }
33 |
34 | .posts-body {
35 | display: flex;
36 | justify-content: center;
37 | flex-direction: column;
38 | align-items: center;
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/common/PostUpdate/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useMemo } from "react";
2 | import { postStatus, getStatus, updatePost } from "../../../api/FirestoreAPI";
3 | import { getCurrentTimeStamp } from "../../../helpers/useMoment";
4 | import ModalComponent from "../Modal";
5 | import { uploadPostImage } from "../../../api/ImageUpload";
6 | import { getUniqueID } from "../../../helpers/getUniqueId";
7 | import PostsCard from "../PostsCard";
8 | import "./index.scss";
9 |
10 | export default function PostStatus({ currentUser }) {
11 | const [modalOpen, setModalOpen] = useState(false);
12 | const [status, setStatus] = useState("");
13 | const [allStatuses, setAllStatus] = useState([]);
14 | const [currentPost, setCurrentPost] = useState({});
15 | const [isEdit, setIsEdit] = useState(false);
16 | const [postImage, setPostImage] = useState("");
17 |
18 | const sendStatus = async () => {
19 | let object = {
20 | status: status,
21 | timeStamp: getCurrentTimeStamp("LLL"),
22 | userEmail: currentUser.email,
23 | userName: currentUser.name,
24 | postID: getUniqueID(),
25 | userID: currentUser.id,
26 | postImage: postImage,
27 | };
28 | await postStatus(object);
29 | await setModalOpen(false);
30 | setIsEdit(false);
31 | await setStatus("");
32 | };
33 |
34 | const getEditData = (posts) => {
35 | setModalOpen(true);
36 | setStatus(posts?.status);
37 | setCurrentPost(posts);
38 | setIsEdit(true);
39 | };
40 |
41 | const updateStatus = () => {
42 | updatePost(currentPost.id, status, postImage);
43 | setModalOpen(false);
44 | };
45 |
46 | useMemo(() => {
47 | getStatus(setAllStatus);
48 | }, []);
49 |
50 | return (
51 |
52 |
53 |

54 |
{currentUser?.name}
55 |
{currentUser?.headline}
56 |
57 |
58 |

63 |
72 |
73 |
74 |
88 |
89 |
90 | {allStatuses.map((posts) => {
91 | return (
92 |
95 | );
96 | })}
97 |
98 |
99 | );
100 | }
101 |
--------------------------------------------------------------------------------
/src/components/common/PostUpdate/index.scss:
--------------------------------------------------------------------------------
1 | .post-status-main {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | flex-direction: column;
6 | position: relative;
7 | .user-details {
8 | width: 550px;
9 | height: auto;
10 | background-color: whitesmoke;
11 | margin-top: 100px;
12 | border: 1px solid #b7b7b7;
13 | border-radius: 7px;
14 | display: flex;
15 | flex-direction: column;
16 | align-items: center;
17 | img {
18 | width: 100px;
19 | height: 100px;
20 | object-fit: cover;
21 | border-radius: 50%;
22 | margin-top: -60px;
23 | border: 1px solid #b7b7b7;
24 | padding: 1px;
25 | }
26 |
27 | .name {
28 | font-weight: 600;
29 | font-family: system-ui;
30 | }
31 |
32 | .headline {
33 | font-family: system-ui;
34 | margin-top: -15px;
35 | }
36 | }
37 | .post-status {
38 | width: 550px;
39 | height: 120px;
40 | background-color: whitesmoke;
41 | margin-top: 30px;
42 | border: 1px solid #b7b7b7;
43 | border-radius: 7px;
44 | display: flex;
45 | justify-content: space-around;
46 | align-items: center;
47 |
48 | .post-image {
49 | width: 60px;
50 | height: 60px;
51 | object-fit: cover;
52 | border-radius: 50%;
53 | }
54 | .open-post-modal {
55 | width: 80%;
56 | height: 50px;
57 | text-align: left;
58 | color: rgba(84, 84, 84, 0.89);
59 | background-color: whitesmoke;
60 | outline: none;
61 | border: 1px solid #b7b7b7;
62 | border-radius: 30px;
63 | margin-left: -30px;
64 | cursor: pointer;
65 | padding: 15px;
66 | font-weight: 600;
67 | font-size: 14px;
68 | font-family: system-ui, sans-serif;
69 | }
70 |
71 | .open-post-modal:hover {
72 | background-color: #dcdbdb;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/components/common/PostsCard/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useState, useEffect } from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import { Button, Modal } from "antd";
4 | import { BsPencil, BsTrash } from "react-icons/bs";
5 | import {
6 | getCurrentUser,
7 | getAllUsers,
8 | deletePost,
9 | getConnections,
10 | } from "../../../api/FirestoreAPI";
11 | import LikeButton from "../LikeButton";
12 | import "./index.scss";
13 |
14 | export default function PostsCard({ posts, id, getEditData }) {
15 | let navigate = useNavigate();
16 | const [currentUser, setCurrentUser] = useState({});
17 | const [allUsers, setAllUsers] = useState([]);
18 | const [imageModal, setImageModal] = useState(false);
19 | const [isConnected, setIsConnected] = useState(false);
20 | useMemo(() => {
21 | getCurrentUser(setCurrentUser);
22 | getAllUsers(setAllUsers);
23 | }, []);
24 |
25 | useEffect(() => {
26 | getConnections(currentUser.id, posts.userID, setIsConnected);
27 | }, [currentUser.id, posts.userID]);
28 |
29 | return isConnected || currentUser.id === posts.userID ? (
30 |
31 |
32 | {currentUser.id === posts.userID ? (
33 |
34 | getEditData(posts)}
38 | />
39 | deletePost(posts.id)}
43 | />
44 |
45 | ) : (
46 | <>>
47 | )}
48 |
49 |

item.id === posts.userID)
55 | .map((item) => item.imageLink)[0]
56 | }
57 | />
58 |
59 |
62 | navigate("/profile", {
63 | state: { id: posts?.userID, email: posts.userEmail },
64 | })
65 | }
66 | >
67 | {allUsers.filter((user) => user.id === posts.userID)[0]?.name}
68 |
69 |
70 | {allUsers.filter((user) => user.id === posts.userID)[0]?.headline}
71 |
72 |
{posts.timeStamp}
73 |
74 |
75 | {posts.postImage ? (
76 |
![]()
setImageModal(true)}
78 | src={posts.postImage}
79 | className="post-image"
80 | alt="post-image"
81 | />
82 | ) : (
83 | <>>
84 | )}
85 |
89 |
90 |
95 |
96 |
setImageModal(false)}
100 | onCancel={() => setImageModal(false)}
101 | footer={[]}
102 | >
103 |
setImageModal(true)}
105 | src={posts.postImage}
106 | className="post-image modal"
107 | alt="post-image"
108 | />
109 |
110 |
111 | ) : (
112 | <>>
113 | );
114 | }
115 |
--------------------------------------------------------------------------------
/src/components/common/PostsCard/index.scss:
--------------------------------------------------------------------------------
1 | .posts-card {
2 | width: 550px;
3 | min-height: auto;
4 | background-color: whitesmoke;
5 | margin-top: 30px;
6 | border: 1px solid #b7b7b7;
7 | border-radius: 7px;
8 | display: flex;
9 | flex-direction: column;
10 | padding-bottom: 20px;
11 | .post-image-wrapper {
12 | display: flex;
13 | padding: 15px;
14 | gap: 10px;
15 | position: relative;
16 | }
17 | .profile-image {
18 | width: 70px;
19 | height: 70px;
20 | border-radius: 50%;
21 | object-fit: cover;
22 | }
23 |
24 | .name {
25 | font-family: system-ui, sans-serif;
26 | font-size: 18px;
27 | color: #212121;
28 | font-weight: 600;
29 | margin: 0px 0 0 0px;
30 | cursor: pointer;
31 | }
32 | .headline {
33 | font-family: system-ui, sans-serif;
34 | font-size: 15px;
35 | color: rgba(0, 0, 0, 0.6);
36 | font-weight: 500;
37 | margin: 0px 0 0 0px;
38 | }
39 | .timestamp {
40 | font-family: system-ui, sans-serif;
41 | font-size: 12px;
42 | color: rgba(0, 0, 0, 0.6);
43 | font-weight: 400;
44 | margin: 0px 0 0 0px;
45 | }
46 |
47 | .status {
48 | text-align: left;
49 | margin: 10px 0 0 10px;
50 | font-family: Inter, sans-serif;
51 | font-size: 18px;
52 | font-weight: 400;
53 | color: rgba(0, 0, 0, 0.9);
54 | }
55 | }
56 |
57 | .action-container {
58 | position: absolute;
59 | right: 20px;
60 | cursor: pointer;
61 | display: flex;
62 | top: 10px;
63 | .action-icon {
64 | color: #474747;
65 | padding: 10px;
66 | }
67 |
68 | .action-icon:hover {
69 | color: #000000;
70 | background-color: #b7b7b7;
71 | padding: 10px;
72 | border-radius: 50%;
73 | }
74 | }
75 |
76 | .post-image {
77 | cursor: pointer;
78 | width: 100%;
79 | }
80 |
81 | .modal {
82 | margin-top: 30px;
83 | }
84 |
--------------------------------------------------------------------------------
/src/components/common/ProfileCard/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useMemo } from "react";
2 | import { getSingleStatus, getSingleUser } from "../../../api/FirestoreAPI";
3 | import PostsCard from "../PostsCard";
4 | import { HiOutlinePencil } from "react-icons/hi";
5 | import { useLocation } from "react-router-dom";
6 | import FileUploadModal from "../FileUploadModal";
7 | import { uploadImage as uploadImageAPI } from "../../../api/ImageUpload";
8 | import "./index.scss";
9 |
10 | export default function ProfileCard({ onEdit, currentUser }) {
11 | let location = useLocation();
12 | const [allStatuses, setAllStatus] = useState([]);
13 | const [currentProfile, setCurrentProfile] = useState({});
14 | const [currentImage, setCurrentImage] = useState({});
15 | const [progress, setProgress] = useState(0);
16 | const [modalOpen, setModalOpen] = useState(false);
17 | const getImage = (event) => {
18 | setCurrentImage(event.target.files[0]);
19 | };
20 | console.log(currentProfile);
21 | const uploadImage = () => {
22 | uploadImageAPI(
23 | currentImage,
24 | currentUser.id,
25 | setModalOpen,
26 | setProgress,
27 | setCurrentImage
28 | );
29 | };
30 |
31 | useMemo(() => {
32 | if (location?.state?.id) {
33 | getSingleStatus(setAllStatus, location?.state?.id);
34 | }
35 |
36 | if (location?.state?.email) {
37 | getSingleUser(setCurrentProfile, location?.state?.email);
38 | }
39 | }, []);
40 |
41 | return (
42 | <>
43 |
51 |
52 | {currentUser.id === location?.state?.id ? (
53 |
54 |
55 |
56 | ) : (
57 | <>>
58 | )}
59 |
60 |
61 |
![]()
setModalOpen(true)}
64 | src={
65 | Object.values(currentProfile).length === 0
66 | ? currentUser.imageLink
67 | : currentProfile?.imageLink
68 | }
69 | alt="profile-image"
70 | />
71 |
72 | {Object.values(currentProfile).length === 0
73 | ? currentUser.name
74 | : currentProfile?.name}
75 |
76 |
77 | {Object.values(currentProfile).length === 0
78 | ? currentUser.headline
79 | : currentProfile?.headline}
80 |
81 | {(currentUser.city || currentUser.country) &&
82 | (currentProfile?.city || currentProfile?.country) ? (
83 |
84 | {Object.values(currentProfile).length === 0
85 | ? `${currentUser.city}, ${currentUser.country} `
86 | : `${currentProfile?.city}, ${currentUser.country}`}
87 |
88 | ) : (
89 | <>>
90 | )}
91 | {currentUser.website || currentProfile?.website ? (
92 |
101 | {Object.values(currentProfile).length === 0
102 | ? `${currentUser.website}`
103 | : currentProfile?.website}
104 |
105 | ) : (
106 | <>>
107 | )}
108 |
109 |
110 |
111 |
112 | {Object.values(currentProfile).length === 0
113 | ? currentUser.college
114 | : currentProfile?.college}
115 |
116 |
117 | {Object.values(currentProfile).length === 0
118 | ? currentUser.company
119 | : currentProfile?.company}
120 |
121 |
122 |
123 |
124 | {Object.values(currentProfile).length === 0
125 | ? currentUser.aboutMe
126 | : currentProfile?.aboutMe}
127 |
128 |
129 | {currentUser.skills || currentProfile?.skills ? (
130 |
131 | Skills:
132 | {Object.values(currentProfile).length === 0
133 | ? currentUser.skills
134 | : currentProfile?.skills}
135 |
136 | ) : (
137 | <>>
138 | )}
139 |
140 |
141 |
142 | {allStatuses?.map((posts) => {
143 | return (
144 |
147 | );
148 | })}
149 |
150 | >
151 | );
152 | }
153 |
--------------------------------------------------------------------------------
/src/components/common/ProfileCard/index.scss:
--------------------------------------------------------------------------------
1 | .profile-card {
2 | width: auto;
3 | height: auto;
4 | background-color: whitesmoke;
5 | margin: 30px;
6 | border-radius: 5px;
7 | padding: 20px;
8 | position: relative;
9 | .profile-info {
10 | display: flex;
11 | justify-content: space-between;
12 | .profile-image {
13 | width: 200px;
14 | height: 200px;
15 | object-fit: contain;
16 | border-radius: 50%;
17 | border: 2px solid #cacaca;
18 | padding: 5px;
19 | cursor: pointer;
20 | }
21 | .right-info {
22 | margin-top: 25px;
23 | margin-right: 20px;
24 | .college,
25 | .company {
26 | font-family: system-ui;
27 | color: rgba(0, 0, 0, 0.9);
28 | font-size: 14px;
29 | font-weight: 600;
30 | line-height: 30px;
31 | }
32 |
33 | .company {
34 | margin-top: -15px;
35 | }
36 | }
37 | }
38 |
39 | .website {
40 | color: #004c75 !important;
41 | -webkit-link: #004c75;
42 | }
43 | .skill-label {
44 | font-weight: 600;
45 | }
46 | .edit-btn {
47 | position: absolute;
48 | right: 20px;
49 |
50 | .edit-icon {
51 | cursor: pointer;
52 | padding: 10px;
53 | }
54 |
55 | .edit-icon:hover {
56 | background-color: rgb(197, 197, 197);
57 | padding: 10px;
58 | border-radius: 50%;
59 | }
60 | }
61 | .userName {
62 | font-family: system-ui;
63 | color: rgba(0, 0, 0, 0.9);
64 | font-weight: 600;
65 | font-size: 24px;
66 | margin-top: 10px;
67 | }
68 |
69 | .heading {
70 | margin-top: -25px;
71 | font-family: system-ui;
72 | font-size: 16px;
73 | font-weight: 400;
74 | color: rgba(0, 0, 0, 0.9);
75 | width: 320px;
76 | line-height: 20px;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/components/common/ProfileEdit/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { AiOutlineClose } from "react-icons/ai";
3 | import { editProfile } from "../../../api/FirestoreAPI";
4 | import "./index.scss";
5 |
6 | export default function ProfileEdit({ onEdit, currentUser }) {
7 | const [editInputs, setEditInputs] = useState(currentUser);
8 | const getInput = (event) => {
9 | let { name, value } = event.target;
10 | let input = { [name]: value };
11 | setEditInputs({ ...editInputs, ...input });
12 | };
13 |
14 | const updateProfileData = async () => {
15 | await editProfile(currentUser?.id, editInputs);
16 | await onEdit();
17 | };
18 |
19 | return (
20 |
114 | );
115 | }
116 |
--------------------------------------------------------------------------------
/src/components/common/ProfileEdit/index.scss:
--------------------------------------------------------------------------------
1 | $linkedinBlue1: #0072b1;
2 | $linkedinBlue2: #0073b1;
3 | $linkedinBlue3: #004c75;
4 |
5 | .profile-edit-inputs {
6 | display: grid;
7 | margin-top: 40px;
8 | gap: 20px;
9 |
10 | label {
11 | font-family: system-ui;
12 | margin-bottom: -15px;
13 | }
14 |
15 | .edit-input {
16 | width: 300px;
17 | height: 40px;
18 | }
19 | }
20 |
21 | .save-container {
22 | display: flex;
23 | justify-content: center;
24 | }
25 |
26 | .save-btn {
27 | width: 300px;
28 | height: 50px;
29 | cursor: pointer;
30 | background-color: $linkedinBlue2;
31 | border-radius: 30px;
32 | outline: none;
33 | border: none;
34 | font-family: system-ui;
35 | font-weight: 600;
36 | color: #ffffff;
37 | font-size: 18px;
38 | margin-top: 20px;
39 | }
40 |
41 | .close-icon {
42 | cursor: pointer;
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/common/ProfilePopup/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useState } from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import { onLogout } from "../../../api/AuthAPI";
4 | import { getCurrentUser } from "../../../api/FirestoreAPI";
5 | import Button from "../Button";
6 | import "./index.scss";
7 |
8 | export default function ProfilePopup() {
9 | let navigate = useNavigate();
10 | const [currentUser, setCurrentUser] = useState({});
11 | useMemo(() => {
12 | getCurrentUser(setCurrentUser);
13 | }, []);
14 | return (
15 |
16 |
{currentUser?.name}
17 |
{currentUser?.headline}
18 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/common/ProfilePopup/index.scss:
--------------------------------------------------------------------------------
1 | .popup-card {
2 | border: 1px solid #a8a8a8;
3 | width: 200px;
4 | height: auto;
5 | background-color: whitesmoke;
6 | border-radius: 5px;
7 | display: flex;
8 | justify-content: center;
9 |
10 | flex-direction: column;
11 | padding: 20px;
12 | .name {
13 | font-size: 16px;
14 | color: rgba(0, 0, 0, 0.9);
15 | font-weight: 600;
16 | text-align: left;
17 | margin-top: -7px;
18 | }
19 |
20 | .headline {
21 | font-size: 14px;
22 | font-weight: 400;
23 | color: rgba(0, 0, 0, 0.9);
24 | margin-top: -15px;
25 | }
26 | .popup-option {
27 | cursor: pointer;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/common/SearchUsers/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { AiOutlineCloseCircle } from "react-icons/ai";
3 | import "./index.scss";
4 |
5 | export default function SearchUsers({ setIsSearch, setSearchInput }) {
6 | return (
7 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/common/SearchUsers/index.scss:
--------------------------------------------------------------------------------
1 | .search-users {
2 | width: 40%;
3 | margin-left: 15px;
4 | display: flex;
5 | align-items: center;
6 | .close-icon {
7 | margin-left: -30px;
8 | color: #707070;
9 | }
10 | input {
11 | width: 100%;
12 | height: 30px;
13 | background-color: whitesmoke;
14 | border: 1px solid #bbbbbb;
15 | border-radius: 10px;
16 | color: #212121;
17 | padding-left: 10px;
18 | font-size: 14px;
19 | outline: none;
20 | font-family: Inter;
21 | }
22 |
23 | input:focus {
24 | border: 1px solid #707070;
25 | }
26 |
27 | ::placeholder {
28 | font-size: 14px;
29 | font-family: Inter;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/common/Topbar/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import LinkedinLogo from "../../../assets/linkedinLogo.png";
3 | import user from "../../../assets/user.png";
4 | import SearchUsers from "../SearchUsers";
5 | import {
6 | AiOutlineHome,
7 | AiOutlineUserSwitch,
8 | AiOutlineSearch,
9 | AiOutlineMessage,
10 | AiOutlineBell,
11 | } from "react-icons/ai";
12 | import { useNavigate } from "react-router-dom";
13 | import { BsBriefcase } from "react-icons/bs";
14 | import { getAllUsers } from "../../../api/FirestoreAPI";
15 | import ProfilePopup from "../ProfilePopup";
16 | import "./index.scss";
17 |
18 | export default function Topbar({ currentUser }) {
19 | const [popupVisible, setPopupVisible] = useState(false);
20 | const [isSearch, setIsSearch] = useState(false);
21 | const [users, setUsers] = useState([]);
22 | const [filteredUsers, setFilteredUsers] = useState([]);
23 | const [searchInput, setSearchInput] = useState("");
24 | let navigate = useNavigate();
25 | const goToRoute = (route) => {
26 | navigate(route);
27 | };
28 |
29 | const displayPopup = () => {
30 | setPopupVisible(!popupVisible);
31 | };
32 |
33 | const openUser = (user) => {
34 | navigate("/profile", {
35 | state: {
36 | id: user.id,
37 | email: user.email,
38 | },
39 | });
40 | };
41 |
42 | const handleSearch = () => {
43 | if (searchInput !== "") {
44 | let searched = users.filter((user) => {
45 | return Object.values(user)
46 | .join("")
47 | .toLowerCase()
48 | .includes(searchInput.toLowerCase());
49 | });
50 |
51 | setFilteredUsers(searched);
52 | } else {
53 | setFilteredUsers(users);
54 | }
55 | };
56 |
57 | useEffect(() => {
58 | let debounced = setTimeout(() => {
59 | handleSearch();
60 | }, 1000);
61 |
62 | return () => clearTimeout(debounced);
63 | }, [searchInput]);
64 |
65 | useEffect(() => {
66 | getAllUsers(setUsers);
67 | }, []);
68 | return (
69 |
70 | {popupVisible ? (
71 |
74 | ) : (
75 | <>>
76 | )}
77 |
78 |

79 | {isSearch ? (
80 |
84 | ) : (
85 |
86 |
setIsSearch(true)}
90 | />
91 | goToRoute("/home")}
95 | />
96 | goToRoute("/connections")}
100 | />
101 |
102 |
103 |
104 |
105 | )}
106 |

112 |
113 | {searchInput.length === 0 ? (
114 | <>>
115 | ) : (
116 |
117 | {filteredUsers.length === 0 ? (
118 |
No Results Found..
119 | ) : (
120 | filteredUsers.map((user) => (
121 |
openUser(user)}>
122 |

123 |
{user.name}
124 |
125 | ))
126 | )}
127 |
128 | )}
129 |
130 | );
131 | }
132 |
--------------------------------------------------------------------------------
/src/components/common/Topbar/index.scss:
--------------------------------------------------------------------------------
1 | .topbar-main {
2 | width: 100%;
3 | height: 70px;
4 | background-color: rgba(255, 255, 255, 0.87);
5 | display: flex;
6 | align-items: center;
7 | }
8 | .linkedin-logo {
9 | width: 65px;
10 | margin-left: 20px;
11 | }
12 | .react-icons {
13 | display: flex;
14 | align-items: center;
15 | justify-content: space-between;
16 | width: 55%;
17 | margin-left: 30px;
18 | }
19 |
20 | .react-icon {
21 | color: #787878;
22 | cursor: pointer;
23 | }
24 |
25 | .react-icon:hover {
26 | color: black;
27 | }
28 |
29 | .user-logo {
30 | width: 40px;
31 | height: 40px;
32 | border-radius: 50%;
33 | object-fit: cover;
34 | position: absolute;
35 | right: 30px;
36 | cursor: pointer;
37 | }
38 |
39 | .popup-position {
40 | position: absolute;
41 | right: 10px;
42 | top: 65px;
43 | z-index: 99;
44 | }
45 |
46 | .search-results {
47 | position: absolute;
48 | left: 100px;
49 | top: 60px;
50 | width: 240px;
51 | height: auto;
52 | background-color: white;
53 | border-radius: 10px;
54 | border: 1px solid #bbbbbb;
55 |
56 | .search-inner {
57 | display: flex;
58 | gap: 10px;
59 | align-items: center;
60 | padding: 10px;
61 | cursor: pointer;
62 | border-radius: 10px;
63 | img {
64 | width: 40px;
65 | height: 40px;
66 | border-radius: 50%;
67 | object-fit: cover;
68 | }
69 |
70 | .name {
71 | font-family: system-ui;
72 | font-size: 18px;
73 | }
74 | }
75 |
76 | .search-inner:hover {
77 | background-color: #bbbbbb;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/firebaseConfig.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 | // TODO: Add SDKs for Firebase products that you want to use
7 | // https://firebase.google.com/docs/web/setup#available-libraries
8 |
9 | // Your web app's Firebase configuration
10 | const firebaseConfig = {};
11 |
12 | // Initialize Firebase
13 | const app = initializeApp(firebaseConfig);
14 | const auth = getAuth(app);
15 | const firestore = getFirestore(app);
16 | const storage = getStorage(app);
17 | export { auth, app, firestore, storage };
18 |
--------------------------------------------------------------------------------
/src/helpers/getUniqueId.jsx:
--------------------------------------------------------------------------------
1 | import uuid from "react-uuid";
2 |
3 | export const getUniqueID = () => {
4 | let id = uuid();
5 |
6 | return id;
7 | };
8 |
--------------------------------------------------------------------------------
/src/helpers/useMoment.jsx:
--------------------------------------------------------------------------------
1 | import moment from "moment/moment";
2 |
3 | export const getCurrentTimeStamp = (timeFormat) => {
4 | return moment().format(timeFormat);
5 | };
6 |
--------------------------------------------------------------------------------
/src/helpers/useNavigate.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useNavigate } from "react-router-dom";
3 |
4 | let instance = useNavigate();
5 |
6 | export const navigate = (param) => {
7 | instance(param);
8 | };
9 |
--------------------------------------------------------------------------------
/src/index.scss:
--------------------------------------------------------------------------------
1 | $linkedinBlue1: #0072b1;
2 | $linkedinBlue2: #0073b1;
3 | $linkedinBlue3: #004c75;
4 |
5 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=Lato:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&family=Poppins:ital,wght@0,300;0,400;0,500;1,400;1,500&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;1,100;1,300;1,400;1,500&display=swap");
6 |
7 | :root {
8 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
9 | line-height: 1.5;
10 | font-weight: 400;
11 | color: #212121;
12 | background-color: rgba(224, 224, 224);
13 | }
14 |
15 | .common-input {
16 | height: 20px;
17 | padding: 10px;
18 | background-color: white;
19 | outline: none;
20 | border: 1px solid #212121;
21 | color: #212121;
22 | border-radius: 3px;
23 | font-family: system-ui;
24 | font-size: 16px;
25 | }
26 | .common-input:focus {
27 | outline: 1px solid $linkedinBlue1;
28 | }
29 | ::placeholder {
30 | font-family: system-ui;
31 | font-size: 16px;
32 | }
33 |
34 | .common-textArea {
35 | padding: 10px;
36 | background-color: white;
37 | outline: none;
38 | border: 1px solid #212121;
39 | color: #212121;
40 | border-radius: 3px;
41 | font-family: system-ui;
42 | font-size: 16px;
43 | margin: -10px 0px;
44 | }
45 |
46 | .common-textArea:focus {
47 | outline: 1px solid $linkedinBlue1;
48 | }
49 | ::placeholder {
50 | font-family: system-ui;
51 | font-size: 16px;
52 | }
53 |
--------------------------------------------------------------------------------
/src/layouts/ConnectionLayout.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useState } from "react";
2 | import Connections from "../Pages/Connections";
3 | import { getCurrentUser } from "../api/FirestoreAPI";
4 | import Topbar from "../components/common/Topbar";
5 |
6 | export default function ConnectionLayout() {
7 | const [currentUser, setCurrentUser] = useState({});
8 |
9 | useMemo(() => {
10 | getCurrentUser(setCurrentUser);
11 | }, []);
12 | return (
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/layouts/HomeLayout.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useState } from "react";
2 | import Home from "../Pages/Home";
3 | import { getCurrentUser } from "../api/FirestoreAPI";
4 | import Topbar from "../components/common/Topbar";
5 |
6 | export default function HomeLayout() {
7 | const [currentUser, setCurrentUser] = useState({});
8 |
9 | useMemo(() => {
10 | getCurrentUser(setCurrentUser);
11 | }, []);
12 | return (
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/layouts/ProfileLayout.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useState } from "react";
2 | import { getCurrentUser } from "../api/FirestoreAPI";
3 | import Topbar from "../components/common/Topbar";
4 | import Profile from "../Pages/Profile";
5 |
6 | export default function ProfileLayout() {
7 | const [currentUser, setCurrentUser] = useState({});
8 |
9 | useMemo(() => {
10 | getCurrentUser(setCurrentUser);
11 | }, []);
12 | return (
13 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { RouterProvider } from "react-router-dom";
4 | import { router } from "./Routes";
5 | import { app, auth } from "./firebaseConfig";
6 | import { ToastContainer } from "react-toastify";
7 | import "react-toastify/dist/ReactToastify.css";
8 | import "react-quill/dist/quill.snow.css";
9 | import "./index.scss";
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 |
16 | );
17 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------