From 4bd3c48010021264442b4e468e3dfb1321d99d0e Mon Sep 17 00:00:00 2001 From: apiboomer Date: Wed, 27 Aug 2025 03:14:15 +0300 Subject: [PATCH] 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, };