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, };