166
README.md
166
README.md
@@ -1,58 +1,154 @@
|
|||||||
# Realtime Chat App with Node.js and WebSockets
|
# Modern Chat App
|
||||||
|
|
||||||
## Overview
|
A real-time chat application built with Node.js, Express, and Socket.IO. Experience seamless communication with a modern, responsive interface.
|
||||||
|
|
||||||
This Realtime Chat App is a simple yet powerful application built with Node.js and WebSockets, allowing users to join different chat rooms and communicate with each other in real-time.
|
## ✨ Features
|
||||||
|
|
||||||
## Features
|
- **Real-time Messaging**: Instant message delivery using WebSocket technology
|
||||||
|
- **Multi-room Support**: Join different chat rooms for organized conversations
|
||||||
|
- **Modern UI**: Clean, responsive design with smooth animations
|
||||||
|
- **User Management**: See who's online in your current room
|
||||||
|
- **Connection Status**: Real-time connection indicator
|
||||||
|
- **Keyboard Shortcuts**: Quick actions for better user experience
|
||||||
|
- **Mobile Responsive**: Works perfectly on all devices
|
||||||
|
|
||||||
- **Real-time Communication:** Utilizes WebSockets to enable instant messaging between users.
|
## 🚀 Tech Stack
|
||||||
- **Multiple Rooms:** Users can join different chat rooms.
|
|
||||||
- **User-friendly Interface:** Provides an intuitive and clean user interface for an enjoyable chat experience.
|
|
||||||
|
|
||||||
## Prerequisites
|
- **Backend**: Node.js, Express.js
|
||||||
|
- **Real-time Communication**: Socket.IO
|
||||||
|
- **Frontend**: Vanilla JavaScript, HTML5, CSS3
|
||||||
|
- **Styling**: Modern CSS with CSS Variables and Flexbox
|
||||||
|
- **Icons**: Font Awesome
|
||||||
|
- **Fonts**: Inter (Google Fonts)
|
||||||
|
|
||||||
Before running the application, make sure you have the following installed:
|
## 📋 Prerequisites
|
||||||
|
|
||||||
- Node.js: [Download and install Node.js](https://nodejs.org/)
|
Before running this application, make sure you have the following installed:
|
||||||
|
|
||||||
## Getting Started
|
- [Node.js](https://nodejs.org/) (version 14 or higher)
|
||||||
|
- npm (comes with Node.js)
|
||||||
|
|
||||||
1. Clone the repository:
|
## 🛠️ Installation
|
||||||
|
|
||||||
|
1. **Clone the repository**
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/your-username/realtime-chat-app.git
|
git clone https://github.com/your-username/modern-chat-app.git
|
||||||
|
cd modern-chat-app
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Navigate to the project directory:
|
2. **Install dependencies**
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Start the server**
|
||||||
|
```bash
|
||||||
|
# Development mode (with auto-restart)
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Production mode
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Open your browser**
|
||||||
|
Navigate to `http://localhost:3000`
|
||||||
|
|
||||||
|
## 🎯 Usage
|
||||||
|
|
||||||
|
1. **Join a Chat Room**
|
||||||
|
- Enter your username
|
||||||
|
- Choose a room name
|
||||||
|
- Click "Join Chat"
|
||||||
|
|
||||||
|
2. **Start Chatting**
|
||||||
|
- Type your message in the input field
|
||||||
|
- Press Enter or click the send button
|
||||||
|
- Use Ctrl/Cmd + Enter for quick sending
|
||||||
|
|
||||||
|
3. **Room Features**
|
||||||
|
- See active users in the sidebar
|
||||||
|
- Real-time connection status
|
||||||
|
- Automatic message scrolling
|
||||||
|
|
||||||
|
## 🎨 Features in Detail
|
||||||
|
|
||||||
|
### Real-time Communication
|
||||||
|
- Instant message delivery
|
||||||
|
- Live user presence
|
||||||
|
- Connection status monitoring
|
||||||
|
- Automatic reconnection
|
||||||
|
|
||||||
|
### Modern Interface
|
||||||
|
- Gradient backgrounds
|
||||||
|
- Smooth animations
|
||||||
|
- Responsive design
|
||||||
|
- Clean typography
|
||||||
|
- Intuitive navigation
|
||||||
|
|
||||||
|
### User Experience
|
||||||
|
- Welcome messages
|
||||||
|
- Loading states
|
||||||
|
- Error notifications
|
||||||
|
- Keyboard shortcuts
|
||||||
|
- Auto-scroll to new messages
|
||||||
|
|
||||||
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
modern-chat-app/
|
||||||
|
├── src/
|
||||||
|
│ ├── index.js # Main server file
|
||||||
|
│ └── utils/
|
||||||
|
│ ├── messages.js # Message utilities
|
||||||
|
│ └── user.js # User management
|
||||||
|
├── public/
|
||||||
|
│ ├── index.html # Join page
|
||||||
|
│ ├── chat.html # Chat interface
|
||||||
|
│ ├── css/
|
||||||
|
│ │ └── styles.css # Modern styling
|
||||||
|
│ └── js/
|
||||||
|
│ └── chat.js # Frontend logic
|
||||||
|
├── package.json
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Configuration
|
||||||
|
|
||||||
|
The application uses environment variables for configuration:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd realtime-nodejs-chat-app
|
# .env file (optional)
|
||||||
|
PORT=3000
|
||||||
|
HOST=0.0.0.0
|
||||||
```
|
```
|
||||||
3. Install dependencies:
|
|
||||||
|
|
||||||
|
## 🚀 Deployment
|
||||||
|
|
||||||
|
### Local Development
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm run dev
|
||||||
```
|
```
|
||||||
4. Start the server:
|
|
||||||
```bash
|
|
||||||
nodemon src/index.js or node src/index.js
|
|
||||||
```
|
|
||||||
5. Open your web browser and go to http://localhost:3000 to access the Realtime Chat App.
|
|
||||||
|
|
||||||
## Usage
|
## 🤝 Contributing
|
||||||
1. Enter a username.
|
|
||||||
2. Choose a chat room to join.
|
|
||||||
3. Start chatting with other users in real-time.
|
|
||||||
|
|
||||||
## File Structure
|
1. Fork the repository
|
||||||
- **src/index.js**: The main server file that handles WebSocket connections and serves the HTML page.
|
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
|
||||||
- **public/index.html**: The HTML template for the chat application.
|
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
||||||
- **public/style.css**: The stylesheet for styling the chat application.
|
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
||||||
|
5. Open a Pull Request
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
- `express`: Web framework for Node.js.
|
|
||||||
- `socket.io`: WebSocket library for Node.js.
|
|
||||||
|
|
||||||
## Contributing
|
## 🙏 Acknowledgments
|
||||||
Feel free to contribute to the development of this Realtime Chat App by creating issues or submitting pull requests. Your feedback and contributions are highly appreciated!
|
|
||||||
|
- [Socket.IO](https://socket.io/) for real-time communication
|
||||||
|
- [Express.js](https://expressjs.com/) for the web framework
|
||||||
|
- [Font Awesome](https://fontawesome.com/) for icons
|
||||||
|
- [Inter Font](https://rsms.me/inter/) for typography
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
If you have any questions or need help, please open an issue on GitHub.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Built with ❤️ using Node.js and Socket.IO**
|
||||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bad-words": "^3.0.4",
|
"bad-words": "^3.0.4",
|
||||||
|
"dotenv": "^16.6.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"socket.io": "^4.7.2"
|
"socket.io": "^4.7.2"
|
||||||
@@ -300,6 +301,18 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||||
|
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
@@ -1509,6 +1522,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
|
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
|
||||||
},
|
},
|
||||||
|
"dotenv": {
|
||||||
|
"version": "16.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||||
|
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="
|
||||||
|
},
|
||||||
"ee-first": {
|
"ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bad-words": "^3.0.4",
|
"bad-words": "^3.0.4",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"socket.io": "^4.7.2"
|
"socket.io": "^4.7.2"
|
||||||
|
|||||||
@@ -4,60 +4,96 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Chat App</title>
|
<title>Modern Chat App - Chat</title>
|
||||||
<link rel="icon" href="./img/favicon.png">
|
<link rel="icon" href="./img/favicon.png">
|
||||||
<link rel="stylesheet" href="./css/styles.css">
|
<link rel="stylesheet" href="./css/styles.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="chat">
|
<div class="chat">
|
||||||
<div id="sidebar" class="chat__sidebar">
|
<div id="sidebar" class="chat__sidebar">
|
||||||
|
<!-- Sidebar content will be populated by JavaScript -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="chat__main">
|
<div class="chat__main">
|
||||||
<div id="messages" class="chat__messages"></div>
|
<div class="chat__header">
|
||||||
|
<div class="header-content">
|
||||||
|
<h2 id="room-name">Chat Room</h2>
|
||||||
|
<div class="connection-status">
|
||||||
|
<span class="status-indicator online"></span>
|
||||||
|
<span class="status-text">Connected</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="back-btn" onclick="location.href='/'">
|
||||||
|
<i class="fas fa-arrow-left"></i>
|
||||||
|
Exit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="messages" class="chat__messages">
|
||||||
|
<div class="welcome-message">
|
||||||
|
<i class="fas fa-comments"></i>
|
||||||
|
<h3>Welcome to Chat!</h3>
|
||||||
|
<p>Start sending messages to begin chatting.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="compose">
|
<div class="compose">
|
||||||
<!-- <button id="increment">+1</button> -->
|
|
||||||
<form id="message-form">
|
<form id="message-form">
|
||||||
<input type="text" name="message" placeholder="Enter message" autocomplete="off">
|
<div class="input-wrapper">
|
||||||
<button type="submit">Send</button>
|
<input type="text" name="message" placeholder="Type your message..." autocomplete="off">
|
||||||
|
<button type="submit" id="send-btn">
|
||||||
|
<i class="fas fa-paper-plane"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<button id="send-location">Send Location</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
<!-- Message Templates -->
|
||||||
<script id="message-template" type="text/html">
|
<script id="message-template" type="text/html">
|
||||||
<div class="message">
|
<div class="message {{#isOwn}}own-message{{/isOwn}}">
|
||||||
<p>
|
<div class="message__header">
|
||||||
<span class="message__name">{{username}}</span>
|
<span class="message__name">{{username}}</span>
|
||||||
<span class="message__meta">{{createdAt}}</span>
|
<span class="message__meta">{{createdAt}}</span>
|
||||||
</p>
|
</div>
|
||||||
|
<div class="message__content">
|
||||||
<p>{{message}}</p>
|
<p>{{message}}</p>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
|
||||||
|
|
||||||
<script id="locmessage-template" type="text/html">
|
|
||||||
<div class="message">
|
|
||||||
<p>
|
|
||||||
<span class="message__name">{{username}}</span>
|
|
||||||
<span class="message__meta">{{createdAt}}</span>
|
|
||||||
</p>
|
|
||||||
<p><a href="{{url}}" target="_blank" style="color: #7C5CBF;">My Current Location</a></p>
|
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id="sidebar-template" type="text/html">
|
<script id="sidebar-template" type="text/html">
|
||||||
<h2 class="room-title" >Room: {{room}}</h2>
|
<div class="sidebar-header">
|
||||||
<h3 class="list-title" >Users</h3>
|
<h2 class="room-title">
|
||||||
<ul class="users" >
|
<i class="fas fa-door-open"></i>
|
||||||
|
{{room}}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-content">
|
||||||
|
<h3 class="list-title">
|
||||||
|
<i class="fas fa-users"></i>
|
||||||
|
Active Users ({{users.length}})
|
||||||
|
</h3>
|
||||||
|
<ul class="users">
|
||||||
{{#users}}
|
{{#users}}
|
||||||
<li> {{username}} </li>
|
<li class="user-item">
|
||||||
|
<span class="online-indicator"></span>
|
||||||
|
<span class="user-name">{{username}}</span>
|
||||||
|
{{#isCurrentUser}}
|
||||||
|
<span class="current-user-badge">You</span>
|
||||||
|
{{/isCurrentUser}}
|
||||||
|
</li>
|
||||||
{{/users}}
|
{{/users}}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- External Libraries -->
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.0.1/mustache.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.0.1/mustache.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.6.0/qs.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.6.0/qs.min.js"></script>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* General Styles */
|
/* Modern Chat App Styles */
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -6,178 +6,808 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
:root {
|
||||||
font-size: 16px;
|
--primary-color: #667eea;
|
||||||
|
--primary-dark: #5a6fd8;
|
||||||
|
--secondary-color: #764ba2;
|
||||||
|
--accent-color: #f093fb;
|
||||||
|
--text-primary: #2d3748;
|
||||||
|
--text-secondary: #718096;
|
||||||
|
--bg-primary: #ffffff;
|
||||||
|
--bg-secondary: #f7fafc;
|
||||||
|
--bg-dark: #2d3748;
|
||||||
|
--border-color: #e2e8f0;
|
||||||
|
--success-color: #48bb78;
|
||||||
|
--error-color: #f56565;
|
||||||
|
--shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
--border-radius: 12px;
|
||||||
|
--border-radius-sm: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
html {
|
||||||
font-size: 14px;
|
font-size: 16px;
|
||||||
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
line-height: 1.4;
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
color: #333333;
|
line-height: 1.6;
|
||||||
font-family: Helvetica, Arial, sans-serif;
|
color: var(--text-primary);
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin-bottom: 16px;
|
font-size: 2.5rem;
|
||||||
|
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
h2 {
|
||||||
display: block;
|
font-size: 1.875rem;
|
||||||
font-size: 14px;
|
color: var(--text-primary);
|
||||||
margin-bottom: 8px;
|
}
|
||||||
color: #777;
|
|
||||||
|
/* Form Elements */
|
||||||
|
input, button, textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 1rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
border: 1px solid #eeeeee;
|
border: 2px solid var(--border-color);
|
||||||
padding: 12px;
|
padding: 1rem 1.25rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
width: 100%;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||||
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 12px;
|
padding: 1rem 2rem;
|
||||||
background: #7C5CBF;
|
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 16px;
|
font-weight: 600;
|
||||||
transition: background .3s ease;
|
font-size: 1rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||||
|
transition: left 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover::before {
|
||||||
|
left: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
background: #6b47b8;
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
button:disabled {
|
button:disabled {
|
||||||
cursor: default;
|
cursor: not-allowed;
|
||||||
background: #7c5cbf94;
|
opacity: 0.6;
|
||||||
|
transform: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Join Page Styles */
|
/* Join Page Styles */
|
||||||
|
|
||||||
.centered-form {
|
.centered-form {
|
||||||
background: #333744;
|
min-height: 100vh;
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 2rem;
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-form::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="rgba(255,255,255,0.1)"/><circle cx="75" cy="75" r="1" fill="rgba(255,255,255,0.1)"/><circle cx="50" cy="10" r="0.5" fill="rgba(255,255,255,0.1)"/><circle cx="10" cy="60" r="0.5" fill="rgba(255,255,255,0.1)"/><circle cx="90" cy="40" r="0.5" fill="rgba(255,255,255,0.1)"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
|
||||||
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.centered-form__box {
|
.centered-form__box {
|
||||||
box-shadow: 0px 0px 17px 1px #1D1F26;
|
background: var(--bg-primary);
|
||||||
background: #F7F7FA;
|
padding: 3rem;
|
||||||
padding: 24px;
|
border-radius: var(--border-radius);
|
||||||
width: 250px;
|
box-shadow: var(--shadow-lg);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.centered-form button {
|
.centered-form__box h1 {
|
||||||
width: 100%;
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-form form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.centered-form input {
|
.centered-form input {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-form button {
|
||||||
|
margin-top: 1rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: 1.25rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chat Page Layout */
|
/* Chat Page Layout */
|
||||||
|
|
||||||
.chat {
|
.chat {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
background: var(--bg-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat__sidebar {
|
.chat__sidebar {
|
||||||
height: 100vh;
|
width: 300px;
|
||||||
|
background: var(--bg-dark);
|
||||||
color: white;
|
color: white;
|
||||||
background: #333744;
|
|
||||||
width: 225px;
|
|
||||||
overflow-y: scroll
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Chat styles */
|
|
||||||
|
|
||||||
.chat__main {
|
|
||||||
flex-grow: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-height: 100vh;
|
border-right: 1px solid var(--border-color);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat__main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar Styles */
|
||||||
|
.sidebar-header {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||||
|
padding: 2rem 1.5rem 1.5rem;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: white;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-title i {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-title {
|
||||||
|
padding: 1.5rem 1.5rem 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-title i {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.users {
|
||||||
|
list-style: none;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
border-left: 3px solid var(--primary-color);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
flex: 1;
|
||||||
|
color: white;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-user-badge {
|
||||||
|
background: var(--accent-color);
|
||||||
|
color: white;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Messages Area */
|
||||||
.chat__messages {
|
.chat__messages {
|
||||||
flex-grow: 1;
|
flex: 1;
|
||||||
padding: 24px 24px 0 24px;
|
padding: 2rem;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Message Styles */
|
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
margin-bottom: 16px;
|
background: var(--bg-primary);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
border-left: 4px solid var(--primary-color);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
max-width: 80%;
|
||||||
|
animation: slideIn 0.3s ease-out;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.own-message {
|
||||||
|
align-self: flex-end;
|
||||||
|
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||||
|
color: white;
|
||||||
|
border-left-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.own-message .message__name,
|
||||||
|
.message.own-message .message__meta {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.own-message .message__content p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.message:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message__name {
|
.message__name {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 14px;
|
color: var(--primary-color);
|
||||||
margin-right: 8px;
|
font-size: 0.875rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message__meta {
|
.message__meta {
|
||||||
color: #777;
|
color: var(--text-secondary);
|
||||||
font-size: 14px;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message a {
|
.message__content {
|
||||||
color: #0070CC;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Message Composition Styles */
|
.message__content p {
|
||||||
|
color: var(--text-primary);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Logo and Header Styles */
|
||||||
|
.logo-container {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
font-size: 3rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
animation: bounce 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group.focused label {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group label i {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.features {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature i {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat Header */
|
||||||
|
.chat__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content h2 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connection-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--success-color);
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator.offline {
|
||||||
|
background: var(--error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
background: linear-gradient(135deg, var(--error-color), #e53e3e);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn:hover {
|
||||||
|
background: linear-gradient(135deg, #e53e3e, var(--error-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Welcome Message */
|
||||||
|
.welcome-message {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
animation: fadeIn 1s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-message i {
|
||||||
|
font-size: 3rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-message h3 {
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compose Area */
|
||||||
.compose {
|
.compose {
|
||||||
display: flex;
|
padding: 1.5rem 2rem;
|
||||||
flex-shrink: 0;
|
background: var(--bg-primary);
|
||||||
margin-top: 16px;
|
border-top: 1px solid var(--border-color);
|
||||||
padding: 24px;
|
box-shadow: 0 -4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.compose form {
|
.input-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
gap: 1rem;
|
||||||
margin-right: 16px;
|
margin-bottom: 1rem;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compose input {
|
.input-wrapper:focus-within {
|
||||||
border: 1px solid #eeeeee;
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper input:focus {
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper button {
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||||
|
min-width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0%, 20%, 50%, 80%, 100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(72, 187, 120, 0.7);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 10px rgba(72, 187, 120, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(72, 187, 120, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes typing {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.chat {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat__sidebar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px;
|
height: auto;
|
||||||
margin: 0 16px 0 0;
|
max-height: 200px;
|
||||||
flex-grow: 1;
|
}
|
||||||
|
|
||||||
|
.centered-form__box {
|
||||||
|
margin: 1rem;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compose form {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
max-width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.compose button {
|
@media (max-width: 480px) {
|
||||||
font-size: 14px;
|
.centered-form {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-form__box {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat__messages {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compose {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chat Sidebar Styles */
|
/* Scrollbar Styling */
|
||||||
|
::-webkit-scrollbar {
|
||||||
.room-title {
|
width: 8px;
|
||||||
font-weight: 400;
|
|
||||||
font-size: 22px;
|
|
||||||
background: #2c2f3a;
|
|
||||||
padding: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-title {
|
::-webkit-scrollbar-track {
|
||||||
font-weight: 500;
|
background: var(--bg-secondary);
|
||||||
font-size: 18px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
padding: 12px 24px 0 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.users {
|
::-webkit-scrollbar-thumb {
|
||||||
list-style-type: none;
|
background: var(--primary-color);
|
||||||
font-weight: 300;
|
border-radius: 4px;
|
||||||
padding: 12px 24px 0 24px;
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading States */
|
||||||
|
.loading {
|
||||||
|
opacity: 0.6;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success/Error States */
|
||||||
|
.success {
|
||||||
|
border-left-color: var(--success-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
border-left-color: var(--error-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typing Indicator */
|
||||||
|
.typing-indicator {
|
||||||
|
padding: 1rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Online Status */
|
||||||
|
.online-indicator {
|
||||||
|
display: inline-block;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: var(--success-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notification System */
|
||||||
|
.notification {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
border-left: 4px solid var(--primary-color);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
z-index: 1000;
|
||||||
|
animation: slideInRight 0.3s ease-out;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-success {
|
||||||
|
border-left-color: var(--success-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-error {
|
||||||
|
border-left-color: var(--error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-warning {
|
||||||
|
border-left-color: #f6ad55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-info {
|
||||||
|
border-left-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification i:first-child {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-success i:first-child {
|
||||||
|
color: var(--success-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-error i:first-child {
|
||||||
|
color: var(--error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-warning i:first-child {
|
||||||
|
color: #f6ad55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-info i:first-child {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.25rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification button:hover {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(72, 187, 120, 0.7);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 10px rgba(72, 187, 120, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(72, 187, 120, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,25 +4,87 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Chat App</title>
|
<title>Modern Chat App - Join Chat</title>
|
||||||
<link rel="icon" href="./img/favicon.png">
|
<link rel="icon" href="./img/favicon.png">
|
||||||
<link rel="stylesheet" href="./css/styles.css">
|
<link rel="stylesheet" href="./css/styles.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="centered-form">
|
<div class="centered-form">
|
||||||
<div class="centered-form__box">
|
<div class="centered-form__box">
|
||||||
<h1>Join</h1>
|
<div class="logo-container">
|
||||||
<form action="./chat.html">
|
<i class="fas fa-comments logo-icon"></i>
|
||||||
<label>Display Name</label>
|
<h1>Chat App</h1>
|
||||||
<input type="text" name="username" required>
|
<p class="subtitle">Real-time chat experience</p>
|
||||||
<label>Room</label>
|
|
||||||
<input type="text" name="room" required>
|
|
||||||
<button>Join</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<form action="./chat.html" id="join-form">
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="username">
|
||||||
|
<i class="fas fa-user"></i>
|
||||||
|
Username
|
||||||
|
</label>
|
||||||
|
<input type="text" name="username" id="username" required
|
||||||
|
placeholder="Enter your username" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="room">
|
||||||
|
<i class="fas fa-door-open"></i>
|
||||||
|
Room Name
|
||||||
|
</label>
|
||||||
|
<input type="text" name="room" id="room" required
|
||||||
|
placeholder="Enter room name to join" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" id="join-btn">
|
||||||
|
<i class="fas fa-sign-in-alt"></i>
|
||||||
|
Join Chat
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Form validation and animations
|
||||||
|
document.getElementById('join-form').addEventListener('submit', function(e) {
|
||||||
|
const username = document.getElementById('username').value.trim();
|
||||||
|
const room = document.getElementById('room').value.trim();
|
||||||
|
const button = document.getElementById('join-btn');
|
||||||
|
|
||||||
|
if (!username || !room) {
|
||||||
|
e.preventDefault();
|
||||||
|
button.innerHTML = '<i class="fas fa-exclamation-triangle"></i> Please fill all fields';
|
||||||
|
button.style.background = 'linear-gradient(135deg, #f56565, #e53e3e)';
|
||||||
|
setTimeout(() => {
|
||||||
|
button.innerHTML = '<i class="fas fa-sign-in-alt"></i> Join Chat';
|
||||||
|
button.style.background = 'linear-gradient(135deg, #667eea, #764ba2)';
|
||||||
|
}, 2000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Joining...';
|
||||||
|
button.disabled = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Input focus animations
|
||||||
|
const inputs = document.querySelectorAll('input');
|
||||||
|
inputs.forEach(input => {
|
||||||
|
input.addEventListener('focus', function() {
|
||||||
|
this.parentElement.classList.add('focused');
|
||||||
|
});
|
||||||
|
|
||||||
|
input.addEventListener('blur', function() {
|
||||||
|
if (!this.value) {
|
||||||
|
this.parentElement.classList.remove('focused');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
const socket = io();
|
// Socket.io connection - for remote server
|
||||||
|
const socket = io(window.location.origin);
|
||||||
|
|
||||||
// Elements
|
// Elements
|
||||||
const $messageForm = document.querySelector("#message-form");
|
const $messageForm = document.querySelector("#message-form");
|
||||||
const $messageFormInput = $messageForm.querySelector("input");
|
const $messageFormInput = $messageForm.querySelector("input");
|
||||||
const $messageFormButton = $messageForm.querySelector("button");
|
const $messageFormButton = $messageForm.querySelector("#send-btn");
|
||||||
const $sendLocationButton = document.querySelector("#send-location");
|
|
||||||
const $messages = document.querySelector("#messages");
|
const $messages = document.querySelector("#messages");
|
||||||
|
const $roomName = document.querySelector("#room-name");
|
||||||
|
const $connectionStatus = document.querySelector(".status-indicator");
|
||||||
|
const $statusText = document.querySelector(".status-text");
|
||||||
|
|
||||||
// Templates
|
// Templates
|
||||||
|
|
||||||
const messageTemplate = document.querySelector("#message-template").innerHTML;
|
const messageTemplate = document.querySelector("#message-template").innerHTML;
|
||||||
const locationTemplate = document.querySelector(
|
|
||||||
"#locmessage-template"
|
|
||||||
).innerHTML;
|
|
||||||
const sidebarTemplate = document.querySelector("#sidebar-template").innerHTML;
|
const sidebarTemplate = document.querySelector("#sidebar-template").innerHTML;
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
@@ -20,11 +19,39 @@ const { username, room } = Qs.parse(location.search, {
|
|||||||
ignoreQueryPrefix: true,
|
ignoreQueryPrefix: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Connection status management
|
||||||
|
let isConnected = true;
|
||||||
|
|
||||||
|
socket.on('connect', () => {
|
||||||
|
isConnected = true;
|
||||||
|
updateConnectionStatus(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
isConnected = false;
|
||||||
|
updateConnectionStatus(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateConnectionStatus(connected) {
|
||||||
|
if (connected) {
|
||||||
|
$connectionStatus.classList.remove('offline');
|
||||||
|
$connectionStatus.classList.add('online');
|
||||||
|
$statusText.textContent = 'Connected';
|
||||||
|
} else {
|
||||||
|
$connectionStatus.classList.remove('online');
|
||||||
|
$connectionStatus.classList.add('offline');
|
||||||
|
$statusText.textContent = 'Disconnected';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto scroll functionality
|
||||||
const autoScroll = () => {
|
const autoScroll = () => {
|
||||||
// New message element
|
// New message element
|
||||||
const $newMessage = $messages.lastElementChild;
|
const $newMessage = $messages.lastElementChild;
|
||||||
|
|
||||||
// hight of the new message
|
if (!$newMessage) return;
|
||||||
|
|
||||||
|
// height of the new message
|
||||||
const newMessageStyle = getComputedStyle($newMessage);
|
const newMessageStyle = getComputedStyle($newMessage);
|
||||||
const newMessageMargin = parseInt(newMessageStyle.marginBottom);
|
const newMessageMargin = parseInt(newMessageStyle.marginBottom);
|
||||||
const newMessageHeight = $newMessage.offsetHeight + newMessageMargin;
|
const newMessageHeight = $newMessage.offsetHeight + newMessageMargin;
|
||||||
@@ -43,107 +70,164 @@ const autoScroll = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// server (emit) -> client (receive) - countUpdated
|
// Message handling
|
||||||
// client (emit) -> server (receive) - increment
|
|
||||||
|
|
||||||
socket.on("message", (message) => {
|
socket.on("message", (message) => {
|
||||||
// console.log(message);
|
const isOwnMessage = message.username === username;
|
||||||
const html = Mustache.render(messageTemplate, {
|
const html = Mustache.render(messageTemplate, {
|
||||||
username: message.username,
|
username: message.username,
|
||||||
message: message.text,
|
message: message.text,
|
||||||
createdAt: moment(message.createdAt).format("h:mm a"),
|
createdAt: moment(message.createdAt).format("HH:mm"),
|
||||||
|
isOwn: isOwnMessage
|
||||||
});
|
});
|
||||||
$messages.insertAdjacentHTML("beforeend", html);
|
|
||||||
autoScroll();
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("locationMessage", (url) => {
|
// Remove welcome message if it exists
|
||||||
// console.log(url.username);
|
const welcomeMessage = $messages.querySelector('.welcome-message');
|
||||||
const html = Mustache.render(locationTemplate, {
|
if (welcomeMessage) {
|
||||||
username: url.username,
|
welcomeMessage.remove();
|
||||||
url: url.url,
|
}
|
||||||
createdAt: moment(url.createdAt).format("h:mm a"),
|
|
||||||
});
|
|
||||||
$messages.insertAdjacentHTML("beforeend", html);
|
$messages.insertAdjacentHTML("beforeend", html);
|
||||||
autoScroll();
|
autoScroll();
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("roomData", ({ room, users }) => {
|
socket.on("roomData", ({ room, users }) => {
|
||||||
|
const usersWithCurrent = users.map(user => ({
|
||||||
|
...user,
|
||||||
|
isCurrentUser: user.username === username
|
||||||
|
}));
|
||||||
|
|
||||||
const html = Mustache.render(sidebarTemplate, {
|
const html = Mustache.render(sidebarTemplate, {
|
||||||
room: room,
|
room: room,
|
||||||
users: users,
|
users: usersWithCurrent,
|
||||||
});
|
});
|
||||||
document.querySelector("#sidebar").innerHTML = html;
|
document.querySelector("#sidebar").innerHTML = html;
|
||||||
|
$roomName.textContent = room;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Form submission
|
||||||
$messageForm.addEventListener("submit", (e) => {
|
$messageForm.addEventListener("submit", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// disable form after submit
|
// disable form after submit
|
||||||
$messageFormButton.setAttribute("disabled", "disabled");
|
$messageFormButton.setAttribute("disabled", "disabled");
|
||||||
|
$messageFormButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||||||
|
|
||||||
//disable
|
const message = $messageFormInput.value.trim();
|
||||||
|
|
||||||
const message = $messageFormInput.value;
|
|
||||||
|
|
||||||
if (message === "") {
|
if (message === "") {
|
||||||
// enable form after submit
|
// enable form after submit
|
||||||
$messageFormButton.removeAttribute("disabled");
|
$messageFormButton.removeAttribute("disabled");
|
||||||
|
$messageFormButton.innerHTML = '<i class="fas fa-paper-plane"></i>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.emit("sendMessage", message, (error) => {
|
socket.emit("sendMessage", message, (error) => {
|
||||||
// enable form after submit
|
// enable form after submit
|
||||||
$messageFormButton.removeAttribute("disabled");
|
$messageFormButton.removeAttribute("disabled");
|
||||||
|
$messageFormButton.innerHTML = '<i class="fas fa-paper-plane"></i>';
|
||||||
// clear input
|
// clear input
|
||||||
$messageFormInput.value = "";
|
$messageFormInput.value = "";
|
||||||
// focus input
|
// focus input
|
||||||
$messageFormInput.focus();
|
$messageFormInput.focus();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
showNotification(error, 'error');
|
||||||
return console.log(error);
|
return console.log(error);
|
||||||
}
|
}
|
||||||
// console.log("Message Delivered");
|
|
||||||
});
|
showNotification('Message sent!', 'success');
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector("#send-location").addEventListener("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!navigator.geolocation) {
|
|
||||||
return alert("Geolocation is not supported by your browser");
|
|
||||||
}
|
|
||||||
|
|
||||||
navigator.permissions.query({ name: "geolocation" }).then((res) => {
|
|
||||||
// console.log(res);
|
|
||||||
if (res.state === "denied") {
|
|
||||||
return alert("Please allow permission to send location!");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
navigator.geolocation.getCurrentPosition((position) => {
|
|
||||||
// console.log(position);
|
|
||||||
$sendLocationButton.setAttribute("disabled", "disabled");
|
|
||||||
|
|
||||||
socket.emit(
|
|
||||||
"sendLocation",
|
|
||||||
{
|
|
||||||
Latitude: position.coords.latitude,
|
|
||||||
Longitude: position.coords.longitude,
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
$sendLocationButton.removeAttribute("disabled");
|
|
||||||
// console.log("Location Shared");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Join room
|
||||||
socket.emit("join", { username, room }, (error) => {
|
socket.emit("join", { username, room }, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
alert(error);
|
showNotification(error, 'error');
|
||||||
|
setTimeout(() => {
|
||||||
location.href = "/";
|
location.href = "/";
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Notification system
|
||||||
|
function showNotification(message, type = 'info') {
|
||||||
|
// Remove existing notifications
|
||||||
|
const existingNotifications = document.querySelectorAll('.notification');
|
||||||
|
existingNotifications.forEach(notification => notification.remove());
|
||||||
|
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.className = `notification notification-${type}`;
|
||||||
|
notification.innerHTML = `
|
||||||
|
<i class="fas fa-${getNotificationIcon(type)}"></i>
|
||||||
|
<span>${message}</span>
|
||||||
|
<button onclick="this.parentElement.remove()">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
|
// Auto remove after 5 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
if (notification.parentElement) {
|
||||||
|
notification.remove();
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNotificationIcon(type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'success': return 'check-circle';
|
||||||
|
case 'error': return 'exclamation-circle';
|
||||||
|
case 'warning': return 'exclamation-triangle';
|
||||||
|
default: return 'info-circle';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyboard shortcuts
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
// Ctrl/Cmd + Enter to send message
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||||
|
$messageForm.dispatchEvent(new Event('submit'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape to clear input
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
$messageFormInput.value = '';
|
||||||
|
$messageFormInput.blur();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Input focus management
|
||||||
|
$messageFormInput.addEventListener('focus', () => {
|
||||||
|
$messageFormInput.parentElement.classList.add('focused');
|
||||||
|
});
|
||||||
|
|
||||||
|
$messageFormInput.addEventListener('blur', () => {
|
||||||
|
if (!$messageFormInput.value) {
|
||||||
|
$messageFormInput.parentElement.classList.remove('focused');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Page visibility API for better UX
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
document.title = '💬 Chat App - Background';
|
||||||
|
} else {
|
||||||
|
document.title = 'Chat App - Chat';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prevent form submission on Enter if Shift is held (for multi-line support)
|
||||||
|
$messageFormInput.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter' && e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
// Insert new line
|
||||||
|
const start = e.target.selectionStart;
|
||||||
|
const end = e.target.selectionEnd;
|
||||||
|
const value = e.target.value;
|
||||||
|
e.target.value = value.substring(0, start) + '\n' + value.substring(end);
|
||||||
|
e.target.selectionStart = e.target.selectionEnd = start + 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// document.querySelector("#increment").addEventListener("click", (e) => {
|
|
||||||
// console.log("clicked");
|
|
||||||
// socket.emit("increment");
|
|
||||||
// });
|
|
||||||
|
|||||||
24
src/index.js
24
src/index.js
@@ -1,3 +1,6 @@
|
|||||||
|
// Load environment variables
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
// emit events
|
// emit events
|
||||||
// socket.emit, io.emit, socket.broadcast.emit
|
// socket.emit, io.emit, socket.broadcast.emit
|
||||||
|
|
||||||
@@ -11,7 +14,6 @@ const socketio = require("socket.io");
|
|||||||
const Filter = require("bad-words");
|
const Filter = require("bad-words");
|
||||||
const {
|
const {
|
||||||
generateMessage,
|
generateMessage,
|
||||||
generateLocationMessage,
|
|
||||||
} = require("./utils/messages");
|
} = require("./utils/messages");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -29,6 +31,7 @@ const server = http.createServer(app);
|
|||||||
const io = socketio(server);
|
const io = socketio(server);
|
||||||
|
|
||||||
const port = process.env.PORT || 3000;
|
const port = process.env.PORT || 3000;
|
||||||
|
const host = process.env.HOST || '0.0.0.0'; // Tüm IP'lerden erişime izin ver
|
||||||
// define paths for express config
|
// define paths for express config
|
||||||
const publicDirectoryPath = path.join(__dirname, "../public");
|
const publicDirectoryPath = path.join(__dirname, "../public");
|
||||||
|
|
||||||
@@ -110,25 +113,10 @@ io.on("connection", (socket) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("sendLocation", (coords, callback) => {
|
|
||||||
const user = getUser(socket.id);
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return callback("You are not authenticated");
|
|
||||||
}
|
|
||||||
|
|
||||||
io.to(user.room).emit(
|
|
||||||
"locationMessage",
|
|
||||||
generateLocationMessage(
|
|
||||||
user.username,
|
|
||||||
`https://google.com/maps?q=${coords.Latitude},${coords.Longitude}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// start the server
|
// start the server
|
||||||
server.listen(port, () => {
|
server.listen(port, host, () => {
|
||||||
console.log(`Server is up on port ${port}!`);
|
console.log(`Server is up on ${host}:${port}!`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,15 +10,6 @@ const generateMessage = (username, text) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateLocationMessage = (username, url) => {
|
|
||||||
return {
|
|
||||||
username: username,
|
|
||||||
url: url,
|
|
||||||
createdAt: new Date().getTime(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
generateMessage,
|
generateMessage,
|
||||||
generateLocationMessage,
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user