├── client
├── src
│ ├── Components
│ │ ├── tempCodeRunnerFile.js
│ │ ├── Loading.js
│ │ ├── Favourite.js
│ │ ├── Chat.js
│ │ ├── ShowPasswordToggle.js
│ │ ├── Welcome.js
│ │ ├── ErrorPage.js
│ │ ├── Toggler.js
│ │ ├── Searchbar.js
│ │ ├── modal
│ │ │ ├── NetworkError.js
│ │ │ └── Invite.js
│ │ ├── Loading1.js
│ │ ├── ScrollToTopButton.js
│ │ ├── Header.js
│ │ ├── Default.js
│ │ ├── Auth
│ │ │ ├── Signup.js
│ │ │ ├── Login.js
│ │ │ ├── LoginForm.js
│ │ │ └── ForgotPassword.js
│ │ ├── Themes.js
│ │ ├── Profile.js
│ │ ├── Technologies.js
│ │ ├── Footer.js
│ │ ├── Contacts.js
│ │ ├── Setting.js
│ │ ├── Contact.js
│ │ ├── Dropdown.js
│ │ ├── SlideMenu
│ │ │ └── UserProfile.js
│ │ ├── ChatMenu.js
│ │ ├── Features.js
│ │ └── HeroSection.js
│ ├── Redux
│ │ ├── Reducer
│ │ │ ├── Tab
│ │ │ │ ├── tabType.js
│ │ │ │ ├── tabAction.js
│ │ │ │ └── tabReducer.js
│ │ │ ├── SetColor
│ │ │ │ ├── setColorType.js
│ │ │ │ ├── setColorAction.js
│ │ │ │ └── setColorReducer.js
│ │ │ ├── Theme
│ │ │ │ ├── theme.type.js
│ │ │ │ ├── theme.action.js
│ │ │ │ └── theme.reducer.js
│ │ │ ├── ProfileImage
│ │ │ │ ├── profileImage.type.js
│ │ │ │ ├── profileImage.reducer.js
│ │ │ │ └── profileImage.action.js
│ │ │ ├── User
│ │ │ │ ├── user.type.js
│ │ │ │ ├── user.reducer.js
│ │ │ │ └── user.action.js
│ │ │ ├── Message
│ │ │ │ ├── message.type.js
│ │ │ │ ├── message.reducer.js
│ │ │ │ └── message.action.js
│ │ │ ├── Auth
│ │ │ │ ├── auth.type.js
│ │ │ │ ├── auth.reducer.js
│ │ │ │ └── auth.action.js
│ │ │ ├── Chat
│ │ │ │ ├── chat.type.js
│ │ │ │ ├── chat.reducer.js
│ │ │ │ └── chat.action.js
│ │ │ └── rootReducers.js
│ │ └── Store.js
│ ├── Pages
│ │ ├── AuthPage.js
│ │ └── HomePage.js
│ ├── Layout
│ │ └── DefaultLayout.js
│ ├── App.css
│ ├── Styles
│ │ ├── Button.js
│ │ ├── Spinner.js
│ │ └── Social.js
│ ├── index.js
│ ├── index.css
│ ├── HelperFunction
│ │ └── chat.Helper.js
│ ├── config.js
│ │ └── data.js
│ ├── GlobalStyle
│ │ └── GlobalStyle.js
│ └── App.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── images
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── atom.png
│ │ ├── logo.png
│ │ ├── logo2.png
│ │ ├── loading.gif
│ │ ├── mongodb.png
│ │ ├── shape-1.png
│ │ ├── shape-3.png
│ │ ├── shape-5.png
│ │ ├── shape-6.png
│ │ ├── tailwind.png
│ │ ├── css-white.png
│ │ ├── pattern-05.png
│ │ ├── pattern-bg.png
│ │ ├── googlefonts.png
│ │ ├── html-5-white.png
│ │ ├── nodejs-logo.png
│ │ ├── contact-shape-1.png
│ │ ├── contact-shape-2.png
│ │ ├── features-icon-1.png
│ │ ├── features-icon-2.png
│ │ ├── features-icon-6.png
│ │ └── email.svg
│ ├── team
│ │ ├── nitesh.png
│ │ ├── narender.jpg
│ │ └── rithuresh.jpg
│ ├── manifest.json
│ ├── social
│ │ ├── facebook.svg
│ │ └── twitter.svg
│ └── index.html
├── postcss.config.js
├── .gitignore
├── tailwind.config.js
├── package.json
└── README.md
├── server
├── config
│ ├── keys.js
│ ├── generateToken.js
│ ├── db.js
│ └── prod.js
├── .babelrc
├── utils
│ ├── cloudinary.js
│ └── sendEmail.js
├── routes
│ ├── messageRoutes.js
│ ├── chatRoutes.js
│ └── userRoutes.js
├── models
│ ├── messageModel.js
│ ├── chatModel.js
│ └── userModel.js
├── middleware
│ ├── errorMiddleware.js
│ └── authMiddleware.js
├── package.json
├── dist
│ └── index.js
├── controllers
│ ├── messageControllers.js
│ └── chatControllers.js
├── data
│ └── data.js
└── index.js
├── .gitignore
└── README.md
/client/src/Components/tempCodeRunnerFile.js:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Tab/tabType.js:
--------------------------------------------------------------------------------
1 | export const TOGGLE_TAB = "TOGGLE_TAB";
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/SetColor/setColorType.js:
--------------------------------------------------------------------------------
1 | export const set_color = "set_color";
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Theme/theme.type.js:
--------------------------------------------------------------------------------
1 | export const TOGGLE_DARKTHEME = "TOGGLE_DARKTHEME";
--------------------------------------------------------------------------------
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/logo192.png
--------------------------------------------------------------------------------
/client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/logo512.png
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/ProfileImage/profileImage.type.js:
--------------------------------------------------------------------------------
1 | export const UPLOAD_IMAGE = "UPLOAD_IMAGE";
2 |
--------------------------------------------------------------------------------
/client/public/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/1.png
--------------------------------------------------------------------------------
/client/public/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/2.png
--------------------------------------------------------------------------------
/client/public/images/atom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/atom.png
--------------------------------------------------------------------------------
/client/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/logo.png
--------------------------------------------------------------------------------
/client/public/images/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/logo2.png
--------------------------------------------------------------------------------
/client/public/team/nitesh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/team/nitesh.png
--------------------------------------------------------------------------------
/client/public/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/loading.gif
--------------------------------------------------------------------------------
/client/public/images/mongodb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/mongodb.png
--------------------------------------------------------------------------------
/client/public/images/shape-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/shape-1.png
--------------------------------------------------------------------------------
/client/public/images/shape-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/shape-3.png
--------------------------------------------------------------------------------
/client/public/images/shape-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/shape-5.png
--------------------------------------------------------------------------------
/client/public/images/shape-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/shape-6.png
--------------------------------------------------------------------------------
/client/public/images/tailwind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/tailwind.png
--------------------------------------------------------------------------------
/client/public/team/narender.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/team/narender.jpg
--------------------------------------------------------------------------------
/client/public/team/rithuresh.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/team/rithuresh.jpg
--------------------------------------------------------------------------------
/client/public/images/css-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/css-white.png
--------------------------------------------------------------------------------
/client/public/images/pattern-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/pattern-05.png
--------------------------------------------------------------------------------
/client/public/images/pattern-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/pattern-bg.png
--------------------------------------------------------------------------------
/client/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/client/public/images/googlefonts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/googlefonts.png
--------------------------------------------------------------------------------
/client/public/images/html-5-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/html-5-white.png
--------------------------------------------------------------------------------
/client/public/images/nodejs-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/nodejs-logo.png
--------------------------------------------------------------------------------
/client/public/images/contact-shape-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/contact-shape-1.png
--------------------------------------------------------------------------------
/client/public/images/contact-shape-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/contact-shape-2.png
--------------------------------------------------------------------------------
/client/public/images/features-icon-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/features-icon-1.png
--------------------------------------------------------------------------------
/client/public/images/features-icon-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/features-icon-2.png
--------------------------------------------------------------------------------
/client/public/images/features-icon-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ideal-Softer/chat-app/HEAD/client/public/images/features-icon-6.png
--------------------------------------------------------------------------------
/server/config/keys.js:
--------------------------------------------------------------------------------
1 | if (process.env.NODE_ENV == "production") {
2 | module.exports = require("./prod");
3 | } else {
4 | module.exports = require("./dev");
5 | }
6 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Theme/theme.action.js:
--------------------------------------------------------------------------------
1 | import { TOGGLE_DARKTHEME } from "./theme.type";
2 |
3 | export const toggleDarkTheme = () => ({
4 | type: TOGGLE_DARKTHEME,
5 | });
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Tab/tabAction.js:
--------------------------------------------------------------------------------
1 | import { TOGGLE_TAB } from "./tabType";
2 |
3 | export const toggleTab = (index ) => ({
4 | type: TOGGLE_TAB,
5 | payload: index,
6 | });
7 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/SetColor/setColorAction.js:
--------------------------------------------------------------------------------
1 | import { set_color } from "./setColorType";
2 |
3 | export const toggleColor = (color) => ({
4 | type: set_color,
5 | payload: color,
6 | });
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /client/node_modules
2 | /client/package-lock.json
3 | /client/.env
4 |
5 | /server/node_modules
6 | /server/package-lock.json
7 | server/.env
8 | server/vercel.json
9 | server/config/dev.js
10 |
11 |
12 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/User/user.type.js:
--------------------------------------------------------------------------------
1 | export const SELF = "SELF"; // PERSONAL DETAILS
2 | export const CLEAR_USER = "CLEAR_USER"; // SIGN OUT
3 |
4 | export const UPDATE_PROFILE = "UPDATE_PROFILE";
5 | export const INVITE_FRIENDS = "INVITE_FRIENDS";
6 |
--------------------------------------------------------------------------------
/client/src/Pages/AuthPage.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Outlet } from "react-router-dom";
3 | import DefaultLayoutHoc from "../Layout/DefaultLayout";
4 |
5 | const AuthPage = () => {
6 | return (
7 | <>
8 |
9 | >
10 | );
11 | };
12 |
13 | export default DefaultLayoutHoc(AuthPage);
14 |
--------------------------------------------------------------------------------
/server/config/generateToken.js:
--------------------------------------------------------------------------------
1 | const jwt = require("jsonwebtoken");
2 | const { JWT_SECRET } = require("./keys");
3 |
4 | const generateToken = (id, tokenValidity = "30d") => {
5 | // console.log(tokenValidity);
6 | return jwt.sign({ id }, JWT_SECRET, {
7 | expiresIn: tokenValidity,
8 | });
9 | };
10 |
11 | module.exports = generateToken;
12 |
--------------------------------------------------------------------------------
/client/src/Layout/DefaultLayout.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const DefaultLayoutHoc =
4 | (Components) =>
5 | ({ ...props }) => {
6 | return (
7 | <>
8 |
9 |
10 |
11 | >
12 | );
13 | };
14 |
15 | export default DefaultLayoutHoc;
16 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Tab/tabReducer.js:
--------------------------------------------------------------------------------
1 | import { TOGGLE_TAB } from "./tabType";
2 |
3 | const initialstate = 3
4 |
5 | const tabReducer = (state = initialstate, action) => {
6 | switch (action.type) {
7 | case TOGGLE_TAB:
8 | return state = action.payload;
9 |
10 | default:
11 | return state;
12 | }
13 | };
14 |
15 | export default tabReducer;
16 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Message/message.type.js:
--------------------------------------------------------------------------------
1 | export const SEND_MESSAGE = "SEND_MESSAGE";
2 | export const GET_ALL_MESSAGE = "GET_ALL_MESSAGE";
3 | export const UPDATE_GET_ALL_MESSAGE = "UPDATE_GET_ALL_MESSAGE";
4 | export const CLEAR_ALL_MESSAGE = "CLEAR_ALL_MESSAGE";
5 | export const SHOW_TOOGLE_LOADING = "SHOW_TOOGLE_LOADING";
6 | export const SHOW_NETWORK_ERROR = "SHOW_NETWORK_ERROR"
7 |
--------------------------------------------------------------------------------
/server/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/env",
5 | {
6 | "targets": {
7 | "node": "current"
8 | }
9 | }
10 | ]
11 | ],
12 | "plugins": [
13 | "@babel/plugin-proposal-class-properties",
14 | "@babel/plugin-proposal-object-rest-spread"
15 | ]
16 | }
--------------------------------------------------------------------------------
/server/utils/cloudinary.js:
--------------------------------------------------------------------------------
1 | const cloudinary = require("cloudinary").v2;
2 | const {
3 | CLOUDINARY_CLOUD_NAME,
4 | CLOUDINARY_API_KEY,
5 | CLOUDINARY_API_SECRET,
6 | } = require("../config/keys");
7 | cloudinary.config({
8 | cloud_name: CLOUDINARY_CLOUD_NAME,
9 | api_key: CLOUDINARY_API_KEY,
10 | api_secret: CLOUDINARY_API_SECRET,
11 | });
12 |
13 | module.exports = cloudinary;
14 |
--------------------------------------------------------------------------------
/server/routes/messageRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const {
3 | allMessages,
4 | sendMessage,
5 | } = require("../controllers/messageControllers");
6 | const { protect } = require("../middleware/authMiddleware");
7 |
8 | const router = express.Router();
9 |
10 | router.route("/").post(protect, sendMessage);
11 | router.route("/:chatId").get(protect, allMessages);
12 | module.exports = router;
13 |
--------------------------------------------------------------------------------
/client/.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 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Auth/auth.type.js:
--------------------------------------------------------------------------------
1 | export const SIGN_IN = "SIGN_IN";
2 | export const SIGN_UP = "SIGN_UP";
3 | export const SIGN_OUT = "SIGN_OUT";
4 | export const USER_VERIFICATION = "USER_VERIFICATION";
5 | export const VERIFY_TOKEN = "VERIFY_TOKEN";
6 | export const FORGOT_PASSWORD = "FORGOT_PASSWORD";
7 | export const RESET_PASSWORD = "RESET_PASSWORD";
8 | export const CLEAR_AUTH_STORE = "CLEAR_AUTH_STORE";
9 | export const ERROR = "ERROR";
10 |
--------------------------------------------------------------------------------
/server/models/messageModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const messageModel = mongoose.Schema(
4 | {
5 | sender: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
6 | content: { type: String, trim: true },
7 | chat: { type: mongoose.Schema.Types.ObjectId, ref: "Chat" },
8 | },
9 | {
10 | timestamps: true,
11 | }
12 | );
13 |
14 | const Message = mongoose.model("Message", messageModel);
15 |
16 | module.exports = Message;
17 |
--------------------------------------------------------------------------------
/client/src/Redux/Store.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from "redux";
2 | import thunk from "redux-thunk";
3 | import rootReducer from "./Reducer/rootReducers";
4 |
5 | // redux middleware
6 | const middlewares = [thunk];
7 |
8 | if (process.env.NODE_ENV === "development") {
9 | const { logger } = require("redux-logger");
10 | middlewares.push(logger);
11 | }
12 |
13 | const Store = createStore(rootReducer, {
14 |
15 | }, applyMiddleware(...middlewares));
16 |
17 | export default Store;
18 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Chat/chat.type.js:
--------------------------------------------------------------------------------
1 | export const FETCH_CHATS = "FETCH_CHATS";
2 | export const FETCH_USER = "FETCH_USER";
3 | export const FETCH_USER_CLEAR = "FETCH_USER_CLEAR";
4 | export const CREATE_CHAT = "CREATE_CHAT";
5 | export const CREATE_GROUP_CHAT = "CREATE_GROUP_CHAT";
6 | export const SELECT_CHAT = "SELECT_CHAT";
7 | export const CLEAR_SELECT_CHAT = "CLEAR_SELECT_CHAT";
8 | export const REMOVE_USER_FROM_GROUP = "REMOVE_USER_FROM_GROUP";
9 | export const SHOW_USER_LOADING = "SHOW_TOOGLE_LOADING";
10 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/ProfileImage/profileImage.reducer.js:
--------------------------------------------------------------------------------
1 | import { UPLOAD_IMAGE } from "./profileImage.type";
2 | const initialState = {
3 | profilePic: "",
4 | };
5 |
6 | const profileImageReducer = (state = initialState, action) => {
7 | switch (action.type) {
8 | case UPLOAD_IMAGE:
9 | return {
10 | ...state,
11 | profilePic: action.payload,
12 | };
13 |
14 | default:
15 | return {
16 | ...state,
17 | };
18 | }
19 | };
20 |
21 | export default profileImageReducer;
22 |
--------------------------------------------------------------------------------
/client/src/App.css:
--------------------------------------------------------------------------------
1 | .input-suffix-end {
2 | right: 0.625rem;
3 | }
4 | .input-suffix-end {
5 | --tw-translate-y: -50%;
6 | display: flex;
7 | position: absolute;
8 | }
9 |
10 | .input-wrapper {
11 | display: flex;
12 | position: relative;
13 | width: 100%;
14 | }
15 |
16 | .form-label {
17 | align-items: center;
18 | display: flex;
19 | font-weight: 500;
20 | }
21 | .form-item.vertical {
22 | display: flex;
23 | flex-direction: column;
24 | }
25 | .form-item {
26 | position: relative;
27 | margin-bottom: 1.75rem;
28 | }
29 |
--------------------------------------------------------------------------------
/server/middleware/errorMiddleware.js:
--------------------------------------------------------------------------------
1 | const notFound = (req,res,next) => {
2 | const error = new Error(`Not Found - ${req.originalUrl}`);
3 | res.status(404);
4 | next(error);
5 | };
6 |
7 |
8 | const errorHandler = (err,req,res,next) => {
9 | const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
10 | res.status(statusCode);
11 | res.json({
12 | message: err.message,
13 | stack: process.env.NODE_ENV === "production" ? null : err.stack,
14 | });
15 | };
16 |
17 | module.exports = { notFound, errorHandler};
--------------------------------------------------------------------------------
/client/src/Styles/Button.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Button = styled.button`
4 | font-weight: 400;
5 | line-height: 1.5;
6 | text-align: center;
7 | vertical-align: middle;
8 | cursor: pointer;
9 | -webkit-user-select: none;
10 | -ms-user-select: none;
11 | user-select: none;
12 | padding: 0.5rem 1rem;
13 | font-size: 0.9375rem;
14 | border-radius: 0.25rem;
15 | transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
16 | border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
17 | `;
18 |
--------------------------------------------------------------------------------
/client/src/Components/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components';
3 |
4 | const Loading = () => {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
12 | const Wrapper = styled.section`
13 | width: 100vw;
14 | height: 100vh;
15 | h1 {
16 | font-size: 5rem;
17 | margin-bottom: 1rem
18 | }
19 | img {
20 | width: 8rem;
21 | margin-bottom: 2rem
22 | }
23 |
24 | `;
25 |
26 | export default Loading
--------------------------------------------------------------------------------
/server/config/db.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const dotenv = require("dotenv");
3 | const { MONGO_URL } = require("./keys");
4 | dotenv.config();
5 | const connectDB = async () => {
6 | try {
7 | const conn = await mongoose.connect(MONGO_URL, {
8 | useNewUrlParser: true,
9 | useUnifiedTopology: true,
10 | });
11 |
12 | console.log(`MongoDB Connected: ${conn.connection.host}`.cyan.underline);
13 | } catch (error) {
14 | console.log(`Error: ${error.message}`.red.bold);
15 | process.exit();
16 | }
17 | };
18 |
19 | module.exports = connectDB;
20 |
--------------------------------------------------------------------------------
/client/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 |
--------------------------------------------------------------------------------
/client/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ["./src/**/*.{js,jsx,ts,tsx}"],
4 |
5 | theme: {
6 | extend: {
7 | colors: {
8 | primary: {
9 | 50: "#eff6ff",
10 | 100: "#dbeafe",
11 | 200: "#bfdbfe",
12 | 300: "#93c5fd",
13 | 400: "#60a5fa",
14 | 500: "#3b82f6",
15 | 600: "#2563eb",
16 | 700: "#1d4ed8",
17 | 800: "#1e40af",
18 | 900: "#1e3a8a",
19 | },
20 | lightblue:{
21 | 50:"#eff7fe"
22 | }
23 | },
24 | },
25 | },
26 | plugins: [],
27 | };
28 |
--------------------------------------------------------------------------------
/server/config/prod.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | MONGO_URL: process.env.MONGO_URL,
3 | JWT_SECRET: process.env.JWT_SECRET,
4 |
5 | CLOUDINARY_CLOUD_NAME: process.env.CLOUDINARY_CLOUD_NAME,
6 | CLOUDINARY_API_KEY: process.env.CLOUDINARY_API_KEY,
7 | CLOUDINARY_API_SECRET: process.env.CLOUDINARY_API_SECRET,
8 |
9 | SMPT_SERVICES: process.env.SMPT_SERVICES,
10 | SMPT_MAIL: process.env.SMPT_MAIL,
11 | SMPT_PASSWORD: process.env.SMPT_PASSWORD,
12 | SMPT_HOST: process.env.SMPT_HOST,
13 | SMPT_PORT: process.env.SMPT_PORT,
14 |
15 | SMPT_HOST: process.env.SMPT_HOST,
16 | SMPT_PORT: process.env.SMPT_PORT,
17 |
18 | PORT: process.env.PORT,
19 | CLIENT_ACCESS_URL: process.env.CLIENT_ACCESS_URL,
20 | };
21 |
--------------------------------------------------------------------------------
/client/public/social/facebook.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/routes/chatRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const {
3 | accessChat,
4 | fetchChats,
5 | createGroupChat,
6 | renameGroup,
7 | removeFromGroup,
8 | addToGroup,
9 | } = require("../controllers/chatControllers");
10 | const { protect } = require("../middleware/authMiddleware");
11 |
12 | const router = express.Router();
13 |
14 | router.route("/").post(protect, accessChat);
15 | router.route("/").get(protect, fetchChats);
16 | router.route("/group").post(protect, createGroupChat);
17 | router.route("/rename").put(protect,renameGroup);
18 | router.route("/groupremove").put(protect,removeFromGroup);
19 | router.route("/groupadd").put(protect,addToGroup);
20 |
21 | module.exports = router;
22 |
--------------------------------------------------------------------------------
/client/src/Components/Favourite.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | const Favourite = () => {
5 | return (
6 |
7 |
8 |
9 |
Favourites
10 |
11 |
12 |
13 |
14 |
This Feature will be available Soon
15 |
16 |
17 | );
18 | };
19 |
20 | const Wrapper = styled.div`
21 | animation: fadeInLeft 1s;
22 | `;
23 |
24 | export default Favourite;
25 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Theme/theme.reducer.js:
--------------------------------------------------------------------------------
1 | import { TOGGLE_DARKTHEME } from "./theme.type";
2 |
3 | const initialstate ={
4 | darkThemeEnabled: JSON.parse(localStorage.getItem("TOGGLE_DARKTHEME")) || false,
5 | }
6 |
7 | const themeReducer = (state = initialstate, action) => {
8 | switch (action.type) {
9 | case TOGGLE_DARKTHEME:
10 | state.darkThemeEnabled = !state.darkThemeEnabled
11 | const mode = localStorage.setItem("TOGGLE_DARKTHEME", JSON.stringify(state.darkThemeEnabled))
12 | return {
13 | ...state,
14 | ...mode
15 | };
16 |
17 | default:
18 | return state;
19 | }
20 | }
21 |
22 | export default themeReducer;
23 |
24 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/SetColor/setColorReducer.js:
--------------------------------------------------------------------------------
1 | import { set_color } from "./setColorType";
2 | import { colors } from "../../../config.js/data";
3 |
4 | const initialstate = {
5 | themeColor: JSON.parse(localStorage.getItem("set_color")) || colors[0].color
6 | }
7 |
8 | const setColorReducer = (state = initialstate, action) =>{
9 | switch (action.type) {
10 | case set_color:
11 | state.themeColor = action.payload;
12 | const mode = localStorage.setItem("set_color", JSON.stringify(state.themeColor))
13 | return {
14 | ...state,
15 | ...mode
16 | }
17 | default:
18 | return state;
19 | }
20 |
21 |
22 | }
23 |
24 | export default setColorReducer;
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/rootReducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 |
3 | // reducers or storage units
4 | import auth from "./Auth/auth.reducer";
5 | import user from "./User/user.reducer";
6 | import chat from "./Chat/chat.reducer";
7 | import message from "./Message/message.reducer";
8 | import profileImage from "./ProfileImage/profileImage.reducer";
9 | import themeReducer from "./Theme/theme.reducer";
10 | import tabReducer from "./Tab/tabReducer";
11 | import setColorReducer from "./SetColor/setColorReducer"
12 |
13 | const rootReducer = combineReducers({
14 | auth,
15 | user,
16 | profileImage,
17 | chat,
18 | message,
19 | themeReducer,
20 | tabReducer,
21 | setColorReducer
22 | });
23 |
24 | export default rootReducer;
25 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/ProfileImage/profileImage.action.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { UPLOAD_IMAGE } from "./profileImage.type";
3 | const SERVER_ACCESS_BASE_URL = process.env.REACT_APP_SERVER_ACCESS_BASE_URL;
4 |
5 | // profile picture update
6 | export const uploadProfilePicture = (image) => async (dispatch) => {
7 | try {
8 | const profileImage = await axios({
9 | method: "PUT",
10 | url: `${SERVER_ACCESS_BASE_URL}/api/user/profilepic`,
11 | data: { image },
12 | headers: { "Content-Type": "multipart/form-data" },
13 | });
14 | // console.log(profileImage.data);
15 | return dispatch({ type: UPLOAD_IMAGE, payload: image });
16 | } catch (error) {
17 | return dispatch({ type: "ERROR", payload: error });
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { BrowserRouter } from "react-router-dom";
4 | import "./index.css";
5 | import App from "./App";
6 |
7 | // Redux
8 | import { Provider } from "react-redux";
9 | import Store from "./Redux/Store";
10 | import axios from "axios";
11 |
12 | if (localStorage.ETalkUser) {
13 | const { token } = JSON.parse(localStorage.ETalkUser);
14 | axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
15 | }
16 |
17 | const root = ReactDOM.createRoot(document.getElementById("root"));
18 | root.render(
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 |
--------------------------------------------------------------------------------
/server/models/chatModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const chatModel = mongoose.Schema(
4 | {
5 | chatName: { type: String, trim: true },
6 | isGroupChat: { type: Boolean, default: false },
7 | users: [
8 | {
9 | type: mongoose.Schema.Types.ObjectId,
10 | ref: "User",
11 | },
12 | ],
13 | latestMessage: {
14 | type: mongoose.Schema.Types.ObjectId,
15 | ref: "Message",
16 | },
17 | groupAdmin: {
18 | type: mongoose.Schema.Types.ObjectId,
19 | ref: "User",
20 | },
21 | },
22 | {
23 | timestamps: true,
24 | }
25 | );
26 |
27 | const Chat = mongoose.model("Chat", chatModel);
28 |
29 | module.exports = Chat;
30 |
31 | // chatName
32 | // isGroup
33 | // users
34 | // latrestUser
35 | // groupAdmin
36 |
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500;600;700;800;900&display=swap');
2 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap');
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;
6 |
7 | body {
8 | margin: 0;
9 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
10 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
11 | sans-serif;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | overflow-x: hidden;
15 | background-color: #f7f7ff;
16 |
17 | }
18 |
19 | code {
20 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
21 | monospace;
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/client/public/social/twitter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/User/user.reducer.js:
--------------------------------------------------------------------------------
1 | import { SELF, CLEAR_USER, UPDATE_PROFILE, INVITE_FRIENDS } from "./user.type";
2 |
3 | const initialState = {
4 | user: {},
5 | };
6 |
7 | const userReducer = (state = initialState, action) => {
8 | switch (action.type) {
9 | case SELF:
10 | return {
11 | ...state,
12 | ...action.payload,
13 | };
14 | case CLEAR_USER:
15 | return {
16 | user: {},
17 | };
18 | case UPDATE_PROFILE:
19 | return {
20 | ...state,
21 | ...action.payload,
22 | };
23 | case INVITE_FRIENDS:
24 | return {
25 | ...state,
26 | InvitingStatus: { ...action.payload },
27 | };
28 | default:
29 | return {
30 | ...state,
31 | };
32 | }
33 | };
34 |
35 | export default userReducer;
36 |
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 | E-Talk
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/server/utils/sendEmail.js:
--------------------------------------------------------------------------------
1 | const nodeMailer = require("nodemailer");
2 | const {
3 | SMPT_HOST,
4 | SMPT_PORT,
5 | SMPT_SERVICES,
6 | SMPT_MAIL,
7 | SMPT_PASSWORD,
8 | } = require("../config/keys");
9 | const sendEmail = async (options) => {
10 | const transporter = nodeMailer.createTransport({
11 | host: SMPT_HOST,
12 | port: SMPT_PORT,
13 | service: SMPT_SERVICES,
14 | auth: {
15 | user: SMPT_MAIL,
16 | pass: SMPT_PASSWORD,
17 | },
18 | });
19 | const mailOptions = {
20 | from: SMPT_MAIL,
21 | to: options.email,
22 | subject: options.subject,
23 | html: options.message_Content,
24 | };
25 | const mailInfo = await transporter.sendMail(mailOptions, (error, result) => {
26 | if (error) {
27 | // console.log(error);
28 | }
29 | });
30 |
31 | // console.log(mailInfo);
32 | };
33 |
34 | module.exports = sendEmail;
35 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "dev": "nodemon index.js"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "bcryptjs": "^2.4.3",
14 | "cloudinary": "^1.34.0",
15 | "colors": "^1.4.0",
16 | "cors": "^2.8.5",
17 | "dotenv": "^16.0.3",
18 | "express": "^4.18.2",
19 | "express-async-handler": "^1.2.0",
20 | "helmet": "^6.0.1",
21 | "jsonwebtoken": "^8.5.1",
22 | "mongoose": "^6.7.2",
23 | "multer": "^1.4.5-lts.1",
24 | "nodemailer": "^6.9.1",
25 | "socket.io": "^4.6.1"
26 | },
27 | "devDependencies": {
28 | "@babel/cli": "^7.19.3",
29 | "@babel/core": "^7.20.2",
30 | "@babel/node": "^7.20.2",
31 | "@babel/preset-env": "^7.20.2",
32 | "nodemon": "^2.0.20"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/client/src/Components/Chat.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Route, Routes } from "react-router-dom";
3 | import styled from "styled-components";
4 | import ChatMenu from "./ChatMenu";
5 | import ChatWindow from "./ChatWindow";
6 | import SideMenu from "./SideMenu";
7 | import { ToastContainer} from "react-toastify";
8 | import NetworkError from "./modal/NetworkError";
9 | import { useSelector } from "react-redux";
10 | const Chat = () => {
11 |
12 | const isNetworkError = useSelector((globalstate)=> globalstate.message.NetworkError)
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 | };
25 | const Wrapper = styled.section`
26 | overflow: hidden;
27 | height: 100vh;
28 | transition: all 0.5s;
29 | `;
30 |
31 | export default Chat;
32 |
--------------------------------------------------------------------------------
/client/src/Components/ShowPasswordToggle.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useState } from "react";
3 | import { AiFillEye, AiOutlineEyeInvisible } from "react-icons/ai";
4 |
5 | const ShowPasswordToggle = () => {
6 | const [visible, setVisible] = useState(false);
7 | const InputType = visible ? "text" : "password";
8 |
9 | const Icon = (
10 |
11 |
12 | {visible ? (
13 | setVisible(false)}
16 | />
17 | ) : (
18 | setVisible(true)}
21 | />
22 | )}
23 |
24 |
25 | )
26 |
27 |
28 | return [Icon, InputType];
29 | };
30 |
31 | export default ShowPasswordToggle;
32 |
--------------------------------------------------------------------------------
/server/middleware/authMiddleware.js:
--------------------------------------------------------------------------------
1 | const jwt = require("jsonwebtoken");
2 | const User = require("../models/userModel.js");
3 | const asyncHandler = require("express-async-handler");
4 | const { JWT_SECRET } = require("../config/keys");
5 |
6 | const protect = asyncHandler(async (req, res, next) => {
7 | let token;
8 |
9 | if (
10 | req.headers.authorization &&
11 | req.headers.authorization.startsWith("Bearer")
12 | ) {
13 | try {
14 | token = req.headers.authorization.split(" ")[1];
15 |
16 | //decodes token id
17 | const decoded = jwt.verify(token, JWT_SECRET);
18 |
19 | req.user = await User.findById(decoded.id).select("-password");
20 |
21 | next();
22 | } catch (error) {
23 | res.status(401);
24 | throw new Error("Not authorized, token failed");
25 | }
26 | }
27 |
28 | if (!token) {
29 | res.status(401);
30 | throw new Error("Not authorized, no token");
31 | }
32 | });
33 |
34 | module.exports = { protect };
35 |
--------------------------------------------------------------------------------
/client/src/Styles/Spinner.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | const Spinner = () => {
5 | return (
6 |
7 |
11 |
12 | Loading...
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default Spinner;
20 |
21 | const Wrapper = styled.div`
22 | width: 100%;
23 | height: 100%;
24 | display: flex;
25 | justify-content: center;
26 | div {
27 | width: 8rem;
28 | height: 8rem;
29 | color: ${({ theme }) => theme.colors.primaryRgb};
30 | }
31 | `
32 |
--------------------------------------------------------------------------------
/client/src/Styles/Social.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { FcGoogle } from "react-icons/fc";
3 | import styled from "styled-components";
4 | import { Button } from "./Button";
5 |
6 | const Social = () => {
7 | return (
8 |
9 |
10 | or
11 |
Sign in With
12 |
13 |
14 |
15 |
23 |
24 |
25 |
26 | );
27 | };
28 |
29 | const Wrapper = styled.section`
30 | `;
31 |
32 | export default Social;
33 |
--------------------------------------------------------------------------------
/client/src/Components/Welcome.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Contact from "./Contact";
3 | import Features from "./Features";
4 | import Team from "./Team";
5 | import Footer from "./Footer";
6 |
7 | import Header from "./Header";
8 | import HeroSection from "./HeroSection";
9 | import Technologies from "./Technologies";
10 | import ScrollToTopButton from "./ScrollToTopButton";
11 | import styled from "styled-components";
12 |
13 |
14 |
15 | const Welcome = () => {
16 |
17 | return (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default Welcome;
32 |
33 | const Wrapper = styled.section`
34 | .top-btn{
35 | background-color: #3180fc;
36 | color: white;
37 | width: 4rem;
38 | height: 4rem;
39 | font-size: 2.4rem;
40 | padding: 0.25rem;
41 | }
42 | `
43 |
44 |
--------------------------------------------------------------------------------
/client/src/Components/ErrorPage.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { Button } from "../Styles/Button";
4 | import { NavLink } from "react-router-dom";
5 |
6 | const ErrorPage = () => {
7 | return (
8 | <>
9 |
10 |
11 |
404
12 |
Page Not Found
13 |
14 |
17 |
18 |
19 |
20 | >
21 | );
22 | };
23 |
24 | export default ErrorPage;
25 |
26 | const Wrapper = styled.section`
27 | width: 100vw;
28 | height: 100vh;
29 | background-color: ${({ theme }) => theme.colors.bg.secondary};
30 |
31 | h1 {
32 | font-size: 10rem;
33 | }
34 | p {
35 | font-size: 2rem;
36 | color: ${({ theme }) => theme.colors.text.secondary};;
37 | }
38 | .button {
39 | width: auto;
40 | height: auto;
41 | font-size: 1.5rem;
42 | }
43 | `;
44 |
45 |
46 |
--------------------------------------------------------------------------------
/client/src/Components/Toggler.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useState } from "react";
3 | import { RiMoonLine, RiSunLine } from "react-icons/ri";
4 | import { useDispatch, useSelector } from "react-redux";
5 | import styled from "styled-components";
6 | import { TOGGLE_DARKTHEME } from "../Redux/Reducer/Theme/theme.type";
7 |
8 | const Toggler = (props) => {
9 | const darkThemeEnabled = useSelector(
10 | (state) => state.themeReducer.darkThemeEnabled
11 | );
12 | const [toggle, setToggle] = useState(darkThemeEnabled);
13 | const dispatch = useDispatch();
14 |
15 | const togglerTheme = () => {
16 | dispatch({ type: TOGGLE_DARKTHEME });
17 | setToggle(!darkThemeEnabled);
18 | };
19 | return (
20 |
21 | {toggle === false ? (
22 | props.setMenuIcon(false)} className="icon" />
23 | ) : (
24 | props.setMenuIcon(false)} className="icon" />
25 | )}
26 |
27 | );
28 | };
29 |
30 | const Wrapper = styled.div`
31 | display: block;
32 | justify-content: center;
33 | align-items: center;
34 | text-align: center;
35 | border-radius: 8px;
36 | cursor: pointer;
37 | `;
38 |
39 | export default Toggler;
40 |
--------------------------------------------------------------------------------
/server/models/userModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const bcrypt = require("bcryptjs");
3 |
4 | const userSchema = mongoose.Schema(
5 | {
6 | name: { type: String, required: true },
7 | email: { type: String, required: true, unique: true },
8 | password: { type: String, required: true, selected: false },
9 | about: { type: String, default: "Hey there! I am using E-Talk" },
10 | contact: { type: Number, required: true },
11 | pic: {
12 | type: String,
13 | default:
14 | "https://static.vecteezy.com/system/resources/thumbnails/002/002/341/small_2x/man-wearing-sunglasses-avatar-character-isolated-icon-free-vector.jpg",
15 | //default Image link : https://icon-library.com/images/anonymous-avatar-icon/anonymous-avatar-icon-25.jpg
16 | },
17 | cloudinary_id: { type: String },
18 | is_verified: { type: Boolean, default: false },
19 | },
20 | { timestamps: true }
21 | );
22 |
23 | userSchema.methods.matchPassword = async function (enteredPassword) {
24 | return await bcrypt.compare(enteredPassword, this.password);
25 | };
26 |
27 | userSchema.pre("save", async function (next) {
28 | if (!this.isModified) {
29 | next();
30 | }
31 |
32 | const salt = await bcrypt.genSalt(10);
33 | this.password = await bcrypt.hash(this.password, salt);
34 | });
35 |
36 | const User = mongoose.model("User", userSchema);
37 |
38 | module.exports = User;
39 |
--------------------------------------------------------------------------------
/server/routes/userRoutes.js:
--------------------------------------------------------------------------------
1 | const { Router } = require("express");
2 | const express = require("express");
3 | // const upload = require("../utils/multer");
4 | const multer = require("multer");
5 | const {
6 | registerUser,
7 | authUser,
8 | allUsers,
9 | getmyself,
10 | uploadProfileImage,
11 | verifyEmail,
12 | resendVerificationLink,
13 | forgotPassword,
14 | updateProfile,
15 | resetPassword,
16 | invitingUser,
17 | } = require("../controllers/userControllers");
18 | const { protect } = require("../middleware/authMiddleware");
19 |
20 | const router = express.Router();
21 |
22 | // // multer configuration
23 | // const storage = multer.memoryStorage();
24 | const storage = multer.diskStorage({});
25 | const upload = multer({ storage });
26 |
27 | router.route("/").post(registerUser).get(protect, allUsers);
28 | router.route("/getmyself").get(protect, getmyself);
29 | router.route("/login").post(authUser);
30 | router.route("/resend/verificationlink").post(resendVerificationLink);
31 | router.route("/verify").put(verifyEmail);
32 | router.route("/forgotpassword").post(forgotPassword);
33 | router.route("/resetpassword").post(resetPassword);
34 | router.route("/updateprofile").put(protect, updateProfile);
35 | router.route("/invitefriends").post(protect, invitingUser);
36 | router
37 | .route("/profilepic")
38 | .put(protect, upload.single("image"), uploadProfileImage);
39 | module.exports = router;
40 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Message/message.reducer.js:
--------------------------------------------------------------------------------
1 | import {} from "./message.action";
2 | import {
3 | CLEAR_ALL_MESSAGE,
4 | GET_ALL_MESSAGE,
5 | SEND_MESSAGE,
6 | SHOW_NETWORK_ERROR,
7 | SHOW_TOOGLE_LOADING,
8 | UPDATE_GET_ALL_MESSAGE,
9 | } from "./message.type";
10 | const initialState = {
11 | allMessages: [],
12 | createdMessage: {},
13 | isLoading: false,
14 | sNetworkError: false,
15 | };
16 |
17 | const messageReducer = (state = initialState, action) => {
18 | switch (action.type) {
19 | case GET_ALL_MESSAGE:
20 | return {
21 | ...state,
22 | allMessages: action.payload,
23 |
24 | };
25 |
26 | case SEND_MESSAGE:
27 | return {
28 | ...state,
29 | createdMessage: action.payload,
30 |
31 | };
32 |
33 | case UPDATE_GET_ALL_MESSAGE:
34 | return {
35 | ...state,
36 | allMessages: [...state.allMessages, action.payload],
37 |
38 | };
39 |
40 | case CLEAR_ALL_MESSAGE:
41 | return {
42 | ...state,
43 | allMessages: [],
44 | };
45 |
46 | case SHOW_TOOGLE_LOADING:
47 | return{
48 | ...state,
49 | isLoading: action.payload
50 | }
51 | case SHOW_NETWORK_ERROR:
52 | return{
53 | ...state,
54 | isNetworkError: action.payload
55 | }
56 |
57 | default:
58 | return {
59 | ...state,
60 | };
61 | }
62 | };
63 |
64 | export default messageReducer;
65 |
--------------------------------------------------------------------------------
/server/dist/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var _cors = _interopRequireDefault(require("cors"));
4 | var _helmet = _interopRequireDefault(require("helmet"));
5 | function _interopRequireDefault(obj) {
6 | return obj && obj.__esModule ? obj : { default: obj };
7 | }
8 | const express = require("express");
9 | const dotenv = require("dotenv");
10 | const connectDB = require("./config/db");
11 | const { chats } = require("./data/data");
12 | const colors = require("colors");
13 | const chatRoutes = require("./routes/chatRoutes");
14 | const userRoutes = require("./routes/userRoutes");
15 | const messageRoutes = require("./routes/messageRoutes");
16 | const { notFound, errorHandler } = require("./middleware/errorMiddleware");
17 | dotenv.config();
18 | const app = express();
19 | connectDB();
20 | app.use(
21 | (0, _cors.default)({
22 | origin: "http://localhost:3000",
23 | })
24 | );
25 | app.use((0, _helmet.default)());
26 | app.use(express.json()); //to accept json data
27 |
28 | app.use(express.json());
29 | app.get("/", (req, res) => {
30 | res.json({
31 | message: "Welcome to E-Talk Server",
32 | });
33 | });
34 | app.get("/api/chat", (req, res) => {
35 | res.send(_data.chats);
36 | });
37 | app.get("/api/chat/:id", (req, res) => {
38 | // console.log(req.params.id);
39 | const singleChat = _data.chats.find((c) => c._id === req.params.id);
40 | res.send(singleChat);
41 | });
42 | const PORT = process.env.PORT || 4000;
43 | app.listen(PORT, () => {
44 | console.log(`Server is Running on PORT: http://localhost:${PORT}`);
45 | });
46 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Auth/auth.reducer.js:
--------------------------------------------------------------------------------
1 | // action Type
2 |
3 | import {
4 | SIGN_IN,
5 | SIGN_UP,
6 | SIGN_OUT,
7 | USER_VERIFICATION,
8 | VERIFY_TOKEN,
9 | ERROR,
10 | FORGOT_PASSWORD,
11 | RESET_PASSWORD,
12 | CLEAR_AUTH_STORE,
13 | } from "./auth.type";
14 |
15 | const intialState = {};
16 |
17 | const authReducer = (state = intialState, action) => {
18 | switch (action.type) {
19 | case SIGN_IN:
20 | return {
21 | ...state,
22 | ...action.payload,
23 | };
24 | case SIGN_UP:
25 | return {
26 | ...state,
27 | ...action.payload,
28 | };
29 |
30 | case SIGN_OUT:
31 | return {
32 | ...state,
33 | };
34 |
35 | case USER_VERIFICATION:
36 | return {
37 | ...state,
38 | ...action.payload,
39 | };
40 |
41 | case VERIFY_TOKEN:
42 | return {
43 | ...state,
44 | ...action.payload,
45 | };
46 |
47 | case FORGOT_PASSWORD:
48 | return {
49 | ...state,
50 | ...action.payload,
51 | };
52 |
53 | case RESET_PASSWORD:
54 | return {
55 | ...state,
56 | ...action.payload,
57 | };
58 |
59 | case CLEAR_AUTH_STORE:
60 | return {
61 |
62 | ...action.payload
63 |
64 | };
65 | case ERROR:
66 | return {
67 | ...state,
68 | ...action.payload,
69 | };
70 | default:
71 | return {
72 | ...state,
73 | };
74 | }
75 | };
76 |
77 | export default authReducer;
78 |
--------------------------------------------------------------------------------
/client/src/Components/Searchbar.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { BiSearch } from "react-icons/bi";
3 | import { RxCross1 } from "react-icons/rx";
4 |
5 | const Searchbar = ({ searchOpen, setSearchOpen, setQuary }) => {
6 |
7 | const searchUser = (e) =>{
8 | setQuary(e.target.value)
9 | }
10 |
11 | const handleChange = (e) => {
12 | setSearchOpen((prev) => !prev);
13 | setQuary("");
14 | };
15 |
16 | return (
17 | <>
18 | {searchOpen ? (
19 | <>
20 |
21 |
22 |
26 |
27 |
searchUser(e)}
32 | />
33 |
34 |
35 |
36 |
37 | >
38 | ) : (
39 | <>
40 |
41 | handleChange(e)} />
42 |
43 | >
44 | )}
45 | >
46 | );
47 | };
48 |
49 | export default Searchbar;
50 |
51 |
52 |
--------------------------------------------------------------------------------
/client/src/Components/modal/NetworkError.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Detector} from "react-detect-offline"
3 | import styled from 'styled-components';
4 |
5 | const NetworkError = (props) => {
6 |
7 | // const Refresh = () =>{
8 | // window.location.reload();
9 | // }
10 | return (
11 | <>
12 | (
14 | online ? props.children :
15 |
16 |
17 |
18 |
19 |
Whoops!
20 |
21 |
22 |
There seems to be a connection with your network connection
23 |
24 | {/*
Refresh()}>
25 | Try Again
26 |
*/}
27 |
28 |
29 |
30 | )}
31 | />
32 | >
33 | )
34 | }
35 |
36 | export default NetworkError;
37 |
38 | const Wrapper = styled.div`
39 | .container{
40 | background-color: ${({ theme }) => theme.colors.bg.primary};
41 | }
42 | .wrapper{
43 | background-color: ${({ theme }) => theme.colors.bg.secondary};
44 | box-shadow: 0 0 20px rgba(0,0,0,0.1)
45 | }
46 | .title.h1{
47 | color: ${({ theme }) => theme.colors.heading};
48 | }
49 | `
--------------------------------------------------------------------------------
/client/src/Components/Loading1.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Loading1 = () => {
4 | return (
5 | <>
6 |
7 |
23 |
Loading...
24 |
25 | >
26 | );
27 | };
28 |
29 | export default Loading1;
30 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/User/user.action.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { CLEAR_USER, INVITE_FRIENDS, SELF, UPDATE_PROFILE } from "./user.type";
3 | const SERVER_ACCESS_BASE_URL = process.env.REACT_APP_SERVER_ACCESS_BASE_URL;
4 | export const getMySelf = () => async (dispatch) => {
5 | try {
6 | const User = await axios({
7 | method: "GET",
8 | url: `${SERVER_ACCESS_BASE_URL}/api/user/getmyself`,
9 | });
10 | return dispatch({ type: SELF, payload: { ...User.data.user } });
11 | } catch (error) {
12 | return dispatch({ type: "ERROR", payload: error });
13 | }
14 | };
15 |
16 | // updating user profile
17 | export const updateUserProfile = (userData) => async (dispatch) => {
18 | try {
19 | const User = await axios({
20 | method: "PUT",
21 | url: `${SERVER_ACCESS_BASE_URL}/api/user/updateprofile`,
22 | data: { ...userData },
23 | });
24 | return dispatch({ type: UPDATE_PROFILE, payload: User.data });
25 | } catch (error) {
26 | return dispatch({ type: "ERROR", payload: error });
27 | }
28 | };
29 | // Inviting User
30 | export const inviteNewUser = (email) => async (dispatch) => {
31 | try {
32 | const res = await axios({
33 | method: "POST",
34 | url: `${SERVER_ACCESS_BASE_URL}/api/user/invitefriends`,
35 | data: { email },
36 | });
37 | return dispatch({ type: INVITE_FRIENDS, payload: res.data });
38 | } catch (error) {
39 | return dispatch({ type: "ERROR", payload: error });
40 | }
41 | };
42 |
43 | export const clearUser = () => async (dispatch) => {
44 | try {
45 | return dispatch({ type: CLEAR_USER, payload: {} });
46 | } catch (error) {
47 | return dispatch({ type: "ERROR", payload: error });
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/client/src/Components/ScrollToTopButton.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import styled from "styled-components";
3 | import { CgChevronDoubleUp } from "react-icons/cg";
4 |
5 | const ScrollToTopButton = () => {
6 | const [visible, setVisible] = useState(false);
7 |
8 | function scrollToTop() {
9 | window.scrollTo({
10 | top: 0,
11 | behavior: "smooth",
12 | });
13 | }
14 | const listenToScroll = () => {
15 | let heightToHidden = 250;
16 | const winScroll =
17 | document.body.scrollTop || document.documentElement.scrollTop;
18 |
19 | if (winScroll > heightToHidden) {
20 | setVisible(true);
21 | } else {
22 | setVisible(false);
23 | }
24 | };
25 | useEffect(() => {
26 | window.addEventListener("scroll", listenToScroll);
27 | return () => window.removeEventListener("scroll", listenToScroll);
28 | }, []);
29 |
30 | return (
31 | <>
32 | {visible && (
33 |
34 |
35 |
36 |
37 |
38 | )}
39 | >
40 | );
41 | };
42 |
43 | export default ScrollToTopButton;
44 |
45 | const Wrapper = styled.div`
46 | .top-btn {
47 | position: fixed;
48 | display: flex;
49 | justify-content: center;
50 | align-items: center;
51 | bottom: 2rem;
52 | right: 2rem;
53 | cursor: pointer;
54 | padding: 0.25rem;
55 | z-index: 999;
56 | border-radius: 50%;
57 | box-shadow: 0px 0px 24px ${({ theme }) => theme.colors.boxShadow.primary};
58 |
59 | .up-icon {
60 | transition: transform 0.5s;
61 | &:hover {
62 | transform: translateY(-2px);
63 | }
64 | }
65 | }
66 | `;
67 |
--------------------------------------------------------------------------------
/server/controllers/messageControllers.js:
--------------------------------------------------------------------------------
1 | const asyncHandler = require("express-async-handler");
2 | const Message = require("../models/messageModel");
3 | const User = require("../models/userModel");
4 | const Chat = require("../models/chatModel");
5 |
6 | //@description Get all Messages
7 | //@route GET /api/Message/:chatId
8 | //@access Protected
9 | const allMessages = asyncHandler(async (req, res) => {
10 | try {
11 | const messages = await Message.find({ chat: req.params.chatId })
12 | .populate("sender", "name pic email")
13 | .populate("chat");
14 | res.json(messages);
15 | } catch (error) {
16 | 0;
17 | res.status(400);
18 | throw new Error(error.message);
19 | }
20 | });
21 |
22 | //@description Create New Message
23 | //@route POST /api/Message/
24 | //@access Protected
25 | const sendMessage = asyncHandler(async (req, res) => {
26 | const { content, chatId } = req.body;
27 |
28 | if (!content || !chatId) {
29 | // console.log("Invalid data passed into request");
30 | return res.sendStatus(400);
31 | }
32 |
33 | var newMessage = {
34 | sender: req.user._id,
35 | content: content,
36 | chat: chatId,
37 | };
38 |
39 | try {
40 | var message = await Message.create(newMessage);
41 |
42 | message = await message.populate("sender", "name pic");
43 | message = await message.populate("chat");
44 | message = await User.populate(message, {
45 | path: "chat.users",
46 | select: "name pic email",
47 | });
48 |
49 | await Chat.findByIdAndUpdate(chatId, {
50 | latestMessage: message,
51 | });
52 |
53 | res.json(message);
54 | } catch (error) {
55 | res.status(400);
56 | throw new Error(error.message);
57 | }
58 | });
59 |
60 | module.exports = { allMessages, sendMessage };
61 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Chat/chat.reducer.js:
--------------------------------------------------------------------------------
1 | // import { fetchUser } from "./chat.action";
2 | import {
3 | CLEAR_SELECT_CHAT,
4 | CREATE_CHAT,
5 | CREATE_GROUP_CHAT,
6 | FETCH_CHATS,
7 | FETCH_USER,
8 | FETCH_USER_CLEAR,
9 | REMOVE_USER_FROM_GROUP,
10 | SELECT_CHAT,
11 | SHOW_USER_LOADING,
12 | } from "./chat.type";
13 | const initialState = {
14 | chats: [],
15 | newUser: [],
16 | createdChat: {},
17 | createdGroupChat: {},
18 | selectedChat: {},
19 | isUserLoading: false,
20 | removedUserFromGroup: {},
21 | };
22 |
23 | const chatReducer = (state = initialState, action) => {
24 | switch (action.type) {
25 | case FETCH_CHATS:
26 | return {
27 | ...state,
28 | chats: action.payload,
29 | };
30 |
31 | case FETCH_USER:
32 | return {
33 | ...state,
34 | newUser: action.payload,
35 | };
36 |
37 | case FETCH_USER_CLEAR:
38 | return {
39 | ...state,
40 | newUser: [],
41 | };
42 |
43 | case CREATE_CHAT:
44 | return {
45 | ...state,
46 | createdChat: action.payload,
47 | };
48 |
49 | case CREATE_GROUP_CHAT:
50 | return {
51 | ...state,
52 | createdGroupChat: action.payload,
53 | };
54 |
55 | case REMOVE_USER_FROM_GROUP:
56 | return {
57 | ...state,
58 | selectedChat: action.payload,
59 | };
60 |
61 | case SELECT_CHAT:
62 | return {
63 | ...state,
64 | selectedChat: action.payload,
65 | };
66 | case CLEAR_SELECT_CHAT:
67 | return {
68 | ...state,
69 | selectedChat: action.payload,
70 | };
71 | case SHOW_USER_LOADING:
72 | return {
73 | ...state,
74 | isUserLoading: action.payload,
75 | };
76 |
77 | default:
78 | return {
79 | ...state,
80 | };
81 | }
82 | };
83 |
84 | export default chatReducer;
85 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@emoji-mart/data": "^1.1.2",
7 | "@emoji-mart/react": "^1.1.1",
8 | "@emotion/react": "^11.10.6",
9 | "@headlessui/react": "^1.7.7",
10 | "@testing-library/jest-dom": "^5.16.5",
11 | "@testing-library/react": "^13.4.0",
12 | "@testing-library/user-event": "^13.5.0",
13 | "aos": "^2.3.4",
14 | "axios": "^1.1.3",
15 | "emoji-mart": "^5.5.2",
16 | "framer-motion": "^10.0.1",
17 | "moment": "^2.29.4",
18 | "react": "^18.2.0",
19 | "react-detect-offline": "^2.4.5",
20 | "react-dom": "^18.2.0",
21 | "react-highlight-words": "^0.20.0",
22 | "react-icons": "^4.7.1",
23 | "react-intersection-observer": "^9.4.3",
24 | "react-redux": "^8.0.5",
25 | "react-router-dom": "^6.4.4",
26 | "react-scripts": "5.0.1",
27 | "react-scroll": "^1.8.9",
28 | "react-toastify": "^9.1.1",
29 | "react-tooltip": "^5.3.4",
30 | "react-wavify": "^1.7.0",
31 | "redux": "^4.2.0",
32 | "redux-logger": "^3.0.6",
33 | "redux-thunk": "^2.4.2",
34 | "socket.io-client": "^4.6.1",
35 | "styled-components": "^5.3.6",
36 | "swiper": "^9.1.0",
37 | "web-vitals": "^2.1.4"
38 | },
39 | "scripts": {
40 | "start": "react-scripts start",
41 | "build": "react-scripts build",
42 | "test": "react-scripts test",
43 | "eject": "react-scripts eject"
44 | },
45 | "proxy": "https://e-talk-server.onrender.com",
46 | "eslintConfig": {
47 | "extends": [
48 | "react-app",
49 | "react-app/jest"
50 | ]
51 | },
52 | "browserslist": {
53 | "production": [
54 | ">0.2%",
55 | "not dead",
56 | "not op_mini all"
57 | ],
58 | "development": [
59 | "last 1 chrome version",
60 | "last 1 firefox version",
61 | "last 1 safari version"
62 | ]
63 | },
64 | "devDependencies": {
65 | "autoprefixer": "^10.4.13",
66 | "postcss": "^8.4.19",
67 | "tailwindcss": "^3.2.4"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/server/data/data.js:
--------------------------------------------------------------------------------
1 | const chats = [
2 | {
3 | isGroupChat: false,
4 | users: [
5 | {
6 | name: "John Doe",
7 | email: "john@example.com",
8 | },
9 | {
10 | name: "Piyush",
11 | email: "piyush@example.com",
12 | },
13 | ],
14 | _id: "617a077e18c25468bc7c4dd4",
15 | chatName: "John Doe",
16 | },
17 | {
18 | isGroupChat: false,
19 | users: [
20 | {
21 | name: "Guest User",
22 | email: "guest@example.com",
23 | },
24 | {
25 | name: "Piyush",
26 | email: "piyush@example.com",
27 | },
28 | ],
29 | _id: "617a077e18c25468b27c4dd4",
30 | chatName: "Guest User",
31 | },
32 | {
33 | isGroupChat: false,
34 | users: [
35 | {
36 | name: "Anthony",
37 | email: "anthony@example.com",
38 | },
39 | {
40 | name: "Piyush",
41 | email: "piyush@example.com",
42 | },
43 | ],
44 | _id: "617a077e18c2d468bc7c4dd4",
45 | chatName: "Anthony",
46 | },
47 | {
48 | isGroupChat: true,
49 | users: [
50 | {
51 | name: "John Doe",
52 | email: "jon@example.com",
53 | },
54 | {
55 | name: "Piyush",
56 | email: "piyush@example.com",
57 | },
58 | {
59 | name: "Guest User",
60 | email: "guest@example.com",
61 | },
62 | ],
63 | _id: "617a518c4081150716472c78",
64 | chatName: "Friends",
65 | groupAdmin: {
66 | name: "Guest User",
67 | email: "guest@example.com",
68 | },
69 | },
70 | {
71 | isGroupChat: false,
72 | users: [
73 | {
74 | name: "Jane Doe",
75 | email: "jane@example.com",
76 | },
77 | {
78 | name: "Piyush",
79 | email: "piyush@example.com",
80 | },
81 | ],
82 | _id: "617a077e18c25468bc7cfdd4",
83 | chatName: "Jane Doe",
84 | },
85 | {
86 | isGroupChat: true,
87 | users: [
88 | {
89 | name: "John Doe",
90 | email: "jon@example.com",
91 | },
92 | {
93 | name: "Piyush",
94 | email: "piyush@example.com",
95 | },
96 | {
97 | name: "Guest User",
98 | email: "guest@example.com",
99 | },
100 | ],
101 | _id: "617a518c4081150016472c78",
102 | chatName: "Chill Zone",
103 | groupAdmin: {
104 | name: "Guest User",
105 | email: "guest@example.com",
106 | },
107 | },
108 | ];
109 |
110 | module.exports = { chats };
111 |
--------------------------------------------------------------------------------
/client/src/Components/Header.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { NavLink } from "react-router-dom";
3 | import styled from "styled-components";
4 | import Nav from './Nav';
5 |
6 | const Header = () => {
7 | const [header, setHeader] = useState(false);
8 | const changeBackground = () =>{
9 | if(window.scrollY >= 80){
10 | setHeader(true);
11 | }
12 | else{
13 | setHeader(false)
14 | }
15 | }
16 |
17 | window.addEventListener('scroll', changeBackground);
18 |
19 | return (
20 |
21 |
22 |
23 |
24 |

25 |
26 |
27 |
28 |
29 |
30 |
31 | )
32 | }
33 |
34 | const MainHeader = styled.header`
35 | position: fixed;
36 | z-index: 99;
37 | transition: 0.5s;
38 | .header{
39 | padding: 0 1.5rem;
40 | height: 100px;
41 | }
42 |
43 | .header.active{
44 | background-color: ${({ theme }) => theme.colors.bg2.primary};
45 | box-shadow: 0 0 20px ${({ theme }) => theme.colors.boxShadow.primary};
46 |
47 | .navbar-lists .navbar-link{
48 | color: ${({ theme }) => theme.colors.heading};
49 | &:hover,
50 | &:active {
51 | border-bottom: 2px solid ${({ theme }) => theme.colors.cyan};;
52 | color: ${({ theme }) => theme.colors.cyan};
53 | }
54 | .button{
55 | color: ${({ theme }) => theme.colors.heading};
56 | border: solid 2px ${({ theme }) => theme.colors.heading};
57 | &:hover{
58 | color: ${({ theme }) => theme.colors.cyan};
59 | border: solid 2px ${({ theme }) => theme.colors.cyan};
60 | }
61 | }
62 | }
63 | }
64 |
65 | .hero-section-logo{
66 | width: 100%
67 | }
68 | .logo {
69 | height: 4rem;
70 | }
71 |
72 | @media (max-width: 1138px) {
73 | .header{
74 | .navbar-lists .navbar-link{
75 | color: ${({ theme }) => theme.colors.heading};
76 | .button{
77 | color: ${({ theme }) => theme.colors.heading};
78 | border-color: ${({ theme }) => theme.colors.heading};
79 | }
80 |
81 | }
82 | background-color: ${({ theme }) => theme.colors.bg2.primary};
83 | }
84 | }
85 |
86 | `;
87 |
88 | export default Header
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Message/message.action.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import {
3 | SEND_MESSAGE,
4 | GET_ALL_MESSAGE,
5 | UPDATE_GET_ALL_MESSAGE,
6 | SHOW_TOOGLE_LOADING,
7 | SHOW_NETWORK_ERROR,
8 | } from "./message.type";
9 |
10 | const SERVER_ACCESS_BASE_URL = process.env.REACT_APP_SERVER_ACCESS_BASE_URL;
11 |
12 | // get all messages
13 | export const getAllChats = (selectedChat) => async (dispatch) => {
14 | try {
15 | dispatch(loadingToggleAction(true));
16 | const allMessage = await axios({
17 | method: "GET",
18 | url: `${SERVER_ACCESS_BASE_URL}/api/message/${selectedChat._id}`,
19 | });
20 | dispatch(loadingToggleAction(false));
21 | // console.log(allMessage);
22 | return dispatch({ type: GET_ALL_MESSAGE, payload: allMessage.data });
23 | } catch (error) {
24 | dispatch(showNetworkError(true));
25 | return dispatch({ type: "ERROR", payload: error });
26 | }
27 | // }
28 | };
29 |
30 | // updateing get all message
31 | export const updateGetAllChats = (messageRecived) => async (dispatch) => {
32 | try {
33 | // console.log(messageRecived);
34 | if (!messageRecived.sender) {
35 | return;
36 | }
37 | const updatedAllMessage = messageRecived;
38 | return dispatch({
39 | type: UPDATE_GET_ALL_MESSAGE,
40 | payload: updatedAllMessage,
41 | });
42 | } catch (error) {
43 | dispatch(showNetworkError(true));
44 | return dispatch({ type: "ERROR", payload: error });
45 | }
46 | };
47 |
48 | // send message
49 | export const sendMessge = (messageData) => async (dispatch) => {
50 | try {
51 | const newMessage = await axios({
52 | method: "POSt",
53 | url: `${SERVER_ACCESS_BASE_URL}/api/message`,
54 | data: { ...messageData },
55 | });
56 |
57 | return dispatch({ type: SEND_MESSAGE, payload: newMessage.data });
58 | } catch (error) {
59 | dispatch(showNetworkError(true));
60 | return dispatch({ type: "ERROR", payload: error });
61 | }
62 | };
63 |
64 | // clear all message
65 | export const clearSelectedMessage = () => async (dispatch) => {
66 | try {
67 | return dispatch({
68 | type: "CLEAR_ALL_MESSAGE",
69 | payload: "",
70 | });
71 | } catch (error) {
72 | dispatch(showNetworkError(true));
73 | return dispatch({ type: "ERROR", payload: error });
74 | }
75 | };
76 |
77 | export const loadingToggleAction = (state) => {
78 | return {
79 | type: SHOW_TOOGLE_LOADING,
80 | payload: state,
81 | };
82 | };
83 |
84 | export const showNetworkError = (state) => {
85 | return {
86 | type: SHOW_NETWORK_ERROR,
87 | payload: state,
88 | };
89 | };
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # SimpleTalk
3 |
4 | E-Talk is a basic chat Application that is build using the MERN Stack stands for MongoDB, ExpressJS, ReactJS, NodeJS.
5 | It was built with JavaScript + TailWind CSS + Socket.io.
6 |
7 | ## Live Website
8 |
9 | ## Features
10 |
11 | - Sign Up
12 | - Sign In
13 | - Email Verification
14 | - Forgot Password
15 | - Reset Password
16 | - Dark/Light mode
17 | - one-on-one
18 | - Group Chat
19 | - Group Creation with min 3 users
20 | - Chat Theme
21 | - Update Profile Image
22 | - Update Profile Details
23 | - View others profile
24 | - Fully Responsive
25 |
26 | ## Tech Stack
27 |
28 | **Client:** React, Redux, TailwindCSS
29 |
30 | **Server:** Node, Express
31 |
32 | **Deployment:** Vercel(Frontend), Backend(Render)
33 |
34 | ## Installation Guide
35 |
36 | To Run E-Talk project on local system follow the simple steps:
37 |
38 | ### Step-1
39 |
40 | clone this project on your local system
41 |
42 | ### Step-2 Installing Dependency
43 |
44 | Installing Dependency for client and Server both
45 |
46 | ```bash
47 | cd E-Talk
48 | ```
49 |
50 | To Installing Dependency for client
51 |
52 | ```bash
53 | cd client
54 | npm i
55 | ```
56 |
57 | To Installing Dependency for server
58 |
59 | ```bash
60 | cd server
61 | npm i
62 | ```
63 |
64 | ### Step-3 Adding Environment Variables
65 |
66 | To run this project, you will need to add the following environment variables to your .env file
67 |
68 | ### Environment Variables for Client
69 |
70 | `REACT_APP_SERVER_ACCESS_BASE_URL`
71 |
72 | ### Environment Variables for Server
73 |
74 | `MONGO_URL`
75 |
76 | `JWT_SECRET`
77 |
78 | `CLOUDINARY_CLOUD_NAME`
79 |
80 | `CLOUDINARY_API_KEY`
81 |
82 | `CLOUDINARY_API_SECRET`
83 |
84 | `SMPT_SERVICES`
85 |
86 | `SMPT_MAIL`
87 |
88 | `SMPT_PASSWORD`
89 |
90 | `SMPT_HOST`
91 |
92 | `SMPT_PORT`
93 |
94 | `CLIENT_ACCESS_URL`
95 |
96 | ### Step-4 Start the Application on local machine
97 |
98 | #### To Start Frontend Server(or client):
99 |
100 | Move into client Directory by
101 |
102 | ```bash
103 | cd client
104 | ```
105 |
106 | start the Frontend server by
107 |
108 | ```bash
109 | npm start
110 | ```
111 |
112 | after ruunning this command, It will start after some time.
113 |
114 | #### To Start Backend Server(or server):
115 |
116 | Move into server Directory by
117 |
118 | ```bash
119 | cd server
120 | ```
121 |
122 | start the Backend server by
123 |
124 | ```bash
125 | npm start
126 | ```
127 |
128 | To start the server automatic after every changes we have to run this command :
129 |
130 | ```bash
131 | npm run dev
132 | ```
133 |
134 | after starting the both Frontend and Backend server you can access application on the browser.
135 |
--------------------------------------------------------------------------------
/client/src/Pages/HomePage.js:
--------------------------------------------------------------------------------
1 | import React, { Suspense, useEffect, useState } from "react";
2 |
3 | import DefaultLayoutHoc from "../Layout/DefaultLayout";
4 | // redux
5 | import { useDispatch, useSelector } from "react-redux";
6 | import { useNavigate } from "react-router-dom";
7 | import { getMySelf } from "../Redux/Reducer/User/user.action";
8 | import { fetchChats } from "../Redux/Reducer/Chat/chat.action";
9 | import Loading from "../Components/Loading";
10 | import { clearAuthStore } from "../Redux/Reducer/Auth/auth.action";
11 | const Welcome = React.lazy(() => import("../Components/Welcome"));
12 | const Chat = React.lazy(() => import("../Components/Chat"));
13 |
14 | const HomePage = () => {
15 | const [loading, setloading] = useState(true);
16 | const navigate = useNavigate();
17 | const dispatch = useDispatch();
18 | const [status, setStatus] = useState();
19 |
20 | // let [isOpen, setIsOpen] = useState(false);
21 |
22 | const user = useSelector((globalState) => globalState.user.userDetails);
23 |
24 |
25 | const getUserData = async () => {
26 | await dispatch(getMySelf());
27 | await dispatch(fetchChats());
28 | };
29 |
30 | useEffect(() => {
31 | if (localStorage.ETalkUser) {
32 | getUserData();
33 | setTimeout(() => {
34 | setloading(false);
35 | }, 1000);
36 | } else {
37 | dispatch(clearAuthStore());
38 | setTimeout(() => {
39 | setloading(false);
40 | }, 1000);
41 | }
42 |
43 | // eslint-disable-next-line
44 | }, [localStorage]);
45 |
46 | useEffect(() => {
47 | if (user) {
48 | setStatus(user.is_verified);
49 | }
50 | }, [user]);
51 | useEffect(() => {
52 | if (status === undefined) {
53 | return;
54 | }
55 | if (!status) {
56 | navigate("/verification");
57 | // alert("mot verified ");
58 | }
59 | // else {
60 | // alert("verified");
61 | // }
62 |
63 | // eslint-disable-next-line react-hooks/exhaustive-deps
64 | }, [status]);
65 |
66 | return (
67 | <>
68 | {loading ? (
69 | <>
70 |
71 | >
72 | ) : (
73 | <>
74 | {user?.name ? (
75 |
78 |
79 | >
80 | }
81 | >
82 |
83 |
84 | ) : (
85 |
88 | {" "}
89 | >
90 | }
91 | >
92 |
93 |
94 | )}
95 | >
96 | )}
97 | >
98 | );
99 | };
100 | export default DefaultLayoutHoc(HomePage);
101 |
--------------------------------------------------------------------------------
/client/src/HelperFunction/chat.Helper.js:
--------------------------------------------------------------------------------
1 | // To get the sender
2 | export const getSender = (loggedUser, users) => {
3 | // console.log(users);
4 | if (!users) {
5 | return;
6 | }
7 | return users[0]._id === loggedUser._id ? users[1].name : users[0].name;
8 | };
9 |
10 | // To get the sender pic
11 | export const getSenderPic = (loggedUser, users) => {
12 | // console.log(users);
13 | if (!users) {
14 | return;
15 | }
16 | return users[0]._id === loggedUser._id ? users[1].pic : users[0].pic;
17 | };
18 |
19 | // get sender profile details
20 | export const getSenderProfileDetails = (loggedUser, sender) => {
21 | const { users } = sender;
22 | const data = {
23 | senderName: "",
24 | senderAbout: "",
25 | senderEmail: "",
26 | senderContact: "",
27 | senderPic: "",
28 | };
29 |
30 | users.forEach((element) => {
31 | if (loggedUser._id !== element._id) {
32 | data.senderName = element.name;
33 | data.senderAbout = element.about;
34 | data.senderEmail = element.email;
35 | data.senderContact = element.contact;
36 | data.senderPic = element.pic;
37 | }
38 | });
39 | // console.log(data);
40 | return data;
41 | };
42 |
43 | // get group public details
44 | export const getGroupProfileDetails = (loggedUser, sender) => {
45 | // console.log(sender);
46 | const { _id, chatName, isGroupChat, users, groupAdmin, createdAt } = sender;
47 |
48 | const data = {
49 | groupId: _id,
50 | groupName: chatName,
51 | groupPic:
52 | "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS6wQvepXb0gM_Ft1QUOs6UyYJjPOmA-gq5Yw&usqp=CAU",
53 | groupCreatedAt: createdAt,
54 | groupCreatedBy: groupAdmin.name,
55 | groupAdmin: {
56 | id: groupAdmin._id,
57 | name: groupAdmin.name,
58 | },
59 | groupUsers: [],
60 | };
61 |
62 | users.forEach((element) => {
63 | const obj = {
64 | id: element._id,
65 | name: element.name,
66 | pic: element.pic,
67 | about: element.about,
68 | };
69 | if (groupAdmin._id !== element._id) {
70 | data.groupUsers.push(obj);
71 | } else {
72 | data.groupUsers.unshift(obj);
73 | }
74 | });
75 |
76 | // console.log(data);
77 | return data;
78 | };
79 |
80 | export const isMyMessage = (loggedUser, message) => {
81 | if (!message.sender || !loggedUser) {
82 | return;
83 | }
84 | if (loggedUser._id === message.sender._id) {
85 | return true;
86 | }
87 | return false;
88 | };
89 |
90 | // getting time
91 | export const getTime = (createdAt) => {
92 | const date = new Date(createdAt);
93 | const dateString = date.toLocaleDateString();
94 | const timeString = date.toLocaleTimeString([], {
95 | hour: "2-digit",
96 | minute: "2-digit",
97 | });
98 | // return `${timeString}`;
99 | return `${dateString} ${timeString}`;
100 | };
101 |
--------------------------------------------------------------------------------
/client/src/Components/Default.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import styled from "styled-components";
3 | import Searchbar from "./Searchbar";
4 | import UserList from "./UserList";
5 | import {
6 | clearSelectChatAction,
7 | selectChatAction,
8 | } from "../Redux/Reducer/Chat/chat.action";
9 | import { getAllChats } from "../Redux/Reducer/Message/message.action";
10 | import { useSelector, useDispatch } from "react-redux";
11 |
12 | import Group from "./modal/Group";
13 |
14 | const Default = () => {
15 | const [query, setQuary] = useState("");
16 | const [searchOpen, setSearchOpen] = useState(false);
17 |
18 | const dispatch = useDispatch();
19 |
20 | const [selectedChat, setSelectedChat] = useState();
21 | const [chatList, setchatList] = useState([]);
22 |
23 | const chat = useSelector((globalState) => globalState.chat.chats);
24 | const loggedUser = useSelector((globalState) => globalState.user.userDetails);
25 | const result = useSelector((globalState) => globalState.chat.selectedChat);
26 |
27 | useEffect(() => {
28 | setchatList(chat);
29 | }, [chat]);
30 |
31 | useEffect(() => {
32 | // dispatch(clearSelectChatAction());
33 | // if (selectedChat ? dispatch(getAllChats(selectedChat._id)) : "")
34 |
35 | dispatch(selectChatAction(selectedChat));
36 | // console.log(selectedChat);
37 |
38 | dispatch(getAllChats(selectedChat));
39 |
40 | // alert(selectedChat._id)
41 | }, [selectedChat]);
42 |
43 |
44 | return (
45 |
46 |
47 | {searchOpen ? (
48 | <> >
49 | ) : (
50 | <>
51 |
52 |
Chat
53 |
Start New Conversation
54 |
55 | >
56 | )}
57 |
58 |
65 |
70 |
71 |
72 |
73 |
74 | {/* User list */}
75 |
85 |
86 | );
87 | };
88 | const Wrapper = styled.div`
89 | animation: fadeInLeft 1s;
90 | .group-icon {
91 | &:hover {
92 | background-color: ${({ theme }) => theme.colors.bg.secondary};
93 | color: ${({ theme }) => theme.colors.primaryRgb};
94 | }
95 | }
96 | `;
97 | export default Default;
98 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const dotenv = require("dotenv");
3 | const connectDB = require("./config/db");
4 | const { chats } = require("./data/data");
5 | const colors = require("colors");
6 | const chatRoutes = require("./routes/chatRoutes");
7 | const userRoutes = require("./routes/userRoutes");
8 | const messageRoutes = require("./routes/messageRoutes");
9 | const cors = require("cors");
10 | const helmet = require("helmet");
11 |
12 | const { socket } = require("socket.io");
13 |
14 | const { notFound, errorHandler } = require("./middleware/errorMiddleware");
15 | const { PORT, CLIENT_ACCESS_URL } = require("./config/keys");
16 |
17 | dotenv.config();
18 | const app = express();
19 | connectDB();
20 |
21 | // socket.io implement
22 |
23 | const http = require("http");
24 | const { Server } = require("socket.io");
25 |
26 | // const cors = require("cors");
27 |
28 | app.use(cors());
29 |
30 | const server = http.createServer(app);
31 |
32 | app.use(helmet());
33 | app.use(express.json()); //to accept json data
34 | app.use(express.json());
35 |
36 | app.get("/", (req, res) => {
37 | res.json({
38 | message: "Welcome to E-Talk Server",
39 | });
40 | });
41 |
42 | // Routes
43 | app.use("/api/chat", chatRoutes);
44 | app.use("/api/user", userRoutes);
45 | app.use("/api/message", messageRoutes);
46 | app.use(notFound);
47 | app.use(errorHandler);
48 |
49 | // PORT
50 | // const PORT = PORT || 4000;
51 | server.listen(PORT, () => {
52 | console.log(
53 | `Server is Running on PORT: http://localhost:${PORT}`.yellow.bold
54 | );
55 | });
56 |
57 | const io = require("socket.io")(server, {
58 | pingTimeout: 60000,
59 | cors: {
60 | origin: CLIENT_ACCESS_URL,
61 | methods: ["GET", "POST"],
62 | allowedHeaders: ["my-custom-header"],
63 | credentials: true,
64 | },
65 | });
66 |
67 | io.on("connection", (socket) => {
68 | console.log("connetcted to Socket.io");
69 |
70 | socket.on("setup", (userData) => {
71 | socket.join(userData._id);
72 | console.log(`${userData.name} with _id: ${userData._id} is connected.`);
73 | socket.emit("connected");
74 | });
75 |
76 | socket.on("join chat", (room) => {
77 | if (!room) {
78 | return console.log(`no room is selected by user`);
79 | }
80 | socket.join(room);
81 | console.log(`user joined room. Room _id : ${room._id}`);
82 | });
83 |
84 | socket.on("typing", (room) => socket.in(room).emit("typing",{
85 | senderId: room.senderId
86 | }));
87 | socket.on("stop typing", (room) => socket.in(room).emit("stop typing"));
88 |
89 | socket.on("new message", (newMessageRecieved) => {
90 | var chat = newMessageRecieved.chat;
91 | if (!chat) {
92 | return;
93 | }
94 | // if (!chat.users) {
95 | // retconsole.log("chat.users is not defined");
96 | // }
97 |
98 | chat.users.forEach((user) => {
99 | if (user._id == newMessageRecieved.sender._id) {
100 | return;
101 | }
102 | socket.in(user._id).emit("message recieved", newMessageRecieved);
103 | });
104 | });
105 | });
106 |
--------------------------------------------------------------------------------
/client/src/Components/Auth/Signup.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import SignupForm from "./SignupForm";
4 | import { NavLink } from "react-router-dom";
5 | import Toggler from "../Toggler";
6 | const Signup = () => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |

17 |
18 |
19 |
20 |
21 | {" "}
22 | Already have an account?
23 |
27 | {" "}
28 | Sign in
29 |
30 |
31 |
© {new Date().getFullYear()} E-Talk created with ❤️
32 |
33 |
34 |
35 |
36 |
37 | );
38 | };
39 | const Wrapper = styled.section`
40 | position: relative;
41 | width: 100vw;
42 | height: auto;
43 | background-color: ${({ theme }) => theme.colors.bg.secondary};
44 | .logo {
45 | img {
46 | height: 50px;
47 | }
48 | }
49 | .toggle-icon {
50 | position: absolute;
51 | top: 10px;
52 | right: 0;
53 | margin-right: 20px;
54 | display: flex;
55 | width: 100vw;
56 | justify-content: flex-end;
57 | }
58 | .auth-page-content {
59 | height: calc(100% - 48px);
60 | margin: 24px;
61 |
62 | .card {
63 | border-radius: 0.25rem;
64 | }
65 | background-color: ${({ theme }) => theme.colors.bg.primary};
66 | a {
67 | color: ${({ theme }) => theme.colors.text.secondary};
68 | }
69 | input {
70 | background-color: ${({ theme }) => theme.colors.btn.light};
71 | border-color: ${({ theme }) => theme.colors.border};
72 | &:focus {
73 | background-color: ${({ theme }) => theme.colors.btn.light};
74 | outline-color: ${({ theme }) => theme.colors.btn.light};
75 | border-color: ${({ theme }) => theme.colors.border};
76 | }
77 | }
78 | p,
79 | label {
80 | color: ${({ theme }) => theme.colors.text.secondary};
81 | }
82 | }
83 | .signin-other-title {
84 | position: relative;
85 | &:after {
86 | content: "";
87 | position: absolute;
88 | width: 100%;
89 | height: 1px;
90 | left: 0;
91 | right: 0;
92 | top: 15px;
93 | }
94 | .title {
95 | display: inline-block;
96 | position: relative;
97 | z-index: 9;
98 | padding: 2px 16px;
99 | }
100 | }
101 | `;
102 | export default Signup;
103 |
--------------------------------------------------------------------------------
/client/src/Components/Auth/Login.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import LoginForm from "./LoginForm";
4 | import { NavLink } from "react-router-dom";
5 | import Toggler from "../Toggler";
6 | import { ToastContainer } from "react-toastify";
7 |
8 | const Login = () => {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |

20 |
21 |
22 |
23 |
24 | Don't have an account yet?
25 |
29 | Sign up
30 |
31 |
32 |
© {new Date().getFullYear()} E-Talk created with ❤️
33 |
34 |
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | const Wrapper = styled.section`
42 | position: relative;
43 | display: flex;
44 | justify-content: center;
45 | align-items: center;
46 | width: 100vw;
47 | height: 100vh;
48 | background-color: ${({ theme }) => theme.colors.bg.secondary};
49 | .logo {
50 | img {
51 | height: 50px;
52 | }
53 | }
54 | .toggle-icon {
55 | position: absolute;
56 | top: 10px;
57 | right: 0;
58 | margin-right: 20px;
59 | display: flex;
60 | width: 100vw;
61 | font-size: 2rem;
62 | justify-content: flex-end;
63 | }
64 |
65 | .auth-page-content {
66 | border-radius: 16px;
67 | margin: 24px 0;
68 | background-color: ${({ theme }) => theme.colors.bg.primary};
69 | a {
70 | color: ${({ theme }) => theme.colors.text.secondary};
71 | }
72 | input {
73 | background-color: ${({ theme }) => theme.colors.btn.light};
74 | border-color: ${({ theme }) => theme.colors.border};
75 | &:focus {
76 | background-color: ${({ theme }) => theme.colors.btn.light};
77 | outline-color: ${({ theme }) => theme.colors.btn.light};
78 | border-color: ${({ theme }) => theme.colors.border};
79 | }
80 | }
81 | p,
82 | label {
83 | color: ${({ theme }) => theme.colors.text.secondary};
84 | }
85 | }
86 | .signin-other-title {
87 | position: relative;
88 | &:after {
89 | content: "";
90 | position: absolute;
91 | width: 100%;
92 | height: 1px;
93 | left: 0;
94 | right: 0;
95 | top: 15px;
96 | }
97 | .title {
98 | display: inline-block;
99 | position: relative;
100 | z-index: 9;
101 | padding: 2px 16px;
102 | }
103 | }
104 | `;
105 |
106 | export default Login;
107 |
--------------------------------------------------------------------------------
/client/src/Components/Themes.js:
--------------------------------------------------------------------------------
1 |
2 | // export const lightTheme = {
3 | // colors: {
4 | // "heading": "rgb(24 24 29)",
5 | // "heading2": "rgb(255, 255, 255)",
6 | // "white": "#fff",
7 | // "black": " #212529",
8 | // "cyan": "#1ca9fe",
9 | // "green": "#4eac6d",
10 | // "danger": "#ff4e2b",
11 | // "light": "#223645",
12 |
13 |
14 | // text:{
15 | // "primary": "#000000",
16 | // "secondary": "rgba(29 ,29, 29, .8)",
17 | // },
18 |
19 | // "rgb":{
20 | // "primary": "111, 201, 252",
21 | // "secondary": "78,172,109",
22 | // "cyan": "28,157,234",
23 | // "heading": "0,0,0",
24 | // },
25 |
26 | // "bg":{
27 | // "primary": "#fff",
28 | // "secondary": "#eff7fe",
29 | // },
30 | // bg2:{
31 | // "primary": "#fff",
32 | // "secondary": "rgba(28,157,234,.05)",
33 | // },
34 |
35 | // btn:{
36 | // "primary":"28, 157, 234",
37 | // "secondary": "22 163 74",
38 | // "danger": "255, 78, 43",
39 | // "light": "#f6f6f9",
40 | // },
41 | // border2:{
42 | // "primary": "#00000026"
43 | // },
44 | // boxShadow:{
45 | // "primary": "rgba(28, 157, 234, 0.2)",
46 | // },
47 |
48 | // "hr": "#ffffff",
49 | // "border": "239, 241, 242",
50 | // "img_border": "255, 255, 255",
51 | // "gradient": "linear-gradient(145deg,#1ca9fe,#1c6ee9);",
52 | // },
53 | // media: {
54 | // mobile: "800px",
55 | // tab: "998px",
56 | // },
57 | // };
58 | // export const darkTheme = {
59 | // colors: {
60 | // "heading": "rgb(255, 255, 255)",
61 | // "heading2": "rgb(24 24 29)",
62 | // "white": "#ffffff",
63 | // "black": "#000000",
64 | // "cyan": "#1ca9fe",
65 | // "green": "#4eac6d",
66 | // "danger": "#ff4e2b",
67 | // "light": "#223645",
68 |
69 | // text:{
70 | // "primary": "#212529",
71 | // "secondary": "#8f9198",
72 | // },
73 |
74 | // "rgb":{
75 | // "primary": "0, 128, 201",
76 | // "secondary": "78,172,109",
77 | // "cyan": "28,157,234",
78 | // "heading": "255,255,255",
79 | // },
80 |
81 | // "bg":{
82 | // "black": "#000000",
83 | // "primary": "#262626",
84 | // "secondary": "#2e2e2e",
85 | // },
86 | // border2:{
87 | // "primary": "#FFFFFF26"
88 | // },
89 |
90 |
91 | // "bg2":{
92 | // "primary": "#0c1631",
93 | // "secondary": "#0e1b38",
94 | // },
95 | // boxShadow:{
96 | // "primary": "rgba(1, 201 ,245, 0.4)",
97 | // },
98 |
99 |
100 | // btn:{
101 | // "primary":"28, 157, 234",
102 | // "secondary": "22 163 74",
103 | // "danger": "255, 78, 43",
104 | // "light": "#25262c",
105 | // },
106 |
107 | // "hr": "#ffffff",
108 | // "border": "65, 66, 72",
109 | // "img_border": "31, 41, 55",
110 | // "gradient": "linear-gradient(145deg,#1ca9fe,#1c6ee9);",
111 | // },
112 | // media: {
113 | // mobile: "800px",
114 | // tab: "998px",
115 | // },
116 | // };
117 |
--------------------------------------------------------------------------------
/client/src/config.js/data.js:
--------------------------------------------------------------------------------
1 | export const featuresImg = [
2 | {
3 | "id" : 1,
4 | "icon" : "/images/features-icon-1.png",
5 | "name": "Clean Code",
6 | "content": "Lorem ipsum dolor sit amet consectetur, adipisicing elit. Ad illo facere vel."
7 | },
8 | {
9 | "id" : 2,
10 | "icon" : "/images/features-icon-2.png",
11 | "name": "Fully Responsive",
12 | "content": "fully app style responsive design for user & easy to operate in device."
13 | },
14 | {
15 | "id" : 3,
16 | "icon" : "/images/features-icon-1.png",
17 | "name": "Easy to use",
18 | "content": "Lorem ipsum dolor sit amet consectetur, adipisicing elit. Ad illo facere vel."
19 | },
20 | {
21 | "id" : 4,
22 | "icon" : "/images/features-icon-1.png",
23 | "name": "single/Group chat",
24 | "content": "Lorem ipsum dolor sit amet consectetur, adipisicing elit. Ad illo facere vel."
25 | },
26 | {
27 | "id" : 5,
28 | "icon" : "/images/features-icon-1.png",
29 | "name": "emoji support",
30 | "content": "Lorem ipsum dolor sit amet consectetur, adipisicing elit. Ad illo facere vel."
31 | },
32 | {
33 | "id" : 6,
34 | "icon" : "/images/features-icon-6.png",
35 | "name": "light / Dark Mode",
36 | "content": "we provide one more smart feature supports day & night mode."
37 | },
38 | ]
39 |
40 | export const technologiesImg = [
41 | {
42 | "id" : 1,
43 | "src" : "images/html-5-white.png",
44 | "name": "Html5"
45 | },
46 | {
47 | "id" : 2,
48 | "src" : "/images/css-white.png",
49 | "name": "css3"
50 | },
51 | {
52 | "id" : 3,
53 | "src" : "/images/tailwind.png",
54 | "name": "Tailwind"
55 | },
56 | {
57 | "id" : 4,
58 | "src" : "/images/googlefonts.png",
59 | "name": "Google fonts"
60 | },
61 | {
62 | "id" : 5,
63 | "src" : "/images/atom.png",
64 | "name": "React"
65 | },
66 | {
67 | "id" : 6,
68 | "src" : "/images/nodejs-logo.png",
69 | "name": "Node js"
70 | },
71 | {
72 | "id": 7,
73 | "src" : "/images/mongodb.png",
74 | "name": "Mongodb"
75 | },
76 |
77 | ]
78 |
79 | export const teamImg = [
80 | {
81 | "id" : 1,
82 | "name": "Narender Singh Bisht",
83 | "src" : "/team/narender.jpg",
84 | "role" : "Frontend Developer",
85 | "link": "https://narenderportfolio.netlify.app",
86 | },
87 | {
88 | "id" : 2,
89 | "name": "Nitesh",
90 | "src" : "/team/nitesh.png",
91 | "role" : "Full-stack Developer",
92 | "link": "https://nitesh-tiwari-portfolio-xyz.vercel.app/",
93 | },
94 | {
95 | "id" : 3,
96 | "name": "Rituresh kumar rai ",
97 | "src" : "/team/rithuresh.jpg",
98 | "role" : "Backend Developer",
99 | "link": "https://infallible-yonath-5de199.netlify.app/",
100 | },
101 |
102 |
103 | ]
104 |
105 | export const colors = [
106 | {
107 | "id": 1,
108 | "color": "rgb(28, 157, 234)"
109 |
110 | },
111 | {
112 | "id": 2,
113 | "color": "rgb(78, 172, 109)"
114 | },
115 | {
116 | "id": 3,
117 | "color": "rgb(232,62,140)"
118 | },
119 | {
120 | "id": 4,
121 | "color": "rgb(155, 74, 255)"
122 | },
123 | {
124 | "id": 5,
125 | "color": "rgb(255, 128, 60)"
126 | }
127 |
128 |
129 | ]
--------------------------------------------------------------------------------
/client/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 |
--------------------------------------------------------------------------------
/client/src/Components/Profile.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | // import { MdOutlineEmail } from "react-icons/md";
4 | // import { FcAbout, FcContacts } from "react-icons/fc";
5 | import { AiFillContacts, AiFillInfoCircle, AiTwotoneMail } from "react-icons/ai";
6 | import { useSelector } from "react-redux";
7 | import { RxCross2 } from "react-icons/rx";
8 |
9 | const Profile = (props) => {
10 |
11 | // console.log(user);
12 | return (
13 |
14 |
15 |
16 |
Profile
17 |
Personal Information
18 |
19 |
27 |
28 |
29 |
30 |
31 |
32 |

33 |
34 |
35 |
{props.name}
36 |
37 |
38 |
39 |
40 |
43 |
44 |
About
45 |
{props.about}
46 |
47 |
48 | {/* contact */}
49 |
50 |
53 |
54 |
Mobile
55 |
{props.contact}
56 |
57 |
58 |
59 |
60 |
63 |
64 |
Email Address
65 |
{props.email}
66 |
67 |
68 |
69 |
70 |
71 |
72 | );
73 | };
74 |
75 | export default Profile;
76 |
77 | const Wrapper = styled.div`
78 | animation: fadeInLeft 1s;
79 |
80 | .chat-menu{
81 | .icon{
82 | display: none;
83 | }
84 | }
85 | .user-details{
86 | height: calc(100vh - 200px)
87 | }
88 | .user-profile-img {
89 | width: 150px;
90 | height: 150px;
91 | img {
92 | min-width: 100%;
93 | }
94 | }
95 | .intro {
96 | border-bottom: 1px solid rgba(${({ theme }) => theme.colors.border});
97 | background-color: ${({ theme }) => theme.colors.bg.primary};
98 |
99 | .icon{
100 | color: ${({ theme }) => theme.colors.primaryRgb}
101 | }
102 | }
103 | @media (max-width: 500px) {
104 | .details {
105 | margin: 10px 40px 0px 40px;
106 | padding: 0;
107 | }
108 | p{
109 | font-size: 1rem;
110 | }
111 | .intro {
112 | padding: 1rem 0rem;
113 | }
114 | }
115 | @media (min-width: 500px) and (max-width: ${({ theme }) => theme.media.mobile}) {
116 | .details {
117 | margin: 10px 50px 0px 50px;
118 | }
119 | p{
120 | font-size: 1rem;
121 | }
122 | .intro {
123 | padding: 1rem 0rem;
124 | }
125 | }
126 | `;
127 |
128 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Chat/chat.action.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import {
3 | FETCH_CHATS,
4 | FETCH_USER,
5 | FETCH_USER_CLEAR,
6 | // SELECT_CHAT,
7 | SHOW_USER_LOADING,
8 | } from "./chat.type";
9 | const SERVER_ACCESS_BASE_URL = process.env.REACT_APP_SERVER_ACCESS_BASE_URL;
10 |
11 | export const loadingUserAction = (state) => {
12 | return {
13 | type: SHOW_USER_LOADING,
14 | payload: state,
15 | };
16 | };
17 |
18 | // fetching all the chats for a particaular user
19 | export const fetchChats = () => async (dispatch) => {
20 | try {
21 | const chats = await axios({
22 | method: "GET",
23 | url: `${SERVER_ACCESS_BASE_URL}/api/chat`,
24 | });
25 | // console.log(chats.data);
26 |
27 | return dispatch({ type: FETCH_CHATS, payload: chats.data });
28 | } catch (error) {
29 | return dispatch({ type: "ERROR", payload: error });
30 | }
31 | };
32 |
33 | // fetching user for creating new one to one chat and group chat
34 | export const fetchUser = (Search) => async (dispatch) => {
35 | try {
36 | dispatch(loadingUserAction(true));
37 | const newUser = await axios({
38 | method: "GET",
39 | url: `${SERVER_ACCESS_BASE_URL}/api/user?search=${Search}`,
40 | });
41 |
42 | // console.log(...newUser.data);
43 | dispatch(loadingUserAction(false));
44 | return dispatch({ type: FETCH_USER, payload: newUser.data });
45 | } catch (error) {
46 | return dispatch({ type: "ERROR", payload: error });
47 | }
48 | };
49 |
50 | // clear the fetching user
51 | export const fetchUserClear = () => async (dispatch) => {
52 | try {
53 | return dispatch({ type: FETCH_USER_CLEAR, payload: "" });
54 | } catch (error) {
55 | return dispatch({ type: "ERROR", payload: error });
56 | }
57 | };
58 |
59 | // creating new one to one chat
60 | export const createChat = (userId) => async (dispatch) => {
61 | try {
62 | const newCreatedChat = await axios({
63 | method: "POST",
64 | url: `${SERVER_ACCESS_BASE_URL}/api/chat`,
65 | data: { userId },
66 | });
67 | return dispatch({ type: "CREATE_CHAT", payload: newCreatedChat.data });
68 | // axios.defaults.headers.common["Authorization"] = `Bearer ${User.data.token}`
69 | } catch (error) {
70 | return dispatch({ type: "ERROR", payload: error });
71 | }
72 | };
73 |
74 | // creating new group chat
75 | export const createGroupChat = (groupInfo) => async (dispatch) => {
76 | try {
77 | // console.log({ ...groupInfo });
78 |
79 | const newCreatedGroupChat = await axios({
80 | method: "POST",
81 | url: `${SERVER_ACCESS_BASE_URL}/api/chat/group`,
82 | data: { ...groupInfo },
83 | });
84 |
85 | return dispatch({
86 | type: "CREAT_GROUP_CHAT",
87 | payload: newCreatedGroupChat.data,
88 | });
89 | } catch (error) {
90 | return dispatch({ type: "ERROR", payload: error });
91 | }
92 | };
93 |
94 | // Remove user from group
95 | export const removeUserFromGroup = (data) => async (dispatch) => {
96 | try {
97 | // console.log({ ...groupInfo });
98 |
99 | const removedUser = await axios({
100 | method: "PUT",
101 | url: `${SERVER_ACCESS_BASE_URL}/api/chat/groupremove`,
102 | data: { ...data },
103 | });
104 |
105 | return dispatch({
106 | type: "REMOVE_USER_FROM_GROUP",
107 | payload: removedUser.data,
108 | });
109 | } catch (error) {
110 | return dispatch({ type: "ERROR", payload: error });
111 | }
112 | };
113 |
114 | // selected chat
115 |
116 | export const selectChatAction = (item) => async (dispatch) => {
117 | try {
118 | return dispatch({
119 | type: "SELECT_CHAT",
120 | payload: item,
121 | });
122 | } catch (error) {
123 | return dispatch({ type: "ERROR", payload: error });
124 | }
125 | };
126 | // clear selected chat
127 |
128 | export const clearSelectChatAction = () => async (dispatch) => {
129 | try {
130 | return dispatch({
131 | type: "CLEAR_SELECT_CHAT",
132 | payload: {},
133 | });
134 | } catch (error) {
135 | return dispatch({ type: "ERROR", payload: error });
136 | }
137 | };
138 |
139 | // export const clearUser = () => async (dispatch) => {
140 | // try {
141 | // return dispatch({ type: CLEAR_USER, payload: {} });
142 | // } catch (error) {
143 | // return dispatch({ type: "ERROR", payload: error });
144 | // }
145 | // };
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/client/public/images/email.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/Redux/Reducer/Auth/auth.action.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | // import dotenv from "dotenv";
3 | // action type
4 |
5 | import {
6 | SIGN_IN,
7 | SIGN_UP,
8 | SIGN_OUT,
9 | USER_VERIFICATION,
10 | VERIFY_TOKEN,
11 | FORGOT_PASSWORD,
12 | RESET_PASSWORD,
13 | CLEAR_AUTH_STORE,
14 | } from "./auth.type";
15 |
16 | const SERVER_ACCESS_BASE_URL = process.env.REACT_APP_SERVER_ACCESS_BASE_URL;
17 |
18 | // Sign IN
19 |
20 | export const signIn = (userData) => async (dispatch) => {
21 | try {
22 | // console.log(SERVER_ACCESS_BASE_URL);
23 | const User = await axios({
24 | method: "POST",
25 | url: `${SERVER_ACCESS_BASE_URL}/api/user/login/`,
26 | data: { ...userData },
27 | });
28 | // console.log(User);
29 |
30 | localStorage.setItem(
31 | "ETalkUser",
32 | JSON.stringify({ token: User.data.token })
33 | );
34 | // window.location.reload();
35 | axios.defaults.headers.common[
36 | "Authorization"
37 | ] = `Bearer ${User.data.token}`;
38 |
39 | return dispatch({ type: SIGN_IN, payload: User.data });
40 | } catch (error) {
41 | return dispatch({ type: "ERROR", payload: error.response.data });
42 | }
43 | };
44 |
45 | // Sign UP
46 |
47 | export const signUp = (userData) => async (dispatch) => {
48 | try {
49 | const User = await axios({
50 | method: "POST",
51 | url: `${SERVER_ACCESS_BASE_URL}/api/user`,
52 | data: { ...userData },
53 | });
54 |
55 | localStorage.setItem(
56 | "ETalkUser",
57 | JSON.stringify({ token: User.data.token })
58 | );
59 | // window.location.reload();
60 |
61 | axios.defaults.headers.common[
62 | "Authorization"
63 | ] = `Bearer ${User.data.token}`;
64 |
65 | return dispatch({ type: SIGN_UP, payload: User.data });
66 | } catch (error) {
67 | return dispatch({ type: "ERROR", payload: error.response.data });
68 | }
69 | };
70 |
71 | // user VErication
72 |
73 | export const userVerification = (data) => async (dispatch) => {
74 | try {
75 | // console.log(data.email);
76 | const verificationLink = await axios({
77 | method: "POST",
78 | url: `${SERVER_ACCESS_BASE_URL}/api/user/resend/verificationlink`,
79 | data: { ...data },
80 | });
81 |
82 | return dispatch({
83 | type: USER_VERIFICATION,
84 | payload: verificationLink.data,
85 | });
86 | } catch (error) {
87 | return dispatch({ type: "ERROR", payload: error });
88 | }
89 | };
90 |
91 | // verify email link
92 | export const verifyEmailLink = (token) => async (dispatch) => {
93 | try {
94 | const verificationStatus = await axios({
95 | method: "PUT",
96 | url: `${SERVER_ACCESS_BASE_URL}/api/user/verify`,
97 | data: { token },
98 | });
99 | return dispatch({ type: VERIFY_TOKEN, payload: verificationStatus.data });
100 | } catch (error) {
101 | return dispatch({ type: "ERROR", payload: error.response.data });
102 | }
103 | };
104 |
105 | // Forgot password
106 | export const forgotPassword = (data) => async (dispatch) => {
107 | try {
108 | // console.log(data.email);
109 | const forgotPasswordStatus = await axios({
110 | method: "POST",
111 | url: `${SERVER_ACCESS_BASE_URL}/api/user/forgotpassword`,
112 | data: { ...data },
113 | });
114 |
115 | return dispatch({
116 | type: FORGOT_PASSWORD,
117 | payload: forgotPasswordStatus.data,
118 | });
119 | } catch (error) {
120 | return dispatch({ type: "ERROR", payload: error.response.data });
121 | }
122 | };
123 |
124 | // Reset password
125 | export const resetPassword = (userData) => async (dispatch) => {
126 | try {
127 | // const { token, password } = userData;
128 | // console.log(data.email);
129 | // const data = {
130 | // token: token,
131 | // password: password,
132 | // };
133 | const resetPasswordStatus = await axios({
134 | method: "POST",
135 | url: `${SERVER_ACCESS_BASE_URL}/api/user/resetpassword`,
136 | data: userData,
137 | });
138 |
139 | return dispatch({
140 | type: RESET_PASSWORD,
141 | payload: resetPasswordStatus.data,
142 | });
143 | } catch (error) {
144 | return dispatch({ type: "ERROR", payload: error.response.data });
145 | }
146 | };
147 |
148 | // clar auth store
149 | export const clearAuthStore = () => async (dispatch) => {
150 | try {
151 | return dispatch({ type: CLEAR_AUTH_STORE, payload: {} });
152 | } catch (error) {
153 | return dispatch({ type: "ERROR", payload: error });
154 | }
155 | };
156 |
157 | // SIGN out
158 |
159 | export const signOut = () => async (dispatch) => {
160 | try {
161 | localStorage.removeItem("ETalkUser");
162 |
163 | window.location.reload();
164 |
165 | return dispatch({ type: SIGN_OUT, payload: {} });
166 | } catch (error) {
167 | return dispatch({ type: "ERROR", payload: error });
168 | }
169 | };
170 |
--------------------------------------------------------------------------------
/client/src/Components/Technologies.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { technologiesImg } from "../config.js/data";
4 |
5 |
6 | const Technologies = () => {
7 | return (
8 |
9 |
10 |
15 |
16 |
POWERFUL
17 | Techonolgies Used
18 |
19 |
20 |
21 |
22 |
23 | {technologiesImg.map((item, index) => (
24 | -
29 |
30 |

31 |
{item.name}
32 |
33 |
34 | ))}
35 |
36 |
37 |
38 | {/* shapes code */}
39 |
40 |
41 |

42 |
43 |
44 |
45 |

46 |
47 |
48 |
49 |
50 |

51 |
52 |
53 |
54 |

55 |
56 |
57 |

58 |
59 |
60 |
61 | );
62 | };
63 |
64 | export default Technologies;
65 |
66 | const Wrapper = styled.section`
67 | position: relative;
68 | padding: 5rem 0;
69 | background-color: ${({ theme }) => theme.colors.bg2.primary};
70 | /* overflow: hidden; */
71 |
72 | .custom-container {
73 | position: relative;
74 | max-width: 100%;
75 | padding-left: 15px;
76 | padding-right: 15px;
77 | margin: 0 auto;
78 | z-index: 20;
79 |
80 | .section-header {
81 | margin: 0 0 25px;
82 | h2 {
83 | font-size: 2.5rem;
84 | font-weight: 700;
85 | color: ${({ theme }) => theme.colors.heading};
86 | margin: 25px 0;
87 | }
88 | }
89 |
90 | .technologies-list {
91 | text-align: center;
92 | margin: 25px 0;
93 | ul {
94 | display: flex;
95 | flex-wrap: wrap;
96 | justify-content: center;
97 | li {
98 | div {
99 | background-color: ${({ theme }) => theme.colors.bg2.secondary};
100 | border-width: 1px 1px 1px 1px;
101 | border-color: ${({ theme }) => theme.colors.border2.primary};
102 | width: 10rem;
103 | height: 10rem;
104 | display: flex;
105 | align-items: center;
106 | justify-content: center;
107 | margin: 0 15px 50px;
108 | border-radius: 10px;
109 | transition: all 0.5s;
110 | &:hover {
111 | box-shadow: 0px 4px 24px
112 | ${({ theme }) => theme.colors.boxShadow.primary};
113 | transform: scale(1.1);
114 | }
115 | }
116 | img {
117 | max-width: 100%;
118 | height: auto;
119 | }
120 | }
121 | }
122 | }
123 | }
124 |
125 | .shapes {
126 | position: absolute;
127 | top: 0;
128 | width: 100%;
129 | height: 100%;
130 | animation: Zoom-fade 5s infinite linear;
131 | z-index: 1;
132 | div {
133 | position: absolute;
134 | }
135 | .shape-1 {
136 | top: -10%;
137 | left: -3%;
138 | opacity: 0.1;
139 | }
140 | .shape-2 {
141 | top: -15%;
142 | right: 16%;
143 | opacity: 0.1;
144 | }
145 | .shape-3 {
146 | top: 70%;
147 | left: -3%;
148 | transform-origin: center;
149 | transform: rotate(20deg);
150 | opacity: 0.1;
151 | }
152 | .shape-4 {
153 | bottom: 0%;
154 | left: 20%;
155 | transition: all 0.5s;
156 | animation: balloonfly-02 12s infinite;
157 | }
158 | .shape-5 {
159 | bottom: 0%;
160 | right: 20%;
161 | transition: all 0.5s;
162 | animation: balloonfly-01 12s infinite;
163 | }
164 | }
165 |
166 | @media only screen and (min-width: 1680px) {
167 | .custom-container {
168 | max-width: 1450px;
169 | padding-right: 15px;
170 | padding-left: 15px;
171 | margin-right: auto;
172 | margin-left: auto;
173 | }
174 | }
175 | `;
176 |
--------------------------------------------------------------------------------
/client/src/Components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { BsGithub } from "react-icons/bs";
4 | import { Link } from "react-scroll";
5 |
6 | const Footer = () => {
7 | return (
8 |
78 | );
79 | };
80 |
81 | export default Footer;
82 |
83 | const Wrapper = styled.footer`
84 | max-width: 100vw;
85 | height: 100%;
86 | background-color: ${({ theme }) => theme.colors.bg2.secondary};
87 | .footer-container {
88 | padding: 3rem 0 3rem 0;
89 | .footer-content {
90 | width: 100vw;
91 | display: flex;
92 | justify-content: center;
93 | align-items: center;
94 | }
95 | .logo-footer-content {
96 | .logo {
97 | a {
98 | width: 10rem;
99 | display: flex !important;
100 | justify-content: center;
101 | align-items: center;
102 | }
103 | img {
104 | width: 100%;
105 | height: auto;
106 | }
107 | }
108 | }
109 | }
110 |
111 | .links {
112 | margin: 2rem auto 0 auto;
113 | max-width: 100%;
114 | .footer-content {
115 | max-width: 100%;
116 | margin: 0;
117 | padding: 0 12%;
118 | ul {
119 | margin: 0 auto;
120 | display: flex;
121 | justify-content: space-between;
122 | align-items: center;
123 | li {
124 | padding: 0 1rem;
125 | cursor: pointer;
126 | .navlink {
127 | padding: 1rem 0;
128 | font-size: 1.2rem;
129 | color: ${({ theme }) => theme.colors.heading};
130 | transition: all 0.1s;
131 | &:hover {
132 | border-bottom: 2px solid ${({ theme }) => theme.colors.cyan};
133 | color: ${({ theme }) => theme.colors.cyan};
134 | }
135 | }
136 | }
137 | }
138 | }
139 | .social-links {
140 | max-width: 100%;
141 | margin: 0;
142 | ul {
143 | li {
144 | a {
145 | color: ${({ theme }) => theme.colors.heading};
146 | font-size: 2rem;
147 | cursor: pointer;
148 | }
149 | }
150 | }
151 | }
152 | }
153 |
154 | .footer-copywrite {
155 | background-color: rgba(0, 0, 0, 0.02);
156 | border-top: 1px solid ${({ theme }) => theme.colors.border2.primary};
157 | width: 100vw;
158 | height: 100%;
159 | padding: 30px 0;
160 | .custom-container {
161 | .footer-bottom {
162 | p {
163 | font-size: 1rem;
164 | margin-bottom: 0;
165 | }
166 | }
167 | }
168 | }
169 | `;
170 |
--------------------------------------------------------------------------------
/client/src/Components/Contacts.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { Button } from "../Styles/Button";
4 |
5 | import { AiOutlinePlus } from "react-icons/ai";
6 | import Spinner from "../Styles/Spinner";
7 | import Invite from "./modal/Invite";
8 |
9 | const Contacts = ({
10 | search,
11 | handleChange,
12 | handleClick,
13 | searchResult,
14 | createNewChat,
15 | UserLoading,
16 | showResult,
17 | }) => {
18 | return (
19 | <>
20 |
21 |
22 |
23 |
Contacts
24 |
Start talking now
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
42 |
48 |
49 |
50 | {/* searched user rendering */}
51 |
52 | {UserLoading && showResult ? (
53 | <>
54 |
55 | >
56 | ) : (
57 | <>
58 | {showResult && searchResult.length === 0 ? (
59 | <>
60 |
67 |
68 | User Not Found
69 |
70 |
71 | >
72 | ) : (
73 | <>
74 | {searchResult.map((item, index) => (
75 |
76 |
77 |
78 |

83 |
84 |
85 |
86 |
87 | {item.name}
88 |
89 |
90 |
91 |
createNewChat(item, index)}
94 | >
95 |
96 |
97 |
98 |
99 | ))}
100 | >
101 | )}
102 | >
103 | )}
104 |
105 |
106 |
107 | >
108 | );
109 | };
110 | const Wrapper = styled.div`
111 | animation: fadeInLeft 1s;
112 | input {
113 | color: ${({ theme }) => theme.colors.heading};
114 | background-color: ${({ theme }) => theme.colors.bg.primary};
115 | border-bottom: 1px solid ${({ theme }) => theme.colors.heading};
116 | &:focus {
117 | background-color: none;
118 | }
119 | }
120 | .contact-list {
121 | height: calc(100vh - 220px);
122 | }
123 | .btn {
124 | background-color: ${({ theme }) => theme.colors.primaryRgb};
125 | }
126 | .search-user-box {
127 | position: relative;
128 | .profile {
129 | width: 50px;
130 | height: 50px;
131 | }
132 | .details {
133 | padding: 12px 12px 12px 60px;
134 | }
135 | .user-add {
136 | position: absolute;
137 | right: 0;
138 | text-align: right;
139 | padding: 12px 0px 12px 0px;
140 | width: 40px;
141 | height: 40px;
142 | &:hover {
143 | background-color: ${({ theme }) => theme.colors.bg.secondary};
144 | }
145 | }
146 | }
147 |
148 | @media (max-width: 800px) {
149 | .contact-list {
150 | height: calc(100vh - 300px);
151 | }
152 | }
153 |
154 | `;
155 |
156 | export default Contacts;
157 |
--------------------------------------------------------------------------------
/server/controllers/chatControllers.js:
--------------------------------------------------------------------------------
1 | const asyncHandler = require("express-async-handler");
2 | const { remove } = require("../models/chatModel");
3 | const Chat = require("../models/chatModel");
4 | const User = require("../models/userModel");
5 |
6 | //@description Create or fetch One to One Chat
7 | //@route POST /api/chat/
8 | //@access Protected
9 | const accessChat = asyncHandler(async (req, res) => {
10 | const { userId } = req.body;
11 | if (!userId) {
12 | // console.log("UserId param not sent with request");
13 | return res.sendStatus(400);
14 | }
15 |
16 | var isChat = await Chat.find({
17 | isGroupChat: false,
18 | $and: [
19 | { users: { $elemMatch: { $eq: req.user._id } } },
20 | { users: { $elemMatch: { $eq: userId } } },
21 | ],
22 | })
23 | .populate("users", "-password")
24 | .populate("latestMessage");
25 |
26 | isChat = await User.populate(isChat, {
27 | path: "latestMessage.sender",
28 | select: "name pic email",
29 | });
30 |
31 | if (isChat.length > 0) {
32 | res.send(isChat[0]);
33 | } else {
34 | var chatData = {
35 | chatName: "sender",
36 | isGroupChat: false,
37 | users: [req.user._id, userId],
38 | };
39 |
40 | try {
41 | const createdChat = await Chat.create(chatData);
42 | const FullChat = await Chat.findOne({ _id: createdChat._id }).populate(
43 | "users",
44 | "-password"
45 | );
46 | res.status(200).json(FullChat);
47 | } catch (error) {
48 | res.status(400);
49 | throw new Error(error.message);
50 | }
51 | }
52 | });
53 |
54 | //@description Fetch all chats for a user
55 | //@route GET /api/chat/
56 | //@access Protected
57 | const fetchChats = asyncHandler(async (req, res) => {
58 | try {
59 | Chat.find({ users: { $elemMatch: { $eq: req.user._id } } })
60 | .populate("users", "-password")
61 | .populate("groupAdmin", "-password")
62 | .populate("latestMessage")
63 | .sort({ updatedAt: -1 })
64 | .then(async (results) => {
65 | results = await User.populate(results, {
66 | path: "latestMessage.sender",
67 | select: "name pic email",
68 | });
69 | res.status(200).send(results);
70 | });
71 | } catch (error) {
72 | res.status(400);
73 | throw new Error(error.message);
74 | }
75 | });
76 |
77 | //@description Create New Group Chat
78 | //@route POST /api/chat/group
79 | //@access Protected
80 | const createGroupChat = asyncHandler(async (req, res) => {
81 | // console.log(req);
82 | // console.log(req.body.name, req.body.users);
83 | if (!req.body.users || !req.body.name) {
84 | return res.status(400).send({ message: "Please Fill all the feilds" });
85 | }
86 |
87 | var users = JSON.parse(req.body.users);
88 | // console.log(users);
89 |
90 | if (users.length < 2) {
91 | return res
92 | .status(400)
93 | .send("More than 2 users are required to form a group chat");
94 | }
95 |
96 | users.push(req.user);
97 |
98 | try {
99 | const groupChat = await Chat.create({
100 | chatName: req.body.name,
101 | users: users,
102 | isGroupChat: true,
103 | groupAdmin: req.user,
104 | });
105 |
106 | const fullGroupChat = await Chat.findOne({ _id: groupChat._id })
107 | .populate("users", "-password")
108 | .populate("groupAdmin", "-password");
109 |
110 | res.status(200).json(fullGroupChat);
111 | } catch (error) {
112 | // console.log(error);
113 | res.status(400);
114 | throw new Error(error.message);
115 | }
116 | });
117 |
118 | const renameGroup = asyncHandler(async (req, res) => {
119 | const { chatId, chatName } = req.body;
120 |
121 | const updatedChat = await Chat.findByIdAndUpdate(
122 | chatId,
123 | {
124 | chatName,
125 | },
126 | {
127 | new: true,
128 | }
129 | )
130 | .populate("users", "-password")
131 | .populate("groupAdmin", "-password");
132 |
133 | if (!updatedChat) {
134 | res.status(404);
135 | throw new Error("chat Not Found");
136 | } else {
137 | res.json(updatedChat);
138 | }
139 | });
140 |
141 | const addToGroup = asyncHandler(async (req, res) => {
142 | const { chatId, userId } = req.body;
143 |
144 | const added = await Chat.findByIdAndUpdate(
145 | chatId,
146 | {
147 | $push: { users: userId },
148 | },
149 | { new: true }
150 | )
151 | .populate("users", "-password")
152 | .populate("groupAdmin", "-password");
153 |
154 | if (!added) {
155 | res.status(404);
156 | throw new Error("chat Not Found");
157 | } else {
158 | res.json(added);
159 | }
160 | });
161 |
162 | const removeFromGroup = asyncHandler(async (req, res) => {
163 | const { chatId, userId } = req.body;
164 |
165 | const removed = await Chat.findByIdAndUpdate(
166 | chatId,
167 | {
168 | $pull: { users: userId },
169 | },
170 | { new: true }
171 | )
172 | .populate("users", "-password")
173 | .populate("groupAdmin", "-password");
174 |
175 | if (!removed) {
176 | res.status(404);
177 | throw new Error("chat Not Found");
178 | } else {
179 | res.json(removed);
180 | }
181 | });
182 | module.exports = {
183 | accessChat,
184 | fetchChats,
185 | createGroupChat,
186 | renameGroup,
187 | addToGroup,
188 | removeFromGroup,
189 | };
190 |
--------------------------------------------------------------------------------
/client/src/Components/Setting.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import styled from "styled-components";
3 | import { Disclosure } from "@headlessui/react";
4 | import { BiChevronUp } from "react-icons/bi";
5 |
6 | import {BsCircleHalf} from "react-icons/bs"
7 | import { useDispatch, useSelector } from "react-redux";
8 |
9 | import ProfileEdit from "./modal/ProfileEdit";
10 | import ImageEdit from "./modal/ImageEdit";
11 | import { colors } from "../config.js/data";
12 | import { FaCheck } from "react-icons/fa";
13 | import {toggleColor} from "../Redux/Reducer/SetColor/setColorAction"
14 |
15 | const Setting = () => {
16 | const user = useSelector((globalState) => globalState.user.userDetails);
17 | const ThemeColor = useSelector((state) => state.setColorReducer.themeColor);
18 | const [color, setColor] = useState(ThemeColor);
19 |
20 |
21 | const dispatch = useDispatch();
22 |
23 | const activeColor = (color)=> {
24 | setColor(color);
25 | dispatch(toggleColor(color));
26 | }
27 |
28 |
29 | return (
30 |
31 |
32 |
33 |
Setting
34 |
Personal Information
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
{user.name}
44 |
45 |
46 |
47 |
48 |
53 |
54 |
55 |
56 |
57 |
58 | {({ open }) => (
59 | <>
60 |
61 |
62 |
63 | Themes
64 |
65 |
66 |
71 |
72 |
73 |
74 |
75 | {
76 | colors.map((item)=>{
77 | return
80 | })
81 | }
82 |
83 |
84 |
85 | >
86 | )}
87 |
88 |
89 |
90 |
91 |
92 | {/*
93 |
Name
94 |
{user.name}
95 |
96 |
97 |
About
98 |
web developer
99 |
*/}
100 |
101 |
102 | );
103 | };
104 |
105 | const Wrapper = styled.div`
106 | animation: fadeInLeft 1s;
107 | .user-profile-img {
108 | width: 150px;
109 | height: 150px;
110 | img {
111 | min-width: 100%;
112 | }
113 | }
114 |
115 | .dialog-box{
116 | .dialog-box-wrapper .dialog-panel{
117 | background-color: ${({ theme }) => theme.colors.bg.secondary};
118 | }
119 |
120 | }
121 | .setting-block{
122 | border-bottom: 1px solid rgba(${({ theme }) => theme.colors.border});
123 | }
124 |
125 | .user-profile {
126 | position: relative;
127 | background-color: ${({ theme }) => theme.colors.bg.primary};
128 |
129 |
130 | .profile-photo-edit {
131 | position: absolute;
132 | right: 0;
133 | left: auto;
134 | bottom: 0;
135 | cursor: pointer;
136 | background-color: ${({ theme }) => theme.colors.bg.primary};
137 | .icon {
138 | color: ${({ theme }) => theme.colors.text.secondary};
139 | border-radius: 50%;
140 | }
141 | }
142 | }
143 | @media (max-width: ${({ theme }) => theme.media.mobile}) {
144 | .details {
145 | margin: 10px 50px 0px 50px;
146 | }
147 | .intro {
148 | padding: 3rem;
149 | }
150 | }
151 | `;
152 |
153 |
154 |
155 | export default Setting;
156 |
--------------------------------------------------------------------------------
/client/src/Components/Contact.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import Wave from "react-wavify";
4 |
5 | const Contact = () => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
Feel free to drop us your feedback
13 |
14 |
15 |
16 |
17 | {/*feedback form */}
18 |
19 |
20 |

25 |
26 |
27 |
28 |
29 |
Send us your feedback
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
62 |
63 |
74 |
85 |
96 |
97 |
98 | );
99 | };
100 |
101 | export default Contact;
102 |
103 | const Wrapper = styled.section`
104 | position: relative;
105 | max-width: 100vw;
106 | height: 100%;
107 | background-color: #0c1631;
108 | overflow: hidden;
109 | padding: 9rem 0;
110 |
111 | .shapes {
112 | position: absolute;
113 | top: 0;
114 | width: 100%;
115 | height: 100%;
116 | z-index: 1;
117 | .svg-1 {
118 | width: 100%;
119 | height: 100%;
120 | position: absolute;
121 | top: 0%;
122 | left: 0%;
123 | svg {
124 | fill: ${({ theme }) => theme.colors.bg2.secondary};
125 | }
126 | }
127 |
128 | .svg-2,
129 | .svg-3,
130 | .svg-4 {
131 | width: 100%;
132 | height: 100%;
133 | position: absolute;
134 | }
135 | .svg-2 {
136 | top: 80%;
137 | left: 0%;
138 | }
139 |
140 | .svg-3 {
141 | top: 75%;
142 | left: 0%;
143 | }
144 | .svg-4 {
145 | top: 70%;
146 | left: 0%;
147 | }
148 | }
149 |
150 | .custom-container {
151 | position: relative;
152 | height: 100%;
153 | padding: 6rem 5rem;
154 | z-index: 5;
155 | .wrapper {
156 | .feedback-content {
157 | height: 100%;
158 | position: relative;
159 | margin-right: 20px;
160 | padding-right: 20px;
161 | .title {
162 | text-align: center;
163 | h1 {
164 | line-height: 1.3;
165 | color: ${({ theme }) => theme.colors.white};
166 | }
167 | }
168 | .feedback-form {
169 | input,textarea {
170 | background-color: #f6f6f9;
171 | }
172 | .title {
173 | text-align: center;
174 | h2 {
175 | line-height: 1.3;
176 | color: ${({ theme }) => theme.colors.black};
177 | }
178 | }
179 | }
180 | }
181 | .feedback-content {
182 | .shape-container {
183 | top: -50px;
184 | right: -60px;
185 | img {
186 | width: 100%;
187 | }
188 | }
189 | .feedback-form {
190 | border-radius: 10px;
191 | input,textarea{
192 | color: ${({ theme }) => theme.colors.black};
193 | }
194 | }
195 | }
196 | }
197 | }
198 |
199 | @media only screen and (max-width: 992px) {
200 | h1{
201 | font-size: 2rem;
202 | margin-bottom: 2rem;
203 | }
204 | .custom-container .wrapper {
205 | .feedback-content{
206 | margin: 0;
207 | padding: 0;
208 | }
209 | }
210 | .feedback-content {
211 | margin-bottom: 2.5rem;
212 | }
213 | }
214 | `;
215 |
--------------------------------------------------------------------------------
/client/src/Components/Dropdown.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, Fragment } from "react";
2 | import { ImBlocked, ImExit } from "react-icons/im";
3 | import { CgProfile } from "react-icons/cg";
4 | import { BiDotsVerticalRounded } from "react-icons/bi";
5 | import { RiDeleteBin6Line } from "react-icons/ri";
6 | import { useSelector } from "react-redux";
7 |
8 | import { Menu, Transition } from "@headlessui/react";
9 | import UserProfile from "./SlideMenu/UserProfile";
10 | import { MdFavorite } from "react-icons/md";
11 | import { toast } from "react-toastify";
12 |
13 | const Dropdown = (props) => {
14 | const [sender, setSender] = useState();
15 |
16 | const senderUser = useSelector(
17 | (globalState) => globalState.chat.selectedChat
18 | );
19 |
20 | const handleClickMarkAsFavourites = () => {
21 | toast.success("We are working this feature. Available Soon", {
22 | position: "top-right",
23 | autoClose: 1000,
24 | hideProgressBar: false,
25 | closeOnClick: true,
26 | pauseOnHover: true,
27 | draggable: true,
28 | progress: undefined,
29 | theme: "light",
30 | });
31 | };
32 | const handleClickDeleteChat = () => {
33 | toast.success("We are working this feature. Available Soon", {
34 | position: "top-right",
35 | autoClose: 1000,
36 | hideProgressBar: false,
37 | closeOnClick: true,
38 | pauseOnHover: true,
39 | draggable: true,
40 | progress: undefined,
41 | theme: "light",
42 | });
43 | };
44 | const handleClickLeaveGroup = () => {
45 | toast.success("We are working this feature. Available Soon", {
46 | position: "top-right",
47 | autoClose: 1000,
48 | hideProgressBar: false,
49 | closeOnClick: true,
50 | pauseOnHover: true,
51 | draggable: true,
52 | progress: undefined,
53 | theme: "light",
54 | });
55 | };
56 | useEffect(() => {
57 | setSender(senderUser);
58 | }, [senderUser]);
59 | return (
60 | <>
61 |
155 | >
156 | );
157 | };
158 |
159 | export default Dropdown;
160 |
--------------------------------------------------------------------------------
/client/src/Components/SlideMenu/UserProfile.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useSelector } from "react-redux";
3 | import styled from "styled-components";
4 | import {
5 | getGroupProfileDetails,
6 | getSender,
7 | getSenderPic,
8 | getSenderProfileDetails,
9 | } from "../../HelperFunction/chat.Helper";
10 | import GroupProfile from "../GroupProfile";
11 | import Profile from "../Profile";
12 |
13 | const UserProfile = ({ closeModal }) => {
14 | const [sender, setSender] = useState("");
15 | const [senderProfileData, setSenderProfileData] = useState();
16 | const senderUser = useSelector(
17 | (globalState) => globalState.chat.selectedChat
18 | );
19 | const loggedUser = useSelector((globalState) => globalState.user.userDetails);
20 |
21 | useEffect(() => {
22 | if (senderProfileData) {
23 | // console.log(senderProfileData);
24 | }
25 | }, [senderProfileData]);
26 |
27 | useEffect(() => {
28 | let senderData;
29 | if (sender) {
30 | if (!sender.isGroupChat) {
31 | senderData = getSenderProfileDetails(loggedUser, sender);
32 | } else {
33 | senderData = getGroupProfileDetails(loggedUser, sender);
34 | }
35 | if (senderData) {
36 | setSenderProfileData(senderData);
37 | }
38 | }
39 | setSenderProfileData(senderData);
40 | }, [sender]);
41 | // console.log(sender);
42 | useEffect(() => {
43 | setSender(senderUser);
44 | }, [senderUser]);
45 |
46 | return sender ? (
47 | <>
48 |
49 | {/*
50 |
51 |
59 |
60 |
61 |
62 |

70 |
71 |
72 | {sender.isGroupChat
73 | ? sender.chatName
74 | : getSender(loggedUser, sender.users)}
75 |
76 |
77 |
About
78 |
Lorem ipsum dolor sit.
79 |
80 |
81 |
Email
82 |
xyz@gmail.com
83 |
84 |
85 |
86 |
87 |
88 |
*/}
89 |
90 | {/* need to change */}
91 |
92 | {!sender.isGroupChat ? (
93 | <>
94 | {/* sender profile */}
95 |
96 | {senderProfileData ? (
97 | <>
98 |
109 | >
110 | ) : (
111 | ""
112 | )}
113 |
114 | >
115 | ) : (
116 | <>
117 | {/* Group profile */}
118 | {/* group name , */}
119 |
120 |
121 | {senderProfileData ? (
122 | <>
123 |
133 | >
134 | ) : (
135 | ""
136 | )}
137 |
138 | >
139 | )}
140 |
141 |
142 | >
143 | ) : (
144 | ""
145 | );
146 | };
147 |
148 | export default UserProfile;
149 |
150 | const Wrapper = styled.section`
151 | .sender-profile {
152 | overflow-y: scroll;
153 | height: 100vh;
154 | .profile-tab {
155 | animation: none;
156 | }
157 | .chat-menu {
158 | margin-top: 2rem;
159 | padding-left: 2rem;
160 | padding-right: 2rem;
161 | .icon {
162 | display: flex;
163 | }
164 | }
165 | .user-details {
166 | overflow: hidden;
167 | height: 100%;
168 | }
169 | .details {
170 | .user-profile-image {
171 | width: 200px;
172 | height: 200px;
173 | img {
174 | width: 100%;
175 | height: auto;
176 | }
177 | }
178 | }
179 | }
180 | `;
181 |
--------------------------------------------------------------------------------
/client/src/Components/ChatMenu.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import styled from "styled-components";
3 | import Profile from "./Profile";
4 | import Favourite from "./Favourite";
5 | import Contacts from "./Contacts";
6 | import Setting from "./Setting";
7 | import Default from "./Default";
8 |
9 | import { toggleTab } from "../Redux/Reducer/Tab/tabAction";
10 | import { useDispatch, useSelector } from "react-redux";
11 | import {
12 | createChat,
13 | fetchChats,
14 | fetchUser,
15 | fetchUserClear,
16 | } from "../Redux/Reducer/Chat/chat.action";
17 | import { toast } from "react-toastify";
18 |
19 | const ChatMenu = () => {
20 | const [search, setSearch] = useState("");
21 | const [searchResult, setSearchResult] = useState([]);
22 |
23 | const tabIndex = useSelector((state) => state.tabReducer);
24 | const result = useSelector((globalState) => globalState.chat.newUser);
25 | const user = useSelector((globalState) => globalState.user.userDetails);
26 | const UserLoading = useSelector((globalState)=> globalState.chat.isUserLoading);
27 | const [showResult, setShowResult] = useState(false)
28 | const chat = useSelector((globalState) => globalState.chat.chats);
29 |
30 | const dispatch = useDispatch();
31 |
32 | const handleChange = (e) => {
33 | setSearch(e.target.value);
34 |
35 | };
36 |
37 | useEffect(() => {
38 | setSearchResult(result);
39 | }, [result]);
40 |
41 | const handleClick = () => {
42 | if (!search) {
43 | toast.warning("Please Enter valid Email or Name", {
44 | autoClose: 1000,
45 | });
46 | return;
47 | }
48 | setShowResult(true)
49 | dispatch(fetchUser(search));
50 | };
51 |
52 | const createNewChat = async (item) => {
53 |
54 | const UserExist = chat.some((elem)=> elem.users[1]._id === item._id)
55 |
56 | if(UserExist){
57 | toast.error("contact already exist", {
58 | autoClose: 1000,
59 | });
60 | return
61 | }
62 | else{
63 | toast.success("contact successfully added", {
64 | autoClose: 1000,
65 | });
66 | }
67 |
68 | await dispatch(createChat(item._id));
69 | await dispatch(fetchChats());
70 | await dispatch(toggleTab(3));
71 | };
72 |
73 | useEffect(() => {
74 | if(tabIndex !== 4 || !search){
75 | setSearch("")
76 | dispatch(fetchUserClear())
77 | }
78 | // eslint-disable-next-line react-hooks/exhaustive-deps
79 | }, [tabIndex, search])
80 |
81 |
82 | return (
83 | <>
84 |
85 |
86 |
105 |
106 |
107 |
108 |
113 |
114 |
115 |
116 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | >
132 | );
133 | };
134 |
135 | const Wrapper = styled.section`
136 | position: relative;
137 | max-width: 20rem;
138 | height: 100vh;
139 | min-width: 20rem;
140 | z-index: 9;
141 | overflow: hidden;
142 | background-color: ${({ theme }) => theme.colors.bg.primary};
143 | border-right: 1px solid rgba(${({ theme }) => theme.colors.border}, 0.3);
144 | animation: fadeInLeft 1s;
145 | /* overflow-x: hidden;
146 | overflow-y: scroll; */
147 | .tab-pane {
148 | display: none;
149 | }
150 | .tab-pane.active {
151 | display: block;
152 | }
153 | .chat-menu {
154 | padding: 1rem 1rem;
155 | background-color: ${({ theme }) => theme.colors.bg.primary};
156 | border-bottom: 1px solid rgba(${({ theme }) => theme.colors.border});
157 | input {
158 | color: ${({ theme }) => theme.colors.heading};
159 | background-color: ${({ theme }) => theme.colors.bg.primary};
160 | border-bottom: 1px solid ${({ theme }) => theme.colors.heading};
161 | &:hover {
162 | background-color: ${({ theme }) => theme.colors.bg.primary};
163 | }
164 | }
165 | .icon {
166 | font-size: 1.5rem;
167 | color: ${({ theme }) => theme.colors.heading};
168 | &:hover{
169 | color: ${({ theme }) => theme.colors.primaryRgb};
170 | }
171 | }
172 | .search-icon {
173 | background-color: ${({ theme }) => theme.colors.bg.primary};
174 | /* background-color: rgb(241, 245, 249); */
175 | &:hover {
176 | background-color: ${({ theme }) => theme.colors.bg.primary};
177 | /* background-color: rgb(226, 232, 240); */
178 | }
179 | }
180 | }
181 | @media (max-width: ${({ theme }) => theme.media.mobile}) {
182 | padding: 1.5rem 0;
183 | margin-top: 60px;
184 | position: relative;
185 | max-width: 100vw;
186 | min-width: 100vw;
187 | .chat-menu {
188 | padding: 2rem 1.5rem;
189 | .icon {
190 | font-size: 2rem;
191 | }
192 | }
193 | input {
194 | font-size: 1.5rem;
195 | width: 100%;
196 | background-color: ${({ theme }) => theme.colors.bg.primary};
197 | &:hover {
198 | background-color: ${({ theme }) => theme.colors.bg.primary};
199 | }
200 | }
201 | h1 {
202 | font-size: 2rem;
203 | }
204 | p {
205 | font-size: 1.5rem;
206 | }
207 | }
208 | `;
209 |
210 | export default ChatMenu;
211 |
--------------------------------------------------------------------------------
/client/src/Components/Auth/LoginForm.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { NavLink } from "react-router-dom";
3 | // import Social from "../../Styles/Social";
4 | import { Button } from "../../Styles/Button";
5 |
6 | import { useDispatch, useSelector } from "react-redux";
7 | import { useNavigate } from "react-router-dom";
8 | // Redux
9 | import { clearAuthStore, signIn } from "../../Redux/Reducer/Auth/auth.action";
10 | import { toast } from "react-toastify";
11 | import ShowPasswordToggle from "../ShowPasswordToggle";
12 | import Loading1 from "../Loading1";
13 |
14 | const LoginForm = () => {
15 | const dispatch = useDispatch();
16 | const navigate = useNavigate();
17 | const [Icon, InputType] = ShowPasswordToggle();
18 | const [message, setMessage] = useState("");
19 | const [userData, setUserData] = useState({
20 | email: "",
21 | password: "",
22 | });
23 | const [loading1, setLoading1] = useState(false);
24 |
25 | const result = useSelector((globalState) => globalState.auth.message);
26 | const status = useSelector((globalState) => globalState.auth.success);
27 | // const user = useSelector((globalState) => globalState.user.userDetails);
28 | // const serverResponse = useSelector((globalState) => globalState.auth);
29 | const navigateToHome = async () => {
30 | await navigate("/");
31 | await dispatch(clearAuthStore());
32 | };
33 |
34 | useEffect(() => {
35 | if (result) {
36 | // if (!user) {
37 | // dispatch(clearAuthStore());
38 | // return;
39 | // }
40 | setMessage(result);
41 | if (!status) {
42 | toast.error(result, {
43 | position: "top-right",
44 | autoClose: 5000,
45 | hideProgressBar: false,
46 | closeOnClick: true,
47 | pauseOnHover: true,
48 | draggable: true,
49 | progress: undefined,
50 | theme: "light",
51 | });
52 | dispatch(clearAuthStore());
53 | } else {
54 | toast.success(result, {
55 | position: "top-right",
56 | autoClose: 5000,
57 | hideProgressBar: false,
58 | closeOnClick: true,
59 | pauseOnHover: true,
60 | draggable: true,
61 | progress: undefined,
62 | theme: "light",
63 | });
64 | // console.log("redirecting");
65 | // dispatch(clearAuthStore());
66 | navigateToHome();
67 | // console.log("redirected");
68 | }
69 | }
70 | }, [result]);
71 |
72 | const handleChange = (e) => {
73 | setUserData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
74 | };
75 |
76 | const handleLogin = async () => {
77 | if (userData.email && userData.password) {
78 | setLoading1(true);
79 | await dispatch(signIn(userData));
80 | setLoading1(false);
81 | // toast.success("login Sucessfully");
82 | // navigate("/verification");
83 | // navigate("/");
84 | // alert("navigated");
85 |
86 | // dispatch(getMySelf());
87 | // setUserData({ email: "", password: "" });
88 | } else {
89 | toast.error("Please Fill the Data", {
90 | autoClose: 1000,
91 | });
92 | }
93 | };
94 |
95 | return (
96 | <>
97 |
98 |
99 |
100 |
101 |
Welcome back !
102 |
Sign in to continue...
103 |
104 |
174 |
175 |
176 | >
177 | );
178 | };
179 |
180 | export default LoginForm;
181 |
--------------------------------------------------------------------------------
/client/src/Components/Features.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { featuresImg } from "../config.js/data";
4 |
5 | const Features = () => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | App Features
13 |
14 |
15 | We provide Best feature for app Design and coding
16 |
17 |
18 |
19 |
20 |
21 |
22 | {featuresImg.map((item) => (
23 | -
24 |
25 |
26 |
27 |

28 |
29 |
30 |
31 |
{item.name}
32 |
{item.content}
33 |
34 |
35 |
36 | ))}
37 |
38 |
39 |
40 |
41 |
49 |
50 | );
51 | };
52 |
53 | export default Features;
54 |
55 | const Wrapper = styled.section`
56 | position: relative;
57 | padding: 5rem 0;
58 | background-image: url("/images/pattern-bg.png");
59 | background-position: center;
60 | background-repeat: no-repeat;
61 | background-color: ${({ theme }) => theme.colors.bg2.secondary};
62 |
63 | .svg {
64 | width: 100%;
65 | height: 100%;
66 | position: absolute;
67 | top: 0;
68 | svg {
69 | position: absolute;
70 | bottom: 0%;
71 | left: 0%;
72 | fill: ${({ theme }) => theme.colors.bg2.primary};
73 | transform: rotate(180deg);
74 | }
75 | }
76 |
77 | .custom-container {
78 | position: relative;
79 | max-width: 100%;
80 | padding-left: 15px;
81 | padding-right: 15px;
82 | margin: 0 auto;
83 | z-index: 20;
84 |
85 | .section-header {
86 | margin: 0 50px;
87 | h2 {
88 | font-size: calc(1rem + 20 * (100vw - 320px) / 1600);
89 | font-weight: 700;
90 | color: ${({ theme }) => theme.colors.heading};
91 | margin: 25px 0;
92 | }
93 | }
94 |
95 | .features-list {
96 | text-align: center;
97 | ul {
98 | display: flex;
99 | flex-wrap: wrap;
100 | justify-content: center;
101 | li {
102 | position: relative;
103 | margin: 0px 20px 40px;
104 | }
105 | }
106 | }
107 |
108 | .features-box {
109 | background: ${({ theme }) => theme.colors.bg2.primary};
110 | border-width: 1px 1px 1px 1px;
111 | border-color: ${({ theme }) => theme.colors.border2.primary};
112 | border-radius: 10px;
113 | text-align: center;
114 | padding: 30px;
115 | transition: all 0.5s;
116 | &:hover {
117 | box-shadow: 0px 0px 24px
118 | ${({ theme }) => theme.colors.boxShadow.primary};
119 | transform: scale(1.05);
120 | }
121 |
122 | .features-box-icon {
123 | div {
124 | display: flex;
125 | justify-content: center;
126 | width: 120px;
127 | margin: 0 auto;
128 | height: 120px;
129 | align-items: center;
130 | border-radius: 50%;
131 | img {
132 | max-width: 100%;
133 | height: auto;
134 | }
135 | }
136 | }
137 | .features-box-content {
138 | color: ${({ theme }) => theme.colors.heading};
139 | h3 {
140 | font-weight: 600;
141 | }
142 | }
143 | }
144 | }
145 | @media only screen and (min-width: 1680px) {
146 | .custom-container {
147 | max-width: 1450px;
148 | padding-right: 15px;
149 | padding-left: 15px;
150 | margin-right: auto;
151 | margin-left: auto;
152 | }
153 | }
154 | @media screen and (min-width: 992px) {
155 | .features-list {
156 | margin: 75px 0;
157 | }
158 | .features-box-content {
159 | h3 {
160 | font-size: 1.5rem;
161 | }
162 | }
163 | .section-header {
164 | h2 {
165 | width: 34rem;
166 | }
167 | }
168 | .features-list li {
169 | width: calc(30% - 20px);
170 | }
171 | .features-list li:nth-child(2),
172 | li:nth-child(5) {
173 | bottom: 50px;
174 | position: relative;
175 | }
176 | }
177 | @media only screen and (max-width: 992px) {
178 | .section-header {
179 | h2 {
180 | width: auto;
181 | }
182 | }
183 | .features-list {
184 | margin: 25px 0;
185 | }
186 | .features-list li:nth-child(2),
187 | li:nth-child(5) {
188 | position: relative;
189 | bottom: 0;
190 | }
191 | }
192 | @media only screen and (min-width: 522px) and (max-width: 992px) {
193 | .features-box-content {
194 | h3 {
195 | font-size: 1.2rem;
196 | }
197 | p {
198 | font-size: 0.8rem;
199 | }
200 | }
201 | .features-list li {
202 | width: 200px;
203 | }
204 | }
205 | @media only screen and (max-width: 521px) {
206 | .features-box-content {
207 | h3 {
208 | font-size: 1.5rem;
209 | }
210 | p {
211 | font-size: 1rem;
212 | }
213 | }
214 | .features-box-content {
215 | h3 {
216 | font-size: 1.1rem;
217 | }
218 | p {
219 | font-size: 0.8rem;
220 | }
221 | }
222 | .section-header {
223 | h2 {
224 | width: auto;
225 | }
226 | }
227 | .features-list li {
228 | width: 260px;
229 | }
230 | .features-list li:nth-child(2),
231 | li:nth-child(5) {
232 | position: relative;
233 | bottom: 0;
234 | }
235 | }
236 | `;
237 |
--------------------------------------------------------------------------------
/client/src/Components/Auth/ForgotPassword.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { useNavigate } from "react-router-dom";
4 | import { toast, ToastContainer } from "react-toastify";
5 | import {
6 | clearAuthStore,
7 | forgotPassword,
8 | } from "../../Redux/Reducer/Auth/auth.action";
9 | const ForgotPassword = () => {
10 | const dispatch = useDispatch();
11 | const navigate = useNavigate();
12 | const [userData, setUserData] = useState({
13 | email: "",
14 | });
15 | const [message, setMessage] = useState("");
16 |
17 | const result = useSelector((globalState) => globalState.auth.message);
18 | const status = useSelector((globalState) => globalState.auth.success);
19 |
20 | useEffect(() => {
21 | if (result) {
22 | setMessage(result);
23 | if (!status) {
24 | toast.error(result, {
25 | position: "top-right",
26 | autoClose: 5000,
27 | hideProgressBar: false,
28 | closeOnClick: true,
29 | pauseOnHover: true,
30 | draggable: true,
31 | progress: undefined,
32 | theme: "light",
33 | });
34 | } else {
35 | toast.success("Password Reset Link sent Successfully", {
36 | position: "top-right",
37 | autoClose: 5000,
38 | hideProgressBar: false,
39 | closeOnClick: true,
40 | pauseOnHover: true,
41 | draggable: true,
42 | progress: undefined,
43 | theme: "light",
44 | });
45 | }
46 | }
47 | }, [result]);
48 | // on change of the email field
49 | const handleChange = (e) => {
50 | setUserData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
51 | };
52 |
53 | const sendPasswordResetLink = () => {
54 | if (userData.email === null || userData.email === "") {
55 | toast.warn("please enter a valid email", {
56 | position: "top-right",
57 | autoClose: 5000,
58 | hideProgressBar: false,
59 | closeOnClick: true,
60 | pauseOnHover: true,
61 | draggable: true,
62 | progress: undefined,
63 | theme: "light",
64 | });
65 | return;
66 | }
67 | // dispatch action htmlFor password reset link
68 | dispatch(forgotPassword(userData));
69 | };
70 |
71 | // Redirecting to forgot password PAge
72 | const NavigateToForgotPasswordPage = () => {
73 | dispatch(clearAuthStore());
74 | // navigate("/forgot-password");
75 | setMessage("");
76 | };
77 |
78 | // Redirecting to Home PAge
79 | const NavigateToHomePage = () => {
80 | dispatch(clearAuthStore());
81 | navigate("/");
82 | };
83 |
84 | return (
85 | <>
86 |
98 |
99 |
100 | {/* Here we have to add a navabar component */}
101 |
102 |
103 |
104 | Forogt Password
105 |
106 | {/*
*/}
159 |
160 |
161 |
162 |
163 | >
164 | );
165 | };
166 |
167 | export default ForgotPassword;
168 |
--------------------------------------------------------------------------------
/client/src/GlobalStyle/GlobalStyle.js:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from "styled-components";
2 |
3 | export const GlobalStyle = createGlobalStyle`
4 | * {
5 | margin: 0;
6 | padding: 0;
7 | box-sizing: border-box;
8 | font-family: 'Montserrat', sans-serif;
9 | scroll-behavior: smooth;
10 | }
11 |
12 |
13 | ::-webkit-scrollbar {
14 | background-color: initial;
15 | width: 5px;
16 | }
17 | ::-webkit-scrollbar-thumb {
18 | background-color: rgba(${({ theme }) => theme.colors.rgb.primary}, .2);
19 | border-radius: 10px;
20 | }
21 | ::-webkit-scrollbar-track {
22 | box-shadow: inset 0 0 6px ${({ theme }) => theme.colors.border};
23 | }
24 |
25 | .App{
26 | position: relative;
27 | background-color: ${({ theme }) => theme.colors.bg.primary};
28 | color: ${({ theme }) => theme.colors.heading};
29 | overflow-x: hidden;
30 | }
31 |
32 | .box{
33 | position: absolute;
34 | z-index: 100;
35 | }
36 |
37 | .dialog-wrapper {
38 | .dialog-container{
39 |
40 | background-color: rgba(${({ theme }) => theme.colors.border},0.5)
41 | }
42 | .dialog-panel{
43 | color: ${({ theme }) => theme.colors.heading};
44 | background-color: ${({ theme }) => theme.colors.bg.primary};
45 | .button, .btn{
46 | background-color: ${({ theme }) => theme.colors.primaryRgb};
47 | }
48 | .btn{
49 | color: ${({ theme }) => theme.colors.white};
50 | }
51 | .close-btn{
52 | color: ${({ theme }) => theme.colors.primaryRgb};
53 | &:hover{
54 | color: ${({ theme }) => theme.colors.white} !important;
55 | background-color: ${({ theme }) => theme.colors.primaryRgb} !important;
56 | }
57 | }
58 | .user-list{
59 | max-height: 190px;
60 | }
61 | .search-user-box {
62 | .profile {
63 | position: absolute;
64 | left: 0;
65 | width: 50px;
66 | height: 50px;
67 | }
68 | .details {
69 | padding: 12px 12px 12px 60px;
70 | }
71 | .user-add{
72 | color: ${({ theme }) => theme.colors.heading};
73 | &:hover{
74 | background-color: ${({ theme }) => theme.colors.bg.secondary};
75 | }
76 | }
77 | }
78 | .close-btn{
79 | &:hover{
80 | color: black;
81 | background-color: rgb(6, 182, 212);
82 | }
83 | }
84 | h3,h5,label{
85 | color: ${({ theme }) => theme.colors.heading}
86 | }
87 | input{
88 | background-color: ${({ theme }) => theme.colors.bg.secondary};
89 | }
90 | }
91 | }
92 |
93 | .user-profile-sidebar {
94 | .dialog-wrapper {
95 | .dialog-container {
96 | background: none;
97 | .dialog-panel{
98 | background-color: ${({ theme }) => theme.colors.bg.primary};
99 | box-shadow: 0 0 10px rgb(${({ theme }) => theme.colors.bg.secondary});
100 |
101 | .sidebar{
102 | color: ${({ theme }) => theme.colors.heading};
103 | .sidebar-active{
104 | background-color: ${({ theme }) => theme.colors.bg.primary};
105 | }
106 | }
107 | }
108 | }
109 | }
110 | }
111 |
112 | .disclosure-Panel{
113 | .btn-style{
114 | width: 2rem;
115 | height: 2rem;
116 | margin-left: 1rem;
117 | border: none;
118 | outline: none;
119 | cursor: pointer;
120 | }
121 | }
122 |
123 | button,
124 | input,
125 | label,
126 | select,
127 | textarea {
128 | font-size: 100%;
129 | margin: 0;
130 | padding: 0;
131 | }
132 |
133 | .input {
134 | -webkit-appearance: none;
135 | appearance: none;
136 | border-color: rgba(209, 213, 219, 1);
137 | border-radius: 0.375rem;
138 | border-width: 1px;
139 | padding: 0.5rem 0.75rem;
140 | width: 100%;
141 | }
142 |
143 | h1,
144 | h2,
145 | h3,
146 | h4,
147 | h5,
148 | h6 {
149 | margin-top: 0;
150 | margin-bottom: 0.5rem;
151 | font-weight: 700;
152 | line-height: 1.2;
153 | color: ${({ theme }) => theme.colors.heading};
154 | }
155 |
156 | a {
157 | font-size: 0.8rem;
158 | color: ${({ theme }) => theme.colors.black};
159 | }
160 |
161 | p {
162 | display: block;
163 | margin-block-start: 1em;
164 | margin-block-end: 1em;
165 | margin-inline-start: 0px;
166 | margin-inline-end: 0px;
167 | }
168 |
169 | p,span {
170 | font-size: 1rem;
171 | margin-top: 0;
172 | margin-bottom: 1rem;
173 |
174 | }
175 |
176 | h1 {
177 | font-size: 3rem;
178 | font-weight: 700;
179 | line-height: 2.5rem;
180 | }
181 |
182 | h2 {
183 | font-size: 2.5rem;
184 | font-weight: 700;
185 | }
186 |
187 | h3 {
188 | font-size: 2rem;
189 | font-weight: 500;
190 | line-height: 2rem;
191 | }
192 | h4{
193 | font-size: 1.5rem;
194 | font-weight: 500;
195 | line-height: 2rem;
196 | }
197 |
198 | ul,
199 | li {
200 | list-style: none;
201 | margin: 0;
202 | padding: 0;
203 | }
204 |
205 | .btn-light {
206 | color: ${({ theme }) => theme.colors.black};
207 | background-color: ${({ theme }) => theme.colors.btnlight};
208 | border-color: ${({ theme }) => theme.colors.btnlight};
209 | }
210 |
211 |
212 |
213 | @keyframes fadeInLeft {
214 | 0% {
215 | opacity: 0;
216 | transform: translate3d(-70%, 0, 0);
217 | }
218 |
219 | 100% {
220 | opacity: 1;
221 | transform: translate3d(0, 0, 0);
222 | }
223 | }
224 |
225 | .typing-loader {
226 | width: 8px;
227 | height: 8px;
228 | border-radius: 100%;
229 | animation: loadertyping .8s ease-in-out infinite alternate;
230 | animation-delay: .32s;
231 | position: absolute;
232 | left: 0;
233 | right: 0;
234 | margin: 0 auto;
235 | top: -28px;
236 | }
237 |
238 | .typing-loader:after, .typing-loader:before {
239 | content: "";
240 | position: absolute;
241 | width: 8px;
242 | height: 8px;
243 | border-radius: 100%;
244 | box-shadow: 0 40px 0 #fff;
245 | animation: loadertyping .8s ease-in-out infinite alternate;
246 | }
247 | .typing-loader:before {
248 | left: -17px;
249 | animation-delay: .48s;
250 | }
251 | .typing-loader:after {
252 | right: -17px;
253 | animation-delay: .16s;
254 | }
255 | @keyframes loadertyping {
256 | 0% {
257 | box-shadow: 0 30px 0 #fff;
258 | }
259 | 100% {
260 | box-shadow: 0 10px 0 #fff;
261 | }
262 | }
263 |
264 |
265 |
266 | @media (max-width: ${({ theme }) => theme.media.mobile}) {
267 | html {
268 | font-size: 75%;
269 | }
270 |
271 | .grid {
272 | gap: 3.2rem;
273 | }
274 |
275 | .grid-cols-2 {
276 | grid-template-columns: 1fr;
277 | }
278 | .user-chat-show{
279 | position: absolute;
280 | top: 0;
281 | left: 0;
282 | width: 100vw;
283 | z-index: 40;
284 | transition: transform 0.25s linear;
285 | }
286 | .fadeInRight2{
287 | transform: translateX(100vw);
288 | }
289 | .fadeInRight{
290 | transform: translateX(0);
291 | }
292 | }
293 |
294 | `;
295 |
--------------------------------------------------------------------------------
/client/src/Components/modal/Invite.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { Fragment, useState } from "react";
3 | import { Dialog, Transition } from "@headlessui/react";
4 | import { BiGroup } from "react-icons/bi";
5 | import { useDispatch, useSelector } from "react-redux";
6 | // import Loading1 from "../Loading1";
7 | // import Loading1 from "../Loading1";
8 | import {
9 | createGroupChat,
10 | fetchChats,
11 | fetchUser,
12 | fetchUserClear,
13 | } from "../../Redux/Reducer/Chat/chat.action";
14 | import { AiOutlinePlus } from "react-icons/ai";
15 | import Spinner from "../../Styles/Spinner";
16 | import { ToastContainer, toast } from "react-toastify";
17 | import { inviteNewUser } from "../../Redux/Reducer/User/user.action";
18 |
19 | const Invite = () => {
20 | const dispatch = useDispatch();
21 | let [isOpen, setIsOpen] = useState(false);
22 | const [loading1, setLoading1] = useState(false);
23 | const [email, setEmail] = useState("");
24 | const [invitingInfo, setInvitingInfo] = useState("");
25 |
26 | function closeModal() {
27 | setIsOpen(false);
28 | }
29 |
30 | function openModal() {
31 | setIsOpen(true);
32 | }
33 |
34 | const sendInvite = async () => {
35 | try {
36 | setLoading1(true);
37 | if (!email) {
38 | toast.error("Please Enter Friend's Email", {
39 | position: "top-right",
40 | autoClose: 5000,
41 | hideProgressBar: false,
42 | closeOnClick: true,
43 | pauseOnHover: true,
44 | draggable: true,
45 | progress: undefined,
46 | theme: "light",
47 | });
48 | return;
49 | }
50 |
51 | await dispatch(inviteNewUser(email));
52 | setLoading1(false);
53 | toast.success("Invitation Sent Successfully", {
54 | position: "top-right",
55 | autoClose: 5000,
56 | hideProgressBar: false,
57 | closeOnClick: true,
58 | pauseOnHover: true,
59 | draggable: true,
60 | progress: undefined,
61 | theme: "light",
62 | });
63 | setEmail("");
64 | setIsOpen(false);
65 | } catch (error) {
66 | toast.error(error.response.data.message, {
67 | position: "top-right",
68 | autoClose: 5000,
69 | hideProgressBar: false,
70 | closeOnClick: true,
71 | pauseOnHover: true,
72 | draggable: true,
73 | progress: undefined,
74 | theme: "light",
75 | });
76 | }
77 | };
78 |
79 | return (
80 | <>
81 | openModal()}>
82 | {/* */}
83 | Invite
84 |
85 | {/* */}
86 |
87 |
176 |
177 | >
178 | );
179 | };
180 |
181 | export default Invite;
182 |
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 | import "./App.css";
3 | import Login from "./Components/Auth/Login";
4 | import Signup from "./Components/Auth/Signup";
5 | import AuthPage from "./Pages/AuthPage";
6 | // import HomePage from "./Pages/HomePage";
7 |
8 | import { ThemeProvider } from "styled-components";
9 | import { GlobalStyle } from "./GlobalStyle/GlobalStyle";
10 | import React, { Suspense, useEffect, useState } from "react";
11 | import Loading from "./Components/Loading";
12 | import Team from "./Components/Team";
13 | import Contact from "./Components/Contact";
14 | import Features from "./Components/Features";
15 | import { useSelector } from "react-redux";
16 |
17 | import { useDispatch } from "react-redux";
18 | import { getMySelf } from "./Redux/Reducer/User/user.action";
19 | import { fetchChats } from "./Redux/Reducer/Chat/chat.action";
20 | import Verification from "./Components/Verification";
21 | import Verify from "./Components/Verify";
22 |
23 | import AOS from "aos";
24 | import "aos/dist/aos.css";
25 | import ForgotPassword from "./Components/Auth/ForgotPassword";
26 | import ResetPassword from "./Components/Auth/ResetPassword";
27 | import ErrorPage from "./Components/ErrorPage";
28 | const HomePage = React.lazy(() => import("./Pages/HomePage"));
29 |
30 | AOS.init({
31 | once: true,
32 | duration: 2000,
33 | offset: 100,
34 | });
35 |
36 | // const socket = io.connect("http://localhost:4000");
37 |
38 | function App() {
39 | const [loading, setloading] = useState(true);
40 | // const [status, setStatus] = useState(false);
41 | const dispatch = useDispatch();
42 | // const navigate = useNavigate();
43 | const darkThemeEnabled = useSelector(
44 | (state) => state.themeReducer.darkThemeEnabled
45 | );
46 | // const user = useSelector((globalState) => globalState.user.userDetails);
47 |
48 | const ThemeColor = useSelector((state) => state.setColorReducer.themeColor);
49 | const rgb = ThemeColor.split(")")[0].split("(")[1];
50 |
51 | const lightTheme = {
52 | colors: {
53 | heading: "rgb(24 24 29)",
54 | heading2: "rgb(255, 255, 255)",
55 | white: "#fff",
56 | black: " #212529",
57 | cyan: "#1ca9fe",
58 | green: "#4eac6d",
59 | danger: "#ff4e2b",
60 | light: "#223645",
61 | primaryRgb: `${ThemeColor}`,
62 |
63 | text: {
64 | primary: "#000000",
65 | secondary: "rgba(29 ,29, 29, .8)",
66 | },
67 |
68 | rgb: {
69 | primary: `${rgb}`,
70 | secondary: "78,172,109",
71 | cyan: "28,157,234",
72 | heading: "0,0,0",
73 | },
74 |
75 | bg: {
76 | primary: "#fff",
77 | secondary: "#eff7fe",
78 | },
79 | bg2: {
80 | primary: "#fff",
81 | secondary: "rgba(28,157,234,.05)",
82 | },
83 |
84 | btn: {
85 | primary: `${rgb}`,
86 | secondary: "22 163 74",
87 | danger: "255, 78, 43",
88 | light: "#f6f6f9",
89 | },
90 | border2: {
91 | primary: "#00000026",
92 | },
93 | boxShadow: {
94 | primary: "rgba(28, 157, 234, 0.2)",
95 | },
96 |
97 | hr: "#ffffff",
98 | border: "181, 181, 181",
99 | img_border: "255, 255, 255",
100 | gradient: "linear-gradient(145deg,#1ca9fe,#1c6ee9);",
101 | },
102 | media: {
103 | mobile: "800px",
104 | tab: "998px",
105 | },
106 | };
107 | const darkTheme = {
108 | colors: {
109 | heading: "rgb(255, 255, 255)",
110 | heading2: "rgb(24 24 29)",
111 | white: "#ffffff",
112 | black: "#000000",
113 | cyan: "#1ca9fe",
114 | green: "#4eac6d",
115 | danger: "#ff4e2b",
116 | light: "#223645",
117 | primaryRgb: `${ThemeColor}`,
118 |
119 | text: {
120 | primary: "#212529",
121 | secondary: "#8f9198",
122 | },
123 |
124 | rgb: {
125 | primary: `${rgb}`,
126 | secondary: "78,172,109",
127 | cyan: "28,157,234",
128 | heading: "255,255,255",
129 | },
130 |
131 | bg: {
132 | black: "#000000",
133 | primary: "#262626",
134 | secondary: "#2e2e2e",
135 | },
136 | border2: {
137 | primary: "#FFFFFF26",
138 | },
139 |
140 | bg2: {
141 | primary: "#0c1631",
142 | secondary: "#0e1b38",
143 | },
144 | boxShadow: {
145 | primary: "rgba(1, 201 ,245, 0.4)",
146 | },
147 |
148 | btn: {
149 | primary: `${rgb}`,
150 | secondary: "22 163 74",
151 | danger: "255, 78, 43",
152 | light: "#25262c",
153 | },
154 |
155 | hr: "#ffffff",
156 | border: "65, 66, 72",
157 | img_border: "31, 41, 55",
158 | gradient: "linear-gradient(145deg,#1ca9fe,#1c6ee9);",
159 | },
160 | media: {
161 | mobile: "800px",
162 | tab: "998px",
163 | },
164 | };
165 |
166 | const getUserData = async () => {
167 | await dispatch(getMySelf());
168 | await dispatch(fetchChats());
169 | };
170 |
171 | useEffect(() => {
172 | if (localStorage.ETalkUser) {
173 | getUserData();
174 | setloading(false);
175 | } else {
176 | setTimeout(() => {
177 | setloading(false);
178 | }, 1000);
179 | }
180 | // eslint-disable-next-line
181 | }, [localStorage]);
182 |
183 | // useEffect(() => {
184 | // if (user) {
185 | // setStatus(user.is_verified);
186 | // }
187 | // if (status !== true) {
188 | // navigate("/verification");
189 | // }
190 | // }, [user]);
191 |
192 | return (
193 |
194 |
195 |
196 | {loading ? (
197 |
198 | ) : (
199 |
202 |
203 | >
204 | }
205 | >
206 |
207 | } />
208 | } />
209 | } />
210 | } />
211 | }
214 | />
215 | } />
216 | } />
217 | } />
218 | }>
219 | } />
220 | } />
221 |
222 | } />
223 |
224 |
225 | )}
226 |
227 |
228 | );
229 | }
230 |
231 | export default App;
232 |
--------------------------------------------------------------------------------
/client/src/Components/HeroSection.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { NavLink } from "react-router-dom";
3 | import styled from "styled-components";
4 | import { Button } from "../Styles/Button";
5 | import { HiChevronDoubleRight } from "react-icons/hi";
6 |
7 |
8 | const HeroSection = () => {
9 | return (
10 |
11 |
12 |
13 |
16 |
Welcome to
17 |
18 | Real time chat application for all your needs
19 |
20 |
21 | Easy to use our chat app, Attractive and clean design, with many
22 | Features Dark & light, Recent Chat And many more.......
23 |
24 |
25 |
26 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {/*

*/}
41 |
42 |
43 | {/* shapes code */}
44 |
45 |
46 |

47 |
48 |
49 |
50 |

51 |
52 |
53 |
54 |
55 |

56 |
57 |
58 |
59 |

60 |
61 |
62 |

63 |
64 |
65 |
66 | );
67 | };
68 |
69 | export default HeroSection;
70 |
71 | const Wrapper = styled.section`
72 | position: relative;
73 | padding-top: 140px;
74 | background: url("/images/2.png");
75 | background-repeat: no-repeat;
76 | background-position: center;
77 | background-size: cover;
78 | height: 100vh;
79 | text-align: left;
80 | background-color: ${({ theme }) => theme.colors.bg2.secondary};
81 | overflow: hidden;
82 |
83 | .custom-container {
84 | position: relative;
85 | z-index: 1;
86 | }
87 |
88 | .hero-section-data {
89 | margin: 0 2rem;
90 | .highlight {
91 | color: #1c77ed;
92 | }
93 | h1 {
94 | font-size: calc(1.1rem + 28 * (100vw - 320px) / 1600);
95 | font-weight: 800;
96 | margin-bottom: 17px;
97 | line-height: 1.2;
98 | max-width: 680px;
99 | }
100 | p {
101 | font-size: calc(1.2 * (1rem + 3 * (100vw - 320px) / 1600));
102 | margin: 0 0;
103 | max-width: 750px;
104 | font-weight: 500;
105 | font-family: Roboto, sans-serif;
106 | line-height: 1.8;
107 | color: rgba(${({ theme }) => theme.colors.rgb.heading}, 0.5);
108 | }
109 |
110 | .button {
111 | display: flex;
112 | justify-content: center;
113 | align-items: center;
114 | width: 200px;
115 | border-radius: 5px;
116 | background: ${({ theme }) => theme.colors.gradient};
117 | font-weight: 600;
118 | padding: 18px 30px;
119 | transition: 0.3s;
120 | box-shadow: 0 0 10px ${({ theme }) => theme.colors.boxShadow.primary};
121 | &:hover {
122 | scale: 1.01;
123 | }
124 | }
125 | }
126 |
127 | .hero-section-image {
128 | position: absolute;
129 | left: 0;
130 | top: 0;
131 | .img-style {
132 | position: absolute;
133 | right: 0;
134 | top: 0;
135 | vertical-align: middle;
136 | width: 50%;
137 | height: auto;
138 | }
139 | }
140 |
141 | .shapes {
142 | position: absolute;
143 | top: 0;
144 | width: 100%;
145 | height: 100%;
146 | animation: zoom-fade 5s infinite linear;
147 | div {
148 | position: absolute;
149 | }
150 | .shape-1 {
151 | top: -10%;
152 | left: -3%;
153 | opacity: 0.1;
154 | }
155 | .shape-2 {
156 | top: 40%;
157 | right: 30%;
158 | opacity: 0.1;
159 | }
160 | .shape-3 {
161 | top: 70%;
162 | left: -3%;
163 | transform-origin: center;
164 | transform: rotate(20deg);
165 | opacity: 0.1;
166 | }
167 | .shape-4 {
168 | bottom: 0%;
169 | left: 20%;
170 | transition: all 0.5s;
171 | animation: balloonfly-02 12s infinite;
172 | }
173 | .shape-5 {
174 | bottom: 0%;
175 | right: 20%;
176 | transition: all 0.5s;
177 | animation: balloonfly-01 12s infinite;
178 | }
179 | }
180 | @keyframes zoom-fade {
181 | 0% {
182 | transform: scale(1.02);
183 | }
184 |
185 | 50% {
186 | transform: scale(1);
187 | }
188 |
189 | 100% {
190 | transform: scale(1.02);
191 | }
192 | }
193 |
194 | @keyframes balloonfly-01 {
195 | 0% {
196 | top: 40%;
197 | opacity: 0;
198 | }
199 | 30% {
200 | transform: scale(1.5);
201 | opacity: 1;
202 | }
203 | 100% {
204 | top: 15%;
205 | opacity: 0;
206 | }
207 | }
208 |
209 | @keyframes balloonfly-02 {
210 | 0% {
211 | top: 40%;
212 | opacity: 0;
213 | }
214 | 30% {
215 | transform: scale(1.5);
216 | opacity: 1;
217 | }
218 | 100% {
219 | top: 10%;
220 | opacity: 0;
221 | }
222 | }
223 |
224 | @media (min-width: ${({ theme }) => theme.media.mobile}) {
225 | .login-btn {
226 | margin: 2.5rem 0;
227 | }
228 | .custom-container {
229 | padding: 0 5rem;
230 | }
231 | .button {
232 | font-size: 1rem;
233 | }
234 | }
235 |
236 | @media (max-width: ${({ theme }) => theme.media.mobile}) {
237 | background-image: url("/images/1.png");
238 | padding: 0rem 0rem;
239 | .custom-container {
240 | padding: 0;
241 | }
242 | .login-btn {
243 | margin: 2.5rem auto;
244 | }
245 | .hero-section-data {
246 | margin-top: 85px;
247 | h1 {
248 | font-size: 2rem;
249 | }
250 | p {
251 | font-size: 1.5rem;
252 | }
253 | h1,
254 | p {
255 | text-align: center;
256 | }
257 | }
258 | .button {
259 | font-size: 1.2rem;
260 | }
261 | .hero-section-image {
262 | visibility: hidden;
263 | }
264 | .container {
265 | padding: 1rem;
266 | }
267 | }
268 | `;
269 |
--------------------------------------------------------------------------------