From e56c743851f1596fe8bf99304aa39bc215a54327 Mon Sep 17 00:00:00 2001 From: apiboomer Date: Wed, 27 Aug 2025 01:48:09 +0300 Subject: [PATCH 1/3] Add dotenv support and update server config Introduced dotenv for environment variable management and created a .env file for port configuration. Updated server to use HOST and PORT from environment, allowing access from all IPs. Modified Socket.io client connection to use window.location.origin for remote server compatibility. --- .env | 1 + package-lock.json | 18 ++++++++++++++++++ package.json | 1 + public/js/chat.js | 3 ++- src/index.js | 8 ++++++-- 5 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..9f7ac38 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +PORT=7010 diff --git a/package-lock.json b/package-lock.json index b8a8b43..1c3c598 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "bad-words": "^3.0.4", + "dotenv": "^16.6.1", "express": "^4.18.2", "moment": "^2.29.4", "socket.io": "^4.7.2" @@ -300,6 +301,18 @@ "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": { "version": "1.1.1", "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", "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": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/package.json b/package.json index 50c7db1..2661672 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "license": "ISC", "dependencies": { "bad-words": "^3.0.4", + "dotenv": "^16.3.1", "express": "^4.18.2", "moment": "^2.29.4", "socket.io": "^4.7.2" diff --git a/public/js/chat.js b/public/js/chat.js index b93ab09..edfa800 100644 --- a/public/js/chat.js +++ b/public/js/chat.js @@ -1,4 +1,5 @@ -const socket = io(); +// Socket.io bağlantısı - uzak sunucu için +const socket = io(window.location.origin); // Elements const $messageForm = document.querySelector("#message-form"); diff --git a/src/index.js b/src/index.js index c91118b..e4d8abf 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,6 @@ +// Load environment variables +require('dotenv').config(); + // emit events // socket.emit, io.emit, socket.broadcast.emit @@ -29,6 +32,7 @@ const server = http.createServer(app); const io = socketio(server); 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 const publicDirectoryPath = path.join(__dirname, "../public"); @@ -129,6 +133,6 @@ io.on("connection", (socket) => { }); // start the server -server.listen(port, () => { - console.log(`Server is up on port ${port}!`); +server.listen(port, host, () => { + console.log(`Server is up on ${host}:${port}!`); }); From 4bd3c48010021264442b4e468e3dfb1321d99d0e Mon Sep 17 00:00:00 2001 From: apiboomer Date: Wed, 27 Aug 2025 03:14:15 +0300 Subject: [PATCH 2/3] Redesign chat UI and remove location sharing Modernizes the chat and join pages with new styles, improved layouts, and enhanced user experience using custom CSS and FontAwesome icons. Removes location sharing functionality from both frontend and backend, including related templates and code. Adds connection status indicators, notification system, and keyboard shortcuts for better usability. --- public/chat.html | 96 +++-- public/css/styles.css | 808 +++++++++++++++++++++++++++++++++++++----- public/index.html | 80 ++++- public/js/chat.js | 215 +++++++---- src/index.js | 16 - src/utils/messages.js | 9 - 6 files changed, 1005 insertions(+), 219 deletions(-) diff --git a/public/chat.html b/public/chat.html index 023ee15..e435d94 100644 --- a/public/chat.html +++ b/public/chat.html @@ -4,60 +4,96 @@ - Chat App + Modern Chat App - Chat + +
+
-
+
+
+

Chat Room

+
+ + Connected +
+
+ +
+ +
+
+ +

Welcome to Chat!

+

Start sending messages to begin chatting.

+
+
-
- - +
+ + +
-
-
- - + diff --git a/public/css/styles.css b/public/css/styles.css index ed550f2..97bb989 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -1,4 +1,4 @@ -/* General Styles */ +/* Modern Chat App Styles */ * { margin: 0; @@ -6,178 +6,808 @@ box-sizing: border-box; } -html { - font-size: 16px; +:root { + --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 { - font-size: 14px; +html { + font-size: 16px; + scroll-behavior: smooth; } body { - line-height: 1.4; - color: #333333; - font-family: Helvetica, Arial, sans-serif; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + line-height: 1.6; + 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 { - 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 { - display: block; - font-size: 14px; - margin-bottom: 8px; - color: #777; +h2 { + font-size: 1.875rem; + color: var(--text-primary); +} + +/* Form Elements */ +input, button, textarea { + font-family: inherit; + font-size: 1rem; + border-radius: var(--border-radius-sm); + transition: all 0.3s ease; } input { - border: 1px solid #eeeeee; - padding: 12px; + border: 2px solid var(--border-color); + padding: 1rem 1.25rem; 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 { cursor: pointer; - padding: 12px; - background: #7C5CBF; + padding: 1rem 2rem; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); border: none; color: white; - font-size: 16px; - transition: background .3s ease; + font-weight: 600; + 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 { - background: #6b47b8; + transform: translateY(-2px); + box-shadow: var(--shadow-lg); +} + +button:active { + transform: translateY(0); } button:disabled { - cursor: default; - background: #7c5cbf94; + cursor: not-allowed; + 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 */ - .centered-form { - background: #333744; - width: 100vw; - height: 100vh; + min-height: 100vh; display: flex; justify-content: 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,'); + opacity: 0.3; } .centered-form__box { - box-shadow: 0px 0px 17px 1px #1D1F26; - background: #F7F7FA; - padding: 24px; - width: 250px; + background: var(--bg-primary); + padding: 3rem; + border-radius: var(--border-radius); + 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 { - width: 100%; +.centered-form__box h1 { + text-align: center; + margin-bottom: 2rem; + font-size: 2.5rem; +} + +.centered-form form { + display: flex; + flex-direction: column; + gap: 1.5rem; } .centered-form input { - margin-bottom: 16px; + margin-bottom: 0; +} + +.centered-form button { + margin-top: 1rem; width: 100%; + padding: 1.25rem; + font-size: 1.1rem; } /* Chat Page Layout */ - .chat { display: flex; + height: 100vh; + background: var(--bg-secondary); } .chat__sidebar { - height: 100vh; + width: 300px; + background: var(--bg-dark); color: white; - background: #333744; - width: 225px; - overflow-y: scroll -} - -/* Chat styles */ - -.chat__main { - flex-grow: 1; display: flex; 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 { - flex-grow: 1; - padding: 24px 24px 0 24px; - overflow-y: scroll; + flex: 1; + padding: 2rem; + overflow-y: auto; + background: var(--bg-secondary); + display: flex; + flex-direction: column; + gap: 1rem; } -/* Message Styles */ - .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 { font-weight: 600; - font-size: 14px; - margin-right: 8px; + color: var(--primary-color); + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.05em; } .message__meta { - color: #777; - font-size: 14px; + color: var(--text-secondary); + font-size: 0.75rem; } -.message a { - color: #0070CC; +.message__content { + 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 { + padding: 1.5rem 2rem; + background: var(--bg-primary); + border-top: 1px solid var(--border-color); + box-shadow: 0 -4px 6px -1px rgba(0, 0, 0, 0.1); +} + +.input-wrapper { display: flex; - flex-shrink: 0; - margin-top: 16px; - padding: 24px; + gap: 1rem; + 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 form { +.input-wrapper:focus-within { + 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%; + height: auto; + max-height: 200px; + } + + .centered-form__box { + margin: 1rem; + padding: 2rem; + } + + .compose form { + flex-direction: column; + } + + .message { + max-width: 95%; + } + + h1 { + font-size: 2rem; + } +} + +@media (max-width: 480px) { + .centered-form { + padding: 1rem; + } + + .centered-form__box { + padding: 1.5rem; + } + + .chat__messages { + padding: 1rem; + } + + .compose { + padding: 1rem; + } +} + +/* Scrollbar Styling */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: var(--bg-secondary); +} + +::-webkit-scrollbar-thumb { + background: var(--primary-color); + border-radius: 4px; +} + +::-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; - flex-grow: 1; - margin-right: 16px; + align-items: center; + gap: 0.75rem; + z-index: 1000; + animation: slideInRight 0.3s ease-out; + max-width: 400px; } -.compose input { - border: 1px solid #eeeeee; - width: 100%; - padding: 12px; - margin: 0 16px 0 0; - flex-grow: 1; +.notification-success { + border-left-color: var(--success-color); } -.compose button { - font-size: 14px; +.notification-error { + border-left-color: var(--error-color); } -/* Chat Sidebar Styles */ - -.room-title { - font-weight: 400; - font-size: 22px; - background: #2c2f3a; - padding: 24px; +.notification-warning { + border-left-color: #f6ad55; } -.list-title { - font-weight: 500; - font-size: 18px; - margin-bottom: 4px; - padding: 12px 24px 0 24px; +.notification-info { + border-left-color: var(--primary-color); } -.users { - list-style-type: none; - font-weight: 300; - padding: 12px 24px 0 24px; +.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); + } } \ No newline at end of file diff --git a/public/index.html b/public/index.html index bf1a572..d3eba51 100644 --- a/public/index.html +++ b/public/index.html @@ -4,25 +4,87 @@ - Chat App + Modern Chat App - Join Chat + +
-

Join

-
- - - - - +
+ +

Chat App

+

Real-time chat experience

+
+ + +
+ + +
+ +
+ + +
+ +
+ +
-
+ + \ No newline at end of file diff --git a/public/js/chat.js b/public/js/chat.js index edfa800..2af5cea 100644 --- a/public/js/chat.js +++ b/public/js/chat.js @@ -1,19 +1,17 @@ -// Socket.io bağlantısı - uzak sunucu için +// Socket.io connection - for remote server const socket = io(window.location.origin); // Elements const $messageForm = document.querySelector("#message-form"); const $messageFormInput = $messageForm.querySelector("input"); -const $messageFormButton = $messageForm.querySelector("button"); -const $sendLocationButton = document.querySelector("#send-location"); +const $messageFormButton = $messageForm.querySelector("#send-btn"); const $messages = document.querySelector("#messages"); +const $roomName = document.querySelector("#room-name"); +const $connectionStatus = document.querySelector(".status-indicator"); +const $statusText = document.querySelector(".status-text"); // Templates - const messageTemplate = document.querySelector("#message-template").innerHTML; -const locationTemplate = document.querySelector( - "#locmessage-template" -).innerHTML; const sidebarTemplate = document.querySelector("#sidebar-template").innerHTML; // Options @@ -21,11 +19,39 @@ const { username, room } = Qs.parse(location.search, { 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 = () => { // New message element const $newMessage = $messages.lastElementChild; - // hight of the new message + if (!$newMessage) return; + + // height of the new message const newMessageStyle = getComputedStyle($newMessage); const newMessageMargin = parseInt(newMessageStyle.marginBottom); const newMessageHeight = $newMessage.offsetHeight + newMessageMargin; @@ -44,107 +70,164 @@ const autoScroll = () => { } }; -// server (emit) -> client (receive) - countUpdated -// client (emit) -> server (receive) - increment - +// Message handling socket.on("message", (message) => { - // console.log(message); + const isOwnMessage = message.username === username; const html = Mustache.render(messageTemplate, { username: message.username, message: message.text, - createdAt: moment(message.createdAt).format("h:mm a"), - }); - $messages.insertAdjacentHTML("beforeend", html); - autoScroll(); -}); - -socket.on("locationMessage", (url) => { - // console.log(url.username); - const html = Mustache.render(locationTemplate, { - username: url.username, - url: url.url, - createdAt: moment(url.createdAt).format("h:mm a"), + createdAt: moment(message.createdAt).format("HH:mm"), + isOwn: isOwnMessage }); + + // Remove welcome message if it exists + const welcomeMessage = $messages.querySelector('.welcome-message'); + if (welcomeMessage) { + welcomeMessage.remove(); + } + $messages.insertAdjacentHTML("beforeend", html); autoScroll(); }); socket.on("roomData", ({ room, users }) => { + const usersWithCurrent = users.map(user => ({ + ...user, + isCurrentUser: user.username === username + })); + const html = Mustache.render(sidebarTemplate, { room: room, - users: users, + users: usersWithCurrent, }); document.querySelector("#sidebar").innerHTML = html; + $roomName.textContent = room; }); +// Form submission $messageForm.addEventListener("submit", (e) => { e.preventDefault(); // disable form after submit $messageFormButton.setAttribute("disabled", "disabled"); + $messageFormButton.innerHTML = ''; - //disable - - const message = $messageFormInput.value; + const message = $messageFormInput.value.trim(); if (message === "") { // enable form after submit $messageFormButton.removeAttribute("disabled"); + $messageFormButton.innerHTML = ''; return; } + socket.emit("sendMessage", message, (error) => { // enable form after submit $messageFormButton.removeAttribute("disabled"); + $messageFormButton.innerHTML = ''; // clear input $messageFormInput.value = ""; // focus input $messageFormInput.focus(); + if (error) { + showNotification(error, 'error'); return console.log(error); } - // console.log("Message Delivered"); - }); -}); - -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"); - } - ); + + showNotification('Message sent!', 'success'); }); }); +// Join room socket.emit("join", { username, room }, (error) => { if (error) { - alert(error); - location.href = "/"; + showNotification(error, 'error'); + setTimeout(() => { + 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 = ` + + ${message} + + `; + + 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"); -// }); diff --git a/src/index.js b/src/index.js index e4d8abf..e7b0d8c 100644 --- a/src/index.js +++ b/src/index.js @@ -14,7 +14,6 @@ const socketio = require("socket.io"); const Filter = require("bad-words"); const { generateMessage, - generateLocationMessage, } = require("./utils/messages"); const { @@ -114,22 +113,7 @@ 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 diff --git a/src/utils/messages.js b/src/utils/messages.js index 0744dfa..58ced88 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -10,15 +10,6 @@ const generateMessage = (username, text) => { }; }; -const generateLocationMessage = (username, url) => { - return { - username: username, - url: url, - createdAt: new Date().getTime(), - }; -}; - module.exports = { generateMessage, - generateLocationMessage, }; From c9237bb5e5c30ce457dceab39ffb63c43c989cd9 Mon Sep 17 00:00:00 2001 From: apiboomer Date: Wed, 27 Aug 2025 03:17:49 +0300 Subject: [PATCH 3/3] Readme Update --- .env | 2 +- README.md | 166 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 132 insertions(+), 36 deletions(-) diff --git a/.env b/.env index 9f7ac38..2fc80e3 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -PORT=7010 +PORT=3000 diff --git a/README.md b/README.md index b1fb4f4..ac7665e 100644 --- a/README.md +++ b/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. -- **Multiple Rooms:** Users can join different chat rooms. -- **User-friendly Interface:** Provides an intuitive and clean user interface for an enjoyable chat experience. +## 🚀 Tech Stack -## 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 - 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 -cd realtime-nodejs-chat-app +# .env file (optional) +PORT=3000 +HOST=0.0.0.0 ``` -3. Install dependencies: +## 🚀 Deployment + +### Local Development ```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 -1. Enter a username. -2. Choose a chat room to join. -3. Start chatting with other users in real-time. +## 🤝 Contributing -## File Structure -- **src/index.js**: The main server file that handles WebSocket connections and serves the HTML page. -- **public/index.html**: The HTML template for the chat application. -- **public/style.css**: The stylesheet for styling the chat application. +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +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 -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! \ No newline at end of file +## 🙏 Acknowledgments + +- [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** \ No newline at end of file