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:
@@ -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");
|
||||
// });
|
||||
|
||||
Reference in New Issue
Block a user