├── .DS_Store ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── feature-request.yml ├── pull-request.md ├── pull_request_template.md └── workflows │ ├── autocomment-iss-close.yml │ ├── greetings.yaml │ └── greetings.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Learn.md ├── README.md ├── app └── globals.css ├── components.json ├── jsconfig.json ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html └── robots.txt ├── server ├── .DS_Store ├── .gitignore ├── Archive.zip ├── Utils │ ├── Messages.js │ └── Users.js ├── index.js ├── package-lock.json └── package.json ├── src ├── .DS_Store ├── @ │ ├── components │ │ └── magicui │ │ │ ├── animated-list.jsx │ │ │ └── cool-mode.jsx │ └── lib │ │ └── utils.js ├── App.css ├── App.js ├── Components │ ├── ChatPage.jsx │ ├── HomePage.css │ ├── HomePage.jsx │ ├── Loader.jsx │ ├── LocationTemplate.jsx │ ├── MapEmbed.jsx │ ├── MessageTemplate.jsx │ ├── MicroPhone.jsx │ ├── MobileMenu.jsx │ ├── PrivateRoute.jsx │ └── ShareBox.jsx ├── Context │ └── UserAuthContext.jsx ├── assets │ ├── Logo-Full-Dark.png │ ├── ReactChat-dark.png │ ├── ReactChat-light.png │ ├── light_svg.svg │ ├── okji.jpeg │ ├── sparkle_sound.mp3 │ └── svg.svg ├── index.css └── index.js └── tailwind.config.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/.DS_Store -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Please fill out the sections below to help everyone identify and fix the bug 9 | - type: textarea 10 | id: description 11 | attributes: 12 | label: Describe your issue 13 | placeholder: When I click here this happens 14 | validations: 15 | required: true 16 | - type: textarea 17 | id: steps 18 | attributes: 19 | label: Steps to reproduce 20 | placeholder: | 21 | 1. Go to page X 22 | 2. Click here 23 | 3. Click there 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: expected 28 | attributes: 29 | label: What was the expected result? 30 | placeholder: I expected this to happen 31 | - type: textarea 32 | id: screenshots 33 | attributes: 34 | label: Put here any screenshots or videos (optional) 35 | - type: dropdown 36 | id: assign 37 | attributes: 38 | label: "Would you like to work on this issue?" 39 | options: 40 | - "Yes" 41 | - type: markdown 42 | attributes: 43 | value: | 44 | Thanks for reporting this issue! We will get back to you as soon as possible.name: Bug Report 45 | description: Report a bug 46 | labels: ["bug"] 47 | body: 48 | - type: markdown 49 | attributes: 50 | value: | 51 | Please fill out the sections below to help everyone identify and fix the bug 52 | - type: textarea 53 | id: description 54 | attributes: 55 | label: Describe your issue 56 | placeholder: When I click here this happens 57 | validations: 58 | required: true 59 | - type: textarea 60 | id: steps 61 | attributes: 62 | label: Steps to reproduce 63 | placeholder: | 64 | 1. Go to page X 65 | 2. Click here 66 | 3. Click there 67 | validations: 68 | required: true 69 | - type: textarea 70 | id: expected 71 | attributes: 72 | label: What was the expected result? 73 | placeholder: I expected this to happen 74 | - type: textarea 75 | id: screenshots 76 | attributes: 77 | label: Put here any screenshots or videos (optional) 78 | - type: dropdown 79 | id: assign 80 | attributes: 81 | label: "Would you like to work on this issue?" 82 | options: 83 | - "Yes" 84 | - type: markdown 85 | attributes: 86 | value: | 87 | Thanks for reporting this issue! We will get back to you as soon as possible. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: New Feature 2 | description: Suggest or request a new feature 3 | labels: ["enhancement"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Please fill out the sections below to properly describe the new feature you are suggesting. 9 | - type: textarea 10 | id: description 11 | attributes: 12 | label: Describe the feature 13 | placeholder: A button in the screen X that allows to do Y 14 | validations: 15 | required: true 16 | - type: textarea 17 | id: rationale 18 | attributes: 19 | label: It should be implemented because 20 | placeholder: It will allow to do Y that is needed for Z 21 | - type: textarea 22 | id: context 23 | attributes: 24 | label: Additional context 25 | placeholder: | 26 | Add any other context or screenshots about the feature request here. 27 | - type: dropdown 28 | id: assign 29 | attributes: 30 | label: "Would you like to work on this issue?" 31 | options: 32 | - "Yes" 33 | - type: markdown 34 | attributes: 35 | value: | 36 | Thanks for your suggestion! Let's see together if it can be implemented. 37 | -------------------------------------------------------------------------------- /.github/pull-request.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | 4 | 5 | Fixes #(issue) 6 | 7 | ### Type of Change 8 | 9 | - [ ] Bug fix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] Documentation update 13 | 14 | ### How Has This Been Tested? 15 | 16 | 17 | 18 | - [ ] Test A 19 | - [ ] Test B 20 | 21 | **Test Configuration**: 22 | * Firmware version: 23 | * Hardware: 24 | * Toolchain: 25 | * SDK: 26 | 27 | ### Checklist: 28 | 29 | - [ ] My code follows the style guidelines of this project 30 | - [ ] I have performed a self-review of my own code 31 | - [ ] I have commented my code, particularly in hard-to-understand areas 32 | - [ ] I have made corresponding changes to the documentation 33 | - [ ] My changes generate no new warnings 34 | - [ ] I have added tests that prove my fix is effective or that my feature works 35 | - [ ] New and existing unit tests pass locally with my changes 36 | - [ ] Any dependent changes have been merged and published in downstream modules 37 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Related Issue 2 | [Cite any related issue(s) this pull request addresses. If none, simply state “None”] 3 | 4 | ## Description 5 | [Please include a brief description of the changes or features added] 6 | 7 | ## Type of PR 8 | 9 | - [ ] Bug fix 10 | - [ ] Feature enhancement 11 | - [ ] Documentation update 12 | - [ ] Other (specify): _______________ 13 | 14 | ## Screenshots / videos (if applicable) 15 | [Attach any relevant screenshots or videos demonstrating the changes] 16 | 17 | ## Checklist: 18 | - [ ] I have performed a self-review of my code 19 | - [ ] I have read and followed the Contribution Guidelines. 20 | - [ ] I have tested the changes thoroughly before submitting this pull request. 21 | - [ ] I have provided relevant issue numbers, screenshots, and videos after making the changes. 22 | - [ ] I have commented my code, particularly in hard-to-understand areas. 23 | 24 | 25 | ## Additional context: 26 | [Include any additional information or context that might be helpful for reviewers.] 27 | -------------------------------------------------------------------------------- /.github/workflows/autocomment-iss-close.yml: -------------------------------------------------------------------------------- 1 | name: Comment on Issue Close 2 | 3 | on: 4 | issues: 5 | types: [closed] 6 | 7 | jobs: 8 | greet-on-close: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | steps: 13 | - name: Greet User 14 | uses: actions/github-script@v5 15 | with: 16 | github-token: ${{ secrets.GITHUB_TOKEN }} 17 | script: | 18 | const issue = context.payload.issue; 19 | const issueCreator = issue.user.login; 20 | const issueNumber = issue.number; 21 | 22 | const greetingMessage = `Hello @${issueCreator}! Your issue #${issueNumber} has been closed. Thank you for your contribution!`; 23 | 24 | github.rest.issues.createComment({ 25 | owner: context.repo.owner, 26 | repo: context.repo.repo, 27 | issue_number: issueNumber, 28 | body: greetingMessage 29 | }); -------------------------------------------------------------------------------- /.github/workflows/greetings.yaml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request_target, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | steps: 12 | - uses: actions/first-interaction@v1 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | issue-message: "Hi there! Thanks for opening this issue. We appreciate your contribution to this open-source project. We aim to respond or assign your issue as soon as possible." 16 | pr-message: "Welcome to Our repository.🎊 Thank you so much for taking the time to point this out." 17 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: "Greetings" 2 | 3 | on: 4 | fork: 5 | push: 6 | branches: [main] 7 | issues: 8 | types: [opened] 9 | pull_request_target: 10 | types: [opened] 11 | 12 | jobs: 13 | welcome: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v1 17 | - uses: EddieHubCommunity/gh-action-community/src/welcome@main 18 | with: 19 | github-token: ${{ secrets.GITHUB_TOKEN }} 20 | issue-message: | 21 | Congratulations, @${{ github.actor }}! 🎉 Thank you for creating your issue. Your contribution is greatly appreciated and we look forward to working with you to resolve the issue. Keep up the great work! 22 | pr-message: | 23 | Great job, @${{ github.actor }}! 🎉 , Thank you for submitting your pull request. Your contribution is valuable and we appreciate your efforts to improve our project. Our team will review your changes shortly. 24 | footer: "We will promptly review your changes and offer feedback. Keep up the excellent work! Kindly remember to check our [contributing guidelines](https://github.com/urstrulynishkarsh/ReactChat/blob/main/CONTRIBUTING.md)" 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | node_modules/ -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | urstrulynishkarsh. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # **Contributing Guidelines** 📄 2 | 3 | This documentation contains a set of guidelines to help you during the contribution process. 4 | We are happy to welcome all the contributions from anyone willing to improve/add new scripts to this project. 5 | Thank you for helping out and remember, **no contribution is too small.** 6 |
7 | Please note we have a [code of conduct](CODE_OF_CONDUCT.md) please follow it in all your interactions with the project. 8 | 9 | 10 | 11 |
12 | 13 | ## **Need some help regarding the basics?🤔** 14 | 15 | 16 | You can refer to the following articles on basics of Git and Github and also contact the Project Mentors, 17 | in case you are stuck: 18 | 19 | - [Forking a Repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) 20 | - [Cloning a Repo](https://help.github.com/en/desktop/contributing-to-projects/creating-an-issue-or-pull-request) 21 | - [How to create a Pull Request](https://opensource.com/article/19/7/create-pull-request-github) 22 | - [Getting started with Git and GitHub](https://towardsdatascience.com/getting-started-with-git-and-github-6fcd0f2d4ac6) 23 | - [Learn GitHub from Scratch](https://docs.github.com/en/get-started/start-your-journey/git-and-github-learning-resources) 24 | 25 |
26 | 27 | ## **Issue Report Process 📌** 28 | 29 | 1. Go to the project's issues. 30 | 2. Give proper description for the issues. 31 | 3. Don't spam to get the assignment of the issue 😀. 32 | 4. Wait for till someone is looking into it !. 33 | 5. Start working on issue only after you got assigned that issue 🚀. 34 | 35 |
36 | 37 | ## **Pull Request Process 🚀** 38 | 39 | 1. Ensure that you have self reviewed your code 😀 40 | 2. Make sure you have added the proper description for the functionality of the code 41 | 3. I have commented my code, particularly in hard-to-understand areas. 42 | 4. Add screenshot it help in review. 43 | 5. Submit your PR by giving the necesarry information in PR template and hang tight we will review it really soon 🚀 44 | 45 |
46 | 47 | # **Thank you for contributing💗** 48 | -------------------------------------------------------------------------------- /Learn.md: -------------------------------------------------------------------------------- 1 | # 🚀 Learn React & Tailwind CSS with ReactChat 🎨 2 | 3 | Welcome to ReactChat, a starter pack for creating React projects with Tailwind CSS configured. This guide will walk you through the steps to build your project using React version 18.2 and Tailwind CSS version 3.2. 4 | 5 | ## 👨‍💻 Demo 6 | ![sample2](https://github.com/IP80808080/ReactChat/assets/94137507/c681db15-9b29-43f0-a3d2-ac4bb50a9509) 7 | 8 | 9 | 10 |
11 |

Table of Contents

12 |
    13 |
  1. 14 | Project Description 15 |
  2. 16 |
  3. 17 | 🔥 Key Features 🔑 18 |
  4. 19 |
  5. 20 | 🌟 Why It Matters 🚀 21 |
  6. 22 |
  7. 23 | 💡 What Sets Us Apart 💡 24 |
  8. 25 |
  9. 26 | 🌟 Tutorial 💡 27 |
  10. 28 |
  11. 29 | Building Your Project 🏗️ 30 |
  12. 31 |
  13. 32 | Contributing 🤝 33 |
  14. 34 |
  15. 35 | About ReactChat ℹ️ 36 |
  16. 37 |
  17. 38 | Conclusion 🎉 39 |
  18. 40 |
41 |
42 | 43 |

Project description:

44 | 45 | 46 | 🌐 The Socket.IO Chat Room with Real-Time Location Sharing Project 🚀 47 | 48 | The goal of this project is to create an exciting and dynamic web-based chat application using Socket.IO. 49 | Our aim is to push the boundaries of real-time communication by incorporating advanced features like location sharing. 50 | In today's fast-paced digital world, users crave more than just text-based interactions. We're here to deliver an experience 51 | that goes beyond expectations, fostering seamless communication and connection among users. This `readme.md` file explains the key steps and results that we got as part of our project. 52 | 53 | 54 | ## 🔥 Key Features 🔑 55 | - Real-time messaging 56 | - Location sharing 57 | - User authentication 58 | - Multimedia support 59 | - Responsive design 60 | - 61 | ## 🌟 Why It Matters 🚀 62 | In the ever-evolving digital landscape, traditional chat applications are no longer sufficient. 63 | Users expect interactive and engaging platforms that keep them hooked. 64 | Our project addresses this demand by integrating cutting-edge technologies and features, making communication not just convenient, but fun and immersive. 65 | 66 | ## 💡 What Sets Us Apart 💡 67 | - Advanced real-time communication 68 | - Seamless location sharing 69 | - User-friendly interface 70 | - Scalable architecture 71 | 72 | ## 🌟 Tutorial 💡 73 | step-by-step tutorials and guides to get started with Socket.IO Chat Room with Real-Time Location Sharing. 74 | 75 | https://github.com/IP80808080/ReactChat/assets/94137507/f3f5953c-e566-4480-9d14-d43ce2b481b3 76 | 77 | 78 | 79 | 80 | ### 1. Clone the Repository 81 | 82 | First, clone the repository to your local machine using the following command: 83 | 84 | ```bash 85 | git clone https://github.com/urstrulynishkarsh/ReactChat.git 86 | ``` 87 | 88 | ### 2. Install Required Packages 89 | Navigate into the cloned ReactChat directory and install the required packages: 90 | 91 | ```bash 92 | cd ReactChat 93 | npm install 94 | ``` 95 | 96 | ### 3. Start the Development Server 97 | To start the development server, run the following command: 98 | 99 | ```bash 100 | npm run dev 101 | ``` 102 | 103 | ### 4. View Your Project 104 | Open your browser and navigate to http://localhost:3000 to view your project. 105 | 106 | ## Building Your Project 🏗️ 107 | Now that you have set up your development environment, you can start building your project: 108 | 109 | - Create React Components: Begin by creating your React components inside the src/components directory. 110 | - Add Styles with Tailwind CSS: Use Tailwind CSS classes to style your components directly in your JSX files or create new CSS files and import them into your components. 111 | - Customize Tailwind Setup: If needed, you can add your own tailwind.config.js file to customize your Tailwind setup. 112 | 113 | ## Contributing 🤝 114 | Contributions to ReactChat are welcome! If you have any suggestions or find any issues, please feel free to open an issue or a pull request on GitHub. 115 | 116 | ## About ReactChat ℹ️ 117 | ReactChat is a project developed for the GSoC2024 ReactChat Challenge. (Visit)[reactchatio.vercel.app] to learn more. 118 | 119 | ## Conclusion 🎉 120 | Wrap up with Socket.IO Chat Room with Real-Time Location Sharing. Reflect on what you've learned and celebrate your achievements! 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #
ReactChat: Real-Time Chat Application with Socket.IO and Location Sharing
2 | 3 | Welcome to ReactChat, a powerful and dynamic web-based chat application built with Socket.IO. Our mission is to provide an immersive chat experience by integrating advanced features such as real-time messaging and location sharing. In this README, we'll guide you through setting up and building your project using React and Tailwind CSS. 4 | 5 |

6 |      7 |      8 |      9 | 10 |

11 | 12 |

Tech-Stack

13 | 14 |

15 |      16 |    17 | 18 |

19 | 20 | ## Project Description 21 | ReactChat is a web-based chat application developed to meet the demands of modern communication. With real-time messaging, location sharing, user authentication, multimedia support, and responsive design, ReactChat offers a comprehensive platform for seamless interaction among users. 22 | 23 | ## 🔥 Key Features 24 | - Real-time messaging 25 | - Location sharing 26 | - User authentication 27 | - Multimedia support 28 | - Responsive design 29 | 30 | ## 🌟 Why It Matters 31 | In today's digital landscape, traditional chat applications fall short of meeting user expectations. ReactChat aims to revolutionize communication by integrating cutting-edge technologies and features, providing users with an engaging and immersive experience. 32 | 33 | ## 💡 What Sets Us Apart 34 | - Advanced real-time communication 35 | - Seamless location sharing 36 | - User-friendly interface 37 | - Scalable architecture 38 | 39 | ## 🌟 Tutorial 40 | Follow our step-by-step tutorial to get started with ReactChat: 41 | 42 | ### 1. Clone the Repository 43 | ```bash 44 | git clone https://github.com/urstrulynishkarsh/ReactChat.git 45 | ``` 46 | 47 | ### 2. Install Required Packages 48 | ```bash 49 | cd ReactChat 50 | npm install 51 | ``` 52 | 53 | ### 3. Start the Development Server 54 | ```bash 55 | npm run dev 56 | ``` 57 | 58 | ### 4. View Your Project 59 | Open your browser and navigate to [http://localhost:3000](http://localhost:3000) to view your project. 60 | 61 | ## Building Your Project 🏗️ 62 | Now that you've set up your development environment, it's time to start building your project: 63 | 64 | - **Create React Components:** Begin by creating your React components inside the `src/components` directory. 65 | - **Add Styles with Tailwind CSS:** Utilize Tailwind CSS classes to style your components directly in your JSX files or create new CSS files and import them into your components. 66 | - **Customize Tailwind Setup:** Customize your Tailwind setup by adding your own `tailwind.config.js` file if needed. 67 | 68 | ## Contributing 🤝 69 | Contributions to ReactChat are welcome! If you have any suggestions or encounter any issues, please feel free to open an issue or a pull request on [GitHub](https://github.com/urstrulynishkarsh/ReactChat). 70 | 71 | ## Conclusion 🎉 72 | Congratulations on setting up your Socket.IO Chat Room with Real-Time Location Sharing project! Reflect on what you've learned and celebrate your achievements! 73 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 222.2 84% 4.9%; 9 | --card: 0 0% 100%; 10 | --card-foreground: 222.2 84% 4.9%; 11 | --popover: 0 0% 100%; 12 | --popover-foreground: 222.2 84% 4.9%; 13 | --primary: 222.2 47.4% 11.2%; 14 | --primary-foreground: 210 40% 98%; 15 | --secondary: 210 40% 96.1%; 16 | --secondary-foreground: 222.2 47.4% 11.2%; 17 | --muted: 210 40% 96.1%; 18 | --muted-foreground: 215.4 16.3% 46.9%; 19 | --accent: 210 40% 96.1%; 20 | --accent-foreground: 222.2 47.4% 11.2%; 21 | --destructive: 0 84.2% 60.2%; 22 | --destructive-foreground: 210 40% 98%; 23 | --border: 214.3 31.8% 91.4%; 24 | --input: 214.3 31.8% 91.4%; 25 | --ring: 222.2 84% 4.9%; 26 | --radius: 0.5rem; 27 | --chart-1: 12 76% 61%; 28 | --chart-2: 173 58% 39%; 29 | --chart-3: 197 37% 24%; 30 | --chart-4: 43 74% 66%; 31 | --chart-5: 27 87% 67%; 32 | } 33 | 34 | .dark { 35 | --background: 222.2 84% 4.9%; 36 | --foreground: 210 40% 98%; 37 | --card: 222.2 84% 4.9%; 38 | --card-foreground: 210 40% 98%; 39 | --popover: 222.2 84% 4.9%; 40 | --popover-foreground: 210 40% 98%; 41 | --primary: 210 40% 98%; 42 | --primary-foreground: 222.2 47.4% 11.2%; 43 | --secondary: 217.2 32.6% 17.5%; 44 | --secondary-foreground: 210 40% 98%; 45 | --muted: 217.2 32.6% 17.5%; 46 | --muted-foreground: 215 20.2% 65.1%; 47 | --accent: 217.2 32.6% 17.5%; 48 | --accent-foreground: 210 40% 98%; 49 | --destructive: 0 62.8% 30.6%; 50 | --destructive-foreground: 210 40% 98%; 51 | --border: 217.2 32.6% 17.5%; 52 | --input: 217.2 32.6% 17.5%; 53 | --ring: 212.7 26.8% 83.9%; 54 | --chart-1: 220 70% 50%; 55 | --chart-2: 160 60% 45%; 56 | --chart-3: 30 80% 55%; 57 | --chart-4: 280 65% 60%; 58 | --chart-5: 340 75% 55%; 59 | } 60 | } 61 | 62 | @layer base { 63 | * { 64 | @apply border-border; 65 | } 66 | body { 67 | @apply bg-background text-foreground; 68 | } 69 | } -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": false, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "magicui": "@/components/magicui" 18 | } 19 | } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs", 5 | "jsx": "react", 6 | "baseUrl": "./src" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-tailwind-css-starter-pack", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "browser-router": "^0.2.0", 7 | "class-variance-authority": "^0.7.0", 8 | "clsx": "^2.1.1", 9 | "concurrently": "^8.2.2", 10 | "dark-mode-toggle": "^0.15.0", 11 | "emoji-picker-react": "^4.9.2", 12 | "framer-motion": "^11.3.2", 13 | "lucide-react": "^0.408.0", 14 | "moment": "^2.30.1", 15 | "multer": "^1.4.5-lts.1", 16 | "mustache": "^4.2.0", 17 | "qs": "^6.11.2", 18 | "react": "^18.2.0", 19 | "react-animated-cursor": "^2.11.2", 20 | "react-avatar": "^5.0.3", 21 | "react-dom": "^18.2.0", 22 | "react-hot-toast": "^2.4.1", 23 | "react-icons": "^4.12.0", 24 | "react-router-dom": "^6.14.1", 25 | "react-scripts": "5.0.1", 26 | "react-tilt": "^1.0.2", 27 | "react-toastify": "^10.0.3", 28 | "socket.io-client": "^4.7.4", 29 | "sweetalert2": "^11.10.3", 30 | "tailwind-merge": "^2.4.0", 31 | "tailwindcss-animate": "^1.0.7", 32 | "web-vitals": "^2.1.4", 33 | "ws": "^8.16.0" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/urstrulynishkarsh/react-tailwind-css-starter-pack.git" 38 | }, 39 | "author": "Nishkarsh Verma", 40 | "bugs": { 41 | "url": "https://github.com/urstrulynishkarsh/react-tailwind-css-starter-pack/issues" 42 | }, 43 | "scripts": { 44 | "start": "react-scripts start", 45 | "build": "react-scripts build", 46 | "eject": "react-scripts eject", 47 | "server": "cd server && npm run dev", 48 | "dev": "concurrently -n \"client,server\" -c \"bgBlue,bgYellow\" \"npm start\" \"npm run server\"" 49 | }, 50 | "eslintConfig": { 51 | "extends": [ 52 | "react-app", 53 | "react-app/jest" 54 | ] 55 | }, 56 | "browserslist": { 57 | "production": [ 58 | ">0.2%", 59 | "not dead", 60 | "not op_mini all" 61 | ], 62 | "development": [ 63 | "last 1 chrome version", 64 | "last 1 firefox version", 65 | "last 1 safari version" 66 | ] 67 | }, 68 | "devDependencies": { 69 | "tailwindcss": "^3.2.7" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ChatApp 8 | 10 | 11 | 16 | 17 | 18 | 19 | 20 |
21 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /server/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/server/.DS_Store -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | node_modules/ 3 | config/ -------------------------------------------------------------------------------- /server/Archive.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/server/Archive.zip -------------------------------------------------------------------------------- /server/Utils/Messages.js: -------------------------------------------------------------------------------- 1 | const generateMessage=(username,text)=>{ 2 | return{ 3 | username, 4 | text, 5 | createdAt: new Date().getTime() 6 | } 7 | } 8 | 9 | const generateLocationMessage=(username,url)=>{ 10 | return{ 11 | username, 12 | url, 13 | createdAt: new Date().getTime() 14 | } 15 | } 16 | 17 | module.exports={generateMessage,generateLocationMessage}; -------------------------------------------------------------------------------- /server/Utils/Users.js: -------------------------------------------------------------------------------- 1 | // first we create an empty array of users 2 | const users = []; 3 | 4 | // add user into the users array 5 | const addUser = ({ id, username, room }) => { 6 | // clean the data 7 | username = username.trim().toLowerCase(); 8 | room = room.trim().toLowerCase(); 9 | 10 | // Validate the data 11 | if (!username || !room) { 12 | return { 13 | status: false, 14 | error: "Username and room are required!", 15 | user: {}, 16 | }; 17 | } 18 | 19 | // check for existing User 20 | 21 | const existingUser = users?.find((user) => { 22 | if (user?.room === room && user?.username === username) return true; 23 | else return false; 24 | }); 25 | 26 | // validate 27 | if (existingUser) { 28 | return { 29 | status: false, 30 | error: "UserName is in Used", 31 | user: {}, 32 | }; 33 | } 34 | 35 | // push into the array 36 | const user = { id, username, room }; 37 | users.push(user); 38 | return { status: true, error: "", user }; 39 | }; 40 | 41 | // remove user by id 42 | const removeUser = (id) => { 43 | const index = users.findIndex((user) => user.id === id); 44 | 45 | if (index !== -1) { 46 | return users.splice(index, 1)[0]; 47 | } 48 | }; 49 | 50 | // getuser by id 51 | const getUser = (id) => { 52 | return users.find((user) => user.id === id); 53 | }; 54 | 55 | // getUsersInRoom by room 56 | 57 | const getUsersInRoom = (room) => { 58 | room = room.trim().toLowerCase(); 59 | return users.filter((user) => user.room === room); 60 | }; 61 | 62 | module.exports = { addUser, removeUser, getUser, getUsersInRoom }; 63 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const http = require("http"); 4 | const socketio = require("socket.io"); 5 | const cors = require("cors"); 6 | 7 | const app = express(); 8 | const server = http.createServer(app); 9 | // const io=socketio(server); 10 | const Filter = require("bad-words"); 11 | 12 | const { 13 | addUser, 14 | removeUser, 15 | getUser, 16 | getUsersInRoom, 17 | } = require("./Utils/Users"); 18 | const { 19 | generateMessage, 20 | generateLocationMessage, 21 | } = require("./Utils/Messages"); 22 | 23 | const PORT = process.env.PORT || 4000; 24 | 25 | // Use cors middleware with WebSocket support 26 | // app.use(cors({ 27 | // origin: 'http://localhost:3000', // Adjust this based on your React app's origin 28 | // methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', 29 | // credentials: true, 30 | // })); 31 | 32 | app.use(cors()); 33 | 34 | const io = socketio(server, { 35 | cors: { 36 | origin: "http://localhost:3000", 37 | methods: ["GET", "POST"], 38 | allowedHeaders: ["my-custom-header"], 39 | credentials: true, 40 | }, 41 | }); 42 | 43 | // Socket.IO CORS middleware 44 | io.use((socket, next) => { 45 | // Set CORS headers for Socket.IO requests 46 | socket.handshake.headers.origin = socket.handshake.headers.referer; 47 | next(); 48 | }); 49 | 50 | io.on("connection", (socket) => { 51 | console.log("New WebSocket Connection"); 52 | 53 | // method ,options,callback 54 | socket.on("join", (options, callback) => { 55 | const { status, error, user } = addUser({ id: socket.id, ...options }); 56 | 57 | if (!status) { 58 | return callback({ 59 | status, 60 | error, 61 | }); 62 | } 63 | socket.join(user.room); 64 | 65 | socket.emit("message", generateMessage(`${user.username}`, "Welcome!")); 66 | socket.broadcast 67 | .to(user.room) 68 | .emit( 69 | "message", 70 | generateMessage(`${user.username}`, `${user.username} has joined!`) 71 | ); 72 | io.to(user.room).emit("roomData", { 73 | room: user.room, 74 | users: getUsersInRoom(user.room), 75 | }); 76 | 77 | callback({ 78 | status, 79 | error, 80 | }); 81 | }); 82 | 83 | socket.on("sendMessage", (message, callback) => { 84 | const user = getUser(socket.id); 85 | 86 | const filter = new Filter(); 87 | if (filter.isProfane(message)) { 88 | console.warn("Profanity is not allowed!"); 89 | return; 90 | } 91 | io.to(user?.room).emit("message", generateMessage(user.username, message)); 92 | callback(); 93 | }); 94 | 95 | socket.on("START_TYPING", () => { 96 | const user = getUser(socket.id); 97 | if (user) { 98 | // Check if user exists 99 | socket.broadcast 100 | .to(user?.room) 101 | .emit("USER_TYPING_START", `${user?.username} is typing...`); 102 | } 103 | }); 104 | socket.on("STOP_TYPING", () => { 105 | const user = getUser(socket.id); 106 | if (user) { 107 | // Check if user exists 108 | socket.broadcast.to(user?.room).emit("STOP_USER_TYPING", ``); 109 | } 110 | }); 111 | 112 | socket.on("sendLocation", (coords, callback) => { 113 | const user = getUser(socket.id); 114 | io.to(user.room).emit( 115 | "locationMessage", 116 | generateLocationMessage( 117 | user.username, 118 | `https://google.com/maps?q=${coords.latitude},${coords.longitude}` 119 | ) 120 | ); 121 | callback(); 122 | }); 123 | 124 | socket.on("disconnect", () => { 125 | const user = removeUser(socket.id); 126 | 127 | if (user) { 128 | io.to(user.room).emit( 129 | "message", 130 | generateMessage(`${user.username}`, `${user.username} has left!`) 131 | ); 132 | io.to(user.room).emit("roomData", { 133 | room: user.room, 134 | users: getUsersInRoom(user.room), 135 | }); 136 | } 137 | }); 138 | }); 139 | 140 | // default route 141 | app.get("/", (req, res) => { 142 | return res.json({ 143 | success: true, 144 | message: "Your socket is up and running....", 145 | }); 146 | }); 147 | 148 | server.listen(PORT, () => { 149 | console.log(`Server is up on port ${PORT}!`); 150 | }); -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "bad-words": "^3.0.4", 4 | "chalk": "^5.3.0", 5 | "cors": "^2.8.5", 6 | "dotenv": "^16.3.1", 7 | "express": "^4.18.2", 8 | "http": "^0.0.1-security", 9 | "leo-profanity": "^1.7.0", 10 | "profanity": "^0.0.2", 11 | "socket.io": "^4.7.5" 12 | }, 13 | "name": "server", 14 | "version": "1.0.0", 15 | "main": "index.js", 16 | "scripts": { 17 | "start": "node index.js", 18 | "dev": "env-cmd ./config/dev.env nodemon index.js" 19 | }, 20 | "keywords": [], 21 | "author": "", 22 | "license": "ISC", 23 | "description": "", 24 | "devDependencies": { 25 | "env-cmd": "^8.0.2", 26 | "jest": "^29.7.0", 27 | "nodemon": "^3.1.0", 28 | "supertest": "^6.3.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/src/.DS_Store -------------------------------------------------------------------------------- /src/@/components/magicui/animated-list.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useEffect, useMemo, useState } from "react"; 3 | import { AnimatePresence, motion } from "framer-motion"; 4 | 5 | export const AnimatedList = React.memo(({ 6 | className, 7 | children, 8 | delay = 1000 9 | }) => { 10 | const [index, setIndex] = useState(0); 11 | const childrenArray = React.Children.toArray(children); 12 | 13 | useEffect(() => { 14 | const interval = setInterval(() => { 15 | setIndex((prevIndex) => (prevIndex + 1) % childrenArray.length); 16 | }, delay); 17 | 18 | return () => clearInterval(interval); 19 | }, [childrenArray.length, delay]); 20 | 21 | const itemsToShow = useMemo(() => childrenArray.slice(0, index + 1).reverse(), [index, childrenArray]); 22 | 23 | return ( 24 | (
25 | 26 | {itemsToShow.map((item) => ( 27 | 28 | {item} 29 | 30 | ))} 31 | 32 |
) 33 | ); 34 | }); 35 | 36 | AnimatedList.displayName = "AnimatedList"; 37 | 38 | export function AnimatedListItem({ 39 | children 40 | }) { 41 | const animations = { 42 | initial: { scale: 0, opacity: 0 }, 43 | animate: { scale: 1, opacity: 1, originY: 0 }, 44 | exit: { scale: 0, opacity: 0 }, 45 | transition: { type: "spring", stiffness: 350, damping: 40 }, 46 | }; 47 | 48 | return ( 49 | ( 50 | {children} 51 | ) 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /src/@/components/magicui/cool-mode.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from "react"; 2 | 3 | const getContainer = () => { 4 | const id = "_coolMode_effect"; 5 | let existingContainer = document.getElementById(id); 6 | 7 | if (existingContainer) { 8 | return existingContainer; 9 | } 10 | 11 | const container = document.createElement("div"); 12 | container.setAttribute("id", id); 13 | container.setAttribute( 14 | "style", 15 | "overflow:hidden; position:fixed; height:100%; top:0; left:0; right:0; bottom:0; pointer-events:none; z-index:2147483647" 16 | ); 17 | 18 | document.body.appendChild(container); 19 | 20 | return container; 21 | }; 22 | 23 | let instanceCounter = 0; 24 | 25 | const applyParticleEffect = (element, options) => { 26 | instanceCounter++; 27 | 28 | const defaultParticle = "circle"; 29 | const particleType = options?.particle || defaultParticle; 30 | const sizes = [15, 20, 25, 35, 45]; 31 | const limit = 45; 32 | 33 | let particles = []; 34 | let autoAddParticle = false; 35 | let mouseX = 0; 36 | let mouseY = 0; 37 | 38 | const container = getContainer(); 39 | 40 | function generateParticle() { 41 | const size = 42 | options?.size || sizes[Math.floor(Math.random() * sizes.length)]; 43 | const speedHorz = options?.speedHorz || Math.random() * 10; 44 | const speedUp = options?.speedUp || Math.random() * 25; 45 | const spinVal = Math.random() * 360; 46 | const spinSpeed = Math.random() * 35 * (Math.random() <= 0.5 ? -1 : 1); 47 | const top = mouseY - size / 2; 48 | const left = mouseX - size / 2; 49 | const direction = Math.random() <= 0.5 ? -1 : 1; 50 | 51 | const particle = document.createElement("div"); 52 | 53 | if (particleType === "circle") { 54 | const svgNS = "http://www.w3.org/2000/svg"; 55 | const circleSVG = document.createElementNS(svgNS, "svg"); 56 | const circle = document.createElementNS(svgNS, "circle"); 57 | circle.setAttributeNS(null, "cx", (size / 2).toString()); 58 | circle.setAttributeNS(null, "cy", (size / 2).toString()); 59 | circle.setAttributeNS(null, "r", (size / 2).toString()); 60 | circle.setAttributeNS(null, "fill", `hsl(${Math.random() * 360}, 70%, 50%)`); 61 | 62 | circleSVG.appendChild(circle); 63 | circleSVG.setAttribute("width", size.toString()); 64 | circleSVG.setAttribute("height", size.toString()); 65 | 66 | particle.appendChild(circleSVG); 67 | } else { 68 | particle.innerHTML = ``; 69 | } 70 | 71 | particle.style.position = "absolute"; 72 | particle.style.transform = `translate3d(${left}px, ${top}px, 0px) rotate(${spinVal}deg)`; 73 | 74 | container.appendChild(particle); 75 | 76 | particles.push({ 77 | direction, 78 | element: particle, 79 | left, 80 | size, 81 | speedHorz, 82 | speedUp, 83 | spinSpeed, 84 | spinVal, 85 | top, 86 | }); 87 | } 88 | 89 | function refreshParticles() { 90 | particles.forEach((p) => { 91 | p.left = p.left - p.speedHorz * p.direction; 92 | p.top = p.top - p.speedUp; 93 | p.speedUp = Math.min(p.size, p.speedUp - 1); 94 | p.spinVal = p.spinVal + p.spinSpeed; 95 | 96 | if ( 97 | p.top >= 98 | Math.max(window.innerHeight, document.body.clientHeight) + p.size 99 | ) { 100 | particles = particles.filter((o) => o !== p); 101 | p.element.remove(); 102 | } 103 | 104 | p.element.setAttribute("style", [ 105 | "position:absolute", 106 | "will-change:transform", 107 | `top:${p.top}px`, 108 | `left:${p.left}px`, 109 | `transform:rotate(${p.spinVal}deg)`, 110 | ].join(";")); 111 | }); 112 | } 113 | 114 | let animationFrame; 115 | 116 | let lastParticleTimestamp = 0; 117 | const particleGenerationDelay = 30; 118 | 119 | function loop() { 120 | const currentTime = performance.now(); 121 | if ( 122 | autoAddParticle && 123 | particles.length < limit && 124 | currentTime - lastParticleTimestamp > particleGenerationDelay 125 | ) { 126 | generateParticle(); 127 | lastParticleTimestamp = currentTime; 128 | } 129 | 130 | refreshParticles(); 131 | animationFrame = requestAnimationFrame(loop); 132 | } 133 | 134 | loop(); 135 | 136 | const isTouchInteraction = "ontouchstart" in window; 137 | 138 | const tap = isTouchInteraction ? "touchstart" : "mousedown"; 139 | const tapEnd = isTouchInteraction ? "touchend" : "mouseup"; 140 | const move = isTouchInteraction ? "touchmove" : "mousemove"; 141 | 142 | const updateMousePosition = (e) => { 143 | if ("touches" in e) { 144 | mouseX = e.touches?.[0].clientX; 145 | mouseY = e.touches?.[0].clientY; 146 | } else { 147 | mouseX = e.clientX; 148 | mouseY = e.clientY; 149 | } 150 | }; 151 | 152 | const tapHandler = (e) => { 153 | updateMousePosition(e); 154 | autoAddParticle = true; 155 | }; 156 | 157 | const disableAutoAddParticle = () => { 158 | autoAddParticle = false; 159 | }; 160 | 161 | element.addEventListener(move, updateMousePosition, { passive: true }); 162 | element.addEventListener(tap, tapHandler, { passive: true }); 163 | element.addEventListener(tapEnd, disableAutoAddParticle, { passive: true }); 164 | element.addEventListener("mouseleave", disableAutoAddParticle, { 165 | passive: true, 166 | }); 167 | 168 | return () => { 169 | element.removeEventListener(move, updateMousePosition); 170 | element.removeEventListener(tap, tapHandler); 171 | element.removeEventListener(tapEnd, disableAutoAddParticle); 172 | element.removeEventListener("mouseleave", disableAutoAddParticle); 173 | 174 | const interval = setInterval(() => { 175 | if (animationFrame && particles.length === 0) { 176 | cancelAnimationFrame(animationFrame); 177 | clearInterval(interval); 178 | 179 | if (--instanceCounter === 0) { 180 | container.remove(); 181 | } 182 | } 183 | }, 500); 184 | }; 185 | }; 186 | 187 | export const CoolMode = ({ children, options }) => { 188 | const ref = useRef(null); 189 | 190 | useEffect(() => { 191 | if (ref.current) { 192 | return applyParticleEffect(ref.current, options); 193 | } 194 | }, [options]); 195 | 196 | return React.cloneElement(children, { ref }); 197 | }; 198 | -------------------------------------------------------------------------------- /src/@/lib/utils.js: -------------------------------------------------------------------------------- 1 | import { clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | width: 100%; 9 | overflow-x: hidden; 10 | overflow-y: auto; 11 | margin: 0; 12 | } 13 | 14 | .cardname { 15 | min-width: 0; 16 | overflow-wrap: break-word; 17 | } 18 | 19 | .container { 20 | display: "flex"; 21 | justify-content: "center"; 22 | align-items: "center"; 23 | height: "100vh"; 24 | margin-left: 48; 25 | } 26 | circle { 27 | transform-origin: center; 28 | transform-box: fill-box; 29 | transform-origin: center; 30 | animation: rotate linear infinite; 31 | } 32 | circle:nth-child(1) { 33 | animation-duration: 1.6s; 34 | } 35 | circle:nth-child(2) { 36 | animation-duration: 1.2s; 37 | } 38 | circle:nth-child(3) { 39 | animation-duration: 0.8s; 40 | } 41 | @keyframes rotate { 42 | 100% { 43 | transform: rotate(360deg); 44 | } 45 | } 46 | 47 | .compose { 48 | display: flex; 49 | flex-shrink: 0; 50 | margin-top: 16px; 51 | padding: 24px; 52 | } 53 | 54 | .compose form { 55 | display: flex; 56 | flex-grow: 1; 57 | margin-right: 16px; 58 | } 59 | 60 | .compose input { 61 | border: 1px solid #eeeeee; 62 | width: 100%; 63 | padding: 12px; 64 | margin: 0 16px 0 0; 65 | flex-grow: 1; 66 | } 67 | 68 | .compose button { 69 | font-size: 14px; 70 | } 71 | 72 | .message { 73 | display: flex; 74 | flex-direction: column; 75 | padding: 10px; 76 | border-radius: 10px; 77 | margin-bottom: 16px; 78 | max-width: 60%; 79 | word-wrap: break-word; 80 | } 81 | 82 | .sent { 83 | align-self: flex-end; 84 | background-color: #c1e3ff; 85 | text-align: left; 86 | margin-left: auto; 87 | } 88 | 89 | .received { 90 | align-self: flex-start; 91 | background-color: rgb(230, 230, 230); 92 | text-align: left; 93 | margin-right: auto; 94 | } 95 | 96 | .message__name { 97 | font-weight: 600; 98 | font-size: 14px; 99 | margin-right: 8px; 100 | } 101 | 102 | .message__meta { 103 | color: #777; 104 | font-size: 14px; 105 | } 106 | .brosize { 107 | min-height: 85vh; 108 | /* min-width: 100vw; */ 109 | } 110 | 111 | /* #messages { 112 | overflow-y: scroll; 113 | } */ 114 | 115 | .message a { 116 | color: #0070cc; 117 | } 118 | 119 | .hello { 120 | display: inline-block; 121 | width: 10px; /* Set the desired size of the circle */ 122 | height: 10px; /* Set the desired size of the circle */ 123 | border-radius: 50%; 124 | background-color: #2ecc71; /* Set the desired green color */ 125 | margin-left: 5px; /* Add some spacing between the circle and the username */ 126 | } 127 | .swal2-popup { 128 | cursor: auto; 129 | } 130 | .swal2-confirm, 131 | .swal2-cancel { 132 | cursor: pointer !important; 133 | } 134 | 135 | .mic { 136 | position: absolute; 137 | top: 50%; 138 | left: 50%; 139 | transform: translate(-50%, -50%); 140 | color: #fff; 141 | } 142 | .mic::before, 143 | .mic::after { 144 | content: ""; 145 | position: absolute; 146 | top: 50%; 147 | left: 50%; 148 | transform: translate(-50%, -50%); 149 | border-radius: 100%; 150 | z-index: 2; 151 | box-shadow: 0 0 20px 20px #1c084f; 152 | } 153 | .mic::before { 154 | width: 100px; 155 | height: 100px; 156 | background-color: rgb(8, 45, 78); 157 | } 158 | .mic::after { 159 | width: 150px; 160 | height: 150px; 161 | background-color: #936cff; 162 | animation: circle-size 0.8s linear infinite alternate; 163 | } 164 | .mic-icon { 165 | box-sizing: border-box; 166 | position: absolute; 167 | top: 50%; 168 | left: 50%; 169 | transform: translate(-50%, -50%) scale(5); 170 | display: block; 171 | width: 14px; 172 | height: 8px; 173 | border-bottom-left-radius: 120px; 174 | border-bottom-right-radius: 120px; 175 | border: 2px solid; 176 | border-top: 0; 177 | margin-top: 20px; 178 | z-index: 3; 179 | } 180 | .mic-icon::before, 181 | .mic-icon::after { 182 | content: ""; 183 | display: block; 184 | box-sizing: border-box; 185 | position: absolute; 186 | } 187 | .mic-icon::before { 188 | width: 2px; 189 | height: 5px; 190 | top: calc(100% + 1px); 191 | left: 50%; 192 | transform: translate(-50%, 0); 193 | background-color: #fff; 194 | } 195 | .mic-icon::after { 196 | border: 2px solid; 197 | width: 5px; 198 | height: 12px; 199 | left: 50%; 200 | top: -10px; 201 | border-radius: 4px; 202 | transform: translate(-50%, 0); 203 | } 204 | iframe { 205 | border-top: 10px solid #addff3; 206 | border-radius: 20px; 207 | border-bottom: 20px solid #addff3; 208 | border-left: 6px solid #addff3; 209 | border-right: 6px solid #addff3; 210 | } 211 | 212 | @keyframes circle-size { 213 | from { 214 | width: 180px; 215 | height: 180px; 216 | } 217 | to { 218 | width: 200px; 219 | height: 200px; 220 | } 221 | } 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | .loader { 233 | --dim: 3rem; 234 | width: var(--dim); 235 | height: var(--dim); 236 | position: relative; 237 | animation: spin988 2s linear infinite; 238 | } 239 | 240 | .loader .circle { 241 | --color: #333; 242 | --dim: 1.2rem; 243 | width: var(--dim); 244 | height: var(--dim); 245 | background-color: var(--color); 246 | border-radius: 50%; 247 | position: absolute; 248 | } 249 | 250 | .loader .circle:nth-child(1) { 251 | top: 0; 252 | left: 0; 253 | } 254 | 255 | .loader .circle:nth-child(2) { 256 | top: 0; 257 | right: 0; 258 | } 259 | 260 | .loader .circle:nth-child(3) { 261 | bottom: 0; 262 | left: 0; 263 | } 264 | 265 | .loader .circle:nth-child(4) { 266 | bottom: 0; 267 | right: 0; 268 | } 269 | 270 | @keyframes spin988 { 271 | 0% { 272 | transform: scale(1) rotate(0); 273 | } 274 | 275 | 20%, 25% { 276 | transform: scale(1.3) rotate(90deg); 277 | } 278 | 279 | 45%, 50% { 280 | transform: scale(1) rotate(180deg); 281 | } 282 | 283 | 70%, 75% { 284 | transform: scale(1.3) rotate(270deg); 285 | } 286 | 287 | 95%, 100% { 288 | transform: scale(1) rotate(360deg); 289 | } 290 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { Route, Routes } from "react-router-dom"; 2 | import "./App.css"; 3 | import HomePage from "./Components/HomePage"; 4 | import ChatPage from "./Components/ChatPage"; 5 | import AnimatedCursor from "react-animated-cursor"; 6 | 7 | import { useEffect, useState } from "react"; 8 | import { WiDaySunny } from "react-icons/wi"; 9 | import { MdModeNight } from "react-icons/md"; 10 | import {UseAuthProvider} from "./Context/UserAuthContext"; 11 | import {PrivateRoute} from "./Components/PrivateRoute"; 12 | 13 | 14 | function App() { 15 | // Retrieve dark mode state from local storage or default to false 16 | const [darkMode, setDarkMode] = useState(() => { 17 | const savedDarkMode = localStorage.getItem("darkMode"); 18 | return savedDarkMode ? JSON.parse(savedDarkMode) : false; 19 | }); 20 | 21 | // Update local storage when dark mode state changes 22 | useEffect(() => { 23 | localStorage.setItem("darkMode", JSON.stringify(darkMode)); 24 | }, [darkMode]); 25 | 26 | 27 | const hanldeDarkMode=()=>{ 28 | setDarkMode(!darkMode); 29 | } 30 | 31 | return ( 32 |
33 | 34 |
35 | 57 |
58 | 59 | 62 | 63 | 64 | }/> 65 | 67 | 68 | 69 | } /> 70 | 71 | 72 | 73 |
74 | 75 | ); 76 | } 77 | 78 | export default App; 79 | -------------------------------------------------------------------------------- /src/Components/ChatPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useMemo, useRef, useState } from "react"; 2 | import { io } from "socket.io-client"; 3 | import { useLocation } from "react-router"; 4 | import MessageTemplate from "./MessageTemplate"; 5 | import LocationTemplate from "./LocationTemplate"; 6 | import Swal from "sweetalert2"; 7 | import Mustache from "mustache"; 8 | import moment from "moment"; 9 | import Qs from "qs"; 10 | import { useNavigate } from "react-router-dom"; 11 | import toast from "react-hot-toast"; 12 | import EmojiPicker from "emoji-picker-react"; 13 | import { 14 | AiOutlineClose, 15 | AiOutlineMenu, 16 | AiOutlineShoppingCart, 17 | } from "react-icons/ai"; 18 | import MobileMenu from "./MobileMenu"; 19 | import ShareBox from "./ShareBox"; 20 | import { FaMicrophone, FaShare } from "react-icons/fa"; 21 | import MicroPhone from "./MicroPhone"; 22 | import Avatar from "react-avatar"; 23 | import { CoolMode } from "@/components/magicui/cool-mode"; 24 | 25 | // const socket = io('ws://localhost:4000/', { transports: ['websocket'] }); 26 | 27 | // wss://reactchat-production-f378.up.railway.app/ 28 | // dev mode http://localhost:5000 29 | const socket = io("wss://reactchathub.onrender.com/", { 30 | transports: ["websocket"], 31 | }); 32 | 33 | const ChatPage = ({ darkMode, setDarkMode }) => { 34 | const location = useLocation(); 35 | const navigate = useNavigate(); 36 | const [messages, setMessages] = useState([]); 37 | const [inputMessage, setInputMessage] = useState(""); 38 | const [users, setUsers] = useState([]); 39 | const [typingUsers, setTypingUsers] = useState([]); 40 | const messagesContainerRef = useRef(null); 41 | const [hasError, setHasError] = useState(false); 42 | const [isMicOpen, setMicOpen] = useState(false); 43 | 44 | const [micHide, setMicHide] = useState(false); 45 | const [showShareBox, setShowShareBox] = useState(false); 46 | const [isMenuOpen, setIsMenuOpen] = useState(false); 47 | 48 | const [iamTyping, setIamTyping] = useState(false); 49 | const typingTimeOut = useRef(); 50 | const [userTyping, setUserTyping] = useState({ 51 | isTyping: false, 52 | data: "", 53 | }); 54 | 55 | const toggleMenu = () => { 56 | setIsMenuOpen(!isMenuOpen); 57 | }; 58 | 59 | // Options 60 | // this will work only if there is ? mark in url 61 | const { username, room } = Qs.parse(location.search, { 62 | ignoreQueryPrefix: true, 63 | }); 64 | 65 | // useEffect(() => { 66 | // socket.on('disconnect', () => { 67 | // console.log('Disconnected from the server'); 68 | // navigate('/'); 69 | // }); 70 | 71 | // return () => { 72 | // socket.off('disconnect'); 73 | // }; 74 | // }, []); 75 | 76 | useEffect(() => { 77 | const localusername = localStorage.getItem("username"); 78 | const localroom = localStorage.getItem("room"); 79 | 80 | if (localusername !== username || localroom !== room) { 81 | return navigate("/"); 82 | } 83 | socket.emit("join", { username, room }, (data) => { 84 | if (!data.status) { 85 | // Display popup if the user is already in a room 86 | Swal.fire({ 87 | title: "Already in Room", 88 | text: "You are already in a room. Redirecting to home page...", 89 | icon: "info", 90 | showConfirmButton: false, 91 | timer: 2000, // Adjust the timer as needed 92 | willClose: () => { 93 | navigate("/"); // Redirect to home page 94 | }, 95 | }); 96 | // console.log("new part") 97 | } else { 98 | setHasError(false); 99 | toast.success(`${username} joined the room!`); 100 | } 101 | }); 102 | }, []); 103 | useEffect(() => { 104 | socket.on("message", (message) => { 105 | setMessages((prevMessages) => [ 106 | ...prevMessages, 107 | { 108 | username: message.username, 109 | message: message.text, 110 | createdAt: moment(message.createdAt).format("h:mm a"), 111 | }, 112 | ]); 113 | scrollToBottom(); 114 | }); 115 | 116 | socket.on("locationMessage", (message) => { 117 | console.log(messages); 118 | setMessages((prevMessages) => [ 119 | ...prevMessages, 120 | { 121 | username: message.username, 122 | url: message.url, 123 | createdAt: moment(message.createdAt).format("h:mm a"), 124 | }, 125 | ]); 126 | scrollToBottom(); 127 | }); 128 | 129 | socket.on("roomData", ({ room, users }) => { 130 | setUsers(users); 131 | console.table(users); 132 | console.log("room", room); 133 | }); 134 | 135 | return () => { 136 | socket.off("message"); 137 | socket.off("locationMessage"); 138 | socket.off("roomData"); 139 | }; 140 | }, []); 141 | 142 | const handleSubmit = (e) => { 143 | e.preventDefault(); 144 | const message = e.target.elements.message.value; 145 | if (message.trim() === "") { 146 | return; 147 | } 148 | socket.emit("sendMessage", message, (error) => { 149 | if (error) { 150 | console.log(error); 151 | } else { 152 | setInputMessage(""); 153 | setTypingUsers([]); 154 | console.log("Message delivered!"); 155 | } 156 | }); 157 | }; 158 | 159 | const handleTyping = (e) => { 160 | setInputMessage(e.target.value); 161 | const message = e.target.value.trim(); 162 | 163 | if (!iamTyping) { 164 | socket.emit("START_TYPING"); 165 | setIamTyping(true); 166 | } 167 | 168 | if (typingTimeOut.current) clearTimeout(typingTimeOut.current); 169 | 170 | typingTimeOut.current = setTimeout(() => { 171 | socket.emit("STOP_TYPING"); 172 | setIamTyping(false); 173 | }, [500]); 174 | }; 175 | 176 | const handleSendLocation = () => { 177 | if (!navigator.geolocation) { 178 | return alert("Geolocation is not supported by your browser."); 179 | } 180 | 181 | navigator.geolocation.getCurrentPosition((position) => { 182 | socket.emit( 183 | "sendLocation", 184 | { 185 | latitude: position.coords.latitude, 186 | longitude: position.coords.longitude, 187 | }, 188 | () => { 189 | console.log("Location shared!"); 190 | } 191 | ); 192 | }); 193 | }; 194 | 195 | const scrollToBottom = () => { 196 | if (messagesContainerRef.current) { 197 | messagesContainerRef.current.scrollTop = 198 | messagesContainerRef.current.scrollHeight; 199 | } 200 | }; 201 | 202 | useEffect(() => { 203 | scrollToBottom(); 204 | }, [messages]); 205 | 206 | useEffect(() => { 207 | const handleTypingStart = (data) => { 208 | setUserTyping({ 209 | ...userTyping, 210 | isTyping: true, 211 | data, 212 | }); 213 | }; 214 | 215 | const handleTypingStop = (data) => { 216 | // console.log("STOP TYPING>>>", data); 217 | setUserTyping({ 218 | ...userTyping, 219 | isTyping: false, 220 | data: "", 221 | }); 222 | }; 223 | 224 | // Add event listeners when component mounts 225 | socket?.on("USER_TYPING_START", handleTypingStart); 226 | socket?.on("STOP_USER_TYPING", handleTypingStop); 227 | 228 | // Clean up event listeners when component unmounts 229 | return () => { 230 | socket?.off("USER_TYPING_START", handleTypingStart); 231 | socket?.off("STOP_USER_TYPING", handleTypingStop); 232 | }; 233 | }, [socket]); 234 | 235 | // window.onbeforeunload = function () { 236 | // // This string won't actually be shown in modern browsers, but returning it triggers the confirmation dialog 237 | // return "Do you really want to leave?"; 238 | // }; 239 | 240 | function handleDisconnectConfirmation() { 241 | Swal.fire({ 242 | title: "Disconnect Chat", 243 | text: "Are you sure you want to disconnect from the chat?", 244 | icon: "question", 245 | showCancelButton: true, 246 | confirmButtonText: "Yes", 247 | cancelButtonText: "No", 248 | }).then((result) => { 249 | if (result.isConfirmed) { 250 | if (socket) { 251 | localStorage.clear("username"); 252 | localStorage.clear("room"); 253 | socket.disconnect(); // Disconnect the socket connection 254 | console.log("Disconnected from the chat server!"); 255 | window.location.href = "/"; 256 | } else { 257 | console.log("No active chat connection to disconnect."); 258 | } 259 | } 260 | }); 261 | } 262 | 263 | const startListening = () => { 264 | const SpeechRecognition = 265 | window.SpeechRecognition || window.webkitSpeechRecognition; 266 | if (!SpeechRecognition) { 267 | setMicHide(true); 268 | return; 269 | } 270 | const recognition = new SpeechRecognition(); 271 | 272 | recognition.onstart = () => { 273 | console.log("Speech recognition service has started"); 274 | setMicOpen(true); 275 | }; 276 | 277 | recognition.onaudioend = () => { 278 | console.log("Audio capturing ended"); 279 | setMicOpen(false); 280 | }; 281 | 282 | recognition.onresult = (event) => { 283 | const currentTranscript = event.results[0][0].transcript; 284 | console.log(currentTranscript); 285 | setMicOpen(false); 286 | setInputMessage((prev) => prev + currentTranscript); 287 | }; 288 | 289 | recognition.start(); 290 | }; 291 | 292 | return ( 293 |
294 |
295 |

{ 298 | setShowShareBox(true); 299 | }} 300 | > 301 | ROOM NO: {room} 302 | 303 |

304 |

308 | Users 309 |

310 | 338 |
339 |
343 |
353 |
354 |
355 | 372 | 378 |
379 |
380 | 381 | {messages.map((msg, index) => 382 | msg.url ? ( 383 | 392 | ) : ( 393 | 402 | ) 403 | )} 404 |
405 | {userTyping.isTyping && ( 406 |

{userTyping.data}

407 | )} 408 | 409 |
410 |
415 | 423 | {!micHide && ( 424 | 431 | )} 432 | 433 | 455 | 456 | 457 | 458 | 459 | 482 | 483 |
484 |
485 | 486 | {isMenuOpen && ( 487 | 488 | )} 489 | {showShareBox && ( 490 | 494 | )} 495 | {isMicOpen && } 496 |
497 | ); 498 | }; 499 | 500 | export default ChatPage; 501 | -------------------------------------------------------------------------------- /src/Components/HomePage.css: -------------------------------------------------------------------------------- 1 | /* .centered-form__box { 2 | background: transparent; 3 | /* padding: 3px; */ 4 | /* padding-left: 20px; */ 5 | /* width: 25vw; */ 6 | /* height: 35vw; */ 7 | 8 | .centered-form__box { 9 | background: transparent; 10 | 11 | @media (max-width: 850px) { 12 | .form { 13 | width: 35vw; 14 | height: 60vw; 15 | } 16 | } 17 | 18 | @media screen and (max-width: 450px) { 19 | .img { 20 | width: 90% !important; 21 | height: auto; 22 | margin-top: 30px; 23 | } 24 | .form { 25 | width: 100% !important; 26 | } 27 | .join { 28 | margin-left: 50vw; 29 | } 30 | .input { 31 | margin-left: 25vw; 32 | } 33 | .image-class { 34 | margin-bottom: -15rem; 35 | } 36 | } 37 | } 38 | 39 | /* Micky mouuse container styling starts here */ 40 | .mickey-clock-container { 41 | margin-bottom: 1rem; /* Added margin below the clock */ 42 | position: relative; /* Ensure relative positioning for absolute content */ 43 | } 44 | 45 | .mickey-clock { 46 | position: relative; 47 | width: 100px; 48 | height: 100px; 49 | background: #333; /* Default background color */ 50 | border-radius: 50%; 51 | display: flex; 52 | justify-content: center; 53 | align-items: center; 54 | color: white; /* Default text color */ 55 | font-family: 'Arial', sans-serif; 56 | font-size: 1.5rem; 57 | animation: color-change 3s infinite alternate; /* Color-changing animation */ 58 | transform-style: preserve-3d; /* Enable 3D transformations */ 59 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); /* Soft shadow for depth */ 60 | outline: 2px solid transparent; /* Transparent outline */ 61 | transition: outline-color 0.3s ease; /* Smooth transition for outline color */ 62 | } 63 | 64 | .mickey-clock:hover { 65 | outline: 2px solid; 66 | outline-color: #FF0000; /* Red outline color on hover */ 67 | box-shadow: 0 0 20px rgba(255, 113, 62, 0.6); /* Shadow color on hover */ 68 | } 69 | 70 | .mickey-clock.dark-mode { 71 | background: #ffffff; /* Dark mode background color */ 72 | color: #fff; /* Dark mode text color */ 73 | } 74 | 75 | .mickey-clock .ears { 76 | transform: translateZ(-10px); /* Adjust the Z-axis for 3D effect */ 77 | position: absolute; 78 | width: 50px; 79 | height: 50px; 80 | background: #333; /* Default ear color */ 81 | border-radius: 50%; 82 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); 83 | /* Soft shadow for depth */ 84 | } 85 | 86 | .mickey-clock .left-ear { 87 | top: -25px; 88 | left: -25px; 89 | } 90 | 91 | .mickey-clock .right-ear { 92 | top: -25px; 93 | right: -25px; 94 | } 95 | 96 | .mickey-clock .face { 97 | position: relative; 98 | z-index: 1; 99 | display: flex; 100 | justify-content: center; 101 | align-items: center; 102 | } 103 | 104 | .mickey-clock .time { 105 | position: absolute; 106 | top: 50%; 107 | left: 50%; 108 | transform: translate(-50%, -50%); 109 | font-size: 1.5rem; 110 | } 111 | 112 | /* this is initiale message will be displayed first then later on all animations */ 113 | 114 | /* Initial message styling */ 115 | .initial-message::before{ 116 | position: absolute; 117 | bottom: 10%; /* Adjust position as needed */ 118 | left: 50%; 119 | transform: translateX(-50%); 120 | text-shadow: #fff 2px solid; 121 | font: bold 11px; 122 | background-color: #333; 123 | color: rgb(206, 17, 17); 124 | padding: 5px 10px; 125 | border-radius: 5px; 126 | font-size: 1rem; 127 | white-space: nowrap; /* Prevent text wrapping */ 128 | visibility: visible; 129 | transition: opacity 0.3s; 130 | opacity: 1; 131 | } 132 | 133 | 134 | /* Animation keyframes for initial message */ 135 | @keyframes initial-message-animation { 136 | 0% { 137 | opacity: 0; 138 | } 139 | 50% { 140 | opacity: 1; 141 | } 142 | 100% { 143 | opacity: 2; 144 | } 145 | } 146 | 147 | 148 | 149 | 150 | .mickey-clock-container::after { 151 | content: attr(data-fortune); 152 | position: absolute; 153 | top: 120px; 154 | left: 50%; 155 | transform: translateX(-50%); 156 | background: #333; 157 | color: white; 158 | padding: 5px 10px; 159 | border-radius: 5px; 160 | font-size: 1rem; 161 | white-space: nowrap; 162 | /* Prevent text wrapping */ 163 | visibility: hidden; 164 | transition: opacity 0.3s visibility 0.3s; 165 | /* smooth transition for visibility */ 166 | } 167 | 168 | /* Animation keyframes for color change */ 169 | @keyframes color-change { 170 | 0% { 171 | background-color: #333; 172 | color: white; 173 | } 174 | 50% { 175 | background-color: #ffc53e; 176 | color: #333; 177 | } 178 | 100% { 179 | background-color: #333; 180 | color: white; 181 | } 182 | } 183 | 184 | Fortune message popup 185 | .mickey-clock-container:hover .mickey-clock::after { 186 | position: absolute; 187 | top: -80%; 188 | left: -10%; 189 | width: 120%; 190 | padding: 10px; 191 | background: rgba(255, 255, 255, 0.9); /* Semi-transparent white background */ 192 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); /* Shadow for depth */ 193 | border-radius: 10px; 194 | text-align: center; 195 | animation: popup 5s forwards; /* Animation for popup effect */ 196 | z-index: 2; /* Ensure popup is above clock */ 197 | } 198 | 199 | .mickey-clock-container:hover::after { 200 | opacity: 1; 201 | visibility: visible; 202 | animation: popup 1.5s forwards, vanish-sound 5s; /* Animation for popup effect and sound */ 203 | } 204 | 205 | 206 | /* Keyframes for fortune message popup animation */ 207 | @keyframes popup { 208 | 0% { 209 | transform: translateY(-100%); 210 | opacity: 0; 211 | } 212 | 10% { 213 | transform: translateY(-90%); 214 | opacity: 1; 215 | } 216 | 90% { 217 | transform: translateY(-90%); 218 | opacity: 1; 219 | } 220 | 100% { 221 | transform: translateY(-100%); 222 | opacity: 0; 223 | } 224 | } 225 | 226 | 227 | /* Sound effect for fortune message */ 228 | @keyframes vanish-sound { 229 | 100% { 230 | 231 | } 232 | } 233 | 234 | -------------------------------------------------------------------------------- /src/Components/HomePage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState , useRef } from "react"; 2 | import { useLocation, useNavigate } from "react-router-dom"; 3 | import Qs from "qs"; 4 | import { io } from "socket.io-client"; 5 | import "./HomePage.css"; 6 | import { useAuth } from "../Context/UserAuthContext"; 7 | import q from "../assets/light_svg.svg"; 8 | import s from "../assets/svg.svg"; 9 | import { Tilt } from "react-tilt"; 10 | import audio from '../assets/sparkle_sound.mp3'; 11 | 12 | const HomePage = ({ darkMode }) => { 13 | const socket = io("wss://reactchat-production-f378.up.railway.app/", { 14 | transports: ["websocket"], 15 | }); 16 | const navigate = useNavigate(); 17 | const location = useLocation(); 18 | const [formData, setFormData] = useState({ 19 | username: "", 20 | room: "", 21 | }); 22 | 23 | 24 | const audioRef = useRef(new Audio(audio)); 25 | 26 | 27 | // Mickey Mouse Clock function ----------------------------------------- 28 | const [currentTime, setCurrentTime] = useState(new Date()); 29 | const [fortuneMessage, setFortuneMessage] = useState(); 30 | const [initial_message] = useState("Hover over Micky Mouse🐭 for a surprise Fortune !!!!!") 31 | // Fortune messages array 32 | const fortunes = [ 33 | "You will have a great day!", 34 | "Good news is coming your way!", 35 | "You will find what you are looking for!", 36 | "Happiness is around the corner!", 37 | "Expect the unexpected!", 38 | ]; 39 | 40 | const { setRoomDetail } = useAuth(); 41 | 42 | useEffect(() => { 43 | const { room } = Qs.parse(location.search, { ignoreQueryPrefix: true }); 44 | if (room) { 45 | setFormData((prevData) => ({ ...prevData, room })); 46 | } 47 | }, [location.search]); 48 | 49 | // Timer function for clock------------------------------------ 50 | useEffect(() => { 51 | const timer = setInterval(() => { 52 | setCurrentTime(new Date()); 53 | }, 1000); 54 | 55 | return () => clearInterval(timer); // Clear the interval on component unmount 56 | }, []); 57 | 58 | // Change fortune message on hover 59 | const handleMouseEnter = () => { 60 | const randomIndex = Math.floor(Math.random() * fortunes.length); 61 | setFortuneMessage(fortunes[randomIndex]); 62 | audioRef.current.play(); 63 | }; 64 | 65 | const handleMouseLeave = () => { 66 | audioRef.current.pause(); 67 | audioRef.current.currentTime = 0; 68 | setFortuneMessage(initial_message); 69 | }; 70 | 71 | 72 | 73 | // ------------------------------------------ 74 | 75 | const handleChange = (e) => { 76 | setFormData({ 77 | ...formData, 78 | [e.target.name]: e.target.value, 79 | }); 80 | }; 81 | 82 | const handleSubmit = (e) => { 83 | e.preventDefault(); 84 | localStorage.setItem("username", formData.username); 85 | localStorage.setItem("room", formData.room); 86 | setRoomDetail({ username: formData.username, room: formData.room }); 87 | navigate(`chat?username=${formData.username}&room=${formData.room}`); 88 | }; 89 | 90 | const defaultOptions = { 91 | reverse: false, // reverse the tilt direction 92 | max: 100, // max tilt rotation (degrees) 93 | perspective: 1000, // Transform perspective, the lower the more extreme the tilt gets. 94 | scale: 1.1, // 2 = 200%, 1.5 = 150%, etc.. 95 | speed: 1000, // Speed of the enter/exit transition 96 | transition: true, // Set a transition on enter/exit. 97 | axis: null, // What axis should be disabled. Can be X or Y. 98 | reset: true, // If the tilt effect has to be reset on exit. 99 | easing: "cubic-bezier(.03,.98,.52,.99)", // Easing on enter/exit. 100 | }; 101 | 102 | // Clock Feature or user interactive functionality addition.---------------------------------------------- 103 | // functionality addition by -- Black shadow 104 | 105 | const formatTime = (time) => { 106 | const hours = String(time.getHours()).padStart(2, "0"); 107 | const minutes = String(time.getMinutes()).padStart(2, "0"); 108 | const seconds = String(time.getSeconds()).padStart(2, "0"); 109 | return `${hours}:${minutes}:${seconds}`; 110 | }; 111 | 112 | // ------------------------------------------------------ 113 | 114 | return ( 115 |
116 |
117 | {/*----------------------- Micky Mouse positioned at the top of the title ---------------------------- */} 118 | {/* Mickey Mouse digital clock */} 119 |
120 | 121 |
126 |
127 |
128 |
129 |
{formatTime(currentTime)}
130 |
131 |
132 |
133 |
134 | {initial_message} 135 |
136 | {/*------------------------- thus it is overlapping with the dark theme thats why positioned at the top of the title --------------- */} 137 | 138 |
139 |

144 | JOIN ROOM 145 |

146 |
147 | 154 | 155 | 165 | 172 | 182 | 188 |
189 |
190 |
191 | 192 |
193 | 194 | svgimage not found 200 | 201 |
202 |
203 | ); 204 | }; 205 | 206 | export default HomePage; 207 | -------------------------------------------------------------------------------- /src/Components/Loader.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Loader = () => { 4 | return( 5 |
6 |
7 |
8 |
9 |
10 |
11 | 12 | ) 13 | } 14 | 15 | export default Loader -------------------------------------------------------------------------------- /src/Components/LocationTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import MapEmbed from "./MapEmbed"; 4 | const LocationTemplate = ({ 5 | username, 6 | createdAt, 7 | url, 8 | darkMode, 9 | isOwnMessage, 10 | }) => { 11 | // console.log(username) 12 | const limitedUsername = 13 | username.length > 10 ? username.slice(0, 10) + "..." : username; 14 | return ( 15 |
16 |

17 | 22 | {limitedUsername} 23 | 24 | 25 | {createdAt && createdAt.toString()} 26 | 27 |

28 |

29 | 35 | My current location 36 | 37 |

38 | 39 |
40 | ); 41 | }; 42 | 43 | export default LocationTemplate; 44 | -------------------------------------------------------------------------------- /src/Components/MapEmbed.jsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | 4 | 5 | const MapEmbed = ({ url}) => { 6 | 7 | return ( 8 |
9 | 10 |
11 | ); 12 | }; 13 | 14 | export default MapEmbed; -------------------------------------------------------------------------------- /src/Components/MessageTemplate.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const MessageTemplate = ({ 4 | username, 5 | createdAt, 6 | message, 7 | darkMode, 8 | isOwnMessage, 9 | }) => { 10 | const limitedUsername = 11 | username.length > 10 ? username.slice(0, 10) + "..." : username; 12 | return ( 13 |
14 |

15 | 20 | {limitedUsername} 21 | 22 | {createdAt} 23 |

24 |

25 | {message} 26 |

27 |
28 | ); 29 | }; 30 | 31 | export default MessageTemplate; 32 | -------------------------------------------------------------------------------- /src/Components/MicroPhone.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function MicroPhone() { 4 | return ( 5 |
6 | 7 |
8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/Components/MobileMenu.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Modal = ({room,users,darkMode}) => { 4 | return ( 5 |
6 |
7 |

ROOM NO- {room}

8 |

Users

9 | 17 |
18 | 19 |
20 | ); 21 | }; 22 | 23 | export default Modal; -------------------------------------------------------------------------------- /src/Components/PrivateRoute.jsx: -------------------------------------------------------------------------------- 1 | import { ReactNode, useState } from "react"; 2 | import { Navigate, useNavigate } from "react-router-dom"; 3 | 4 | import { useAuth } from "../Context/UserAuthContext"; 5 | 6 | export function PrivateRoute({ children }) { 7 | let username = localStorage.getItem("username"); 8 | let room = localStorage.getItem("room"); 9 | const { roomDetail } = useAuth(); 10 | 11 | return room !== null && username !== null ? ( 12 | <>{children} 13 | ) : ( 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/Components/ShareBox.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { FaFacebook, FaInstagram, FaLinkedin, FaTwitter, FaWhatsapp } from 'react-icons/fa'; 3 | import { BsLink45Deg, BsX } from "react-icons/bs"; 4 | 5 | function ShareBox({ link, showShareBox }) { 6 | const [copy, setCopy] = useState("Copy"); 7 | 8 | const copyLink = () => { 9 | // Implement logic to copy link to clipboard 10 | navigator.clipboard.writeText(link) 11 | .then(() => setCopy("Copied")) 12 | .catch((error) => setCopy('Failed to copy')); 13 | 14 | setTimeout(() => { 15 | setCopy('Copy'); // Clear the message after 3 seconds 16 | }, 3000); 17 | }; 18 | 19 | const hideShareBox = () => { 20 | showShareBox(false); 21 | }; 22 | 23 | return ( 24 | <> 25 |
26 |
27 |
28 |

Share via Link

29 | 30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | < a href={`https://www.linkedin.com/shareArticle?url=${link}&title=react-chat&summary=Join-room`} target='_blank'> 39 | 40 | 41 | 42 | 43 | 44 | < a href={`https://www.instagram.com/?url=${link}`} target='_blank'> 45 | 46 | 47 |
48 |
49 |

Copy Link

50 |
51 | 52 |
53 |
54 | 55 |
56 |
57 | 58 | ); 59 | } 60 | 61 | export default ShareBox; 62 | -------------------------------------------------------------------------------- /src/Context/UserAuthContext.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useState } from "react"; 2 | import { ReactNode } from "react"; 3 | 4 | const userAuthContext = createContext(); 5 | 6 | export function useAuth() { 7 | return useContext(userAuthContext); 8 | } 9 | 10 | export function UseAuthProvider({ children }) { 11 | const [roomDetail, setRoomDetail] = useState({ 12 | username: "", 13 | room: "", 14 | }); 15 | 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/assets/Logo-Full-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/src/assets/Logo-Full-Dark.png -------------------------------------------------------------------------------- /src/assets/ReactChat-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/src/assets/ReactChat-dark.png -------------------------------------------------------------------------------- /src/assets/ReactChat-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/src/assets/ReactChat-light.png -------------------------------------------------------------------------------- /src/assets/okji.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/src/assets/okji.jpeg -------------------------------------------------------------------------------- /src/assets/sparkle_sound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urstrulynishkarsh/ReactChat/6ad5257e87fd3f81d244173502b39b1bde663d8a/src/assets/sparkle_sound.mp3 -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import "./index.css"; 5 | import { BrowserRouter } from "react-router-dom"; 6 | import { ToastContainer } from "react-toastify"; 7 | import { Toaster } from "react-hot-toast"; 8 | import Loader from "./Components/Loader"; 9 | import { DarkModeToggle } from "dark-mode-toggle"; 10 | 11 | const RootComponent = () => { 12 | const [isLoading, setIsLoading] = useState(true); 13 | 14 | useEffect(() => { 15 | // Check if it's the initial load or a reload 16 | const isReload = sessionStorage.getItem("isReloaded"); 17 | 18 | if (isReload) { 19 | // If it's a reload, disable the loader immediately 20 | setIsLoading(false); 21 | } else { 22 | // If it's the initial load, set a flag in sessionStorage and show the loader 23 | sessionStorage.setItem("isReloaded", "true"); 24 | 25 | // Simulate an async operation (e.g., loading data) 26 | setTimeout(() => { 27 | setIsLoading(false); // Set isLoading to false when done loading 28 | }, 2000); // Simulate a 2-second loading time (replace with your actual loading logic) 29 | } 30 | }, []); 31 | 32 | return ( 33 | <> 34 | {isLoading ? ( 35 |
36 | 37 |
38 | ) : ( 39 | 40 | 41 | 42 | 43 | 44 | )} 45 | 46 | ); 47 | }; 48 | 49 | const root = ReactDOM.createRoot(document.getElementById("root")); 50 | root.render(); 51 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | darkMode: ["class"], 4 | content: [ 5 | './pages/**/*.{js,jsx}', 6 | './components/**/*.{js,jsx}', 7 | './app/**/*.{js,jsx}', 8 | './src/**/*.{js,jsx}', 9 | ], 10 | prefix: "", 11 | theme: { 12 | container: { 13 | center: true, 14 | padding: "2rem", 15 | screens: { 16 | "2xl": "1400px", 17 | }, 18 | }, 19 | extend: { 20 | colors: { 21 | border: "hsl(var(--border))", 22 | input: "hsl(var(--input))", 23 | ring: "hsl(var(--ring))", 24 | background: "hsl(var(--background))", 25 | foreground: "hsl(var(--foreground))", 26 | primary: { 27 | DEFAULT: "hsl(var(--primary))", 28 | foreground: "hsl(var(--primary-foreground))", 29 | }, 30 | secondary: { 31 | DEFAULT: "hsl(var(--secondary))", 32 | foreground: "hsl(var(--secondary-foreground))", 33 | }, 34 | destructive: { 35 | DEFAULT: "hsl(var(--destructive))", 36 | foreground: "hsl(var(--destructive-foreground))", 37 | }, 38 | muted: { 39 | DEFAULT: "hsl(var(--muted))", 40 | foreground: "hsl(var(--muted-foreground))", 41 | }, 42 | accent: { 43 | DEFAULT: "hsl(var(--accent))", 44 | foreground: "hsl(var(--accent-foreground))", 45 | }, 46 | popover: { 47 | DEFAULT: "hsl(var(--popover))", 48 | foreground: "hsl(var(--popover-foreground))", 49 | }, 50 | card: { 51 | DEFAULT: "hsl(var(--card))", 52 | foreground: "hsl(var(--card-foreground))", 53 | }, 54 | }, 55 | borderRadius: { 56 | lg: "var(--radius)", 57 | md: "calc(var(--radius) - 2px)", 58 | sm: "calc(var(--radius) - 4px)", 59 | }, 60 | keyframes: { 61 | "accordion-down": { 62 | from: { height: "0" }, 63 | to: { height: "var(--radix-accordion-content-height)" }, 64 | }, 65 | "accordion-up": { 66 | from: { height: "var(--radix-accordion-content-height)" }, 67 | to: { height: "0" }, 68 | }, 69 | }, 70 | animation: { 71 | "accordion-down": "accordion-down 0.2s ease-out", 72 | "accordion-up": "accordion-up 0.2s ease-out", 73 | }, 74 | }, 75 | }, 76 | plugins: [require("tailwindcss-animate")], 77 | } --------------------------------------------------------------------------------