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.
This commit is contained in:
apiboomer
2025-08-27 03:14:15 +03:00
parent e56c743851
commit 4bd3c48010
6 changed files with 1005 additions and 219 deletions

View File

@@ -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 = '<i class="fas fa-spinner fa-spin"></i>';
//disable
const message = $messageFormInput.value;
const message = $messageFormInput.value.trim();
if (message === "") {
// enable form after submit
$messageFormButton.removeAttribute("disabled");
$messageFormButton.innerHTML = '<i class="fas fa-paper-plane"></i>';
return;
}
socket.emit("sendMessage", message, (error) => {
// enable form after submit
$messageFormButton.removeAttribute("disabled");
$messageFormButton.innerHTML = '<i class="fas fa-paper-plane"></i>';
// 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 = `
<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");
// });