Files
chatapp/backend/templates/chat.html.tera
T
2025-10-09 01:12:08 +01:00

234 lines
9.0 KiB
Plaintext

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Discord Clone - Group Chat</title>
<link rel="stylesheet" href="static/css/index.css"/>
</head>
<body>
<div class="chat-container">
<!--<div class="chat-container" style="background-image: url('cdn/background.png'); backdrop-filter: blur(10px); background-size: cover; background-position: center; background-repeat: no-repeat;">-->
<!-- Chat Header -->
<div class="chat-header">
<div class="chat-title">
<img class="user-avatar" src="cdn/profile/0"></img>
<h1>Chat title</h1>
</div>
</div>
<!-- Live Location Notification Bubble -->
<div class="notification-container">
<div class="live-location-bubble" id="locationBubble">
<div class="map-container">
<img src="cdn/map.png" alt="Map" />
</div>
<div class="location-content">
<div class="location-icon">
<img src="cdn/icons/location.svg" alt="Location"></img>
</div>
<button class="join-button" id="joinButton">
Join
</button>
<div class="location-text">Live Location</div>
<div class="location-users" id="locationUsers">
<!-- Users will be added dynamically -->
</div>
</div>
</div>
</div>
<!-- Messages Container -->
<!--<div class="messages-container" style="background-image: url('cdn/background.png'); backdrop-filter: blur(10px); background-size: cover; background-position: center; background-repeat: no-repeat;">-->
<div class="messages-container"></div>
<!-- Input Container -->
<div class="input-container">
<div class="input-wrapper">
<input type="text" placeholder="Start Typing..." />
<button class="send-button">
<img src="cdn/icons/send.svg" alt="Send" />
</button>
</div>
</div>
</div>
<script>
const user_id = {{ user_id }};
var users = {};
// Location tracker state
const locationUsers = [
{ id: 1, color: "linear-gradient(135deg, #ff6b6b, #ff8e8e)" },
{ id: 2, color: "linear-gradient(135deg, #4ecdc4, #7fdbda)" },
{ id: 3, color: "linear-gradient(135deg, #45b7d1, #6cc5e0)" },
];
let hiddenUsersCount = 5; // Users not shown in the visible stack
let currentUserInLocation = false;
function updateLocationUsers() {
const container = document.getElementById("locationUsers");
container.innerHTML = "";
const maxVisible = 3;
const visibleUsers = locationUsers.slice(0, maxVisible);
visibleUsers.forEach((user) => {
const pic = document.createElement("div");
pic.className = "location-user-pic";
pic.style.background = user.color;
container.appendChild(pic);
});
// Calculate total hidden users (hidden users + current user if tracking)
const totalHidden =
hiddenUsersCount + (currentUserInLocation ? 1 : 0);
// Add count indicator if there are more users
if (totalHidden > 0) {
const count = document.createElement("div");
count.className = "location-count";
count.textContent = `+${totalHidden}`;
container.appendChild(count);
}
}
// Toggle location tracking
const locationBubble = document.getElementById("locationBubble");
const joinButton = document.getElementById("joinButton");
let isExpanded = false;
locationBubble.addEventListener("click", function (e) {
// Don't toggle expanded state if clicking the join button
if (e.target.id === "joinButton") return;
isExpanded = !isExpanded;
this.classList.toggle("expanded", isExpanded);
});
joinButton.addEventListener("click", function (e) {
e.stopPropagation();
currentUserInLocation = !currentUserInLocation;
this.classList.toggle("active", currentUserInLocation);
this.textContent = currentUserInLocation ? "Leave" : "Join";
locationBubble.classList.toggle(
"active",
currentUserInLocation,
);
// Animate click
this.style.transform = "scale(0.95)";
setTimeout(() => {
this.style.transform = "";
}, 150);
updateLocationUsers();
});
// Initialize location users
updateLocationUsers();
// Handle message sending
const input = document.querySelector("input");
const sendButton = document.querySelector(".send-button");
const messagesContainer = document.querySelector(
".messages-container",
);
function insertMessage(message) {
const date = new Date(message.timestamp).toLocaleTimeString("en-GB", {
hour: "numeric",
minute: "2-digit",
hour12: true,
});
const messageEl = document.createElement("div");
messageEl.className = "message";
messageEl.innerHTML = `
<img class="user-avatar" src="cdn/profile/${message.user_id}">
<div class="message-content">
<div class="message-header">
<span class="username">${message.display_name}</span>
<span class="timestamp">${date}</span>
</div>
<div class="message-text">${message.text}</div>
</div>
`;
messagesContainer.appendChild(messageEl);
messagesContainer.scrollTop =
messagesContainer.scrollHeight;
}
function getCurrentTime() {
const now = new Date();
return now.toLocaleTimeString("en-GB", {
hour: "numeric",
minute: "2-digit",
hour12: true,
});
}
function sendMessage() {
const message = input.value.trim();
if (message) {
fetch("http://localhost:8000/chat", {
method: "POST",
body: JSON.stringify({
user_id: user_id,
text: message,
timestamp: new Date().getTime(),
}),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
});
input.value = "";
}
}
sendButton.addEventListener("click", sendMessage);
input.addEventListener("keypress", function (e) {
if (e.key === "Enter") {
sendMessage();
}
});
async function loadData() {
try {
const userIds = await fetch("http://localhost:8000/users/")
.then(r => r.json());
const userPromises = userIds.map(userId =>
fetch(`http://localhost:8000/users/${userId}`)
.then(r => r.text())
.then(username => ({ userId, username }))
);
const userData = await Promise.all(userPromises);
userData.forEach(({ userId, username }) => {
users[userId] = username;
});
console.log('Users loaded:', users);
const messageSource = new EventSource("http://localhost:8000/events");
messageSource.onmessage = (event) => insertMessage(JSON.parse(event.data));
messageSource.onerror = (error) => {
console.error('EventSource error:', error);
};
} catch (error) {
console.error('Error loading data:', error);
}
}
loadData();
</script>
</body>
</html>