├── Procfile ├── .gitignore ├── frontend ├── build │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── static │ │ ├── media │ │ │ ├── hero.cdade95684e864459848.png │ │ │ ├── mui.c78de51d000137fbffd3.webp │ │ │ ├── slick.a4e97f5a2a64f0ab1323.eot │ │ │ ├── slick.c94f7671dcc99dce43e2.ttf │ │ │ ├── aws-s3.51640b4de3d80fb51823.webp │ │ │ ├── axios.56fc2f13fcebf9762c0c.webp │ │ │ ├── heroku.c2c5cccdafe77e3b59c0.webp │ │ │ ├── multer.c2a3c0310a42b05e0188.webp │ │ │ ├── nodejs.90478dd570aa54a25de3.webp │ │ │ ├── redux.51aa90023ce0ceaa0082.webp │ │ │ ├── slick.295183786cd8a1389865.woff │ │ │ ├── expressjs.a2e86fd3965b0781dc31.webp │ │ │ ├── homepage.225baac9eb2a22aae1a7.webp │ │ │ ├── mongodb.d753b795305bda0741bb.webp │ │ │ ├── reactjs.67281ba7f66d2108f454.webp │ │ │ ├── sendgrid.fce5f541441ff976d2dd.webp │ │ │ ├── socketio.aafffdfd652a68a2d466.webp │ │ │ ├── tailwind.89af3bced34f432936d5.webp │ │ │ ├── toastify.08fda808d10d7ed9dada.webp │ │ │ ├── javascript.70416db3411be6deeb08.webp │ │ │ └── slick.2630a3e3eab21c607e21.svg │ │ ├── js │ │ │ ├── 270.ccd3f4c8.chunk.js.LICENSE.txt │ │ │ ├── main.99425b85.js.LICENSE.txt │ │ │ ├── 666.85fb12e7.chunk.js │ │ │ ├── 355.ff33d956.chunk.js │ │ │ ├── 244.013b1587.chunk.js │ │ │ ├── 666.85fb12e7.chunk.js.map │ │ │ ├── 11.d7687b78.chunk.js │ │ │ ├── 355.ff33d956.chunk.js.map │ │ │ └── 25.2f1eb84b.chunk.js │ │ └── css │ │ │ └── 812.4165d163.chunk.css.map │ ├── manifest.json │ ├── index.html │ └── asset-manifest.json ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── postcss.config.js ├── src │ ├── assests │ │ └── images │ │ │ ├── hero.png │ │ │ ├── phones.webp │ │ │ ├── homepage.webp │ │ │ ├── logos │ │ │ ├── mui.webp │ │ │ ├── aws-s3.webp │ │ │ ├── axios.webp │ │ │ ├── heroku.webp │ │ │ ├── multer.webp │ │ │ ├── nodejs.webp │ │ │ ├── redux.webp │ │ │ ├── expressjs.webp │ │ │ ├── mongodb.webp │ │ │ ├── reactjs.webp │ │ │ ├── sendgrid.webp │ │ │ ├── socketio.webp │ │ │ ├── tailwind.webp │ │ │ ├── toastify.webp │ │ │ └── javascript.webp │ │ │ └── upload_success.gif │ ├── components │ │ ├── Layouts │ │ │ ├── MetaData.jsx │ │ │ ├── BackdropLoader.jsx │ │ │ ├── SkeletonUserItem.jsx │ │ │ ├── SkeletonPost.jsx │ │ │ ├── UsersDialog.jsx │ │ │ ├── UserListItem.jsx │ │ │ └── SpinLoader.jsx │ │ ├── Home │ │ │ ├── Home.jsx │ │ │ ├── Sidebar │ │ │ │ ├── UserListItem.jsx │ │ │ │ └── Sidebar.jsx │ │ │ ├── StoriesContainer.jsx │ │ │ ├── SvgIcons.jsx │ │ │ └── PostsContainer.jsx │ │ ├── User │ │ │ ├── Update │ │ │ │ ├── Update.jsx │ │ │ │ ├── UpdatePassword.jsx │ │ │ │ └── Sidebar.jsx │ │ │ ├── Auth.jsx │ │ │ ├── Posts │ │ │ │ └── PostContainer.jsx │ │ │ ├── ForgotPassword.jsx │ │ │ ├── Login.jsx │ │ │ ├── ResetPassword.jsx │ │ │ ├── SvgIcons.jsx │ │ │ └── SignUp.jsx │ │ ├── Errors │ │ │ └── NotFound.jsx │ │ ├── Navbar │ │ │ ├── Notifications.jsx │ │ │ ├── SearchBar │ │ │ │ ├── SearchUserItem.jsx │ │ │ │ └── SearchBox.jsx │ │ │ ├── ProfileDetails.jsx │ │ │ └── Header.jsx │ │ └── Chats │ │ │ ├── Message.jsx │ │ │ ├── ChatListItem.jsx │ │ │ ├── Sidebar.jsx │ │ │ └── SearchModal.jsx │ ├── constants │ │ ├── chatConstants.js │ │ ├── messageConstants.js │ │ ├── postConstants.js │ │ └── userConstants.js │ ├── Routes │ │ └── PrivateRoute.jsx │ ├── index.js │ ├── index.css │ ├── utils │ │ └── constants.js │ ├── actions │ │ ├── chatAction.js │ │ ├── messageAction.js │ │ └── postAction.js │ ├── store.js │ ├── reducers │ │ ├── chatsReducer.js │ │ └── messageReducer.js │ └── App.js ├── .gitignore ├── package.json └── README.md ├── backend ├── middlewares │ ├── catchAsync.js │ ├── auth.js │ └── error.js ├── utils │ ├── errorHandler.js │ ├── sendCookie.js │ ├── sendEmail.js │ └── awsFunctions.js ├── routes │ ├── chatRoute.js │ ├── messageRoute.js │ ├── postRoute.js │ └── userRoute.js ├── config │ ├── database.js │ └── config.env.example ├── models │ ├── chatModel.js │ ├── messageModel.js │ ├── postModel.js │ └── userModel.js ├── app.js └── controllers │ ├── messageController.js │ ├── chatController.js │ └── postController.js ├── vercel.json ├── package.json ├── LICENSE.md ├── server.js └── README.md /Procfile: -------------------------------------------------------------------------------- 1 | web: node backend/server.js -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # local env files 5 | /backend/config/config.env -------------------------------------------------------------------------------- /frontend/build/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/favicon.ico -------------------------------------------------------------------------------- /frontend/build/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/logo192.png -------------------------------------------------------------------------------- /frontend/build/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/logo512.png -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/public/logo512.png -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/assests/images/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/hero.png -------------------------------------------------------------------------------- /frontend/src/assests/images/phones.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/phones.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/homepage.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/homepage.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/mui.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/mui.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/aws-s3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/aws-s3.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/axios.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/axios.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/heroku.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/heroku.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/multer.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/multer.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/nodejs.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/nodejs.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/redux.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/redux.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/expressjs.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/expressjs.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/mongodb.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/mongodb.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/reactjs.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/reactjs.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/sendgrid.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/sendgrid.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/socketio.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/socketio.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/tailwind.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/tailwind.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/toastify.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/toastify.webp -------------------------------------------------------------------------------- /frontend/src/assests/images/upload_success.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/upload_success.gif -------------------------------------------------------------------------------- /backend/middlewares/catchAsync.js: -------------------------------------------------------------------------------- 1 | module.exports = errorFunction => (req, res, next) => { 2 | Promise.resolve(errorFunction(req, res, next)).catch(next); 3 | } -------------------------------------------------------------------------------- /frontend/src/assests/images/logos/javascript.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/src/assests/images/logos/javascript.webp -------------------------------------------------------------------------------- /frontend/build/static/media/hero.cdade95684e864459848.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/hero.cdade95684e864459848.png -------------------------------------------------------------------------------- /frontend/build/static/media/mui.c78de51d000137fbffd3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/mui.c78de51d000137fbffd3.webp -------------------------------------------------------------------------------- /frontend/build/static/media/slick.a4e97f5a2a64f0ab1323.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/slick.a4e97f5a2a64f0ab1323.eot -------------------------------------------------------------------------------- /frontend/build/static/media/slick.c94f7671dcc99dce43e2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/slick.c94f7671dcc99dce43e2.ttf -------------------------------------------------------------------------------- /frontend/build/static/media/aws-s3.51640b4de3d80fb51823.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/aws-s3.51640b4de3d80fb51823.webp -------------------------------------------------------------------------------- /frontend/build/static/media/axios.56fc2f13fcebf9762c0c.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/axios.56fc2f13fcebf9762c0c.webp -------------------------------------------------------------------------------- /frontend/build/static/media/heroku.c2c5cccdafe77e3b59c0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/heroku.c2c5cccdafe77e3b59c0.webp -------------------------------------------------------------------------------- /frontend/build/static/media/multer.c2a3c0310a42b05e0188.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/multer.c2a3c0310a42b05e0188.webp -------------------------------------------------------------------------------- /frontend/build/static/media/nodejs.90478dd570aa54a25de3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/nodejs.90478dd570aa54a25de3.webp -------------------------------------------------------------------------------- /frontend/build/static/media/redux.51aa90023ce0ceaa0082.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/redux.51aa90023ce0ceaa0082.webp -------------------------------------------------------------------------------- /frontend/build/static/media/slick.295183786cd8a1389865.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/slick.295183786cd8a1389865.woff -------------------------------------------------------------------------------- /frontend/build/static/media/expressjs.a2e86fd3965b0781dc31.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/expressjs.a2e86fd3965b0781dc31.webp -------------------------------------------------------------------------------- /frontend/build/static/media/homepage.225baac9eb2a22aae1a7.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/homepage.225baac9eb2a22aae1a7.webp -------------------------------------------------------------------------------- /frontend/build/static/media/mongodb.d753b795305bda0741bb.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/mongodb.d753b795305bda0741bb.webp -------------------------------------------------------------------------------- /frontend/build/static/media/reactjs.67281ba7f66d2108f454.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/reactjs.67281ba7f66d2108f454.webp -------------------------------------------------------------------------------- /frontend/build/static/media/sendgrid.fce5f541441ff976d2dd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/sendgrid.fce5f541441ff976d2dd.webp -------------------------------------------------------------------------------- /frontend/build/static/media/socketio.aafffdfd652a68a2d466.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/socketio.aafffdfd652a68a2d466.webp -------------------------------------------------------------------------------- /frontend/build/static/media/tailwind.89af3bced34f432936d5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/tailwind.89af3bced34f432936d5.webp -------------------------------------------------------------------------------- /frontend/build/static/media/toastify.08fda808d10d7ed9dada.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/toastify.08fda808d10d7ed9dada.webp -------------------------------------------------------------------------------- /frontend/build/static/media/javascript.70416db3411be6deeb08.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/000Jerry000/instagram-mern/HEAD/frontend/build/static/media/javascript.70416db3411be6deeb08.webp -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "builds": [ 3 | { 4 | "src": "server.js", 5 | "use": "@vercel/node" 6 | } 7 | ], 8 | "routes": [ 9 | { 10 | "src": "/.*", 11 | "dest": "server.js" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/components/Layouts/MetaData.jsx: -------------------------------------------------------------------------------- 1 | import { Helmet } from "react-helmet-async"; 2 | 3 | const MetaData = ({ title }) => { 4 | return ( 5 | 6 | {title} 7 | 8 | ); 9 | }; 10 | 11 | export default MetaData; -------------------------------------------------------------------------------- /backend/utils/errorHandler.js: -------------------------------------------------------------------------------- 1 | class ErrorHandler extends Error { 2 | constructor(message, statusCode) { 3 | super(message) 4 | this.statusCode = statusCode 5 | 6 | Error.captureStackTrace(this, this.constructor) 7 | } 8 | } 9 | 10 | module.exports = ErrorHandler; -------------------------------------------------------------------------------- /backend/routes/chatRoute.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { newChat, getChats } = require('../controllers/chatController'); 3 | const { isAuthenticated } = require('../middlewares/auth'); 4 | 5 | const router = express(); 6 | 7 | router.route("/newChat").post(isAuthenticated, newChat); 8 | router.route("/chats").get(isAuthenticated, getChats); 9 | 10 | module.exports = router; -------------------------------------------------------------------------------- /backend/config/database.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const connectDatabase = () => { 4 | mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }) 5 | .then(() => { 6 | console.log("Mongoose Connected"); 7 | }).catch((error) => { 8 | console.log(error); 9 | }); 10 | } 11 | 12 | module.exports = connectDatabase; -------------------------------------------------------------------------------- /backend/routes/messageRoute.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { newMessage, getMessages } = require('../controllers/messageController'); 3 | const { isAuthenticated } = require('../middlewares/auth'); 4 | 5 | const router = express(); 6 | 7 | router.route("/newMessage").post(isAuthenticated, newMessage); 8 | router.route("/messages/:chatId").get(isAuthenticated, getMessages); 9 | 10 | module.exports = router; -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | # /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /frontend/src/components/Home/Home.jsx: -------------------------------------------------------------------------------- 1 | import PostsContainer from './PostsContainer' 2 | import Sidebar from './Sidebar/Sidebar' 3 | import MetaData from '../Layouts/MetaData'; 4 | 5 | const Home = () => { 6 | return ( 7 | <> 8 | 9 | 10 |
11 | 12 | 13 |
14 | 15 | ) 16 | } 17 | 18 | export default Home -------------------------------------------------------------------------------- /frontend/src/components/Layouts/BackdropLoader.jsx: -------------------------------------------------------------------------------- 1 | import Backdrop from '@mui/material/Backdrop'; 2 | import CircularProgress from '@mui/material/CircularProgress'; 3 | 4 | const BackdropLoader = () => { 5 | return ( 6 | 10 | 11 | 12 | ); 13 | }; 14 | 15 | export default BackdropLoader; 16 | -------------------------------------------------------------------------------- /frontend/src/constants/chatConstants.js: -------------------------------------------------------------------------------- 1 | 2 | export const ALL_CHATS_REQUEST = "ALL_CHATS_REQUEST"; 3 | export const ALL_CHATS_SUCCESS = "ALL_CHATS_SUCCESS"; 4 | export const ALL_CHATS_FAIL = "ALL_CHATS_FAIL"; 5 | 6 | export const NEW_CHAT_REQUEST = "NEW_CHAT_REQUEST"; 7 | export const NEW_CHAT_SUCCESS = "NEW_CHAT_SUCCESS"; 8 | export const NEW_CHAT_RESET = "NEW_CHAT_RESET"; 9 | export const NEW_CHAT_FAIL = "NEW_CHAT_FAIL"; 10 | 11 | export const CLEAR_ERRORS = "CLEAR_ERRORS"; -------------------------------------------------------------------------------- /backend/utils/sendCookie.js: -------------------------------------------------------------------------------- 1 | const sendCookie = (user = {}, statusCode, res) => { 2 | const token = user.generateToken(); 3 | 4 | const options = { 5 | expires: new Date( 6 | Date.now() + process.env.COOKIE_EXPIRE * 24 * 60 * 60 * 1000 7 | ), 8 | httpOnly: true 9 | } 10 | 11 | res.status(statusCode).cookie('token', token, options).json({ 12 | success: true, 13 | user, 14 | }); 15 | } 16 | 17 | module.exports = sendCookie; -------------------------------------------------------------------------------- /backend/config/config.env.example: -------------------------------------------------------------------------------- 1 | PORT=4000 2 | MONGO_URI=mongodb://localhost:27017/Instagram 3 | 4 | JWT_SECRET=U3YU23wef32BFE48t48br4tGERbvrtbrtb45n4ty848t4nerS 5 | JWT_EXPIRE=7d 6 | COOKIE_EXPIRE=5 7 | 8 | SENDGRID_API_KEY=SG.UUwefwewrgI.efewfwewef 9 | SENDGRID_MAIL=jhondoe@gmail.com 10 | SENDGRID_RESET_TEMPLATEID=d-wefiniwnef1ewf848erg18er8 11 | 12 | AWS_BUCKET_NAME=mybucket 13 | AWS_BUCKET_REGION=ap-south-1 14 | AWS_IAM_USER_KEY=BUBEWUBVIWS 15 | AWS_IAM_USER_SECRET=UIEFIEBfbiwbviuwe 16 | 17 | NODE_ENV=development -------------------------------------------------------------------------------- /frontend/src/Routes/PrivateRoute.jsx: -------------------------------------------------------------------------------- 1 | import { useSelector } from 'react-redux'; 2 | import { Navigate } from 'react-router-dom'; 3 | 4 | const PrivateRoute = ({ children }) => { 5 | 6 | const { loading, isAuthenticated, user } = useSelector(state => state.user); 7 | 8 | return ( 9 | <> 10 | {loading === false && ( 11 | isAuthenticated === false ? : children 12 | )} 13 | 14 | ); 15 | }; 16 | 17 | export default PrivateRoute; -------------------------------------------------------------------------------- /backend/models/chatModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const chatSchema = new mongoose.Schema( 4 | { 5 | users: [ 6 | { 7 | type: mongoose.Schema.Types.ObjectId, 8 | ref: "User" 9 | } 10 | ], 11 | latestMessage: { 12 | type: mongoose.Schema.Types.ObjectId, 13 | ref: "Message", 14 | }, 15 | }, 16 | { timestamps: true } 17 | ); 18 | 19 | module.exports = mongoose.model("Chat", chatSchema); -------------------------------------------------------------------------------- /frontend/src/constants/messageConstants.js: -------------------------------------------------------------------------------- 1 | export const ALL_MESSAGES_REQUEST = "ALL_MESSAGES_REQUEST"; 2 | export const ALL_MESSAGES_SUCCESS = "ALL_MESSAGES_SUCCESS"; 3 | export const ALL_MESSAGES_FAIL = "ALL_MESSAGES_FAIL"; 4 | export const ALL_MESSAGES_ADD = "ALL_MESSAGES_ADD"; 5 | 6 | export const NEW_MESSAGE_REQUEST = "NEW_MESSAGE_REQUEST"; 7 | export const NEW_MESSAGE_SUCCESS = "NEW_MESSAGE_SUCCESS"; 8 | export const NEW_MESSAGE_RESET = "NEW_MESSAGE_RESET"; 9 | export const NEW_MESSAGE_FAIL = "NEW_MESSAGE_FAIL"; 10 | 11 | export const CLEAR_ERRORS = "CLEAR_ERRORS"; -------------------------------------------------------------------------------- /backend/utils/sendEmail.js: -------------------------------------------------------------------------------- 1 | const sgMail = require('@sendgrid/mail') 2 | sgMail.setApiKey(process.env.SENDGRID_API_KEY); 3 | 4 | const sendEmail = async (options) => { 5 | 6 | const msg = { 7 | to: options.email, 8 | from: process.env.SENDGRID_MAIL, 9 | templateId: options.templateId, 10 | dynamic_template_data: options.data, 11 | } 12 | 13 | sgMail.send(msg).then(() => { 14 | console.log('Email Sent') 15 | }).catch((error) => { 16 | console.error(error) 17 | }); 18 | }; 19 | 20 | module.exports = sendEmail; -------------------------------------------------------------------------------- /frontend/src/components/User/Update/Update.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Header from '../../Navbar/Header' 3 | import Sidebar from './Sidebar' 4 | 5 | const Update = ({ children, activeTab }) => { 6 | return ( 7 | <> 8 |
9 |
10 | 11 | {children} 12 |
13 |
14 | 15 | ) 16 | } 17 | 18 | export default Update -------------------------------------------------------------------------------- /backend/middlewares/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const User = require("../models/userModel"); 3 | const ErrorHandler = require("../utils/errorHandler"); 4 | const catchAsync = require("./catchAsync"); 5 | 6 | exports.isAuthenticated = catchAsync(async (req, res, next) => { 7 | 8 | const { token } = req.cookies; 9 | 10 | if(!token) { 11 | return next(new ErrorHandler("Please Login to Access", 401)); 12 | } 13 | 14 | const decodedData = jwt.verify(token, process.env.JWT_SECRET); 15 | req.user = await User.findById(decodedData.id); 16 | next(); 17 | }); -------------------------------------------------------------------------------- /backend/models/messageModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const messageSchema = new mongoose.Schema( 4 | { 5 | sender: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | ref: "User" 8 | }, 9 | chatId: { 10 | type: mongoose.Schema.Types.ObjectId, 11 | ref: "Chat" 12 | }, 13 | content: { 14 | type: String, 15 | trim: true, 16 | required: true, 17 | }, 18 | }, 19 | { timestamps: true } 20 | ); 21 | 22 | module.exports = mongoose.model("Message", messageSchema); -------------------------------------------------------------------------------- /frontend/build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import { BrowserRouter as Router } from 'react-router-dom'; 6 | import { Provider } from 'react-redux'; 7 | import store from './store'; 8 | import { HelmetProvider } from 'react-helmet-async'; 9 | 10 | ReactDOM.render( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | , 20 | document.getElementById('root') 21 | ); -------------------------------------------------------------------------------- /frontend/src/components/Layouts/SkeletonUserItem.jsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from '@mui/material' 2 | 3 | const SkeletonUserItem = () => { 4 | return ( 5 |
6 | 7 |
8 | 9 | 10 |
11 |
12 | ) 13 | } 14 | 15 | export default SkeletonUserItem -------------------------------------------------------------------------------- /frontend/build/index.html: -------------------------------------------------------------------------------- 1 | Instagram
-------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | background-color: #fafafa; 7 | font-family: "Segoe UI", sans-serif; 8 | } 9 | 10 | .slick-arrow { 11 | margin: 0 2.3rem; 12 | background-color: #ffffff; 13 | z-index: 100; 14 | } 15 | 16 | .likeEffect { 17 | position: absolute; 18 | opacity: 0.7; 19 | width: 6rem; 20 | transform: scale(0); 21 | animation: like 0.5s linear 1; 22 | } 23 | 24 | @keyframes like { 25 | 0% { 26 | transform: scale(0.2); 27 | } 28 | 25% { 29 | transform: scale(0.6); 30 | } 31 | 50% { 32 | transform: scale(1.04); 33 | } 34 | 100% { 35 | transform: scale(0.4); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /frontend/src/components/Errors/NotFound.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | 4 | const NotFound = () => { 5 | return ( 6 |
7 |

Sorry, this page isn't available.

8 |

The link you followed may be broken, or the page may have been removed. 9 | Go back to Instagram. 10 |

11 | Go to Home 12 |
13 | ) 14 | } 15 | 16 | export default NotFound -------------------------------------------------------------------------------- /frontend/src/components/Navbar/Notifications.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Notifications = () => { 4 | return ( 5 |
6 |
7 | 8 |
9 | {Array(5).fill("").map((el) => ( 10 |
11 | Profile 12 |
13 | ))} 14 |
15 |
16 | ) 17 | } 18 | 19 | export default Notifications -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "instagram", 3 | "version": "1.0.0", 4 | "description": "Full Stack Instagram Social Media App", 5 | "main": "server.js", 6 | "scripts": { 7 | "dev": "nodemon server.js", 8 | "start": "node server.js", 9 | "heroku-postbuild": "NPM_CONFIG_PRODUCTION=false && npm install --prefix frontend && npm run build --prefix frontend" 10 | }, 11 | "author": "Jigar Sable", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@sendgrid/mail": "^7.6.2", 15 | "aws-sdk": "^2.1095.0", 16 | "bcrypt": "^5.0.1", 17 | "cookie-parser": "^1.4.6", 18 | "dotenv": "^14.2.0", 19 | "express": "^4.17.2", 20 | "jsonwebtoken": "^8.5.1", 21 | "mongoose": "^6.1.7", 22 | "multer": "^1.4.4", 23 | "multer-s3": "^2.10.0", 24 | "socket.io": "^4.4.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/components/Layouts/SkeletonPost.jsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from '@mui/material' 2 | 3 | const SkeletonPost = () => { 4 | return ( 5 |
6 |
7 | 8 |
9 | 10 | 11 |
12 |
13 | 14 |
15 | ) 16 | } 17 | 18 | export default SkeletonPost -------------------------------------------------------------------------------- /frontend/src/components/Navbar/SearchBar/SearchUserItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | 4 | const SearchUserItem = ({ _id, username, name, avatar }) => { 5 | return ( 6 | 7 |
8 | avatar 9 |
10 | {username} 11 | {name} 12 |
13 |
14 | 15 | ) 16 | } 17 | 18 | export default SearchUserItem -------------------------------------------------------------------------------- /backend/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cookieParser = require('cookie-parser'); 3 | const errorMiddleware = require('./middlewares/error'); 4 | 5 | const app = express(); 6 | 7 | app.use(express.json()); 8 | app.use(express.urlencoded({ extended: true })); 9 | app.use(cookieParser()); 10 | app.use('/public', express.static('public')); 11 | 12 | if (process.env.NODE_ENV != "production") { 13 | require('dotenv').config({ path: 'backend/config/config.env' }); 14 | } 15 | 16 | // import routes 17 | const post = require('./routes/postRoute'); 18 | const user = require('./routes/userRoute'); 19 | const chat = require('./routes/chatRoute'); 20 | const message = require('./routes/messageRoute'); 21 | 22 | app.use('/api/v1', post); 23 | app.use('/api/v1', user); 24 | app.use('/api/v1', chat); 25 | app.use('/api/v1', message); 26 | 27 | // error middleware 28 | app.use(errorMiddleware); 29 | 30 | module.exports = app; -------------------------------------------------------------------------------- /backend/controllers/messageController.js: -------------------------------------------------------------------------------- 1 | const catchAsync = require("../middlewares/catchAsync"); 2 | const Message = require("../models/messageModel"); 3 | const Chat = require("../models/chatModel"); 4 | 5 | // Send New Message 6 | exports.newMessage = catchAsync(async (req, res, next) => { 7 | 8 | const { chatId, content } = req.body; 9 | 10 | const msgData = { 11 | sender: req.user._id, 12 | chatId, 13 | content, 14 | } 15 | 16 | const newMessage = await Message.create(msgData); 17 | 18 | await Chat.findByIdAndUpdate(chatId, { latestMessage: newMessage }); 19 | 20 | res.status(200).json({ 21 | success: true, 22 | newMessage, 23 | }); 24 | }); 25 | 26 | // Get All Messages 27 | exports.getMessages = catchAsync(async (req, res, next) => { 28 | 29 | const messages = await Message.find({ 30 | chatId: req.params.chatId 31 | }); 32 | 33 | res.status(200).json({ 34 | success: true, 35 | messages, 36 | }); 37 | }); -------------------------------------------------------------------------------- /backend/routes/postRoute.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { newPost, likeUnlikePost, deletePost, newComment, allPosts, getPostsOfFollowing, updateCaption, saveUnsavePost, getPostDetails } = require('../controllers/postController'); 3 | const { isAuthenticated } = require('../middlewares/auth'); 4 | const { uploadPost } = require('../utils/awsFunctions'); 5 | 6 | const router = express(); 7 | 8 | router.route("/post/new").post(isAuthenticated, uploadPost.single('post'), newPost); 9 | 10 | router.route("/posts/all").get(allPosts); 11 | 12 | router.route("/posts").get(isAuthenticated, getPostsOfFollowing); 13 | 14 | router.route("/post/detail/:id").get(isAuthenticated, getPostDetails); 15 | 16 | router.route("/post/:id") 17 | .get(isAuthenticated, likeUnlikePost) 18 | .post(isAuthenticated, saveUnsavePost) 19 | .put(isAuthenticated, updateCaption) 20 | .delete(isAuthenticated, deletePost); 21 | 22 | router.route("/post/comment/:id").post(isAuthenticated, newComment) 23 | 24 | module.exports = router; -------------------------------------------------------------------------------- /frontend/build/static/js/270.ccd3f4c8.chunk.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | Copyright (c) 2018 Jed Watson. 3 | Licensed under the MIT License (MIT), see 4 | http://jedwatson.github.io/classnames 5 | */ 6 | 7 | /*! ***************************************************************************** 8 | Copyright (c) Microsoft Corporation. All rights reserved. 9 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 10 | this file except in compliance with the License. You may obtain a copy of the 11 | License at http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 15 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 16 | MERCHANTABLITY OR NON-INFRINGEMENT. 17 | 18 | See the Apache Version 2.0 License for specific language governing permissions 19 | and limitations under the License. 20 | ***************************************************************************** */ 21 | -------------------------------------------------------------------------------- /backend/models/postModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const postSchema = new mongoose.Schema({ 4 | caption: { 5 | type: String, 6 | trim: true 7 | }, 8 | image: { 9 | type: String 10 | }, 11 | postedBy: { 12 | type: mongoose.Schema.Types.ObjectId, 13 | ref: "User", 14 | }, 15 | likes: [ 16 | { 17 | type: mongoose.Schema.Types.ObjectId, 18 | ref: "User", 19 | } 20 | ], 21 | comments: [ 22 | { 23 | user: { 24 | type: mongoose.Schema.Types.ObjectId, 25 | ref: "User", 26 | }, 27 | comment: { 28 | type: String, 29 | required: true, 30 | trim: true, 31 | } 32 | } 33 | ], 34 | savedBy: [ 35 | { 36 | type: mongoose.Schema.Types.ObjectId, 37 | ref: "User", 38 | } 39 | ], 40 | createdAt: { 41 | type: Date, 42 | default: Date.now, 43 | } 44 | }); 45 | 46 | module.exports = mongoose.model("Post", postSchema); -------------------------------------------------------------------------------- /frontend/src/components/Chats/Message.jsx: -------------------------------------------------------------------------------- 1 | 2 | const Message = ({ ownMsg, avatar, content }) => { 3 | return ( 4 | ownMsg ? 5 | content === '❤️' ? 6 | {content} 7 | : 8 | {content} 9 | : 10 | content === '❤️' ? 11 |
12 | avatar 13 | {content} 14 |
15 | : 16 |
17 | avatar 18 | {content} 19 |
20 | ) 21 | } 22 | 23 | export default Message -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jigar Sable 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /backend/middlewares/error.js: -------------------------------------------------------------------------------- 1 | const ErrorHandler = require("../utils/errorHandler"); 2 | 3 | module.exports = (err, req, res, next) => { 4 | err.statusCode = err.statusCode || 500; 5 | err.message = err.message || "Internal Server Error"; 6 | 7 | // mongodb id error 8 | if (err.name === "CastError") { 9 | const message = `Resource Not Found. Invalid: ${err.path}`; 10 | err = new ErrorHandler(message, 400); 11 | } 12 | 13 | // mongoose duplicate key error 14 | if (err.code === 11000) { 15 | const message = `${Object.keys(err.keyValue)} already exists`; 16 | err = new ErrorHandler(message, 400); 17 | } 18 | 19 | // wrong jwt error 20 | if (err.code === "JsonWebTokenError") { 21 | const message = 'JWT Error'; 22 | err = new ErrorHandler(message, 400); 23 | } 24 | 25 | // jwt expire error 26 | if (err.code === "JsonWebTokenError") { 27 | const message = 'JWT is Expired'; 28 | err = new ErrorHandler(message, 400); 29 | } 30 | 31 | res.status(err.statusCode).json({ 32 | success: false, 33 | message: err.message, 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /backend/controllers/chatController.js: -------------------------------------------------------------------------------- 1 | const catchAsync = require("../middlewares/catchAsync"); 2 | const Chat = require("../models/chatModel"); 3 | 4 | // Create New Chat 5 | exports.newChat = catchAsync(async (req, res, next) => { 6 | 7 | const chatExists = await Chat.findOne({ 8 | users: { 9 | $all: [req.user._id, req.body.receiverId] 10 | } 11 | }); 12 | 13 | if (chatExists) { 14 | return res.status(200).json({ 15 | success: true, 16 | newChat: chatExists 17 | }); 18 | } 19 | 20 | const newChat = await Chat.create({ 21 | users: [req.user._id, req.body.receiverId], 22 | }); 23 | 24 | res.status(200).json({ 25 | success: true, 26 | newChat 27 | }); 28 | }); 29 | 30 | // Get All Chats 31 | exports.getChats = catchAsync(async (req, res, next) => { 32 | 33 | const chats = await Chat.find( 34 | { 35 | users: { 36 | $in: [req.user._id] 37 | } 38 | } 39 | ).sort({ updatedAt: -1 }).populate("users latestMessage"); 40 | 41 | res.status(200).json({ 42 | success: true, 43 | chats 44 | }); 45 | }); -------------------------------------------------------------------------------- /frontend/src/components/Layouts/UsersDialog.jsx: -------------------------------------------------------------------------------- 1 | import { Dialog } from '@mui/material' 2 | import UserListItem from './UserListItem' 3 | 4 | const UsersDialog = ({ open, onClose, title, usersList }) => { 5 | 6 | return ( 7 | 8 |
9 |
10 | {title} 11 | 12 |
13 |
14 | 15 | {usersList?.map((u) => ( 16 | 17 | ))} 18 | 19 |
20 |
21 |
22 | ) 23 | } 24 | 25 | export default UsersDialog -------------------------------------------------------------------------------- /frontend/src/components/Home/Sidebar/UserListItem.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import { useDispatch } from 'react-redux'; 3 | import { Link } from 'react-router-dom'; 4 | import { followUser } from '../../../actions/userAction'; 5 | 6 | const UserListItem = ({ _id, username, avatar }) => { 7 | 8 | const dispatch = useDispatch(); 9 | 10 | const [follow, setFollow] = useState(false); 11 | 12 | const handleFollow = () => { 13 | setFollow(!follow); 14 | dispatch(followUser(_id)); 15 | } 16 | 17 | return ( 18 |
19 |
20 | avatar 21 |
22 | {username} 23 | New to Instagram 24 |
25 |
26 | 27 |
28 | ) 29 | } 30 | 31 | export default UserListItem -------------------------------------------------------------------------------- /frontend/src/utils/constants.js: -------------------------------------------------------------------------------- 1 | // export const SOCKET_ENDPOINT = "http://localhost:4000"; 2 | export const SOCKET_ENDPOINT = "https://instagrammern.herokuapp.com"; 3 | 4 | export const stories = [ 5 | { 6 | title: "JavaScript", 7 | image: "javascript" 8 | }, 9 | { 10 | title: "Node.js", 11 | image: "nodejs" 12 | }, 13 | { 14 | title: "Express.js", 15 | image: "expressjs" 16 | }, 17 | { 18 | title: "MongoDB", 19 | image: "mongodb" 20 | }, 21 | { 22 | title: "React.js", 23 | image: "reactjs" 24 | }, 25 | { 26 | title: "Socket.io", 27 | image: "socketio" 28 | }, 29 | { 30 | title: "TailwindCSS", 31 | image: "tailwind" 32 | }, 33 | { 34 | title: "Heroku", 35 | image: "heroku" 36 | }, 37 | { 38 | title: "MaterialUI", 39 | image: "mui" 40 | }, 41 | { 42 | title: "Redux", 43 | image: "redux" 44 | }, 45 | { 46 | title: "Multer", 47 | image: "multer" 48 | }, 49 | { 50 | title: "AWS S3", 51 | image: "aws-s3" 52 | }, 53 | { 54 | title: "Sendgrid", 55 | image: "sendgrid" 56 | }, 57 | { 58 | title: "Axios", 59 | image: "axios" 60 | }, 61 | { 62 | title: "Toastify", 63 | image: "toastify" 64 | }, 65 | ] -------------------------------------------------------------------------------- /frontend/src/components/User/Auth.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import homepage from '../../assests/images/homepage.webp'; 3 | 4 | const Auth = ({ children }) => { 5 | return ( 6 |
7 | 8 |
9 | 10 |
11 | homepage 12 |
13 | 14 |
15 | 16 | {children} 17 | 18 |

Get the app.

19 |
20 | appstore 21 | playstore 22 |
23 | 24 |
25 |
26 |
27 | ) 28 | } 29 | 30 | export default Auth -------------------------------------------------------------------------------- /backend/routes/userRoute.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { loginUser, signupUser, logoutUser, followUser, updateProfile, updatePassword, forgotPassword, resetPassword, getUserDetails, getAccountDetails, getAllUsers, searchUsers, getUserDetailsById, deleteProfile } = require('../controllers/userController'); 3 | const { isAuthenticated } = require('../middlewares/auth'); 4 | const { uploadAvatar } = require('../utils/awsFunctions'); 5 | 6 | const router = express(); 7 | 8 | router.route("/signup").post(uploadAvatar.single('avatar'), signupUser); 9 | router.route("/login").post(loginUser); 10 | router.route("/logout").get(logoutUser); 11 | 12 | router.route("/me") 13 | .get(isAuthenticated, getAccountDetails) 14 | .delete(isAuthenticated, deleteProfile); 15 | 16 | router.route("/user/:username").get(isAuthenticated, getUserDetails); 17 | router.route("/userdetails/:id").get(isAuthenticated, getUserDetailsById); 18 | 19 | router.route("/users/suggested").get(isAuthenticated, getAllUsers); 20 | router.route("/users").get(isAuthenticated, searchUsers); 21 | 22 | router.route("/follow/:id").get(isAuthenticated, followUser); 23 | 24 | router.route("/update/profile").put(isAuthenticated, uploadAvatar.single('avatar'), updateProfile); 25 | router.route("/update/password").put(isAuthenticated, updatePassword); 26 | 27 | router.route('/password/forgot').post(forgotPassword); 28 | router.route('/password/reset/:token').put(resetPassword); 29 | 30 | module.exports = router; -------------------------------------------------------------------------------- /frontend/src/actions/chatAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { ALL_CHATS_FAIL, ALL_CHATS_REQUEST, ALL_CHATS_SUCCESS, CLEAR_ERRORS, NEW_CHAT_FAIL, NEW_CHAT_REQUEST, NEW_CHAT_SUCCESS } from "../constants/chatConstants"; 3 | 4 | // Get All Chats 5 | export const getAllChats = () => async (dispatch) => { 6 | try { 7 | 8 | dispatch({ type: ALL_CHATS_REQUEST }); 9 | 10 | const { data } = await axios.get('/api/v1/chats'); 11 | 12 | dispatch({ 13 | type: ALL_CHATS_SUCCESS, 14 | payload: data.chats, 15 | }); 16 | 17 | } catch (error) { 18 | dispatch({ 19 | type: ALL_CHATS_FAIL, 20 | payload: error.response.data.message, 21 | }); 22 | } 23 | }; 24 | 25 | // New Chat 26 | export const addNewChat = (userId) => async (dispatch) => { 27 | try { 28 | 29 | dispatch({ type: NEW_CHAT_REQUEST }); 30 | const config = { header: { "Content-Type": "application/json" } } 31 | const { data } = await axios.post("/api/v1/newChat", { receiverId: userId }, config); 32 | 33 | dispatch({ 34 | type: NEW_CHAT_SUCCESS, 35 | payload: data, 36 | }); 37 | 38 | } catch (error) { 39 | dispatch({ 40 | type: NEW_CHAT_FAIL, 41 | payload: error.response.data.message, 42 | }); 43 | } 44 | } 45 | 46 | // Clear All Errors 47 | export const clearErrors = () => (dispatch) => { 48 | dispatch({ type: CLEAR_ERRORS }); 49 | } -------------------------------------------------------------------------------- /frontend/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers, applyMiddleware } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import { composeWithDevTools } from 'redux-devtools-extension'; 4 | import { allUsersReducer, followUserReducer, forgotPasswordReducer, profileReducer, userDetailsReducer, userReducer } from './reducers/userReducer'; 5 | import { deletePostReducer, likePostReducer, newCommentReducer, newPostReducer, postDetailsReducer, postOfFollowingReducer, savePostReducer } from './reducers/postReducer'; 6 | import { allChatsReducer, newChatReducer } from './reducers/chatsReducer'; 7 | import { allMessagesReducer, newMessageReducer } from './reducers/messageReducer'; 8 | 9 | const reducer = combineReducers({ 10 | user: userReducer, 11 | forgotPassword: forgotPasswordReducer, 12 | newPost: newPostReducer, 13 | userDetails: userDetailsReducer, 14 | allUsers: allUsersReducer, 15 | postOfFollowing: postOfFollowingReducer, 16 | likePost: likePostReducer, 17 | followUser: followUserReducer, 18 | newComment: newCommentReducer, 19 | savePost: savePostReducer, 20 | deletePost: deletePostReducer, 21 | profile: profileReducer, 22 | postDetails: postDetailsReducer, 23 | allChats: allChatsReducer, 24 | allMessages: allMessagesReducer, 25 | newMessage: newMessageReducer, 26 | newChat: newChatReducer, 27 | }); 28 | 29 | const store = createStore( 30 | reducer, 31 | composeWithDevTools(applyMiddleware(thunk)) 32 | ); 33 | 34 | export default store; -------------------------------------------------------------------------------- /frontend/src/actions/messageAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { ALL_MESSAGES_FAIL, ALL_MESSAGES_REQUEST, ALL_MESSAGES_SUCCESS, CLEAR_ERRORS, NEW_MESSAGE_FAIL, NEW_MESSAGE_REQUEST, NEW_MESSAGE_SUCCESS } from "../constants/messageConstants"; 3 | 4 | // Get All Messages 5 | export const getAllMessages = (chatId) => async (dispatch) => { 6 | try { 7 | 8 | dispatch({ type: ALL_MESSAGES_REQUEST }); 9 | 10 | const { data } = await axios.get(`/api/v1/messages/${chatId}`); 11 | 12 | dispatch({ 13 | type: ALL_MESSAGES_SUCCESS, 14 | payload: data.messages, 15 | }); 16 | 17 | } catch (error) { 18 | dispatch({ 19 | type: ALL_MESSAGES_FAIL, 20 | payload: error.response.data.message, 21 | }); 22 | } 23 | }; 24 | 25 | // New Message 26 | export const sendMessage = (msgData) => async (dispatch) => { 27 | try { 28 | 29 | dispatch({ type: NEW_MESSAGE_REQUEST }); 30 | const config = { header: { "Content-Type": "application/json" } } 31 | const { data } = await axios.post('/api/v1/newMessage/', msgData, config); 32 | 33 | dispatch({ 34 | type: NEW_MESSAGE_SUCCESS, 35 | payload: data, 36 | }); 37 | 38 | } catch (error) { 39 | dispatch({ 40 | type: NEW_MESSAGE_FAIL, 41 | payload: error.response.data.message, 42 | }); 43 | } 44 | } 45 | 46 | // Clear All Errors 47 | export const clearErrors = () => (dispatch) => { 48 | dispatch({ type: CLEAR_ERRORS }); 49 | } -------------------------------------------------------------------------------- /frontend/src/components/Layouts/UserListItem.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | import { Link } from 'react-router-dom' 4 | import { followUser } from '../../actions/userAction' 5 | 6 | const UserListItem = ({ _id, avatar, username, name, followers }) => { 7 | 8 | const dispatch = useDispatch(); 9 | 10 | const { user } = useSelector((state) => state.user); 11 | 12 | const [follow, setFollow] = useState(followers?.some((id) => id === user._id)); 13 | 14 | const handleFollow = () => { 15 | setFollow(!follow); 16 | dispatch(followUser(_id)); 17 | } 18 | 19 | return ( 20 |
21 |
22 | 23 |
24 | {username} 25 | {name} 26 |
27 |
28 | {_id !== user._id && ( 29 | follow ? ( 30 | 31 | ) : ( 32 | 33 | ) 34 | )} 35 |
36 | ) 37 | } 38 | 39 | export default UserListItem -------------------------------------------------------------------------------- /frontend/src/components/Home/StoriesContainer.jsx: -------------------------------------------------------------------------------- 1 | import Slider from "react-slick"; 2 | import "slick-carousel/slick/slick.css"; 3 | import "slick-carousel/slick/slick-theme.css"; 4 | import { stories } from "../../utils/constants"; 5 | 6 | const StoriesContainer = () => { 7 | 8 | const settings = { 9 | dots: false, 10 | infinite: false, 11 | speed: 500, 12 | slidesToShow: 7.5, 13 | slidesToScroll: 3, 14 | responsive: [ 15 | { 16 | breakpoint: 1050, 17 | settings: { 18 | slidesToShow: 5, 19 | slidesToScroll: 3 20 | } 21 | }, 22 | { 23 | breakpoint: 400, 24 | settings: { 25 | slidesToShow: 4, 26 | slidesToScroll: 2 27 | } 28 | } 29 | ] 30 | }; 31 | 32 | return ( 33 | <> 34 | 35 | 36 | {stories.map((s, i) => ( 37 |
38 |
39 | story 40 |
41 | {s.title} 42 |
43 | ))} 44 | 45 |
46 | 47 | 48 | ) 49 | } 50 | 51 | export default StoriesContainer -------------------------------------------------------------------------------- /frontend/src/components/Layouts/SpinLoader.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const SpinLoader = () => { 4 | return ( 5 | 6 | 7 | 8 | ) 9 | } 10 | 11 | export default SpinLoader -------------------------------------------------------------------------------- /frontend/src/constants/postConstants.js: -------------------------------------------------------------------------------- 1 | export const NEW_POST_REQUEST = "NEW_POST_REQUEST"; 2 | export const NEW_POST_SUCCESS = "NEW_POST_SUCCESS"; 3 | export const NEW_POST_RESET = "NEW_POST_RESET"; 4 | export const NEW_POST_FAIL = "NEW_POST_FAIL"; 5 | 6 | export const POST_FOLLOWING_REQUEST = "POST_FOLLOWING_REQUEST"; 7 | export const POST_FOLLOWING_SUCCESS = "POST_FOLLOWING_SUCCESS"; 8 | export const POST_FOLLOWING_RESET = "POST_FOLLOWING_RESET"; 9 | export const POST_FOLLOWING_FAIL = "POST_FOLLOWING_FAIL"; 10 | 11 | export const LIKE_UNLIKE_POST_REQUEST = "LIKE_UNLIKE_POST_REQUEST"; 12 | export const LIKE_UNLIKE_POST_SUCCESS = "LIKE_UNLIKE_POST_SUCCESS"; 13 | export const LIKE_UNLIKE_POST_RESET = "LIKE_UNLIKE_POST_RESET"; 14 | export const LIKE_UNLIKE_POST_FAIL = "LIKE_UNLIKE_POST_FAIL"; 15 | 16 | export const NEW_COMMENT_REQUEST = "NEW_COMMENT_REQUEST"; 17 | export const NEW_COMMENT_SUCCESS = "NEW_COMMENT_SUCCESS"; 18 | export const NEW_COMMENT_RESET = "NEW_COMMENT_RESET"; 19 | export const NEW_COMMENT_FAIL = "NEW_COMMENT_FAIL"; 20 | 21 | export const SAVE_UNSAVE_POST_REQUEST = "SAVE_UNSAVE_POST_REQUEST"; 22 | export const SAVE_UNSAVE_POST_SUCCESS = "SAVE_UNSAVE_POST_SUCCESS"; 23 | export const SAVE_UNSAVE_POST_RESET = "SAVE_UNSAVE_POST_RESET"; 24 | export const SAVE_UNSAVE_POST_FAIL = "SAVE_UNSAVE_POST_FAIL"; 25 | 26 | export const DELETE_POST_REQUEST = "DELETE_POST_REQUEST"; 27 | export const DELETE_POST_SUCCESS = "DELETE_POST_SUCCESS"; 28 | export const DELETE_POST_RESET = "DELETE_POST_RESET"; 29 | export const DELETE_POST_FAIL = "DELETE_POST_FAIL"; 30 | 31 | export const POST_DETAILS_REQUEST = "POST_DETAILS_REQUEST"; 32 | export const POST_DETAILS_SUCCESS = "POST_DETAILS_SUCCESS"; 33 | export const POST_DETAILS_RESET = "POST_DETAILS_RESET"; 34 | export const POST_DETAILS_FAIL = "POST_DETAILS_FAIL"; 35 | 36 | export const CLEAR_ERRORS = "CLEAR_ERRORS"; -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "proxy": "http://localhost:4000", 6 | "dependencies": { 7 | "@emotion/react": "^11.8.1", 8 | "@emotion/styled": "^11.8.1", 9 | "@mui/icons-material": "^5.4.4", 10 | "@mui/material": "^5.4.4", 11 | "@testing-library/jest-dom": "^5.16.1", 12 | "@testing-library/react": "^12.1.2", 13 | "@testing-library/user-event": "^13.5.0", 14 | "axios": "^0.26.0", 15 | "emoji-mart": "^3.0.1", 16 | "moment": "^2.29.1", 17 | "react": "^17.0.2", 18 | "react-dom": "^17.0.2", 19 | "react-helmet-async": "^1.2.3", 20 | "react-infinite-scroll-component": "^6.1.0", 21 | "react-redux": "^7.2.6", 22 | "react-router-dom": "^6.2.1", 23 | "react-scripts": "5.0.0", 24 | "react-scroll-to-bottom": "^4.2.0", 25 | "react-slick": "^0.28.1", 26 | "react-toastify": "^8.2.0", 27 | "redux": "^4.1.2", 28 | "redux-devtools-extension": "^2.13.9", 29 | "redux-thunk": "^2.4.1", 30 | "slick-carousel": "^1.8.1", 31 | "socket.io-client": "^4.4.1", 32 | "web-vitals": "^2.1.3", 33 | "webfontloader": "^1.6.28" 34 | }, 35 | "scripts": { 36 | "start": "react-scripts start", 37 | "build": "react-scripts build", 38 | "test": "react-scripts test", 39 | "eject": "react-scripts eject" 40 | }, 41 | "eslintConfig": { 42 | "extends": [ 43 | "react-app", 44 | "react-app/jest" 45 | ] 46 | }, 47 | "browserslist": { 48 | "production": [ 49 | ">0.2%", 50 | "not dead", 51 | "not op_mini all" 52 | ], 53 | "development": [ 54 | "last 1 chrome version", 55 | "last 1 firefox version", 56 | "last 1 safari version" 57 | ] 58 | }, 59 | "devDependencies": { 60 | "autoprefixer": "^10.4.2", 61 | "postcss": "^8.4.8", 62 | "tailwindcss": "^3.0.23" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Instagram 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /backend/utils/awsFunctions.js: -------------------------------------------------------------------------------- 1 | const aws = require('aws-sdk') 2 | const multer = require('multer') 3 | const multerS3 = require('multer-s3'); 4 | const path = require('path'); 5 | 6 | const s3Config = new aws.S3({ 7 | accessKeyId: process.env.AWS_IAM_USER_KEY, 8 | secretAccessKey: process.env.AWS_IAM_USER_SECRET, 9 | Bucket: process.env.AWS_BUCKET_NAME 10 | }); 11 | 12 | const avatarS3Config = multerS3({ 13 | s3: s3Config, 14 | bucket: process.env.AWS_BUCKET_NAME, 15 | acl: 'public-read', 16 | metadata: function (req, file, cb) { 17 | cb(null, { fieldName: file.fieldname }); 18 | }, 19 | key: function (req, file, cb) { 20 | const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9) 21 | cb(null, "profiles/" + file.fieldname + '_' + uniqueSuffix + path.extname(file.originalname)) 22 | } 23 | }); 24 | 25 | const postS3Config = multerS3({ 26 | s3: s3Config, 27 | bucket: process.env.AWS_BUCKET_NAME, 28 | acl: 'public-read', 29 | metadata: function (req, file, cb) { 30 | cb(null, { fieldName: file.fieldname }); 31 | }, 32 | key: function (req, file, cb) { 33 | const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9) 34 | cb(null, "posts/" + file.fieldname + '_' + uniqueSuffix + path.extname(file.originalname)) 35 | } 36 | }); 37 | 38 | exports.uploadAvatar = multer({ 39 | storage: avatarS3Config, 40 | limits: { 41 | fileSize: 1024 * 1024 * 5 42 | } 43 | }); 44 | 45 | exports.uploadPost = multer({ 46 | storage: postS3Config, 47 | limits: { 48 | fileSize: 1024 * 1024 * 5 49 | } 50 | }); 51 | 52 | exports.deleteFile = async (fileuri) => { 53 | const fileKey = fileuri.split('/').slice(-2).join("/"); 54 | return await s3Config.deleteObject({ 55 | Bucket: process.env.AWS_BUCKET_NAME, 56 | Key: fileKey 57 | }).promise(); 58 | } -------------------------------------------------------------------------------- /frontend/src/components/Chats/ChatListItem.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { Link, useParams } from 'react-router-dom' 4 | import { SOCKET_ENDPOINT } from '../../utils/constants'; 5 | import { io } from 'socket.io-client'; 6 | 7 | const ChatListItem = ({ _id, users, latestMessage }) => { 8 | 9 | const dispatch = useDispatch(); 10 | const params = useParams(); 11 | const [friend, setFriend] = useState({}); 12 | 13 | const socket = useRef(null); 14 | const [isOnline, setIsOnline] = useState(false); 15 | 16 | const { user } = useSelector((state) => state.user); 17 | 18 | useEffect(() => { 19 | const friendDetails = users.find((u) => u._id !== user._id); 20 | setFriend(friendDetails) 21 | }, [users]); 22 | 23 | useEffect(() => { 24 | socket.current = io(SOCKET_ENDPOINT); 25 | }, []); 26 | 27 | useEffect(() => { 28 | socket.current.on("getUsers", users => { 29 | // console.log(users); 30 | setIsOnline(users.some((u) => u.userId === friend._id)); 31 | }) 32 | }, [friend._id]) 33 | 34 | return ( 35 | 36 |
37 | avatar 38 | {isOnline &&
} 39 |
40 |
41 | {friend.name} 42 | {latestMessage?.content} 43 |
44 | 45 | ) 46 | } 47 | 48 | export default ChatListItem -------------------------------------------------------------------------------- /frontend/src/reducers/chatsReducer.js: -------------------------------------------------------------------------------- 1 | import { ALL_CHATS_FAIL, ALL_CHATS_REQUEST, ALL_CHATS_SUCCESS, CLEAR_ERRORS, NEW_CHAT_FAIL, NEW_CHAT_REQUEST, NEW_CHAT_RESET, NEW_CHAT_SUCCESS } from "../constants/chatConstants"; 2 | 3 | export const allChatsReducer = (state = { chats: [] }, { type, payload }) => { 4 | switch (type) { 5 | case ALL_CHATS_REQUEST: 6 | return { 7 | ...state, 8 | loading: true, 9 | }; 10 | case ALL_CHATS_SUCCESS: 11 | return { 12 | loading: false, 13 | chats: payload, 14 | }; 15 | case ALL_CHATS_FAIL: 16 | return { 17 | ...state, 18 | loading: false, 19 | error: payload, 20 | }; 21 | case CLEAR_ERRORS: 22 | return { 23 | ...state, 24 | error: null, 25 | }; 26 | default: 27 | return state; 28 | } 29 | } 30 | 31 | // New Chat Reducer 32 | export const newChatReducer = (state = {}, { type, payload }) => { 33 | switch (type) { 34 | case NEW_CHAT_REQUEST: 35 | return { 36 | ...state, 37 | loading: true, 38 | }; 39 | case NEW_CHAT_SUCCESS: 40 | return { 41 | ...state, 42 | loading: false, 43 | success: payload.success, 44 | chat: payload.newChat, 45 | }; 46 | case NEW_CHAT_FAIL: 47 | return { 48 | ...state, 49 | loading: false, 50 | error: payload, 51 | }; 52 | case NEW_CHAT_RESET: 53 | return { 54 | ...state, 55 | success: false, 56 | }; 57 | case CLEAR_ERRORS: 58 | return { 59 | ...state, 60 | error: null, 61 | }; 62 | default: 63 | return state; 64 | } 65 | } -------------------------------------------------------------------------------- /frontend/build/static/media/slick.2630a3e3eab21c607e21.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by Fontastic.me 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /frontend/src/reducers/messageReducer.js: -------------------------------------------------------------------------------- 1 | import { ALL_MESSAGES_ADD, ALL_MESSAGES_FAIL, ALL_MESSAGES_REQUEST, ALL_MESSAGES_SUCCESS, CLEAR_ERRORS, NEW_MESSAGE_FAIL, NEW_MESSAGE_REQUEST, NEW_MESSAGE_RESET, NEW_MESSAGE_SUCCESS } from "../constants/messageConstants"; 2 | 3 | export const allMessagesReducer = (state = { messages: [] }, { type, payload }) => { 4 | switch (type) { 5 | case ALL_MESSAGES_REQUEST: 6 | return { 7 | ...state, 8 | loading: true, 9 | }; 10 | case ALL_MESSAGES_SUCCESS: 11 | return { 12 | loading: false, 13 | messages: payload, 14 | }; 15 | case ALL_MESSAGES_FAIL: 16 | return { 17 | ...state, 18 | loading: false, 19 | error: payload, 20 | }; 21 | case ALL_MESSAGES_ADD: 22 | return { 23 | ...state, 24 | messages: [...state.messages, payload] 25 | }; 26 | case CLEAR_ERRORS: 27 | return { 28 | ...state, 29 | error: null, 30 | }; 31 | default: 32 | return state; 33 | } 34 | } 35 | 36 | export const newMessageReducer = (state = {}, { type, payload }) => { 37 | switch (type) { 38 | case NEW_MESSAGE_REQUEST: 39 | return { 40 | ...state, 41 | loading: true, 42 | }; 43 | case NEW_MESSAGE_SUCCESS: 44 | return { 45 | loading: false, 46 | success: payload.success, 47 | newMessage: payload.newMessage, 48 | }; 49 | case NEW_MESSAGE_RESET: 50 | return { 51 | ...state, 52 | success: false, 53 | newMessage: {} 54 | }; 55 | case NEW_MESSAGE_FAIL: 56 | return { 57 | ...state, 58 | loading: false, 59 | error: payload, 60 | }; 61 | case CLEAR_ERRORS: 62 | return { 63 | ...state, 64 | error: null, 65 | }; 66 | default: 67 | return state; 68 | } 69 | } -------------------------------------------------------------------------------- /frontend/src/constants/userConstants.js: -------------------------------------------------------------------------------- 1 | export const LOGIN_USER_REQUEST = "LOGIN_USER_REQUEST"; 2 | export const LOGIN_USER_SUCCESS = "LOGIN_USER_SUCCESS"; 3 | export const LOGIN_USER_FAIL = "LOGIN_USER_FAIL"; 4 | 5 | export const REGISTER_USER_REQUEST = "REGISTER_USER_REQUEST"; 6 | export const REGISTER_USER_SUCCESS = "REGISTER_USER_SUCCESS"; 7 | export const REGISTER_USER_FAIL = "REGISTER_USER_FAIL"; 8 | 9 | export const LOAD_USER_REQUEST = "LOAD_USER_REQUEST"; 10 | export const LOAD_USER_SUCCESS = "LOAD_USER_SUCCESS"; 11 | export const LOAD_USER_FAIL = "LOAD_USER_FAIL"; 12 | 13 | export const USER_DETAILS_REQUEST = "USER_DETAILS_REQUEST"; 14 | export const USER_DETAILS_SUCCESS = "USER_DETAILS_SUCCESS"; 15 | export const USER_DETAILS_RESET = "USER_DETAILS_RESET"; 16 | export const USER_DETAILS_FAIL = "USER_DETAILS_FAIL"; 17 | 18 | export const LOGOUT_USER_SUCCESS = "LOGOUT_USER_SUCCESS"; 19 | export const LOGOUT_USER_FAIL = "LOGOUT_USER_FAIL"; 20 | 21 | export const FORGOT_PASSWORD_REQUEST = "FORGOT_PASSWORD_REQUEST"; 22 | export const FORGOT_PASSWORD_SUCCESS = "FORGOT_PASSWORD_SUCCESS"; 23 | export const FORGOT_PASSWORD_FAIL = "FORGOT_PASSWORD_FAIL"; 24 | 25 | export const RESET_PASSWORD_REQUEST = "RESET_PASSWORD_REQUEST"; 26 | export const RESET_PASSWORD_SUCCESS = "RESET_PASSWORD_SUCCESS"; 27 | export const RESET_PASSWORD_FAIL = "RESET_PASSWORD_FAIL"; 28 | 29 | export const ALL_USERS_REQUEST = "ALL_USERS_REQUEST"; 30 | export const ALL_USERS_SUCCESS = "ALL_USERS_SUCCESS"; 31 | export const ALL_USERS_FAIL = "ALL_USERS_FAIL"; 32 | 33 | export const FOLLOW_USER_REQUEST = "FOLLOW_USER_REQUEST"; 34 | export const FOLLOW_USER_SUCCESS = "FOLLOW_USER_SUCCESS"; 35 | export const FOLLOW_USER_RESET = "FOLLOW_USER_RESET"; 36 | export const FOLLOW_USER_FAIL = "FOLLOW_USER_FAIL"; 37 | 38 | export const UPDATE_PROFILE_REQUEST = "UPDATE_PROFILE_REQUEST"; 39 | export const UPDATE_PROFILE_SUCCESS = "UPDATE_PROFILE_SUCCESS"; 40 | export const UPDATE_PROFILE_RESET = "UPDATE_PROFILE_RESET"; 41 | export const UPDATE_PROFILE_FAIL = "UPDATE_PROFILE_FAIL"; 42 | 43 | export const UPDATE_PASSWORD_REQUEST = "UPDATE_PASSWORD_REQUEST"; 44 | export const UPDATE_PASSWORD_SUCCESS = "UPDATE_PASSWORD_SUCCESS"; 45 | export const UPDATE_PASSWORD_RESET = "UPDATE_PASSWORD_RESET"; 46 | export const UPDATE_PASSWORD_FAIL = "UPDATE_PASSWORD_FAIL"; 47 | 48 | export const CLEAR_ERRORS = "CLEAR_ERRORS"; -------------------------------------------------------------------------------- /frontend/src/components/Navbar/ProfileDetails.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link, useNavigate } from 'react-router-dom'; 3 | import { profileIcon, savedIcon, settingsIcon, switchAccountIcon } from './SvgIcons' 4 | import { logoutUser } from '../../actions/userAction'; 5 | import { useDispatch, useSelector } from 'react-redux'; 6 | import { toast } from "react-toastify"; 7 | import { ClickAwayListener } from '@mui/material'; 8 | 9 | const ProfileDetails = ({ setProfileToggle }) => { 10 | 11 | const dispatch = useDispatch(); 12 | const navigate = useNavigate(); 13 | 14 | const { user } = useSelector((state) => state.user); 15 | 16 | const tabs = [ 17 | { 18 | title: "Profile", 19 | icon: profileIcon, 20 | redirect: `/${user.username}` 21 | }, 22 | { 23 | title: "Saved", 24 | icon: savedIcon, 25 | redirect: `/${user.username}` 26 | }, 27 | { 28 | title: "Settings", 29 | icon: settingsIcon, 30 | redirect: "/accounts/edit" 31 | }, 32 | { 33 | title: "Switch Account", 34 | icon: switchAccountIcon, 35 | redirect: "/" 36 | }, 37 | ] 38 | 39 | const handleLogout = () => { 40 | dispatch(logoutUser()); 41 | navigate("/login"); 42 | toast.success("Logout Successfully"); 43 | } 44 | 45 | 46 | return ( 47 | setProfileToggle(false)}> 48 |
49 |
50 | 51 |
52 | {tabs.map((el, i) => ( 53 | 54 | {el.icon} 55 | {el.title} 56 | 57 | ))} 58 | 61 |
62 |
63 |
64 | 65 | ) 66 | } 67 | 68 | export default ProfileDetails -------------------------------------------------------------------------------- /frontend/build/static/js/main.99425b85.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | object-assign 3 | (c) Sindre Sorhus 4 | @license MIT 5 | */ 6 | 7 | /** 8 | * React Router DOM v6.2.2 9 | * 10 | * Copyright (c) Remix Software Inc. 11 | * 12 | * This source code is licensed under the MIT license found in the 13 | * LICENSE.md file in the root directory of this source tree. 14 | * 15 | * @license MIT 16 | */ 17 | 18 | /** 19 | * React Router v6.2.2 20 | * 21 | * Copyright (c) Remix Software Inc. 22 | * 23 | * This source code is licensed under the MIT license found in the 24 | * LICENSE.md file in the root directory of this source tree. 25 | * 26 | * @license MIT 27 | */ 28 | 29 | /** @license MUI v5.4.4 30 | * 31 | * This source code is licensed under the MIT license found in the 32 | * LICENSE file in the root directory of this source tree. 33 | */ 34 | 35 | /** @license React v0.20.2 36 | * scheduler.production.min.js 37 | * 38 | * Copyright (c) Facebook, Inc. and its affiliates. 39 | * 40 | * This source code is licensed under the MIT license found in the 41 | * LICENSE file in the root directory of this source tree. 42 | */ 43 | 44 | /** @license React v16.13.1 45 | * react-is.production.min.js 46 | * 47 | * Copyright (c) Facebook, Inc. and its affiliates. 48 | * 49 | * This source code is licensed under the MIT license found in the 50 | * LICENSE file in the root directory of this source tree. 51 | */ 52 | 53 | /** @license React v17.0.2 54 | * react-dom.production.min.js 55 | * 56 | * Copyright (c) Facebook, Inc. and its affiliates. 57 | * 58 | * This source code is licensed under the MIT license found in the 59 | * LICENSE file in the root directory of this source tree. 60 | */ 61 | 62 | /** @license React v17.0.2 63 | * react-is.production.min.js 64 | * 65 | * Copyright (c) Facebook, Inc. and its affiliates. 66 | * 67 | * This source code is licensed under the MIT license found in the 68 | * LICENSE file in the root directory of this source tree. 69 | */ 70 | 71 | /** @license React v17.0.2 72 | * react-jsx-runtime.production.min.js 73 | * 74 | * Copyright (c) Facebook, Inc. and its affiliates. 75 | * 76 | * This source code is licensed under the MIT license found in the 77 | * LICENSE file in the root directory of this source tree. 78 | */ 79 | 80 | /** @license React v17.0.2 81 | * react.production.min.js 82 | * 83 | * Copyright (c) Facebook, Inc. and its affiliates. 84 | * 85 | * This source code is licensed under the MIT license found in the 86 | * LICENSE file in the root directory of this source tree. 87 | */ 88 | 89 | //! moment.js 90 | -------------------------------------------------------------------------------- /frontend/src/components/User/Posts/PostContainer.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { useParams } from 'react-router-dom'; 4 | import { toast } from 'react-toastify'; 5 | import { clearErrors } from '../../../actions/postAction'; 6 | import { getUserDetails } from '../../../actions/userAction'; 7 | import { DELETE_POST_RESET, LIKE_UNLIKE_POST_RESET, NEW_COMMENT_RESET, SAVE_UNSAVE_POST_RESET } from '../../../constants/postConstants'; 8 | import PostItem from './PostItem' 9 | 10 | const PostContainer = ({ posts, id }) => { 11 | 12 | const dispatch = useDispatch(); 13 | const params = useParams(); 14 | 15 | const { error: likeError, message, success } = useSelector((state) => state.likePost) 16 | const { error: commentError, success: commentSuccess } = useSelector((state) => state.newComment) 17 | const { error: saveError, success: saveSuccess, message: saveMessage } = useSelector((state) => state.savePost) 18 | const { error: deleteError, success: deleteSuccess } = useSelector((state) => state.deletePost) 19 | 20 | useEffect(() => { 21 | dispatch(getUserDetails(params.username)); 22 | if (likeError) { 23 | toast.error(likeError); 24 | dispatch(clearErrors()); 25 | } 26 | if (success) { 27 | toast.success(message) 28 | dispatch({ type: LIKE_UNLIKE_POST_RESET }); 29 | } 30 | if (commentError) { 31 | toast.error(commentError); 32 | dispatch(clearErrors()); 33 | } 34 | if (commentSuccess) { 35 | toast.success("Comment Added") 36 | dispatch({ type: NEW_COMMENT_RESET }); 37 | } 38 | if (saveError) { 39 | toast.error(saveError); 40 | dispatch(clearErrors()); 41 | } 42 | if (saveSuccess) { 43 | toast.success(saveMessage) 44 | dispatch({ type: SAVE_UNSAVE_POST_RESET }); 45 | } 46 | if (deleteError) { 47 | toast.error(deleteError); 48 | dispatch(clearErrors()); 49 | } 50 | if (deleteSuccess) { 51 | toast.success("Post Deleted") 52 | dispatch({ type: DELETE_POST_RESET }); 53 | } 54 | }, [dispatch, success, likeError, message, commentError, commentSuccess, saveError, saveSuccess, saveMessage, deleteError, deleteSuccess]); 55 | 56 | return ( 57 |
58 | {posts?.map((post, i) => ( 59 | 60 | )).reverse() 61 | } 62 |
63 | ) 64 | } 65 | 66 | export default PostContainer -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const express = require('express'); 3 | const app = require('./backend/app'); 4 | const connectDatabase = require('./backend/config/database'); 5 | const PORT = process.env.PORT || 4000; 6 | 7 | connectDatabase(); 8 | 9 | // deployment 10 | __dirname = path.resolve(); 11 | if (process.env.NODE_ENV === 'production') { 12 | app.use(express.static(path.join(__dirname, '/frontend/build'))) 13 | 14 | app.get('*', (req, res) => { 15 | res.sendFile(path.resolve(__dirname, 'frontend', 'build', 'index.html')) 16 | }); 17 | } else { 18 | app.get('/', (req, res) => { 19 | res.send('Server is Running! 🚀'); 20 | }); 21 | } 22 | 23 | const server = app.listen(PORT, () => { 24 | console.log(`Server Running on http://localhost:${PORT}`); 25 | }); 26 | 27 | 28 | // ============= socket.io ============== 29 | 30 | const io = require("socket.io")(server, { 31 | // pingTimeout: 60000, 32 | cors: { 33 | origin: "http://localhost:3000", 34 | } 35 | }); 36 | 37 | let users = []; 38 | 39 | const addUser = (userId, socketId) => { 40 | !users.some((user) => user.userId === userId) && 41 | users.push({ userId, socketId }); 42 | } 43 | 44 | const removeUser = (socketId) => { 45 | users = users.filter((user) => user.socketId !== socketId); 46 | } 47 | 48 | const getUser = (userId) => { 49 | return users.find((user) => user.userId === userId); 50 | } 51 | 52 | io.on("connection", (socket) => { 53 | console.log("🚀 Someone connected!"); 54 | // console.log(users); 55 | 56 | // get userId and socketId from client 57 | socket.on("addUser", (userId) => { 58 | addUser(userId, socket.id); 59 | io.emit("getUsers", users); 60 | }); 61 | 62 | // get and send message 63 | socket.on("sendMessage", ({ senderId, receiverId, content }) => { 64 | 65 | const user = getUser(receiverId); 66 | 67 | io.to(user?.socketId).emit("getMessage", { 68 | senderId, 69 | content, 70 | }); 71 | }); 72 | 73 | // typing states 74 | socket.on("typing", ({ senderId, receiverId }) => { 75 | const user = getUser(receiverId); 76 | console.log(user) 77 | io.to(user?.socketId).emit("typing", senderId); 78 | }); 79 | 80 | socket.on("typing stop", ({ senderId, receiverId }) => { 81 | const user = getUser(receiverId); 82 | io.to(user?.socketId).emit("typing stop", senderId); 83 | }); 84 | 85 | // user disconnected 86 | socket.on("disconnect", () => { 87 | console.log("⚠️ Someone disconnected") 88 | removeUser(socket.id); 89 | io.emit("getUsers", users); 90 | // console.log(users); 91 | }); 92 | }); -------------------------------------------------------------------------------- /frontend/src/components/User/ForgotPassword.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import TextField from '@mui/material/TextField'; 3 | import Auth from './Auth'; 4 | import { Link } from 'react-router-dom'; 5 | import { clearErrors, forgotPassword } from '../../actions/userAction'; 6 | import { toast } from 'react-toastify'; 7 | import { useDispatch, useSelector } from 'react-redux'; 8 | import BackdropLoader from '../Layouts/BackdropLoader'; 9 | 10 | const ForgotPassword = () => { 11 | 12 | const dispatch = useDispatch(); 13 | 14 | const { error, message, loading } = useSelector((state) => state.forgotPassword); 15 | 16 | const [email, setEmail] = useState(""); 17 | 18 | const handleSubmit = (e) => { 19 | e.preventDefault(); 20 | dispatch(forgotPassword(email)); 21 | setEmail(""); 22 | } 23 | 24 | useEffect(() => { 25 | if (error) { 26 | toast.error(error); 27 | dispatch(clearErrors()); 28 | } 29 | if (message) { 30 | toast.success(message); 31 | } 32 | }, [dispatch, error, message]); 33 | 34 | return ( 35 | <> 36 | {loading && } 37 | 38 |
39 | 40 |
41 | setEmail(e.target.value)} 48 | required 49 | /> 50 | 51 | OR 52 | Forgot password? 53 | 54 |
55 | 56 |
57 | Don't have an account? Sign up 58 |
59 |
60 | 61 | ) 62 | } 63 | 64 | export default ForgotPassword -------------------------------------------------------------------------------- /backend/models/userModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const bcrypt = require('bcrypt'); 3 | const jwt = require('jsonwebtoken'); 4 | const crypto = require('crypto'); 5 | 6 | const userSchema = new mongoose.Schema({ 7 | name: { 8 | type: String, 9 | required: [true, "Please enter name"] 10 | }, 11 | email: { 12 | type: String, 13 | required: [true, "Please enter email"], 14 | unique: [true, "Email already exists"], 15 | }, 16 | username: { 17 | type: String, 18 | required: [true, "Please enter username"], 19 | minlength: [6, "Username must be of minimum 6 characters"], 20 | unique: [true, "Username already exists"], 21 | }, 22 | password: { 23 | type: String, 24 | required: [true, "Please enter password"], 25 | minlength: [6, "Password must be of minimum 6 characters"], 26 | select: false, 27 | }, 28 | avatar: { 29 | type: String 30 | }, 31 | bio: { 32 | type: String, 33 | default: "Hi👋 Welcome To My Profile" 34 | }, 35 | website: { 36 | type: String, 37 | trim: true, 38 | }, 39 | posts: [ 40 | { 41 | type: mongoose.Schema.Types.ObjectId, 42 | ref: "Post", 43 | } 44 | ], 45 | saved: [ 46 | { 47 | type: mongoose.Schema.Types.ObjectId, 48 | ref: "Post", 49 | } 50 | ], 51 | followers: [ 52 | { 53 | type: mongoose.Schema.Types.ObjectId, 54 | ref: "User", 55 | } 56 | ], 57 | following: [ 58 | { 59 | type: mongoose.Schema.Types.ObjectId, 60 | ref: "User", 61 | } 62 | ], 63 | resetPasswordToken: String, 64 | resetPasswordExpiry: Date, 65 | }); 66 | 67 | userSchema.pre("save", async function(next) { 68 | if(this.isModified("password")) { 69 | this.password = await bcrypt.hash(this.password, 10); 70 | } 71 | next(); 72 | }); 73 | 74 | userSchema.methods.comparePassword = async function(enteredPassword) { 75 | return await bcrypt.compare(enteredPassword, this.password); 76 | } 77 | 78 | userSchema.methods.generateToken = function() { 79 | return jwt.sign({ id: this._id }, process.env.JWT_SECRET, { 80 | expiresIn: process.env.JWT_EXPIRE 81 | }); 82 | } 83 | 84 | userSchema.methods.getResetPasswordToken = async function() { 85 | 86 | const resetToken = crypto.randomBytes(20).toString("hex"); 87 | 88 | this.resetPasswordToken = crypto.createHash("sha256").update(resetToken).digest("hex"); 89 | this.resetPasswordExpiry = Date.now() + 15 * 60 * 1000; 90 | 91 | return resetToken; 92 | } 93 | 94 | module.exports = mongoose.model("User", userSchema); -------------------------------------------------------------------------------- /frontend/build/static/js/666.85fb12e7.chunk.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([[666],{44145:function(e,a,s){s(72791);var t=s(5884),l=s(80184);a.Z=function(e){var a=e.children;return(0,l.jsx)("div",{className:"w-full h-full",children:(0,l.jsxs)("div",{className:"flex w-full h-screen md:w-2/3 py-8 mx-auto",children:[(0,l.jsx)("div",{className:"hidden md:block bg-[url('https://www.instagram.com/static/images/homepage/phones/home-phones.png/1dc085cdb87d.png')] my-10 h-full bg-no-repeat",children:(0,l.jsx)("img",{draggable:"false",className:"mr-[80px] mt-[1.8rem] ml-[155px]",src:t,alt:"homepage"})}),(0,l.jsxs)("div",{className:"flex flex-col gap-3 w-full md:w-2/5",children:[a,(0,l.jsx)("p",{className:"text-center text-sm my-2",children:"Get the app."}),(0,l.jsxs)("div",{className:"flex gap-3 justify-center",children:[(0,l.jsx)("img",{draggable:"false",width:"130px",src:"https://www.instagram.com/static/images/appstore-install-badges/badge_ios_english-en.png/180ae7a0bcf7.png",alt:"appstore"}),(0,l.jsx)("img",{draggable:"false",width:"130px",src:"https://www.instagram.com/static/images/appstore-install-badges/badge_android_english-en.png/e9cd846dc748.png",alt:"playstore"})]})]})]})})}},32666:function(e,a,s){s.r(a);var t=s(70885),l=s(72791),n=s(19913),r=s(44145),i=s(43504),c=s(37689),m=s(56960),o=s(16030),d=s(23671),g=s(80184);a.default=function(){var e=(0,o.I0)(),a=(0,o.v9)((function(e){return e.forgotPassword})),s=a.error,p=a.message,u=a.loading,h=(0,l.useState)(""),f=(0,t.Z)(h,2),x=f[0],b=f[1];return(0,l.useEffect)((function(){s&&(m.Am.error(s),e((0,c.b9)())),p&&m.Am.success(p)}),[e,s,p]),(0,g.jsxs)(g.Fragment,{children:[u&&(0,g.jsx)(d.Z,{}),(0,g.jsxs)(r.Z,{children:[(0,g.jsxs)("div",{className:"bg-white border flex flex-col gap-2 p-4 pt-10",children:[(0,g.jsx)("img",{draggable:"false",className:"mx-auto h-30 w-36 object-contain",src:"https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png",alt:""}),(0,g.jsxs)("form",{onSubmit:function(a){a.preventDefault(),e((0,c.gF)(x)),b("")},className:"flex flex-col justify-center items-center gap-3 m-3 md:m-8",children:[(0,g.jsx)(n.Z,{label:"Email",variant:"outlined",size:"small",fullWidth:!0,value:x,onChange:function(e){return b(e.target.value)},required:!0}),(0,g.jsx)("button",{type:"submit",className:"bg-primary-blue font-medium py-2 rounded text-white w-full",children:"Submit"}),(0,g.jsx)("span",{className:"my-3 text-gray-500",children:"OR"}),(0,g.jsx)(i.rU,{to:"/password/forgot",className:"text-sm font-medium text-blue-800",children:"Forgot password?"})]})]}),(0,g.jsx)("div",{className:"bg-white border p-5 text-center",children:(0,g.jsxs)("span",{children:["Don't have an account? ",(0,g.jsx)(i.rU,{to:"/register",className:"text-primary-blue",children:"Sign up"})]})})]})]})}},5884:function(e,a,s){e.exports=s.p+"static/media/homepage.225baac9eb2a22aae1a7.webp"}}]); 2 | //# sourceMappingURL=666.85fb12e7.chunk.js.map -------------------------------------------------------------------------------- /frontend/src/components/Navbar/Header.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { exploreOutline, homeFill, homeOutline, likeFill, likeOutline, messageFill, messageOutline, postUploadOutline } from './SvgIcons' 3 | import { Link, useLocation } from 'react-router-dom'; 4 | import ProfileDetails from './ProfileDetails'; 5 | import NewPost from './NewPost'; 6 | import { useSelector } from 'react-redux'; 7 | import SearchBox from './SearchBar/SearchBox'; 8 | import { ClickAwayListener } from '@mui/material'; 9 | 10 | const Header = () => { 11 | 12 | const { user } = useSelector((state) => state.user); 13 | 14 | const [profileToggle, setProfileToggle] = useState(false) 15 | const [newPost, setNewPost] = useState(false); 16 | 17 | const location = useLocation(); 18 | const [onHome, setOnHome] = useState(false); 19 | const [onChat, setOnChat] = useState(false); 20 | 21 | useEffect(() => { 22 | setOnHome(location.pathname === "/") 23 | setOnChat(location.pathname.split('/').includes("direct")) 24 | }, [location]); 25 | 26 | return ( 27 | 58 | ) 59 | } 60 | 61 | export default Header -------------------------------------------------------------------------------- /frontend/build/static/js/355.ff33d956.chunk.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([[355],{44145:function(e,a,s){s(72791);var t=s(5884),l=s(80184);a.Z=function(e){var a=e.children;return(0,l.jsx)("div",{className:"w-full h-full",children:(0,l.jsxs)("div",{className:"flex w-full h-screen md:w-2/3 py-8 mx-auto",children:[(0,l.jsx)("div",{className:"hidden md:block bg-[url('https://www.instagram.com/static/images/homepage/phones/home-phones.png/1dc085cdb87d.png')] my-10 h-full bg-no-repeat",children:(0,l.jsx)("img",{draggable:"false",className:"mr-[80px] mt-[1.8rem] ml-[155px]",src:t,alt:"homepage"})}),(0,l.jsxs)("div",{className:"flex flex-col gap-3 w-full md:w-2/5",children:[a,(0,l.jsx)("p",{className:"text-center text-sm my-2",children:"Get the app."}),(0,l.jsxs)("div",{className:"flex gap-3 justify-center",children:[(0,l.jsx)("img",{draggable:"false",width:"130px",src:"https://www.instagram.com/static/images/appstore-install-badges/badge_ios_english-en.png/180ae7a0bcf7.png",alt:"appstore"}),(0,l.jsx)("img",{draggable:"false",width:"130px",src:"https://www.instagram.com/static/images/appstore-install-badges/badge_android_english-en.png/e9cd846dc748.png",alt:"playstore"})]})]})]})})}},80355:function(e,a,s){s.r(a);var t=s(70885),l=s(72791),n=s(19913),r=s(44145),i=s(16871),c=s(43504),m=s(56960),o=s(23671),d=s(16030),p=s(37689),g=s(80184);a.default=function(){var e=(0,d.I0)(),a=(0,i.s0)(),s=(0,d.v9)((function(e){return e.user})),u=s.loading,h=s.isAuthenticated,x=s.error,f=s.user,b=(0,l.useState)(""),w=(0,t.Z)(b,2),j=w[0],v=w[1],y=(0,l.useState)(""),N=(0,t.Z)(y,2),Z=N[0],_=N[1];return(0,l.useEffect)((function(){x&&(m.Am.error(x),e((0,p.b9)())),h&&a("/".concat(f.username))}),[e,x,h,a]),(0,g.jsxs)(g.Fragment,{children:[u&&(0,g.jsx)(o.Z,{}),(0,g.jsxs)(r.Z,{children:[(0,g.jsxs)("div",{className:"bg-white border flex flex-col gap-2 p-4 pt-10",children:[(0,g.jsx)("img",{draggable:"false",className:"mx-auto h-30 w-36 object-contain",src:"https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png",alt:""}),(0,g.jsxs)("form",{onSubmit:function(a){a.preventDefault(),e((0,p.pH)(j,Z))},className:"flex flex-col justify-center items-center gap-3 m-3 md:m-8",children:[(0,g.jsx)(n.Z,{label:"Email/Username",type:"text",value:j,onChange:function(e){return v(e.target.value)},required:!0,size:"small",fullWidth:!0}),(0,g.jsx)(n.Z,{label:"Password",type:"password",value:Z,onChange:function(e){return _(e.target.value)},required:!0,size:"small",fullWidth:!0}),(0,g.jsx)("button",{type:"submit",className:"bg-primary-blue font-medium py-2 rounded text-white w-full",children:"Log In"}),(0,g.jsx)("span",{className:"my-3 text-gray-500",children:"OR"}),(0,g.jsx)(c.rU,{to:"/password/forgot",className:"text-sm font-medium text-blue-800",children:"Forgot password?"})]})]}),(0,g.jsx)("div",{className:"bg-white border p-5 text-center",children:(0,g.jsxs)("span",{children:["Don't have an account? ",(0,g.jsx)(c.rU,{to:"/register",className:"text-primary-blue",children:"Sign up"})]})})]})]})}},5884:function(e,a,s){e.exports=s.p+"static/media/homepage.225baac9eb2a22aae1a7.webp"}}]); 2 | //# sourceMappingURL=355.ff33d956.chunk.js.map -------------------------------------------------------------------------------- /frontend/src/components/Chats/Sidebar.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { Link, useParams } from 'react-router-dom'; 4 | import { toast } from 'react-toastify'; 5 | import { clearErrors, getAllChats } from '../../actions/chatAction'; 6 | import ChatListItem from './ChatListItem'; 7 | import SkeletonUserItem from '../Layouts/SkeletonUserItem'; 8 | import { Skeleton } from '@mui/material'; 9 | 10 | const Sidebar = ({ openModal, socket }) => { 11 | 12 | const dispatch = useDispatch(); 13 | const params = useParams(); 14 | 15 | const { user } = useSelector((state) => state.user) 16 | const { loading, error, chats } = useSelector((state) => state.allChats) 17 | 18 | useEffect(() => { 19 | if (error) { 20 | toast.error(error); 21 | dispatch(clearErrors()); 22 | } 23 | dispatch(getAllChats()); 24 | }, [dispatch, error, params.chatId]); 25 | 26 | return ( 27 | <> 28 |
29 | 30 |
31 | {user.username} 32 | 33 |
34 | 35 |
36 | 37 | Messages 38 | 39 | {/* {loading && 40 | Array(10).fill("").map((el, i) => ( 41 |
42 | 43 |
44 | 45 | 46 |
47 |
48 | )) 49 | } */} 50 | 51 | {chats?.map((c) => ( 52 | 53 | ))} 54 | 55 |
56 | 57 |
58 | 59 | 60 | ) 61 | } 62 | 63 | export default Sidebar -------------------------------------------------------------------------------- /frontend/src/components/User/Login.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import TextField from '@mui/material/TextField'; 3 | import Auth from './Auth'; 4 | import { Link, useNavigate } from 'react-router-dom'; 5 | import { toast } from "react-toastify"; 6 | import BackdropLoader from '../Layouts/BackdropLoader'; 7 | import { useDispatch, useSelector } from 'react-redux'; 8 | import { clearErrors, loginUser } from '../../actions/userAction'; 9 | 10 | const Login = () => { 11 | 12 | const dispatch = useDispatch(); 13 | const navigate = useNavigate(); 14 | 15 | const { loading, isAuthenticated, error, user } = useSelector((state) => state.user); 16 | 17 | const [email, setEmail] = useState(""); 18 | const [password, setPassword] = useState(""); 19 | 20 | const handleLogin = (e) => { 21 | e.preventDefault(); 22 | dispatch(loginUser(email, password)); 23 | } 24 | 25 | useEffect(() => { 26 | if (error) { 27 | toast.error(error); 28 | dispatch(clearErrors()); 29 | } 30 | if (isAuthenticated) { 31 | navigate(`/${user.username}`) 32 | } 33 | }, [dispatch, error, isAuthenticated, navigate]); 34 | 35 | 36 | return ( 37 | <> 38 | {loading && } 39 | 40 |
41 | 42 |
43 | setEmail(e.target.value)} 48 | required 49 | size="small" 50 | fullWidth 51 | /> 52 | setPassword(e.target.value)} 57 | required 58 | size="small" 59 | fullWidth 60 | /> 61 | 62 | OR 63 | Forgot password? 64 | 65 |
66 | 67 |
68 | Don't have an account? Sign up 69 |
70 |
71 | 72 | ) 73 | } 74 | 75 | export default Login -------------------------------------------------------------------------------- /frontend/build/static/js/244.013b1587.chunk.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([[244],{44145:function(e,s,a){a(72791);var t=a(5884),r=a(80184);s.Z=function(e){var s=e.children;return(0,r.jsx)("div",{className:"w-full h-full",children:(0,r.jsxs)("div",{className:"flex w-full h-screen md:w-2/3 py-8 mx-auto",children:[(0,r.jsx)("div",{className:"hidden md:block bg-[url('https://www.instagram.com/static/images/homepage/phones/home-phones.png/1dc085cdb87d.png')] my-10 h-full bg-no-repeat",children:(0,r.jsx)("img",{draggable:"false",className:"mr-[80px] mt-[1.8rem] ml-[155px]",src:t,alt:"homepage"})}),(0,r.jsxs)("div",{className:"flex flex-col gap-3 w-full md:w-2/5",children:[s,(0,r.jsx)("p",{className:"text-center text-sm my-2",children:"Get the app."}),(0,r.jsxs)("div",{className:"flex gap-3 justify-center",children:[(0,r.jsx)("img",{draggable:"false",width:"130px",src:"https://www.instagram.com/static/images/appstore-install-badges/badge_ios_english-en.png/180ae7a0bcf7.png",alt:"appstore"}),(0,r.jsx)("img",{draggable:"false",width:"130px",src:"https://www.instagram.com/static/images/appstore-install-badges/badge_android_english-en.png/e9cd846dc748.png",alt:"playstore"})]})]})]})})}},31244:function(e,s,a){a.r(s);var t=a(70885),r=a(72791),l=a(19913),n=a(44145),c=a(16871),i=a(43504),o=a(56960),d=a(37689),m=a(16030),g=a(23671),p=a(80184);s.default=function(){var e=(0,m.I0)(),s=(0,c.s0)(),a=(0,c.UO)(),u=(0,m.v9)((function(e){return e.forgotPassword})),h=u.error,f=u.success,x=u.loading,w=(0,r.useState)(""),b=(0,t.Z)(w,2),j=b[0],v=b[1],y=(0,r.useState)(""),N=(0,t.Z)(y,2),P=N[0],Z=N[1];return(0,r.useEffect)((function(){h&&(o.Am.error(h),e((0,d.b9)())),f&&(o.Am.success("Password Updated Successfully"),s("/login"))}),[e,h,f,s]),(0,p.jsxs)(p.Fragment,{children:[x&&(0,p.jsx)(g.Z,{}),(0,p.jsxs)(n.Z,{children:[(0,p.jsxs)("div",{className:"bg-white border flex flex-col gap-2 p-4 pt-10",children:[(0,p.jsx)("img",{draggable:"false",className:"mx-auto h-30 w-36 object-contain",src:"https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png",alt:""}),(0,p.jsxs)("form",{onSubmit:function(s){s.preventDefault(),j.length<8?o.Am.warn("Password length must be atleast 8 characters"):j===P?e((0,d.c0)(a.token,j)):o.Am.error("Password Doesn't Match")},className:"flex flex-col justify-center items-center gap-3 m-3 md:m-8",children:[(0,p.jsx)(l.Z,{fullWidth:!0,size:"small",label:"New Password",type:"password",name:"newPassword",value:j,onChange:function(e){return v(e.target.value)},required:!0}),(0,p.jsx)(l.Z,{fullWidth:!0,size:"small",label:"Confirm New Password",type:"password",name:"confirmPassword",value:P,onChange:function(e){return Z(e.target.value)},required:!0}),(0,p.jsx)("button",{type:"submit",className:"bg-primary-blue font-medium py-2 rounded text-white w-full",children:"Submit"}),(0,p.jsx)("span",{className:"my-3 text-gray-700",children:"OR"}),(0,p.jsx)(i.rU,{to:"/password/forgot",className:"text-sm font-medium text-blue-800",children:"Forgot password?"})]})]}),(0,p.jsx)("div",{className:"bg-white border p-5 text-center",children:(0,p.jsxs)("span",{children:["Already have an account? ",(0,p.jsx)(i.rU,{to:"/login",className:"text-primary-blue",children:"Log in"})]})})]})]})}},5884:function(e,s,a){e.exports=a.p+"static/media/homepage.225baac9eb2a22aae1a7.webp"}}]); 2 | //# sourceMappingURL=244.013b1587.chunk.js.map -------------------------------------------------------------------------------- /frontend/src/components/Home/SvgIcons.jsx: -------------------------------------------------------------------------------- 1 | export const moreIcons = 2 | 3 | export const likeIconOutline = 4 | 5 | export const likeIconFill = 6 | 7 | export const commentIcon = 8 | 9 | export const shareIcon = 10 | 11 | export const saveIconOutline = 12 | 13 | export const saveIconFill = 14 | 15 | export const emojiIcon = 16 | 17 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /frontend/src/components/User/ResetPassword.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import TextField from '@mui/material/TextField'; 3 | import Auth from './Auth'; 4 | import { Link, useNavigate, useParams } from 'react-router-dom'; 5 | import { toast } from 'react-toastify'; 6 | import { clearErrors, resetPassword } from '../../actions/userAction'; 7 | import { useDispatch, useSelector } from 'react-redux'; 8 | import BackdropLoader from '../Layouts/BackdropLoader'; 9 | 10 | const ResetPassword = () => { 11 | 12 | const dispatch = useDispatch(); 13 | const navigate = useNavigate(); 14 | const params = useParams(); 15 | 16 | const { error, success, loading } = useSelector((state) => state.forgotPassword); 17 | 18 | const [newPassword, setNewPassword] = useState(""); 19 | const [confirmPassword, setConfirmPassword] = useState(""); 20 | 21 | const handleSubmit = (e) => { 22 | e.preventDefault(); 23 | 24 | if (newPassword.length < 8) { 25 | toast.warn("Password length must be atleast 8 characters"); 26 | return; 27 | } 28 | if (newPassword !== confirmPassword) { 29 | toast.error("Password Doesn't Match"); 30 | return; 31 | } 32 | dispatch(resetPassword(params.token, newPassword)); 33 | } 34 | 35 | useEffect(() => { 36 | if (error) { 37 | toast.error(error); 38 | dispatch(clearErrors()); 39 | } 40 | if (success) { 41 | toast.success("Password Updated Successfully"); 42 | navigate("/login") 43 | } 44 | }, [dispatch, error, success, navigate]); 45 | 46 | return ( 47 | <> 48 | {loading && } 49 | 50 |
51 | 52 |
53 | setNewPassword(e.target.value)} 61 | required 62 | /> 63 | setConfirmPassword(e.target.value)} 71 | required 72 | /> 73 | 74 | OR 75 | Forgot password? 76 | 77 |
78 | 79 |
80 | Already have an account? Log in 81 |
82 |
83 | 84 | ) 85 | } 86 | 87 | export default ResetPassword -------------------------------------------------------------------------------- /frontend/build/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "main.css": "/static/css/main.c369afd6.css", 4 | "main.js": "/static/js/main.99425b85.js", 5 | "static/css/812.4165d163.chunk.css": "/static/css/812.4165d163.chunk.css", 6 | "static/js/812.01810cb5.chunk.js": "/static/js/812.01810cb5.chunk.js", 7 | "static/js/25.2f1eb84b.chunk.js": "/static/js/25.2f1eb84b.chunk.js", 8 | "static/js/355.ff33d956.chunk.js": "/static/js/355.ff33d956.chunk.js", 9 | "static/js/666.85fb12e7.chunk.js": "/static/js/666.85fb12e7.chunk.js", 10 | "static/js/244.013b1587.chunk.js": "/static/js/244.013b1587.chunk.js", 11 | "static/js/11.d7687b78.chunk.js": "/static/js/11.d7687b78.chunk.js", 12 | "static/js/943.dd11f041.chunk.js": "/static/js/943.dd11f041.chunk.js", 13 | "static/js/913.ce2b0a76.chunk.js": "/static/js/913.ce2b0a76.chunk.js", 14 | "static/js/270.ccd3f4c8.chunk.js": "/static/js/270.ccd3f4c8.chunk.js", 15 | "static/js/576.d07a5c30.chunk.js": "/static/js/576.d07a5c30.chunk.js", 16 | "static/media/hero.png": "/static/media/hero.cdade95684e864459848.png", 17 | "static/media/reactjs.webp": "/static/media/reactjs.67281ba7f66d2108f454.webp", 18 | "static/media/aws-s3.webp": "/static/media/aws-s3.51640b4de3d80fb51823.webp", 19 | "static/media/homepage.webp": "/static/media/homepage.225baac9eb2a22aae1a7.webp", 20 | "static/media/mui.webp": "/static/media/mui.c78de51d000137fbffd3.webp", 21 | "static/media/axios.webp": "/static/media/axios.56fc2f13fcebf9762c0c.webp", 22 | "static/media/multer.webp": "/static/media/multer.c2a3c0310a42b05e0188.webp", 23 | "static/media/sendgrid.webp": "/static/media/sendgrid.fce5f541441ff976d2dd.webp", 24 | "static/media/toastify.webp": "/static/media/toastify.08fda808d10d7ed9dada.webp", 25 | "static/media/javascript.webp": "/static/media/javascript.70416db3411be6deeb08.webp", 26 | "static/media/socketio.webp": "/static/media/socketio.aafffdfd652a68a2d466.webp", 27 | "static/media/redux.webp": "/static/media/redux.51aa90023ce0ceaa0082.webp", 28 | "static/media/expressjs.webp": "/static/media/expressjs.a2e86fd3965b0781dc31.webp", 29 | "static/media/mongodb.webp": "/static/media/mongodb.d753b795305bda0741bb.webp", 30 | "static/media/tailwind.webp": "/static/media/tailwind.89af3bced34f432936d5.webp", 31 | "static/media/nodejs.webp": "/static/media/nodejs.90478dd570aa54a25de3.webp", 32 | "static/media/heroku.webp": "/static/media/heroku.c2c5cccdafe77e3b59c0.webp", 33 | "static/media/slick.svg": "/static/media/slick.2630a3e3eab21c607e21.svg", 34 | "static/media/slick.eot": "/static/media/slick.a4e97f5a2a64f0ab1323.eot", 35 | "static/media/slick.ttf": "/static/media/slick.c94f7671dcc99dce43e2.ttf", 36 | "static/media/slick.woff": "/static/media/slick.295183786cd8a1389865.woff", 37 | "index.html": "/index.html", 38 | "main.c369afd6.css.map": "/static/css/main.c369afd6.css.map", 39 | "main.99425b85.js.map": "/static/js/main.99425b85.js.map", 40 | "812.4165d163.chunk.css.map": "/static/css/812.4165d163.chunk.css.map", 41 | "812.01810cb5.chunk.js.map": "/static/js/812.01810cb5.chunk.js.map", 42 | "25.2f1eb84b.chunk.js.map": "/static/js/25.2f1eb84b.chunk.js.map", 43 | "355.ff33d956.chunk.js.map": "/static/js/355.ff33d956.chunk.js.map", 44 | "666.85fb12e7.chunk.js.map": "/static/js/666.85fb12e7.chunk.js.map", 45 | "244.013b1587.chunk.js.map": "/static/js/244.013b1587.chunk.js.map", 46 | "11.d7687b78.chunk.js.map": "/static/js/11.d7687b78.chunk.js.map", 47 | "943.dd11f041.chunk.js.map": "/static/js/943.dd11f041.chunk.js.map", 48 | "913.ce2b0a76.chunk.js.map": "/static/js/913.ce2b0a76.chunk.js.map", 49 | "270.ccd3f4c8.chunk.js.map": "/static/js/270.ccd3f4c8.chunk.js.map", 50 | "576.d07a5c30.chunk.js.map": "/static/js/576.d07a5c30.chunk.js.map" 51 | }, 52 | "entrypoints": [ 53 | "static/css/main.c369afd6.css", 54 | "static/js/main.99425b85.js" 55 | ] 56 | } -------------------------------------------------------------------------------- /frontend/src/components/Home/PostsContainer.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | import { toast } from 'react-toastify' 4 | import { clearErrors, getPostsOfFollowing } from '../../actions/postAction' 5 | import { LIKE_UNLIKE_POST_RESET, NEW_COMMENT_RESET, POST_FOLLOWING_RESET, SAVE_UNSAVE_POST_RESET } from '../../constants/postConstants' 6 | import UsersDialog from '../Layouts/UsersDialog' 7 | import PostItem from './PostItem' 8 | import StoriesContainer from './StoriesContainer' 9 | import InfiniteScroll from 'react-infinite-scroll-component'; 10 | import SpinLoader from '../Layouts/SpinLoader' 11 | import SkeletonPost from '../Layouts/SkeletonPost' 12 | 13 | const PostsContainer = () => { 14 | 15 | const dispatch = useDispatch(); 16 | 17 | const [usersList, setUsersList] = useState([]); 18 | const [usersDialog, setUsersDialog] = useState(false); 19 | const [page, setPage] = useState(2); 20 | 21 | const { loading, error, posts, totalPosts } = useSelector((state) => state.postOfFollowing) 22 | const { error: likeError, message, success } = useSelector((state) => state.likePost) 23 | const { error: commentError, success: commentSuccess } = useSelector((state) => state.newComment) 24 | const { error: saveError, success: saveSuccess, message: saveMessage } = useSelector((state) => state.savePost) 25 | 26 | const handleClose = () => setUsersDialog(false); 27 | 28 | useEffect(() => { 29 | if (error) { 30 | toast.error(error); 31 | dispatch(clearErrors()); 32 | } 33 | dispatch(getPostsOfFollowing()); 34 | dispatch({ type: POST_FOLLOWING_RESET }); 35 | }, [dispatch, error]); 36 | 37 | useEffect(() => { 38 | if (likeError) { 39 | toast.error(likeError); 40 | dispatch(clearErrors()); 41 | } 42 | if (success) { 43 | toast.success(message) 44 | dispatch({ type: LIKE_UNLIKE_POST_RESET }); 45 | } 46 | if (commentError) { 47 | toast.error(commentError); 48 | dispatch(clearErrors()); 49 | } 50 | if (commentSuccess) { 51 | toast.success("Comment Added") 52 | dispatch({ type: NEW_COMMENT_RESET }); 53 | } 54 | if (saveError) { 55 | toast.error(saveError); 56 | dispatch(clearErrors()); 57 | } 58 | if (saveSuccess) { 59 | toast.success(saveMessage) 60 | dispatch({ type: SAVE_UNSAVE_POST_RESET }); 61 | } 62 | }, [dispatch, success, likeError, message, commentError, commentSuccess, saveError, saveSuccess, saveMessage]) 63 | 64 | const fetchMorePosts = () => { 65 | setPage((prev) => prev + 1) 66 | dispatch(getPostsOfFollowing(page)); 67 | } 68 | 69 | return ( 70 | <> 71 |
72 | 73 | 74 | 75 | {loading && 76 | Array(5).fill("").map((el, i) => ()) 77 | } 78 | } 83 | > 84 |
85 | {posts?.map((post) => ( 86 | 87 | ))} 88 |
89 |
90 | 91 | 92 | 93 |
94 | 95 | ) 96 | } 97 | 98 | export default PostsContainer -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from 'react-redux'; 2 | import { loadUser } from './actions/userAction'; 3 | import { lazy, Suspense, useEffect } from 'react'; 4 | import { Routes, Route, useLocation } from 'react-router-dom'; 5 | import { toast } from 'react-toastify'; 6 | import "react-toastify/dist/ReactToastify.css"; 7 | import 'emoji-mart/css/emoji-mart.css'; 8 | import Header from './components/Navbar/Header'; 9 | import PrivateRoute from './Routes/PrivateRoute'; 10 | import Profile from './components/User/Profile'; 11 | import UpdateProfile from './components/User/Update/UpdateProfile'; 12 | import UpdatePassword from './components/User/Update/UpdatePassword'; 13 | import SpinLoader from './components/Layouts/SpinLoader'; 14 | 15 | const Home = lazy(() => import('./components/Home/Home')); 16 | const SignUp = lazy(() => import('./components/User/SignUp')); 17 | const Login = lazy(() => import('./components/User/Login')); 18 | const ForgotPassword = lazy(() => import('./components/User/ForgotPassword')); 19 | const ResetPassword = lazy(() => import('./components/User/ResetPassword')); 20 | const Update = lazy(() => import('./components/User/Update/Update')); 21 | const Inbox = lazy(() => import('./components/Chats/Inbox')); 22 | const NotFound = lazy(() => import('./components/Errors/NotFound')); 23 | 24 | function App() { 25 | 26 | const dispatch = useDispatch(); 27 | const { pathname } = useLocation(); 28 | 29 | toast.configure({ 30 | theme: 'colored', 31 | position: toast.POSITION.TOP_RIGHT, 32 | autoClose: 2500, 33 | }); 34 | 35 | const { isAuthenticated } = useSelector((state) => state.user); 36 | 37 | useEffect(() => { 38 | dispatch(loadUser()); 39 | }, [dispatch]); 40 | 41 | // always scroll to top on route/path change 42 | useEffect(() => { 43 | window.scrollTo({ 44 | top: 0, 45 | left: 0, 46 | behavior: "smooth" 47 | }); 48 | }, [pathname]) 49 | 50 | // disable right click 51 | window.addEventListener("contextmenu", (e) => e.preventDefault()); 52 | window.addEventListener("keydown", (e) => { 53 | if (e.keyCode == 123) e.preventDefault(); 54 | if (e.ctrlKey && e.shiftKey && e.keyCode === 73) e.preventDefault(); 55 | if (e.ctrlKey && e.shiftKey && e.keyCode === 74) e.preventDefault(); 56 | }); 57 | 58 | return ( 59 | <> 60 | {isAuthenticated &&
} 61 | } > 62 | 63 | 65 | 66 | 67 | } /> 68 | } /> 69 | } /> 70 | } /> 71 | } /> 72 | 73 | 75 | 76 | 77 | } /> 78 | 80 | 81 | 82 | 83 | 84 | } 85 | /> 86 | 88 | 89 | 90 | 91 | 92 | } 93 | /> 94 | 95 | 97 | 98 | 99 | } /> 100 | 101 | 103 | 104 | 105 | } /> 106 | 107 | } /> 108 | 109 | 110 | 111 | 112 | 113 | ); 114 | } 115 | 116 | export default App; 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Instagram MERN 2 | Full-Stack Instagram Clone using MERN Stack and Socket.IO 3 | 4 | [Visit Now](https://instagramweb-mern.vercel.app) 🚀 5 | 6 | ## 🖥️ Tech Stack 7 | **Frontend:** 8 | 9 | ![reactjs](https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB)  10 | ![react-router](https://img.shields.io/badge/React_Router-CA4245?style=for-the-badge&logo=react-router&logoColor=white)  11 | ![redux](https://img.shields.io/badge/Redux-593D88?style=for-the-badge&logo=redux&logoColor=white)  12 | ![tailwindcss](https://img.shields.io/badge/Tailwind_CSS-38B2AC?style=for-the-badge&logo=tailwind-css&logoColor=white)  13 | ![mui](https://img.shields.io/badge/Material--UI-0081CB?style=for-the-badge&logo=material-ui&logoColor=white)  14 | 15 | **Backend:** 16 | 17 | ![nodejs](https://img.shields.io/badge/Node.js-43853D?style=for-the-badge&logo=node.js&logoColor=white)  18 | ![expressjs](https://img.shields.io/badge/Express.js-000000?style=for-the-badge&logo=express&logoColor=white)  19 | ![mongodb](https://img.shields.io/badge/MongoDB-4EA94B?style=for-the-badge&logo=mongodb&logoColor=white)  20 | ![jwt]( https://img.shields.io/badge/JWT-000000?style=for-the-badge&logo=JSON%20web%20tokens&logoColor=white)  21 | 22 | **Realtime Communication:** 23 | 24 | ![socketio](https://img.shields.io/badge/Socket.io-010101?&style=for-the-badge&logo=Socket.io&logoColor=white) 25 | 26 | **Cloud Storage:** 27 | 28 | ![aws-s3](https://img.shields.io/badge/Amazon_AWS-FF9900?style=for-the-badge&logo=amazonaws&logoColor=white) 29 | 30 | **Cloud Storage:** [Cloudinary](https://cloudinary.com/) 31 | 32 | **Mail Service:** [Sendgrid](https://sendgrid.com/) 33 | 34 | ## 🚀 Features 35 | **Authentication and User Management** 36 | - ✨ Secure Login/Signup Functionality 37 | - 🚪 Support for Email or Username-based Login 38 | - 🔐 Seamless Profile and Password Management 39 | - 🔄 Password Reset via Sendgrid Integration 40 | 41 | **Content Management** 42 | - 📜 Dynamic Infinite Scroll for Posts 43 | - 🔍 Suggestions for User Connections 44 | - 🔎 Intuitive Search for Users by Name or Username 45 | - 🔗 Follow/Unfollow Capability for User Networking 46 | - ❤️ Like/Unlike Posts with Double-Tap Feature 47 | - 💬 Commenting on Posts for Interaction 48 | - 📌 Save/Unsave Posts for Bookmarking 49 | - 📤 Effortless Post-Sharing Functionality 50 | 51 | **Messaging System** 52 | - 🔍 User-Friendly Search for Chat Participants 53 | - 📝 Seamless Creation of New Chats 54 | - 💬 Real-Time Messaging Between Users 55 | - 🕒 Status Indicators for Typing and Online Presence 56 | 57 | **Social Features** 58 | - 📊 Comprehensive View of Followers/Following Lists 59 | - 👍 Post Liked By Users Tracking 60 | - 😄 Emoji Integration via Emoji Mart 61 | 62 | **Image Storage** 63 | - 📷 Branch [dev-v3-cloudinary](https://github.com/jigar-sable/instagram-mern/tree/dev-v3-cloudinary): Utilizes Cloudinary for Image Storage 64 | - ☁️ Branch [dev-v2-aws](https://github.com/jigar-sable/instagram-mern/tree/dev-v2-aws): Leverages AWS S3 for Image Storage 65 | - 💾 Branch [dev-v1](https://github.com/jigar-sable/instagram-mern/tree/dev-v1): Relies on Local Storage Solution 66 | 67 | ## Sneak Peek of Home Page 🙈 : 68 | ![home](https://user-images.githubusercontent.com/64949957/159116089-cfc2b4c5-dc7d-4d4b-a0c8-668f89ce5412.png) 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
mockupmockups
mockupmockups
80 | 81 |

📬 Contact

82 | 83 | Feel free to reach me through the below handles if you'd like to contact me. 84 | 85 | [![linkedin](https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/jigar-sablee) 86 | [![instagram](https://img.shields.io/badge/Instagram-E4405F?style=for-the-badge&logo=instagram&logoColor=white)](https://www.instagram.com/jigarsable.dev) 87 | -------------------------------------------------------------------------------- /frontend/src/components/User/Update/UpdatePassword.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { useNavigate } from 'react-router-dom'; 4 | import { toast } from 'react-toastify'; 5 | import { clearErrors, loadUser, updatePassword } from '../../../actions/userAction'; 6 | import { UPDATE_PASSWORD_RESET } from '../../../constants/userConstants'; 7 | import MetaData from '../../Layouts/MetaData'; 8 | 9 | const UpdatePassword = () => { 10 | 11 | const dispatch = useDispatch(); 12 | const navigate = useNavigate(); 13 | 14 | const { user } = useSelector((state) => state.user); 15 | const { error, isUpdated, loading } = useSelector((state) => state.profile); 16 | 17 | const [oldPassword, setOldPassword] = useState(""); 18 | const [newPassword, setNewPassword] = useState(""); 19 | const [confirmPassword, setConfirmPassword] = useState(""); 20 | 21 | const handlePasswordUpdate = (e) => { 22 | e.preventDefault(); 23 | 24 | if (newPassword.length < 8) { 25 | toast.warn("Password length must be atleast 8 characters"); 26 | return; 27 | } 28 | if (newPassword !== confirmPassword) { 29 | toast.error("Password Doesn't Match"); 30 | return; 31 | } 32 | 33 | dispatch(updatePassword({ oldPassword, newPassword })); 34 | } 35 | 36 | useEffect(() => { 37 | if (error) { 38 | toast.error(error); 39 | dispatch(clearErrors()); 40 | } 41 | if (isUpdated) { 42 | toast.success("Password Updated"); 43 | dispatch(loadUser()); 44 | navigate(`/${user?.username}`); 45 | 46 | dispatch({ type: UPDATE_PASSWORD_RESET }); 47 | } 48 | }, [dispatch, error, isUpdated, navigate]); 49 | 50 | return ( 51 | <> 52 | 53 | 54 |
55 |
56 | 57 | {user.username} 58 |
59 |
60 | Current Password 61 | setOldPassword(e.target.value)} 68 | required 69 | /> 70 |
71 |
72 | New Password 73 | setNewPassword(e.target.value)} 80 | required 81 | /> 82 |
83 |
84 | Confirm New Password 85 | setConfirmPassword(e.target.value)} 92 | required 93 | /> 94 |
95 | 96 |
97 | 98 | ) 99 | } 100 | 101 | export default UpdatePassword -------------------------------------------------------------------------------- /frontend/src/components/Home/Sidebar/Sidebar.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { Link } from 'react-router-dom'; 4 | import { toast } from 'react-toastify'; 5 | import { getPostsOfFollowing } from '../../../actions/postAction'; 6 | import { clearErrors, getSuggestedUsers, loadUser } from '../../../actions/userAction'; 7 | import { POST_FOLLOWING_RESET } from '../../../constants/postConstants'; 8 | import { FOLLOW_USER_RESET } from '../../../constants/userConstants'; 9 | import SkeletonUserItem from '../../Layouts/SkeletonUserItem'; 10 | import UserListItem from './UserListItem'; 11 | 12 | const Sidebar = () => { 13 | 14 | const dispatch = useDispatch(); 15 | 16 | const { user } = useSelector((state) => state.user); 17 | 18 | const { error, users, loading } = useSelector((state) => state.allUsers) 19 | const { error: followError, success, message } = useSelector((state) => state.followUser) 20 | 21 | useEffect(() => { 22 | if (error) { 23 | toast.error(error); 24 | dispatch(clearErrors()); 25 | } 26 | dispatch(getSuggestedUsers()); 27 | }, [dispatch, error]); 28 | 29 | useEffect(() => { 30 | if (followError) { 31 | toast.error(followError); 32 | dispatch(clearErrors()); 33 | } 34 | if (success) { 35 | toast.success(message); 36 | // dispatch({ type: POST_FOLLOWING_RESET }); 37 | // dispatch(getPostsOfFollowing()); 38 | dispatch({ type: FOLLOW_USER_RESET }); 39 | } 40 | }, [success, followError]) 41 | 42 | 43 | return ( 44 |
45 | 46 |
47 | 48 | {/* */} 49 |
50 |
51 | {user.name} 52 |
53 | {user.username} 54 | {user.name} 55 |
56 |
57 | Switch 58 |
59 | 60 | {/* */} 61 |
62 |

Suggestions For You

63 | See All 64 |
65 | 66 | {/* */} 67 |
68 | 69 | {loading ? 70 | Array(5).fill("").map((el, i) => ()) 71 | : 72 | users?.map((user) => ( 73 | 74 | )) 75 | } 76 |
77 | 78 | {/* */} 79 |
80 |
81 |
82 | {['About', 'Help', 'Press', 'API', 'Jobs', 'Privacy', 'Terms', 'Locations'].map((el, i) => ( 83 | {el} 84 | ))} 85 |
86 |
87 | {['Top Accounts', 'Hashtags', 'Language'].map((el, i) => ( 88 | {el} 89 | ))} 90 |
91 |
92 | © {new Date().getFullYear()} INSTAGRAM FROM META 93 |
94 | 95 |
96 |
97 | ) 98 | } 99 | 100 | export default Sidebar -------------------------------------------------------------------------------- /frontend/src/components/Navbar/SearchBar/SearchBox.jsx: -------------------------------------------------------------------------------- 1 | import { ClickAwayListener } from '@mui/material'; 2 | import axios from 'axios'; 3 | import { useEffect, useState } from 'react'; 4 | import { searchIcon } from '../SvgIcons'; 5 | import SearchUserItem from './SearchUserItem'; 6 | 7 | const SearchBox = () => { 8 | 9 | const [users, setUsers] = useState([]); 10 | const [searchTerm, setSearchTerm] = useState(""); 11 | const [loading, setLoading] = useState(false); 12 | const [searchResult, setSearchResult] = useState(false); 13 | const [searching, setSearching] = useState(false); 14 | 15 | const fetchUsers = async (term) => { 16 | setLoading(true); 17 | const { data } = await axios.get(`/api/v1/users?keyword=${term}`); 18 | setUsers(data.users); 19 | setLoading(false); 20 | } 21 | 22 | useEffect(() => { 23 | if (searchTerm.trim().length > 0) { 24 | fetchUsers(searchTerm); 25 | } 26 | 27 | return () => { 28 | setUsers([]) 29 | } 30 | }, [searchTerm]); 31 | 32 | const handleClickAway = () => { 33 | setSearchTerm(""); 34 | setSearchResult(false) 35 | setSearching(false) 36 | } 37 | 38 | const handleFocus = () => { 39 | setSearchResult(true) 40 | setSearching(true) 41 | } 42 | 43 | return ( 44 | 45 |
46 | {!searching && searchIcon} 47 | setSearchTerm(e.target.value)} 53 | placeholder="Search" 54 | /> 55 | {searchResult && 56 | <> 57 |
58 | 59 |
60 | 61 | {loading ? 62 | 63 | : users.length > 0 ? 64 | users.map((user) => ( 65 | 66 | )) 67 | : 68 | No results found. 69 | } 70 | 71 |
72 | 73 | } 74 |
75 |
76 | ) 77 | } 78 | 79 | export default SearchBox -------------------------------------------------------------------------------- /frontend/src/actions/postAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { CLEAR_ERRORS, DELETE_POST_FAIL, DELETE_POST_REQUEST, DELETE_POST_SUCCESS, LIKE_UNLIKE_POST_FAIL, LIKE_UNLIKE_POST_REQUEST, LIKE_UNLIKE_POST_SUCCESS, NEW_COMMENT_FAIL, NEW_COMMENT_REQUEST, NEW_COMMENT_SUCCESS, NEW_POST_FAIL, NEW_POST_REQUEST, NEW_POST_SUCCESS, POST_DETAILS_FAIL, POST_DETAILS_REQUEST, POST_DETAILS_SUCCESS, POST_FOLLOWING_FAIL, POST_FOLLOWING_REQUEST, POST_FOLLOWING_SUCCESS, SAVE_UNSAVE_POST_FAIL, SAVE_UNSAVE_POST_REQUEST, SAVE_UNSAVE_POST_SUCCESS } from "../constants/postConstants"; 3 | 4 | 5 | // New Post 6 | export const addNewPost = (postData) => async (dispatch) => { 7 | try { 8 | 9 | dispatch({ type: NEW_POST_REQUEST }); 10 | const config = { header: { "Content-Type": "application/json" } } 11 | const { data } = await axios.post("/api/v1/post/new", postData, config); 12 | 13 | dispatch({ 14 | type: NEW_POST_SUCCESS, 15 | payload: data, 16 | }); 17 | 18 | } catch (error) { 19 | dispatch({ 20 | type: NEW_POST_FAIL, 21 | payload: error.response.data.message, 22 | }); 23 | } 24 | } 25 | 26 | // Get Post of Followings 27 | export const getPostsOfFollowing = (page = 1) => async (dispatch) => { 28 | try { 29 | 30 | dispatch({ type: POST_FOLLOWING_REQUEST }); 31 | 32 | setTimeout(async () => { 33 | 34 | const { data } = await axios.get(`/api/v1/posts?page=${page}`); 35 | 36 | dispatch({ 37 | type: POST_FOLLOWING_SUCCESS, 38 | payload: data, 39 | }); 40 | 41 | }, 300); 42 | 43 | } catch (error) { 44 | dispatch({ 45 | type: POST_FOLLOWING_FAIL, 46 | payload: error.response.data.message, 47 | }); 48 | } 49 | }; 50 | 51 | // Like | Unlike Post 52 | export const likePost = (postId) => async (dispatch) => { 53 | try { 54 | 55 | dispatch({ type: LIKE_UNLIKE_POST_REQUEST }); 56 | const { data } = await axios.get(`/api/v1/post/${postId}`); 57 | 58 | dispatch({ 59 | type: LIKE_UNLIKE_POST_SUCCESS, 60 | payload: data, 61 | }); 62 | 63 | } catch (error) { 64 | dispatch({ 65 | type: LIKE_UNLIKE_POST_FAIL, 66 | payload: error.response.data.message, 67 | }); 68 | } 69 | }; 70 | 71 | // Add Comment 72 | export const addComment = (postId, comment) => async (dispatch) => { 73 | try { 74 | 75 | dispatch({ type: NEW_COMMENT_REQUEST }); 76 | const config = { header: { "Content-Type": "application/json" } } 77 | const { data } = await axios.post(`/api/v1/post/comment/${postId}`, { comment }, config); 78 | 79 | dispatch({ 80 | type: NEW_COMMENT_SUCCESS, 81 | payload: data.success, 82 | }); 83 | 84 | } catch (error) { 85 | dispatch({ 86 | type: NEW_COMMENT_FAIL, 87 | payload: error.response.data.message, 88 | }); 89 | } 90 | } 91 | 92 | // Save | Unsave Post 93 | export const savePost = (postId) => async (dispatch) => { 94 | try { 95 | 96 | dispatch({ type: SAVE_UNSAVE_POST_REQUEST }); 97 | const { data } = await axios.post(`/api/v1/post/${postId}`); 98 | 99 | dispatch({ 100 | type: SAVE_UNSAVE_POST_SUCCESS, 101 | payload: data, 102 | }); 103 | 104 | } catch (error) { 105 | dispatch({ 106 | type: SAVE_UNSAVE_POST_FAIL, 107 | payload: error.response.data.message, 108 | }); 109 | } 110 | }; 111 | 112 | // Delete Post 113 | export const deletePost = (postId) => async (dispatch) => { 114 | try { 115 | 116 | dispatch({ type: DELETE_POST_REQUEST }); 117 | const { data } = await axios.delete(`/api/v1/post/${postId}`); 118 | 119 | dispatch({ 120 | type: DELETE_POST_SUCCESS, 121 | payload: data.success, 122 | }); 123 | 124 | } catch (error) { 125 | dispatch({ 126 | type: DELETE_POST_FAIL, 127 | payload: error.response.data.message, 128 | }); 129 | } 130 | }; 131 | 132 | // Get Post Details 133 | export const getPostDetails = (postId) => async (dispatch) => { 134 | try { 135 | 136 | dispatch({ type: POST_DETAILS_REQUEST }); 137 | const { data } = await axios.get(`/api/v1/post/detail/${postId}`); 138 | 139 | dispatch({ 140 | type: POST_DETAILS_SUCCESS, 141 | payload: data, 142 | }); 143 | 144 | } catch (error) { 145 | dispatch({ 146 | type: POST_DETAILS_FAIL, 147 | payload: error.response.data.message, 148 | }); 149 | } 150 | }; 151 | 152 | // Clear All Errors 153 | export const clearErrors = () => (dispatch) => { 154 | dispatch({ type: CLEAR_ERRORS }); 155 | } -------------------------------------------------------------------------------- /frontend/src/components/Chats/SearchModal.jsx: -------------------------------------------------------------------------------- 1 | import Dialog from '@mui/material/Dialog'; 2 | import axios from 'axios'; 3 | import { useEffect, useState } from 'react'; 4 | import { useDispatch, useSelector } from 'react-redux'; 5 | import { useNavigate } from 'react-router-dom'; 6 | import { NEW_CHAT_RESET } from '../../constants/chatConstants'; 7 | import { toast } from 'react-toastify'; 8 | import { addNewChat, clearErrors } from '../../actions/chatAction'; 9 | import { Skeleton } from '@mui/material'; 10 | 11 | const NewDialog = ({ open, onClose }) => { 12 | 13 | const dispatch = useDispatch(); 14 | const navigate = useNavigate(); 15 | 16 | const [users, setUsers] = useState([]); 17 | const [searchTerm, setSearchTerm] = useState(""); 18 | const [loading, setLoading] = useState(false); 19 | 20 | const { user: self } = useSelector((state) => state.user); 21 | const { error, chat } = useSelector((state) => state.newChat); 22 | 23 | const fetchUsers = async (term) => { 24 | setLoading(true); 25 | const { data } = await axios.get(`/api/v1/users?keyword=${term}`); 26 | setUsers(data.users.filter((u) => u._id !== self._id)); 27 | setLoading(false); 28 | } 29 | 30 | useEffect(() => { 31 | if (searchTerm.trim().length > 0) { 32 | fetchUsers(searchTerm); 33 | } 34 | 35 | return () => { 36 | setUsers([]); 37 | } 38 | }, [searchTerm]); 39 | 40 | const addToChat = (userId) => { 41 | dispatch(addNewChat(userId)); 42 | setSearchTerm("") 43 | } 44 | 45 | useEffect(() => { 46 | if (error) { 47 | toast.error(error); 48 | dispatch(clearErrors()); 49 | } 50 | if (chat) { 51 | const friendId = chat.users?.find((id) => id !== self._id); 52 | navigate(`/direct/t/${chat._id}/${friendId}`); 53 | dispatch({ type: NEW_CHAT_RESET }); 54 | onClose(); 55 | } 56 | }, [dispatch, error, chat, navigate]); 57 | 58 | return ( 59 | 60 |
61 |
62 | New Message 63 | 64 |
65 | 66 |
67 | To: 68 | setSearchTerm(e.target.value)} 74 | /> 75 |
76 | 77 |
78 | 79 | {loading ? 80 | Array(8).fill("").map((el, i) => ( 81 |
82 | 83 |
84 | 85 | 86 |
87 |
88 | )) 89 | : users.length > 0 ? 90 | users.map((u) => ( 91 |
addToChat(u._id)} className="flex items-center hover:bg-gray-50 py-2 px-4 cursor-pointer" key={u._id}> 92 |
93 | avatar 94 |
95 | {u.username} 96 | {u.name} 97 |
98 |
99 |
100 | )) 101 | : 102 | No accounts found. 103 | } 104 | 105 |
106 |
107 |
108 | ) 109 | } 110 | 111 | export default NewDialog -------------------------------------------------------------------------------- /frontend/src/components/User/SvgIcons.jsx: -------------------------------------------------------------------------------- 1 | export const settingsIcon = 2 | 3 | export const metaballsMenu = 4 | 5 | export const postsIconFill = 6 | 7 | export const postsIconOutline = 8 | 9 | export const savedIconFill = 10 | 11 | export const savedIconOutline = 12 | 13 | export const taggedIcon = 14 | 15 | export const reelsIcon = 16 | 17 | 18 | -------------------------------------------------------------------------------- /frontend/build/static/js/666.85fb12e7.chunk.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"static/js/666.85fb12e7.chunk.js","mappings":"4IA6BA,IA1Ba,SAAC,GAAkB,IAAhBA,EAAe,EAAfA,SACZ,OACI,gBAAKC,UAAU,gBAAf,UAEI,iBAAKA,UAAU,6CAAf,WAEI,gBAAKA,UAAU,iJAAf,UACI,gBAAKC,UAAU,QAAQD,UAAU,mCAAmCE,IAAKC,EAAUC,IAAI,gBAG3F,iBAAKJ,UAAU,sCAAf,UAEKD,GAED,cAAGC,UAAU,2BAAb,2BACA,iBAAKA,UAAU,4BAAf,WACI,gBAAKC,UAAU,QAAQI,MAAM,QAAQH,IAAI,4GAA4GE,IAAI,cACzJ,gBAAKH,UAAU,QAAQI,MAAM,QAAQH,IAAI,gHAAgHE,IAAI,2B,+IC2CrL,UAtDuB,WAEnB,IAAME,GAAWC,EAAAA,EAAAA,MAEjB,GAAoCC,EAAAA,EAAAA,KAAY,SAACC,GAAD,OAAWA,EAAMC,kBAAzDC,EAAR,EAAQA,MAAOC,EAAf,EAAeA,QAASC,EAAxB,EAAwBA,QAExB,GAA0BC,EAAAA,EAAAA,UAAS,IAAnC,eAAOC,EAAP,KAAcC,EAAd,KAkBA,OAVAC,EAAAA,EAAAA,YAAU,WACFN,IACAO,EAAAA,GAAAA,MAAYP,GACZL,GAASa,EAAAA,EAAAA,QAETP,GACAM,EAAAA,GAAAA,QAAcN,KAEnB,CAACN,EAAUK,EAAOC,KAGjB,gCACKC,IAAW,SAAC,IAAD,KACZ,UAAC,IAAD,YACI,iBAAKb,UAAU,gDAAf,WACI,gBAAKC,UAAU,QAAQD,UAAU,mCAAmCE,IAAI,wFAAwFE,IAAI,MACpK,kBAAMgB,SAtBD,SAACC,GAClBA,EAAEC,iBACFhB,GAASI,EAAAA,EAAAA,IAAeK,IACxBC,EAAS,KAmBiChB,UAAU,6DAAxC,WACI,SAAC,IAAD,CACIuB,MAAM,QACNC,QAAQ,WACRC,KAAK,QACLC,WAAS,EACTC,MAAOZ,EACPa,SAAU,SAACP,GAAD,OAAOL,EAASK,EAAEQ,OAAOF,QACnCG,UAAQ,KAEZ,mBAAQC,KAAK,SAAS/B,UAAU,6DAAhC,qBACA,iBAAMA,UAAU,qBAAhB,iBACA,SAAC,KAAD,CAAMgC,GAAG,mBAAmBhC,UAAU,oCAAtC,qCAIR,gBAAKA,UAAU,kCAAf,UACI,uDAA6B,SAAC,KAAD,CAAMgC,GAAG,YAAYhC,UAAU,oBAA/B,kC","sources":["components/User/Auth.jsx","components/User/ForgotPassword.jsx"],"sourcesContent":["import React from 'react';\r\nimport homepage from '../../assests/images/homepage.webp';\r\n\r\nconst Auth = ({ children }) => {\r\n return (\r\n
\r\n\r\n
\r\n\r\n
\r\n \"homepage\"\r\n
\r\n\r\n
\r\n\r\n {children}\r\n\r\n

Get the app.

\r\n
\r\n \"appstore\"\r\n \"playstore\"\r\n
\r\n\r\n
\r\n
\r\n
\r\n )\r\n}\r\n\r\nexport default Auth","import React, { useEffect, useState } from 'react'\r\nimport TextField from '@mui/material/TextField';\r\nimport Auth from './Auth';\r\nimport { Link } from 'react-router-dom';\r\nimport { clearErrors, forgotPassword } from '../../actions/userAction';\r\nimport { toast } from 'react-toastify';\r\nimport { useDispatch, useSelector } from 'react-redux';\r\nimport BackdropLoader from '../Layouts/BackdropLoader';\r\n\r\nconst ForgotPassword = () => {\r\n\r\n const dispatch = useDispatch();\r\n\r\n const { error, message, loading } = useSelector((state) => state.forgotPassword);\r\n\r\n const [email, setEmail] = useState(\"\");\r\n\r\n const handleSubmit = (e) => {\r\n e.preventDefault();\r\n dispatch(forgotPassword(email));\r\n setEmail(\"\");\r\n }\r\n\r\n useEffect(() => {\r\n if (error) {\r\n toast.error(error);\r\n dispatch(clearErrors());\r\n }\r\n if (message) {\r\n toast.success(message);\r\n }\r\n }, [dispatch, error, message]);\r\n\r\n return (\r\n <>\r\n {loading && }\r\n \r\n
\r\n \"\"\r\n
\r\n setEmail(e.target.value)}\r\n required\r\n />\r\n \r\n OR\r\n Forgot password?\r\n \r\n
\r\n\r\n
\r\n Don't have an account? Sign up\r\n
\r\n
\r\n \r\n )\r\n}\r\n\r\nexport default ForgotPassword"],"names":["children","className","draggable","src","homepage","alt","width","dispatch","useDispatch","useSelector","state","forgotPassword","error","message","loading","useState","email","setEmail","useEffect","toast","clearErrors","onSubmit","e","preventDefault","label","variant","size","fullWidth","value","onChange","target","required","type","to"],"sourceRoot":""} -------------------------------------------------------------------------------- /frontend/build/static/js/11.d7687b78.chunk.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([[11],{46011:function(e,t,s){s.r(t),s.d(t,{default:function(){return r}});s(72791),s(66830);var a=s(43504),n=s(80184),c=[{title:"Edit Profile",nav:"/accounts/edit"},{title:"Change Password",nav:"/accounts/password/change"},{title:"Apps and Websites",nav:"/accounts/edit"},{title:"Email and SMS",nav:"/accounts/edit"},{title:"Push Notifications",nav:"/accounts/edit"},{title:"Manage Contacts",nav:"/accounts/edit"},{title:"Privacy and Security",nav:"/accounts/edit"},{title:"Login Activity",nav:"/accounts/edit"},{title:"Emails from Instagram",nav:"/accounts/edit"},{title:"Help",nav:"/accounts/edit"}],i=function(e){var t=e.activeTab;return(0,n.jsxs)("div",{className:"hidden sm:flex flex-col border-r w-1/4",children:[c.map((function(e,s){return(0,n.jsx)(a.rU,{to:e.nav,className:"".concat(t===s?"border-black text-black border-l-2 font-medium":"hover:border-gray-300 text-gray-600"," py-3 px-6 hover:border-l-2 hover:bg-gray-50 cursor-pointer"),children:e.title})})),(0,n.jsxs)("div",{className:"flex border-t mt-12 flex-col gap-2 p-6",children:[(0,n.jsxs)("svg",{"aria-label":"Facebook wordmark and family of apps logo",height:"12",viewBox:"0 0 500 100",width:"60",children:[(0,n.jsxs)("defs",{children:[(0,n.jsxs)("linearGradient",{gradientUnits:"userSpaceOnUse",id:"b",x1:"125",x2:"160.217",y1:"97",y2:"57.435",children:[(0,n.jsx)("stop",{offset:".21",stopColor:"#0278F1"}),(0,n.jsx)("stop",{offset:".533",stopColor:"#0180FA"})]}),(0,n.jsxs)("linearGradient",{gradientUnits:"userSpaceOnUse",id:"c",x1:"44",x2:"0",y1:"5",y2:"64",children:[(0,n.jsx)("stop",{offset:".427",stopColor:"#0165E0"}),(0,n.jsx)("stop",{offset:".917",stopColor:"#0180FA"})]}),(0,n.jsxs)("linearGradient",{gradientUnits:"userSpaceOnUse",id:"d",x1:"28.5",x2:"135",y1:"29",y2:"72",children:[(0,n.jsx)("stop",{stopColor:"#0064E0"}),(0,n.jsx)("stop",{offset:".656",stopColor:"#0066E2"}),(0,n.jsx)("stop",{offset:"1",stopColor:"#0278F1"})]}),(0,n.jsx)("clipPath",{id:"a",children:(0,n.jsx)("path",{d:"M0 0h496.236v100H0z",fill:"#fff"})})]}),(0,n.jsxs)("g",{clipPath:"url(#a)",children:[(0,n.jsx)("path",{d:"M182.141 3.213h18.808l31.98 57.849 31.979-57.849h18.401V98.27h-15.345V25.416l-28.042 50.448h-14.394l-28.042-50.448V98.27h-15.345V3.213ZM332.804 99.967c-7.107 0-13.353-1.573-18.739-4.718-5.387-3.146-9.586-7.504-12.595-13.07-3.011-5.569-4.515-11.95-4.515-19.148 0-7.287 1.47-13.738 4.413-19.35 2.942-5.613 7.027-10.004 12.255-13.173 5.229-3.168 11.238-4.753 18.027-4.753 6.744 0 12.55 1.596 17.416 4.787 4.865 3.191 8.611 7.661 11.237 13.41 2.624 5.749 3.938 12.492 3.938 20.233v4.21h-52.077c.95 5.794 3.292 10.354 7.027 13.68 3.735 3.328 8.453 4.991 14.157 4.991 4.571 0 8.509-.679 11.814-2.037 3.303-1.358 6.404-3.417 9.302-6.178l8.147 9.98c-8.103 7.425-18.038 11.136-29.807 11.136Zm11.204-56.389c-3.215-3.281-7.425-4.923-12.629-4.923-5.07 0-9.314 1.676-12.731 5.025-3.418 3.35-5.58 7.854-6.484 13.512h37.343c-.453-5.794-2.286-10.331-5.499-13.614ZM382.846 40.014h-14.123V27.453h14.123V6.676h14.802v20.777h21.455v12.561h-21.455v31.844c0 5.295.905 9.075 2.716 11.338 1.809 2.264 4.911 3.395 9.302 3.395 1.945 0 3.598-.078 4.956-.237a92.35 92.35 0 0 0 4.481-.646v12.425c-1.675.498-3.564.906-5.669 1.223a44.63 44.63 0 0 1-6.62.475c-15.979 0-23.968-8.735-23.968-26.208V40.014ZM496.236 98.27h-14.53v-9.913c-2.58 3.712-5.862 6.575-9.845 8.588-3.983 2.014-8.51 3.022-13.579 3.022-6.247 0-11.78-1.596-16.601-4.787s-8.612-7.581-11.373-13.172c-2.761-5.59-4.142-11.983-4.142-19.18 0-7.243 1.403-13.648 4.21-19.216 2.806-5.567 6.688-9.935 11.645-13.104 4.956-3.168 10.648-4.753 17.075-4.753 4.844 0 9.189.94 13.037 2.818a25.768 25.768 0 0 1 9.573 7.978v-9.098h14.53V98.27Zm-14.801-46.035c-1.585-4.028-4.085-7.207-7.503-9.54-3.418-2.33-7.367-3.496-11.848-3.496-6.338 0-11.384 2.128-15.141 6.382-3.758 4.255-5.635 10.004-5.635 17.246 0 7.289 1.809 13.06 5.431 17.314 3.621 4.255 8.532 6.382 14.734 6.382 4.571 0 8.645-1.176 12.222-3.53 3.575-2.353 6.155-5.522 7.74-9.506V52.235Z",fill:"#1C2B33"}),(0,n.jsx)("path",{d:"M108 0C95.66 0 86.015 9.294 77.284 21.1 65.284 5.821 55.25 0 43.24 0 18.76 0 0 31.862 0 65.586 0 86.69 10.21 100 27.31 100c12.308 0 21.16-5.803 36.897-33.31 0 0 6.56-11.584 11.072-19.564 1.582 2.553 3.243 5.3 4.997 8.253l7.38 12.414C102.03 91.848 110.038 100 124.551 100c16.659 0 25.931-13.492 25.931-35.034C150.483 29.656 131.301 0 108 0ZM52.207 59.241c-12.759 20-17.172 24.483-24.276 24.483-7.31 0-11.655-6.418-11.655-17.862 0-24.483 12.207-49.517 26.759-49.517 7.88 0 14.465 4.55 24.552 18.991-9.578 14.691-15.38 23.905-15.38 23.905Zm48.153-2.517-8.823-14.715a301.425 301.425 0 0 0-6.884-10.723c7.952-12.274 14.511-18.39 22.313-18.39 16.206 0 29.172 23.863 29.172 53.173 0 11.172-3.659 17.655-11.241 17.655-7.268 0-10.739-4.8-24.537-27Z",fill:"#0180FA"}),(0,n.jsx)("path",{d:"M145.586 35H130.66c3.452 8.746 5.478 19.482 5.478 31.069 0 11.172-3.659 17.655-11.241 17.655-1.407 0-2.672-.18-3.897-.631V99.82c1.143.122 2.324.18 3.552.18 16.659 0 25.931-13.492 25.931-35.034 0-10.737-1.774-20.95-4.897-29.966Z",fill:"url(#b)"}),(0,n.jsx)("path",{d:"M43.241 0c.254 0 .507.003.759.008v16.36c-.32-.015-.642-.023-.965-.023-14.183 0-26.139 23.782-26.736 47.655H.014C.59 30.87 19.143 0 43.24 0Z",fill:"url(#c)"}),(0,n.jsx)("path",{d:"M43.241 0c11.152 0 20.601 5.02 31.502 17.971 3.065 3.828 6.761 8.805 10.716 14.557l.017.025.025-.003a311.041 311.041 0 0 1 6.036 9.459l8.823 14.715c13.798 22.2 17.269 27 24.537 27H125v16.273c-.149.002-.298.003-.448.003-14.513 0-22.522-8.152-36.897-32.207l-7.38-12.414a596.368 596.368 0 0 0-2.294-3.834L78 51.5c-5.5-9-9-14.5-12-18.5l-.05.038c-9.18-12.63-15.47-16.693-22.916-16.693H43V0L43.241 0Z",fill:"url(#d)"})]})]}),(0,n.jsx)("span",{className:"text-primary-blue font-medium",children:"Accounts Center"}),(0,n.jsx)("p",{className:"text-xs text-gray-400",children:"Control settings for connected experiences across Instagram, the Facebook app and Messenger, including story and post sharing and logging in."})]})]})},r=function(e){var t=e.children,s=e.activeTab;return(0,n.jsx)(n.Fragment,{children:(0,n.jsx)("div",{className:"my-24 xl:w-2/3 mx-auto sm:pr-14 sm:pl-8",children:(0,n.jsxs)("div",{className:"flex border rounded w-full bg-white",children:[(0,n.jsx)(i,{activeTab:s}),t]})})})}}}]); 2 | //# sourceMappingURL=11.d7687b78.chunk.js.map -------------------------------------------------------------------------------- /frontend/src/components/User/Update/Sidebar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | 4 | const tabs = [ 5 | { 6 | title: "Edit Profile", 7 | nav: "/accounts/edit" 8 | }, 9 | { 10 | title: "Change Password", 11 | nav: "/accounts/password/change" 12 | }, 13 | { 14 | title: "Apps and Websites", 15 | nav: "/accounts/edit" 16 | }, 17 | { 18 | title: "Email and SMS", 19 | nav: "/accounts/edit" 20 | }, 21 | { 22 | title: "Push Notifications", 23 | nav: "/accounts/edit" 24 | }, 25 | { 26 | title: "Manage Contacts", 27 | nav: "/accounts/edit" 28 | }, 29 | { 30 | title: "Privacy and Security", 31 | nav: "/accounts/edit" 32 | }, 33 | { 34 | title: "Login Activity", 35 | nav: "/accounts/edit" 36 | }, 37 | { 38 | title: "Emails from Instagram", 39 | nav: "/accounts/edit" 40 | }, 41 | { 42 | title: "Help", 43 | nav: "/accounts/edit" 44 | } 45 | ] 46 | 47 | const Sidebar = ({ activeTab }) => { 48 | 49 | return ( 50 |
51 | {tabs.map((el, i) => ( 52 | {el.title} 53 | ))} 54 | 55 |
56 | 57 | 58 | Accounts Center 59 |

Control settings for connected experiences across Instagram, the Facebook app and Messenger, including story and post sharing and logging in.

60 |
61 |
62 | ) 63 | } 64 | 65 | export default Sidebar -------------------------------------------------------------------------------- /frontend/build/static/js/355.ff33d956.chunk.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"static/js/355.ff33d956.chunk.js","mappings":"4IA6BA,IA1Ba,SAAC,GAAkB,IAAhBA,EAAe,EAAfA,SACZ,OACI,gBAAKC,UAAU,gBAAf,UAEI,iBAAKA,UAAU,6CAAf,WAEI,gBAAKA,UAAU,iJAAf,UACI,gBAAKC,UAAU,QAAQD,UAAU,mCAAmCE,IAAKC,EAAUC,IAAI,gBAG3F,iBAAKJ,UAAU,sCAAf,UAEKD,GAED,cAAGC,UAAU,2BAAb,2BACA,iBAAKA,UAAU,4BAAf,WACI,gBAAKC,UAAU,QAAQI,MAAM,QAAQH,IAAI,4GAA4GE,IAAI,cACzJ,gBAAKH,UAAU,QAAQI,MAAM,QAAQH,IAAI,gHAAgHE,IAAI,2B,0JCsDrL,UAjEc,WAEV,IAAME,GAAWC,EAAAA,EAAAA,MACXC,GAAWC,EAAAA,EAAAA,MAEjB,GAAkDC,EAAAA,EAAAA,KAAY,SAACC,GAAD,OAAWA,EAAMC,QAAvEC,EAAR,EAAQA,QAASC,EAAjB,EAAiBA,gBAAiBC,EAAlC,EAAkCA,MAAOH,EAAzC,EAAyCA,KAEzC,GAA0BI,EAAAA,EAAAA,UAAS,IAAnC,eAAOC,EAAP,KAAcC,EAAd,KACA,GAAgCF,EAAAA,EAAAA,UAAS,IAAzC,eAAOG,EAAP,KAAiBC,EAAjB,KAkBA,OAXAC,EAAAA,EAAAA,YAAU,WACFN,IACAO,EAAAA,GAAAA,MAAYP,GACZT,GAASiB,EAAAA,EAAAA,QAETT,GACAN,EAAS,IAAD,OAAKI,EAAKY,aAEvB,CAAClB,EAAUS,EAAOD,EAAiBN,KAIlC,gCACKK,IAAW,SAAC,IAAD,KACZ,UAAC,IAAD,YACI,iBAAKb,UAAU,gDAAf,WACI,gBAAKC,UAAU,QAAQD,UAAU,mCAAmCE,IAAI,wFAAwFE,IAAI,MACpK,kBAAMqB,SAtBF,SAACC,GACjBA,EAAEC,iBACFrB,GAASsB,EAAAA,EAAAA,IAAUX,EAAOE,KAoBenB,UAAU,6DAAvC,WACI,SAAC,IAAD,CACI6B,MAAM,iBACNC,KAAK,OACLC,MAAOd,EACPe,SAAU,SAACN,GAAD,OAAOR,EAASQ,EAAEO,OAAOF,QACnCG,UAAQ,EACRC,KAAK,QACLC,WAAS,KAEb,SAAC,IAAD,CACIP,MAAM,WACNC,KAAK,WACLC,MAAOZ,EACPa,SAAU,SAACN,GAAD,OAAON,EAAYM,EAAEO,OAAOF,QACtCG,UAAQ,EACRC,KAAK,QACLC,WAAS,KAEb,mBAAQN,KAAK,SAAS9B,UAAU,6DAAhC,qBACA,iBAAMA,UAAU,qBAAhB,iBACA,SAAC,KAAD,CAAMqC,GAAG,mBAAmBrC,UAAU,oCAAtC,qCAIR,gBAAKA,UAAU,kCAAf,UACI,uDAA6B,SAAC,KAAD,CAAMqC,GAAG,YAAYrC,UAAU,oBAA/B,kC","sources":["components/User/Auth.jsx","components/User/Login.jsx"],"sourcesContent":["import React from 'react';\r\nimport homepage from '../../assests/images/homepage.webp';\r\n\r\nconst Auth = ({ children }) => {\r\n return (\r\n
\r\n\r\n
\r\n\r\n
\r\n \"homepage\"\r\n
\r\n\r\n
\r\n\r\n {children}\r\n\r\n

Get the app.

\r\n
\r\n \"appstore\"\r\n \"playstore\"\r\n
\r\n\r\n
\r\n
\r\n
\r\n )\r\n}\r\n\r\nexport default Auth","import React, { useEffect, useState } from 'react'\r\nimport TextField from '@mui/material/TextField';\r\nimport Auth from './Auth';\r\nimport { Link, useNavigate } from 'react-router-dom';\r\nimport { toast } from \"react-toastify\";\r\nimport BackdropLoader from '../Layouts/BackdropLoader';\r\nimport { useDispatch, useSelector } from 'react-redux';\r\nimport { clearErrors, loginUser } from '../../actions/userAction';\r\n\r\nconst Login = () => {\r\n\r\n const dispatch = useDispatch();\r\n const navigate = useNavigate();\r\n\r\n const { loading, isAuthenticated, error, user } = useSelector((state) => state.user);\r\n\r\n const [email, setEmail] = useState(\"\");\r\n const [password, setPassword] = useState(\"\");\r\n\r\n const handleLogin = (e) => {\r\n e.preventDefault();\r\n dispatch(loginUser(email, password));\r\n }\r\n\r\n useEffect(() => {\r\n if (error) {\r\n toast.error(error);\r\n dispatch(clearErrors());\r\n }\r\n if (isAuthenticated) {\r\n navigate(`/${user.username}`)\r\n }\r\n }, [dispatch, error, isAuthenticated, navigate]);\r\n\r\n\r\n return (\r\n <>\r\n {loading && }\r\n \r\n
\r\n \"\"\r\n
\r\n setEmail(e.target.value)}\r\n required\r\n size=\"small\"\r\n fullWidth\r\n />\r\n setPassword(e.target.value)}\r\n required\r\n size=\"small\"\r\n fullWidth\r\n />\r\n \r\n OR\r\n Forgot password?\r\n \r\n
\r\n\r\n
\r\n Don't have an account? Sign up\r\n
\r\n
\r\n \r\n )\r\n}\r\n\r\nexport default Login"],"names":["children","className","draggable","src","homepage","alt","width","dispatch","useDispatch","navigate","useNavigate","useSelector","state","user","loading","isAuthenticated","error","useState","email","setEmail","password","setPassword","useEffect","toast","clearErrors","username","onSubmit","e","preventDefault","loginUser","label","type","value","onChange","target","required","size","fullWidth","to"],"sourceRoot":""} -------------------------------------------------------------------------------- /backend/controllers/postController.js: -------------------------------------------------------------------------------- 1 | const Post = require('../models/postModel'); 2 | const User = require('../models/userModel'); 3 | const catchAsync = require('../middlewares/catchAsync'); 4 | const ErrorHandler = require('../utils/errorHandler'); 5 | const { deleteFile } = require('../utils/awsFunctions'); 6 | 7 | // Create New Post 8 | exports.newPost = catchAsync(async (req, res, next) => { 9 | 10 | const postData = { 11 | caption: req.body.caption, 12 | image: req.file.location, 13 | postedBy: req.user._id 14 | } 15 | 16 | const post = await Post.create(postData); 17 | 18 | const user = await User.findById(req.user._id); 19 | user.posts.push(post._id); 20 | await user.save(); 21 | 22 | res.status(201).json({ 23 | success: true, 24 | post, 25 | }); 26 | }); 27 | 28 | // Like or Unlike Post 29 | exports.likeUnlikePost = catchAsync(async (req, res, next) => { 30 | 31 | const post = await Post.findById(req.params.id); 32 | 33 | if (!post) { 34 | return next(new ErrorHandler("Post Not Found", 404)); 35 | } 36 | 37 | if (post.likes.includes(req.user._id)) { 38 | const index = post.likes.indexOf(req.user._id); 39 | 40 | post.likes.splice(index, 1); 41 | await post.save(); 42 | 43 | return res.status(200).json({ 44 | success: true, 45 | message: "Post Unliked" 46 | }); 47 | } else { 48 | post.likes.push(req.user._id) 49 | 50 | await post.save(); 51 | 52 | return res.status(200).json({ 53 | success: true, 54 | message: "Post Liked" 55 | }); 56 | } 57 | }); 58 | 59 | // Delete Post 60 | exports.deletePost = catchAsync(async (req, res, next) => { 61 | 62 | const post = await Post.findById(req.params.id); 63 | 64 | if (!post) { 65 | return next(new ErrorHandler("Post Not Found", 404)); 66 | } 67 | 68 | if (post.postedBy.toString() !== req.user._id.toString()) { 69 | return next(new ErrorHandler("Unauthorized", 401)); 70 | } 71 | 72 | await deleteFile(post.image); 73 | 74 | await post.remove(); 75 | 76 | const user = await User.findById(req.user._id); 77 | 78 | const index = user.posts.indexOf(req.params.id); 79 | user.posts.splice(index, 1); 80 | await user.save(); 81 | 82 | res.status(200).json({ 83 | success: true, 84 | message: "Post Deleted" 85 | }); 86 | }); 87 | 88 | // Update Caption 89 | exports.updateCaption = catchAsync(async (req, res, next) => { 90 | 91 | const post = await Post.findById(req.params.id); 92 | 93 | if (!post) { 94 | return next(new ErrorHandler("Post Not Found", 404)); 95 | } 96 | 97 | if (post.postedBy.toString() !== req.user._id.toString()) { 98 | return next(new ErrorHandler("Unauthorized", 401)); 99 | } 100 | 101 | post.caption = req.body.caption; 102 | 103 | await post.save(); 104 | 105 | res.status(200).json({ 106 | success: true, 107 | message: "Post Updated" 108 | }); 109 | }); 110 | 111 | // Add Comment 112 | exports.newComment = catchAsync(async (req, res, next) => { 113 | 114 | const post = await Post.findById(req.params.id); 115 | 116 | if (!post) { 117 | return next(new ErrorHandler("Post Not Found", 404)); 118 | } 119 | 120 | if (post.comments.includes(req.user._id)) { 121 | return next(new ErrorHandler("Already Commented", 500)); 122 | } 123 | 124 | post.comments.push({ 125 | user: req.user._id, 126 | comment: req.body.comment 127 | }); 128 | 129 | await post.save(); 130 | 131 | return res.status(200).json({ 132 | success: true, 133 | message: "Comment Added" 134 | }); 135 | }); 136 | 137 | // Posts of Following 138 | exports.getPostsOfFollowing = catchAsync(async (req, res, next) => { 139 | 140 | const user = await User.findById(req.user._id) 141 | 142 | const currentPage = Number(req.query.page) || 1; 143 | 144 | const skipPosts = 4 * (currentPage - 1); 145 | 146 | const totalPosts = await Post.find({ 147 | postedBy: { 148 | $in: user.following 149 | } 150 | }).countDocuments(); 151 | 152 | const posts = await Post.find({ 153 | postedBy: { 154 | $in: user.following 155 | } 156 | }).populate("postedBy likes").populate({ 157 | path: 'comments', 158 | populate: { 159 | path: 'user' 160 | } 161 | }).sort({ createdAt: -1 }).limit(4).skip(skipPosts) 162 | 163 | return res.status(200).json({ 164 | success: true, 165 | posts: posts, 166 | totalPosts 167 | }); 168 | }); 169 | 170 | // Save or Unsave Post 171 | exports.saveUnsavePost = catchAsync(async (req, res, next) => { 172 | 173 | const user = await User.findById(req.user._id) 174 | 175 | const post = await Post.findById(req.params.id); 176 | 177 | if (!post) { 178 | return next(new ErrorHandler("Post Not Found", 404)); 179 | } 180 | 181 | if (user.saved.includes(post._id.toString())) { 182 | user.saved = user.saved.filter((p) => p.toString() !== post._id.toString()) 183 | post.savedBy = post.savedBy.filter((p) => p.toString() !== req.user._id.toString()) 184 | await user.save(); 185 | await post.save(); 186 | 187 | return res.status(200).json({ 188 | success: true, 189 | message: "Post Unsaved" 190 | }); 191 | } else { 192 | user.saved.push(post._id) 193 | post.savedBy.push(req.user._id) 194 | 195 | await user.save(); 196 | await post.save(); 197 | 198 | return res.status(200).json({ 199 | success: true, 200 | message: "Post Saved" 201 | }); 202 | } 203 | }); 204 | 205 | // Get Post Details 206 | exports.getPostDetails = catchAsync(async (req, res, next) => { 207 | 208 | const post = await Post.findById(req.params.id).populate("postedBy likes").populate({ 209 | path: 'comments', 210 | populate: { 211 | path: 'user' 212 | } 213 | }); 214 | 215 | if (!post) { 216 | return next(new ErrorHandler("Post Not Found", 404)); 217 | } 218 | 219 | res.status(200).json({ 220 | success: true, 221 | post, 222 | }); 223 | }); 224 | 225 | // Get All Posts 226 | exports.allPosts = catchAsync(async (req, res, next) => { 227 | 228 | const posts = await Post.find(); 229 | 230 | return res.status(200).json({ 231 | posts 232 | }); 233 | }); -------------------------------------------------------------------------------- /frontend/build/static/css/812.4165d163.chunk.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"static/css/812.4165d163.chunk.css","mappings":"AAwFA,gBAGA,CC1FA,cAYI,0BAA2B,CAI3B,uCAAwC,CAXxC,qBAAsB,CAUlB,kBAAmB,CARvB,wBAAyB,CAErB,oBAAqB,CACjB,gBAAiB,CAGzB,uBAIJ,CAEA,0BAfI,aAAc,CAFd,iBA0BJ,CATA,YAOI,QAAS,CAFT,eAAgB,CAGhB,SACJ,CACA,kBAEI,YACJ,CACA,qBAEI,cAAe,CACf,WACJ,CAEA,qDAGI,+BAAuC,CAI/B,uBACZ,CAEA,aAMI,aAAc,CAFd,MAAO,CAGP,gBAAiB,CACjB,iBAAkB,CANlB,iBAAkB,CAClB,KAMJ,CACA,uCAKI,UAAW,CAFX,aAGJ,CACA,mBAEI,UACJ,CACA,4BAEI,iBACJ,CAEA,aAEI,YAAa,CACb,UAAW,CAEX,WAAY,CACZ,cACJ,CACA,uBAEI,WACJ,CACA,iBAEI,aACJ,CACA,+BAEI,YACJ,CACA,0BAEI,mBACJ,CACA,gCAEI,aACJ,CACA,4BAEI,iBACJ,CACA,6BAMI,4BAA6B,CAJ7B,aAAc,CAEd,WAGJ,CACA,0BACI,YACJ,CDpHA,2BAEI,6/KACJ,CAGA,WAEI,iBAAoB,CAEpB,iBAAkB,CADlB,eAAmB,CAGnB,qDAA6B,CAC7B,qSACJ,CAEA,wBAqBI,WAAY,CAHZ,cAAe,CATf,aAAc,CANd,WAAY,CASZ,WAAY,CARZ,aAAc,CASd,SAAU,CAPV,iBAAkB,CAClB,OAAQ,CAOR,kCAAqC,CAErC,0BAA6B,CAL7B,UAaJ,CACA,gGAFI,sBAAuB,CAHvB,iBAAkB,CAElB,YAWJ,CACA,oGAKI,SACJ,CACA,oEAGI,WACJ,CAEA,sCAUI,kCAAmC,CACnC,iCAAkC,CAHlC,UAAY,CALZ,iBAAoB,CACpB,cAAe,CACf,aAAc,CAEd,WAKJ,CAEA,YAEI,UACJ,CACA,sBAGI,SAAU,CADV,WAEJ,CACA,mBAEI,WACJ,CACA,6BAEI,WACJ,CAEA,YAEI,WACJ,CACA,sBAGI,UAAW,CADX,UAEJ,CACA,mBAEI,WACJ,CACA,6BAEI,WACJ,CAGA,2BAEI,kBACJ,CAEA,YAGI,YAAa,CAEb,aAAc,CAMd,eAAgB,CAFhB,QAAS,CADT,SAAU,CANV,iBAAkB,CAWlB,iBAAkB,CANlB,UAOJ,CACA,eAII,oBAAqB,CAIrB,YAAa,CACb,SAAU,CAPV,iBAUJ,CACA,qCAFI,cAAe,CAJf,WAAY,CADZ,UAwBJ,CAjBA,sBAgBI,sBAAuB,CAFvB,QAAS,CADT,iBAAkB,CARlB,aAAc,CAHd,WAAY,CACZ,aAAc,CAYd,YAAa,CANb,WAQJ,CACA,wDAGI,YACJ,CACA,sEAGI,SACJ,CACA,6BAmBI,kCAAmC,CACnC,iCAAkC,CAHlC,UAAY,CAJZ,WAAY,CAXZ,iBAAoB,CACpB,aAAc,CAQd,WAAY,CAHZ,MAAO,CAJP,gBAAiB,CAYjB,WAAY,CAVZ,iBAAkB,CAQlB,iBAAkB,CAPlB,KAAM,CAGN,UAWJ,CACA,0CAGI,UAAY,CADZ,WAEJ","sources":["../node_modules/slick-carousel/slick/slick-theme.css","../node_modules/slick-carousel/slick/slick.css"],"sourcesContent":["@charset 'UTF-8';\n/* Slider */\n.slick-loading .slick-list\n{\n background: #fff url('./ajax-loader.gif') center center no-repeat;\n}\n\n/* Icons */\n@font-face\n{\n font-family: 'slick';\n font-weight: normal;\n font-style: normal;\n\n src: url('./fonts/slick.eot');\n src: url('./fonts/slick.eot?#iefix') format('embedded-opentype'), url('./fonts/slick.woff') format('woff'), url('./fonts/slick.ttf') format('truetype'), url('./fonts/slick.svg#slick') format('svg');\n}\n/* Arrows */\n.slick-prev,\n.slick-next\n{\n font-size: 0;\n line-height: 0;\n\n position: absolute;\n top: 50%;\n\n display: block;\n\n width: 20px;\n height: 20px;\n padding: 0;\n -webkit-transform: translate(0, -50%);\n -ms-transform: translate(0, -50%);\n transform: translate(0, -50%);\n\n cursor: pointer;\n\n color: transparent;\n border: none;\n outline: none;\n background: transparent;\n}\n.slick-prev:hover,\n.slick-prev:focus,\n.slick-next:hover,\n.slick-next:focus\n{\n color: transparent;\n outline: none;\n background: transparent;\n}\n.slick-prev:hover:before,\n.slick-prev:focus:before,\n.slick-next:hover:before,\n.slick-next:focus:before\n{\n opacity: 1;\n}\n.slick-prev.slick-disabled:before,\n.slick-next.slick-disabled:before\n{\n opacity: .25;\n}\n\n.slick-prev:before,\n.slick-next:before\n{\n font-family: 'slick';\n font-size: 20px;\n line-height: 1;\n\n opacity: .75;\n color: white;\n\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n.slick-prev\n{\n left: -25px;\n}\n[dir='rtl'] .slick-prev\n{\n right: -25px;\n left: auto;\n}\n.slick-prev:before\n{\n content: '←';\n}\n[dir='rtl'] .slick-prev:before\n{\n content: '→';\n}\n\n.slick-next\n{\n right: -25px;\n}\n[dir='rtl'] .slick-next\n{\n right: auto;\n left: -25px;\n}\n.slick-next:before\n{\n content: '→';\n}\n[dir='rtl'] .slick-next:before\n{\n content: '←';\n}\n\n/* Dots */\n.slick-dotted.slick-slider\n{\n margin-bottom: 30px;\n}\n\n.slick-dots\n{\n position: absolute;\n bottom: -25px;\n\n display: block;\n\n width: 100%;\n padding: 0;\n margin: 0;\n\n list-style: none;\n\n text-align: center;\n}\n.slick-dots li\n{\n position: relative;\n\n display: inline-block;\n\n width: 20px;\n height: 20px;\n margin: 0 5px;\n padding: 0;\n\n cursor: pointer;\n}\n.slick-dots li button\n{\n font-size: 0;\n line-height: 0;\n\n display: block;\n\n width: 20px;\n height: 20px;\n padding: 5px;\n\n cursor: pointer;\n\n color: transparent;\n border: 0;\n outline: none;\n background: transparent;\n}\n.slick-dots li button:hover,\n.slick-dots li button:focus\n{\n outline: none;\n}\n.slick-dots li button:hover:before,\n.slick-dots li button:focus:before\n{\n opacity: 1;\n}\n.slick-dots li button:before\n{\n font-family: 'slick';\n font-size: 6px;\n line-height: 20px;\n\n position: absolute;\n top: 0;\n left: 0;\n\n width: 20px;\n height: 20px;\n\n content: '•';\n text-align: center;\n\n opacity: .25;\n color: black;\n\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.slick-dots li.slick-active button:before\n{\n opacity: .75;\n color: black;\n}\n","/* Slider */\n.slick-slider\n{\n position: relative;\n\n display: block;\n box-sizing: border-box;\n\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n\n -webkit-touch-callout: none;\n -khtml-user-select: none;\n -ms-touch-action: pan-y;\n touch-action: pan-y;\n -webkit-tap-highlight-color: transparent;\n}\n\n.slick-list\n{\n position: relative;\n\n display: block;\n overflow: hidden;\n\n margin: 0;\n padding: 0;\n}\n.slick-list:focus\n{\n outline: none;\n}\n.slick-list.dragging\n{\n cursor: pointer;\n cursor: hand;\n}\n\n.slick-slider .slick-track,\n.slick-slider .slick-list\n{\n -webkit-transform: translate3d(0, 0, 0);\n -moz-transform: translate3d(0, 0, 0);\n -ms-transform: translate3d(0, 0, 0);\n -o-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n}\n\n.slick-track\n{\n position: relative;\n top: 0;\n left: 0;\n\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.slick-track:before,\n.slick-track:after\n{\n display: table;\n\n content: '';\n}\n.slick-track:after\n{\n clear: both;\n}\n.slick-loading .slick-track\n{\n visibility: hidden;\n}\n\n.slick-slide\n{\n display: none;\n float: left;\n\n height: 100%;\n min-height: 1px;\n}\n[dir='rtl'] .slick-slide\n{\n float: right;\n}\n.slick-slide img\n{\n display: block;\n}\n.slick-slide.slick-loading img\n{\n display: none;\n}\n.slick-slide.dragging img\n{\n pointer-events: none;\n}\n.slick-initialized .slick-slide\n{\n display: block;\n}\n.slick-loading .slick-slide\n{\n visibility: hidden;\n}\n.slick-vertical .slick-slide\n{\n display: block;\n\n height: auto;\n\n border: 1px solid transparent;\n}\n.slick-arrow.slick-hidden {\n display: none;\n}\n"],"names":[],"sourceRoot":""} -------------------------------------------------------------------------------- /frontend/build/static/js/25.2f1eb84b.chunk.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([[25],{44145:function(e,a,r){r(72791);var t=r(5884),s=r(80184);a.Z=function(e){var a=e.children;return(0,s.jsx)("div",{className:"w-full h-full",children:(0,s.jsxs)("div",{className:"flex w-full h-screen md:w-2/3 py-8 mx-auto",children:[(0,s.jsx)("div",{className:"hidden md:block bg-[url('https://www.instagram.com/static/images/homepage/phones/home-phones.png/1dc085cdb87d.png')] my-10 h-full bg-no-repeat",children:(0,s.jsx)("img",{draggable:"false",className:"mr-[80px] mt-[1.8rem] ml-[155px]",src:t,alt:"homepage"})}),(0,s.jsxs)("div",{className:"flex flex-col gap-3 w-full md:w-2/5",children:[a,(0,s.jsx)("p",{className:"text-center text-sm my-2",children:"Get the app."}),(0,s.jsxs)("div",{className:"flex gap-3 justify-center",children:[(0,s.jsx)("img",{draggable:"false",width:"130px",src:"https://www.instagram.com/static/images/appstore-install-badges/badge_ios_english-en.png/180ae7a0bcf7.png",alt:"appstore"}),(0,s.jsx)("img",{draggable:"false",width:"130px",src:"https://www.instagram.com/static/images/appstore-install-badges/badge_android_english-en.png/e9cd846dc748.png",alt:"playstore"})]})]})]})})}},89025:function(e,a,r){r.r(a),r.d(a,{default:function(){return D}});var t=r(4942),s=r(1413),l=r(70885),n=r(72791),i=r(19913),o=r(44145),c=r(43504),m=r(16871),u=r(63366),d=r(87462),f=r(28182),g=r(90767),p=r(59553),h=r(10551),x=r(76189),v=r(80184),b=(0,x.Z)((0,v.jsx)("path",{d:"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"}),"Person"),w=r(95159);function j(e){return(0,w.Z)("MuiAvatar",e)}(0,r(30208).Z)("MuiAvatar",["root","colorDefault","circular","rounded","square","img","fallback"]);var y=["alt","children","className","component","imgProps","sizes","src","srcSet","variant"],Z=(0,p.ZP)("div",{name:"MuiAvatar",slot:"Root",overridesResolver:function(e,a){var r=e.ownerState;return[a.root,a[r.variant],r.colorDefault&&a.colorDefault]}})((function(e){var a=e.theme,r=e.ownerState;return(0,d.Z)({position:"relative",display:"flex",alignItems:"center",justifyContent:"center",flexShrink:0,width:40,height:40,fontFamily:a.typography.fontFamily,fontSize:a.typography.pxToRem(20),lineHeight:1,borderRadius:"50%",overflow:"hidden",userSelect:"none"},"rounded"===r.variant&&{borderRadius:a.shape.borderRadius},"square"===r.variant&&{borderRadius:0},r.colorDefault&&{color:a.palette.background.default,backgroundColor:"light"===a.palette.mode?a.palette.grey[400]:a.palette.grey[600]})})),N=(0,p.ZP)("img",{name:"MuiAvatar",slot:"Img",overridesResolver:function(e,a){return a.img}})({width:"100%",height:"100%",textAlign:"center",objectFit:"cover",color:"transparent",textIndent:1e4}),S=(0,p.ZP)(b,{name:"MuiAvatar",slot:"Fallback",overridesResolver:function(e,a){return a.fallback}})({width:"75%",height:"75%"});var k=n.forwardRef((function(e,a){var r=(0,h.Z)({props:e,name:"MuiAvatar"}),t=r.alt,s=r.children,i=r.className,o=r.component,c=void 0===o?"div":o,m=r.imgProps,p=r.sizes,x=r.src,b=r.srcSet,w=r.variant,k=void 0===w?"circular":w,A=(0,u.Z)(r,y),P=null,R=function(e){var a=e.crossOrigin,r=e.referrerPolicy,t=e.src,s=e.srcSet,i=n.useState(!1),o=(0,l.Z)(i,2),c=o[0],m=o[1];return n.useEffect((function(){if(t||s){m(!1);var e=!0,l=new Image;return l.onload=function(){e&&m("loaded")},l.onerror=function(){e&&m("error")},l.crossOrigin=a,l.referrerPolicy=r,l.src=t,s&&(l.srcset=s),function(){e=!1}}}),[a,r,t,s]),c}((0,d.Z)({},m,{src:x,srcSet:b})),z=x||b,D=z&&"error"!==R,C=(0,d.Z)({},r,{colorDefault:!D,component:c,variant:k}),F=function(e){var a=e.classes,r={root:["root",e.variant,e.colorDefault&&"colorDefault"],img:["img"],fallback:["fallback"]};return(0,g.Z)(r,j,a)}(C);return P=D?(0,v.jsx)(N,(0,d.Z)({alt:t,src:x,srcSet:b,sizes:p,ownerState:C,className:F.img},m)):null!=s?s:z&&t?t[0]:(0,v.jsx)(S,{className:F.fallback}),(0,v.jsx)(Z,(0,d.Z)({as:c,ownerState:C,className:(0,f.Z)(F.root,i),ref:a},A,{children:P}))})),A=r(16030),P=r(56960),R=r(37689),z=r(23671),D=function(){var e=(0,A.I0)(),a=(0,m.s0)(),r=(0,A.v9)((function(e){return e.user})),u=r.loading,d=r.isAuthenticated,f=r.error,g=(0,n.useState)({email:"",name:"",username:"",password:""}),p=(0,l.Z)(g,2),h=p[0],x=p[1],b=h.email,w=h.name,j=h.username,y=h.password,Z=(0,n.useState)(),N=(0,l.Z)(Z,2),S=N[0],D=N[1],C=(0,n.useState)(),F=(0,l.Z)(C,2),_=F[0],M=F[1],q=function(e){if("avatar"===e.target.name){var a=new FileReader;a.onload=function(){2===a.readyState&&M(a.result)},a.readAsDataURL(e.target.files[0]),D(e.target.files[0])}else x((0,s.Z)((0,s.Z)({},h),{},(0,t.Z)({},e.target.name,e.target.value)))};return(0,n.useEffect)((function(){f&&(P.Am.error(f),e((0,R.b9)())),d&&a("/")}),[e,f,d,a]),(0,v.jsxs)(v.Fragment,{children:[u&&(0,v.jsx)(z.Z,{}),(0,v.jsxs)(o.Z,{children:[(0,v.jsxs)("div",{className:"bg-white border flex flex-col gap-2 p-4 pt-10",children:[(0,v.jsx)("img",{draggable:"false",className:"mx-auto h-30 w-36 object-contain",src:"https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png",alt:""}),(0,v.jsxs)("form",{onSubmit:function(a){a.preventDefault();if(y.length<8)P.Am.error("Password length must be atleast 8 characters");else if(S)if(/^[a-z0-9_.-]{6,25}$/gim.test(j)){var r=new FormData;r.set("email",b),r.set("name",w),r.set("username",j),r.set("password",y),r.set("avatar",S),e((0,R.a$)(r))}else P.Am.error("Invalid Username");else P.Am.error("Select Profile Pic")},encType:"multipart/form-data",className:"flex flex-col justify-center items-center gap-3 m-3 md:m-8",children:[(0,v.jsx)(i.Z,{fullWidth:!0,label:"Email",type:"email",name:"email",value:b,onChange:q,required:!0,size:"small"}),(0,v.jsx)(i.Z,{fullWidth:!0,label:"Full Name",name:"name",value:w,onChange:q,required:!0,size:"small"}),(0,v.jsx)(i.Z,{label:"Username",type:"text",name:"username",value:j,onChange:q,size:"small",required:!0,fullWidth:!0}),(0,v.jsx)(i.Z,{label:"Password",type:"password",name:"password",value:y,onChange:q,required:!0,size:"small",fullWidth:!0}),(0,v.jsxs)("div",{className:"flex w-full justify-between gap-3 items-center",children:[(0,v.jsx)(k,{alt:"Avatar Preview",src:_,sx:{width:48,height:48}}),(0,v.jsx)("label",{children:(0,v.jsx)("input",{type:"file",accept:"image/*",name:"avatar",onChange:q,className:"block w-full text-sm text-gray-400\r file:mr-3 file:py-2 file:px-6\r file:rounded-full file:border-0\r file:text-sm file:cursor-pointer file:font-semibold\r file:bg-blue-100 file:text-blue-700\r hover:file:bg-blue-200\r "})})]}),(0,v.jsx)("button",{type:"submit",className:"bg-primary-blue font-medium py-2 rounded text-white w-full",children:"Sign up"}),(0,v.jsx)("span",{className:"my-3 text-gray-500",children:"OR"}),(0,v.jsx)(c.rU,{to:"/password/forgot",className:"text-sm font-medium text-blue-800",children:"Forgot password?"})]})]}),(0,v.jsx)("div",{className:"bg-white border p-5 text-center",children:(0,v.jsxs)("span",{children:["Already have an account? ",(0,v.jsx)(c.rU,{to:"/login",className:"text-primary-blue",children:"Log in"})]})})]})]})}},5884:function(e,a,r){e.exports=r.p+"static/media/homepage.225baac9eb2a22aae1a7.webp"}}]); 2 | //# sourceMappingURL=25.2f1eb84b.chunk.js.map -------------------------------------------------------------------------------- /frontend/src/components/User/SignUp.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import TextField from '@mui/material/TextField'; 3 | import Auth from './Auth'; 4 | import { Link, useNavigate } from 'react-router-dom'; 5 | import { Avatar } from '@mui/material'; 6 | import { useDispatch, useSelector } from 'react-redux'; 7 | import { toast } from 'react-toastify'; 8 | import { clearErrors, registerUser } from '../../actions/userAction'; 9 | import BackdropLoader from '../Layouts/BackdropLoader'; 10 | 11 | const SignUp = () => { 12 | 13 | const dispatch = useDispatch(); 14 | const navigate = useNavigate(); 15 | 16 | const { loading, isAuthenticated, error } = useSelector((state) => state.user); 17 | 18 | const [user, setUser] = useState({ 19 | email: "", 20 | name: "", 21 | username: "", 22 | password: "", 23 | }); 24 | 25 | const { email, name, username, password } = user; 26 | 27 | const [avatar, setAvatar] = useState(); 28 | const [avatarPreview, setAvatarPreview] = useState(); 29 | 30 | const handleRegister = (e) => { 31 | e.preventDefault(); 32 | 33 | const userCheck = /^[a-z0-9_.-]{6,25}$/igm; 34 | 35 | if (password.length < 8) { 36 | toast.error("Password length must be atleast 8 characters"); 37 | return; 38 | } 39 | if (!avatar) { 40 | toast.error("Select Profile Pic"); 41 | return; 42 | } 43 | if (!userCheck.test(username)) { 44 | toast.error("Invalid Username"); 45 | return; 46 | } 47 | 48 | const formData = new FormData(); 49 | formData.set("email", email); 50 | formData.set("name", name); 51 | formData.set("username", username); 52 | formData.set("password", password); 53 | formData.set("avatar", avatar); 54 | 55 | dispatch(registerUser(formData)); 56 | } 57 | 58 | const handleDataChange = (e) => { 59 | if (e.target.name === 'avatar') { 60 | const reader = new FileReader(); 61 | 62 | reader.onload = () => { 63 | if (reader.readyState === 2) { 64 | setAvatarPreview(reader.result); 65 | } 66 | }; 67 | 68 | reader.readAsDataURL(e.target.files[0]); 69 | // console.log(e.target.files[0]) 70 | setAvatar(e.target.files[0]) 71 | 72 | } else { 73 | setUser({ ...user, [e.target.name]: e.target.value }); 74 | } 75 | } 76 | 77 | useEffect(() => { 78 | if (error) { 79 | toast.error(error); 80 | dispatch(clearErrors()); 81 | } 82 | if (isAuthenticated) { 83 | navigate('/') 84 | } 85 | }, [dispatch, error, isAuthenticated, navigate]); 86 | 87 | return ( 88 | <> 89 | {loading && } 90 | 91 |
92 | 93 |
98 | 108 | 117 | 127 | 137 |
138 | 143 | 157 |
158 | 159 | 160 | OR 161 | Forgot password? 162 | 163 |
164 | 165 |
166 | Already have an account? Log in 167 |
168 |
169 | 170 | ) 171 | } 172 | 173 | export default SignUp --------------------------------------------------------------------------------