Huge update.
This commit is contained in:
4
main.py
4
main.py
@@ -19,6 +19,10 @@ registry = RoomRegistry()
|
|||||||
def index():
|
def index():
|
||||||
return render_template("home.html")
|
return render_template("home.html")
|
||||||
|
|
||||||
|
@app.route('/settings')
|
||||||
|
def settings():
|
||||||
|
return render_template("settings.html")
|
||||||
|
|
||||||
@app.route('/room/<room_code>')
|
@app.route('/room/<room_code>')
|
||||||
def room(room_code):
|
def room(room_code):
|
||||||
return render_template('room.html', room_code=room_code)
|
return render_template('room.html', room_code=room_code)
|
||||||
|
|||||||
@@ -6,14 +6,33 @@
|
|||||||
--text-highlight-color: white;
|
--text-highlight-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Navigation */
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0px;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark {
|
||||||
|
--primary-color: #121212;
|
||||||
|
--secondary-color: rgb(7, 155, 152);
|
||||||
|
--tertiary-color: #1e1e1e;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--text-highlight-color: rgb(19, 18, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.transition, body.transition {
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top:0px;
|
top:0px;
|
||||||
left:0px;
|
left:0px;
|
||||||
right:0px;
|
right:0px;
|
||||||
background-color: var(--tertiary-color);
|
background-color: var(--tertiary-color);
|
||||||
border-bottom: 1px solid var(--secondary-color); /* Changed color to lightsteelblue */
|
border-bottom: 1px solid var(--secondary-color);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
@@ -24,14 +43,14 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: background-color 0.15s ease, color 0.15s ease, padding 0.15s ease; /* Only transition the background-color */
|
transition: background-color 0.15s ease, color 0.15s ease, padding 0.15s ease;
|
||||||
color: var(--text-color); /* Set default text color */
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar a:hover {
|
.navbar a:hover {
|
||||||
background-color: var(--secondary-color);
|
background-color: var(--secondary-color);
|
||||||
color: var(--text-highlight-color); /* Change text color on hover for better contrast */
|
color: var(--text-highlight-color);
|
||||||
padding: 8px 20px; /* Increase padding on hover */
|
padding: 8px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navname{
|
.navname{
|
||||||
@@ -43,19 +62,14 @@
|
|||||||
background-color: var(--primary-color) !important;
|
background-color: var(--primary-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Body */
|
|
||||||
body {
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
margin: 0px;
|
|
||||||
color: var(--text-color);
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Container */
|
|
||||||
.container {
|
.container {
|
||||||
padding: 35px 10px;
|
padding: 35px 10px 0px 10px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
|
min-height: calc(100% - 35px - 10px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 87vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-join-box {
|
.room-join-box {
|
||||||
@@ -102,12 +116,13 @@ body {
|
|||||||
.chat-container {
|
.chat-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 70vh; /* or whatever height works well for your layout */
|
flex-grow: 1;
|
||||||
|
min-height: 0; /* Allow it to shrink */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#chat-box {
|
#chat-box {
|
||||||
flex: 1;
|
flex: 1 1 auto;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid var(--secondary-color);
|
border: 1px solid var(--secondary-color);
|
||||||
@@ -122,9 +137,9 @@ body {
|
|||||||
|
|
||||||
|
|
||||||
#chat-box p {
|
#chat-box p {
|
||||||
margin: 0 0 5px 0; /* Adjust message spacing */
|
margin: 0 0 5px 0;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
word-wrap: break-word; /* Ensures long words break and wrap */
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input-container {
|
.chat-input-container {
|
||||||
@@ -142,7 +157,7 @@ body {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: padding 0.15s ease, border-color 0.15s ease;
|
transition: padding 0.15s ease, border-color 0.15s ease;
|
||||||
flex: 1; /* Makes the input take up available horizontal space */
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#send-button {
|
#send-button {
|
||||||
@@ -154,7 +169,7 @@ body {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.15s ease, color 0.15s ease, padding 0.15s ease;
|
transition: background-color 0.15s ease, color 0.15s ease, padding 0.15s ease;
|
||||||
flex-shrink: 0; /* Prevents the button from shrinking */
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#send-button:hover {
|
#send-button:hover {
|
||||||
@@ -163,3 +178,45 @@ body {
|
|||||||
padding-left: 18px;
|
padding-left: 18px;
|
||||||
padding-right: 18px;
|
padding-right: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.setting-item {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-label {
|
||||||
|
gap: 10px;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-toggle {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
accent-color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-select {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid var(--secondary-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: var(--tertiary-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 16px;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-select:focus {
|
||||||
|
border-color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-transitions * {
|
||||||
|
transition: none !important;
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
67
resource/static/js/base.js
Normal file
67
resource/static/js/base.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
const STORAGE_VERSION = '11'; // Update this when changing storage structure
|
||||||
|
|
||||||
|
if (localStorage.getItem('version') !== STORAGE_VERSION) {
|
||||||
|
localStorage.clear();
|
||||||
|
localStorage.setItem('version', STORAGE_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedSettingDarkPersist = localStorage.getItem('dark-mode-toggle');
|
||||||
|
if (savedSettingDarkPersist === 'true') {
|
||||||
|
document.body.classList.add('dark');
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove('dark');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.classList.add('no-transitions');
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
document.documentElement.classList.remove('no-transitions');
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
function updateNavbarWithRooms() {
|
||||||
|
const navbar = document.querySelector('.navbar');
|
||||||
|
if (!navbar) return;
|
||||||
|
|
||||||
|
const linksToRemove = [];
|
||||||
|
navbar.querySelectorAll('a').forEach(link => {
|
||||||
|
if (link.href && link.href.includes('/room/')) {
|
||||||
|
linksToRemove.push(link);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
linksToRemove.forEach(link => link.remove());
|
||||||
|
|
||||||
|
const allUsernames = JSON.parse(localStorage.getItem('usernames') || '{}');
|
||||||
|
const rooms = Object.keys(allUsernames);
|
||||||
|
|
||||||
|
rooms.forEach(roomCode => {
|
||||||
|
const roomLink = document.createElement('a');
|
||||||
|
roomLink.href = `/room/${roomCode}`;
|
||||||
|
roomLink.textContent = `Room: ${roomCode}`;
|
||||||
|
|
||||||
|
const removeBtn = document.createElement('span');
|
||||||
|
removeBtn.textContent = ' ×';
|
||||||
|
removeBtn.onclick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
removeRoom(roomCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
roomLink.appendChild(removeBtn);
|
||||||
|
|
||||||
|
const settingsLink = navbar.querySelector('a[href="/settings"]');
|
||||||
|
if (settingsLink) {
|
||||||
|
settingsLink.before(roomLink);
|
||||||
|
} else {
|
||||||
|
navbar.appendChild(roomLink);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRoom(roomCode) {
|
||||||
|
const allUsernames = JSON.parse(localStorage.getItem('usernames')) || '{}';
|
||||||
|
delete allUsernames[roomCode];
|
||||||
|
localStorage.setItem('usernames', JSON.stringify(allUsernames));
|
||||||
|
updateNavbarWithRooms();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', updateNavbarWithRooms);
|
||||||
33
resource/static/js/home.js
Normal file
33
resource/static/js/home.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
let pendingUsername = null;
|
||||||
|
|
||||||
|
function getUsername() {
|
||||||
|
const username = document.getElementById('username-input').value.trim();
|
||||||
|
if (!username) {
|
||||||
|
alert("Please enter your name.");
|
||||||
|
throw new Error("Username required");
|
||||||
|
}
|
||||||
|
pendingUsername = username;
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('create-button').addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
const username = getUsername();
|
||||||
|
const res = await fetch('/api/room', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.code) {
|
||||||
|
const allUsernames = JSON.parse(localStorage.getItem('usernames') || '{}');
|
||||||
|
allUsernames[data.code] = pendingUsername;
|
||||||
|
localStorage.setItem('usernames', JSON.stringify(allUsernames));
|
||||||
|
|
||||||
|
window.location.href = `/room/${data.code}`;
|
||||||
|
} else {
|
||||||
|
alert("Failed to create room.");
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
}
|
||||||
|
});
|
||||||
147
resource/static/js/room.js
Normal file
147
resource/static/js/room.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
const socket = io();
|
||||||
|
|
||||||
|
let originalTitle = document.title;
|
||||||
|
let notificationActive = false;
|
||||||
|
let notificationInterval = null;
|
||||||
|
|
||||||
|
// The 'room' variable is now directly available from the template,
|
||||||
|
// so you don't need to get it from the URL.
|
||||||
|
|
||||||
|
function getUsername(roomCode) {
|
||||||
|
const allUsernames = JSON.parse(localStorage.getItem('usernames') || '{}');
|
||||||
|
let storedUsername = allUsernames[roomCode];
|
||||||
|
|
||||||
|
// If a username exists and is not just whitespace, return it
|
||||||
|
if (storedUsername && storedUsername.trim()) {
|
||||||
|
return storedUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no stored username, or it's empty, prompt the user
|
||||||
|
let newUsername = prompt("Please enter your name:");
|
||||||
|
|
||||||
|
// If the user cancels the prompt or enters only whitespace
|
||||||
|
if (newUsername === null || !newUsername.trim()) {
|
||||||
|
return null; // Indicates user didn't provide a valid name
|
||||||
|
}
|
||||||
|
|
||||||
|
newUsername = newUsername.trim();
|
||||||
|
|
||||||
|
// Store for future use
|
||||||
|
allUsernames[roomCode] = newUsername;
|
||||||
|
localStorage.setItem('usernames', JSON.stringify(allUsernames));
|
||||||
|
|
||||||
|
return newUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the 'room' variable provided by the template directly
|
||||||
|
const name = getUsername(room); // 'room' is now defined by your template script
|
||||||
|
if (!name) {
|
||||||
|
// If the user cancels or provides no name, redirect them
|
||||||
|
window.location.href = '/';
|
||||||
|
} else {
|
||||||
|
socket.emit('join', { room, sender: name });
|
||||||
|
loadMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
socket.on('message', (data) => {
|
||||||
|
const chatBox = document.getElementById('chat-box');
|
||||||
|
const msgElem = document.createElement('p');
|
||||||
|
|
||||||
|
msgElem.textContent = `${data.sender}: ${data.text}`;
|
||||||
|
if (data.sender === 'System') {
|
||||||
|
msgElem.style.fontStyle = 'italic';
|
||||||
|
msgElem.style.color = 'gray';
|
||||||
|
}
|
||||||
|
|
||||||
|
chatBox.appendChild(msgElem);
|
||||||
|
chatBox.scrollTop = chatBox.scrollHeight;
|
||||||
|
|
||||||
|
showNotification("Echo | New Message", `${data.sender}: ${data.text}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
function showNotification(title, body) {
|
||||||
|
const notificationSetting = localStorage.getItem('notification-setting') || 'off';
|
||||||
|
|
||||||
|
if (notificationSetting === 'off') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notificationSetting === 'system') {
|
||||||
|
if (document.hasFocus()) return;
|
||||||
|
|
||||||
|
if (Notification.permission === 'granted') {
|
||||||
|
new Notification(title, { body });
|
||||||
|
} else if (Notification.permission !== 'denied') {
|
||||||
|
Notification.requestPermission().then(permission => {
|
||||||
|
if (permission === 'granted') {
|
||||||
|
new Notification(title, { body });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (notificationSetting === 'tab') {
|
||||||
|
if (!document.hasFocus() && !notificationActive) {
|
||||||
|
originalTitle = document.title;
|
||||||
|
notificationInterval = setInterval(() => {
|
||||||
|
document.title = document.title === originalTitle
|
||||||
|
? "Echo | New Message!"
|
||||||
|
: originalTitle;
|
||||||
|
}, 1000);
|
||||||
|
notificationActive = true;
|
||||||
|
|
||||||
|
if (!window.hasNotificationListener) {
|
||||||
|
const returnToNormal = () => {
|
||||||
|
clearInterval(notificationInterval);
|
||||||
|
document.title = originalTitle;
|
||||||
|
notificationActive = false;
|
||||||
|
};
|
||||||
|
window.addEventListener('focus', returnToNormal);
|
||||||
|
window.hasNotificationListener = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('send-button').onclick = () => {
|
||||||
|
const text = document.getElementById('chat-input').value;
|
||||||
|
const allUsernames = JSON.parse(localStorage.getItem('usernames') || '{}');
|
||||||
|
const sender = allUsernames[room] || 'Anonymous';
|
||||||
|
|
||||||
|
if (text.trim()) {
|
||||||
|
socket.emit('message', {
|
||||||
|
room,
|
||||||
|
text,
|
||||||
|
sender
|
||||||
|
});
|
||||||
|
document.getElementById('chat-input').value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementById('chat-input').addEventListener('keydown', (event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
document.getElementById('send-button').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function loadMessages() {
|
||||||
|
fetch(`/api/room/${room}/messages`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
const chatBox = document.getElementById('chat-box');
|
||||||
|
chatBox.innerHTML = '';
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
data.forEach(msg => {
|
||||||
|
const msgElem = document.createElement('p');
|
||||||
|
msgElem.textContent = `${msg.sender}: ${msg.text}`;
|
||||||
|
chatBox.appendChild(msgElem);
|
||||||
|
});
|
||||||
|
chatBox.scrollTop = chatBox.scrollHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('pageshow', (event) => {
|
||||||
|
if (event.persisted) {
|
||||||
|
loadMessages();
|
||||||
|
}
|
||||||
|
});
|
||||||
32
resource/static/js/settings.js
Normal file
32
resource/static/js/settings.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
const darkToggle = document.getElementById('dark-mode-toggle');
|
||||||
|
const notificationDropDown = document.getElementById('notification-setting');
|
||||||
|
|
||||||
|
const savedSettingNotif = localStorage.getItem('notification-setting');
|
||||||
|
if (savedSettingNotif) {
|
||||||
|
notificationDropDown.value = savedSettingNotif;
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedSettingDark = localStorage.getItem('dark-mode-toggle');
|
||||||
|
if (savedSettingDark === 'true') {
|
||||||
|
darkToggle.checked = true;
|
||||||
|
document.body.classList.add('dark');
|
||||||
|
} else {
|
||||||
|
darkToggle.checked = false;
|
||||||
|
document.body.classList.remove('dark');
|
||||||
|
}
|
||||||
|
|
||||||
|
darkToggle.addEventListener('change', () => {
|
||||||
|
if (darkToggle.checked) {
|
||||||
|
document.body.classList.add('transition');
|
||||||
|
document.body.classList.add('dark');
|
||||||
|
localStorage.setItem('dark-mode-toggle', 'true');
|
||||||
|
} else {
|
||||||
|
document.body.classList.add('transition');
|
||||||
|
document.body.classList.remove('dark');
|
||||||
|
localStorage.setItem('dark-mode-toggle', 'false');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationDropDown.addEventListener('change', () => {
|
||||||
|
localStorage.setItem('notification-setting', notificationDropDown.value);
|
||||||
|
});
|
||||||
@@ -5,9 +5,11 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||||
<link rel="icon" href="data:,">
|
<link rel="icon" href="data:,">
|
||||||
|
|
||||||
<title>{% block title %}{% endblock %}</title>
|
<title>{% block title %}{% endblock %}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<script src="{{ url_for('static', filename='js/base.js') }}"></script>
|
||||||
{% include 'navbar.html' %}
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|||||||
@@ -1,63 +1,27 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block title %}Home{% endblock %}
|
{% block title %}Echo | Home{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Hello, hello, hello...</h1>
|
<h1>Hello, hello, hello...</h1>
|
||||||
<p>Yeah, it's early.</p>
|
<p>Yeah, it's early.</p>
|
||||||
|
|
||||||
|
<h3>Firstly, name:</h3>
|
||||||
<div class="room-join-box">
|
<div class="room-join-box">
|
||||||
<input type="text" id="username-input" class="room-input" placeholder="Your name">
|
<input type="text" id="username-input" class="room-input" placeholder="Your name">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3>Join an Existing Room?</h3>
|
||||||
<div class="room-join-box">
|
<div class="room-join-box">
|
||||||
<input type="text" id="room-code-input" class="room-input" placeholder="Enter Room Code" maxlength="10">
|
<input type="text" id="room-code-input" class="room-input" placeholder="Enter Room Code" maxlength="10">
|
||||||
<button class="room-button" id="join-button">Join</button>
|
<button class="room-button" id="join-button">Join</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Create a New Room?</h3>
|
||||||
<div class="room-join-box">
|
<div class="room-join-box">
|
||||||
<button class="room-button" id="create-button">Create New Room</button>
|
<button class="room-button" id="create-button">Create New Room</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function getUsername() {
|
|
||||||
const username = document.getElementById('username-input').value.trim();
|
|
||||||
if (!username) {
|
|
||||||
alert("Please enter your name.");
|
|
||||||
throw new Error("Username required");
|
|
||||||
}
|
|
||||||
localStorage.setItem('username', username);
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('join-button').addEventListener('click', () => {
|
<script src="{{ url_for('static', filename='js/home.js') }}"></script>
|
||||||
try {
|
|
||||||
getUsername();
|
|
||||||
const code = document.getElementById('room-code-input').value.trim();
|
|
||||||
if (code) {
|
|
||||||
window.location.href = `/room/${code}`;
|
|
||||||
} else {
|
|
||||||
alert("Enter a room code.");
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('create-button').addEventListener('click', async () => {
|
|
||||||
try {
|
|
||||||
getUsername();
|
|
||||||
const res = await fetch('/api/room', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({})
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
if (data.code) {
|
|
||||||
window.location.href = `/room/${data.code}`;
|
|
||||||
} else {
|
|
||||||
alert("Failed to create room.");
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<a class="navname">Echo</a>
|
<a class="navname">Echo</a>
|
||||||
<a href="/">Home</a>
|
<a href="/">Home</a>
|
||||||
|
<a href="/settings">Settings</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends 'room_base.html' %}
|
{% extends 'room_base.html' %}
|
||||||
{% block title %}Room {{ room_code }}{% endblock %}
|
{% block title %}Echo | Room {{ room_code }}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Room {{ room_code }}</h1>
|
<h1>Room {{ room_code }}</h1>
|
||||||
<div class="chat-container">
|
<div class="chat-container">
|
||||||
@@ -11,63 +11,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const socket = io();
|
|
||||||
const room = "{{ room_code }}";
|
const room = "{{ room_code }}";
|
||||||
|
|
||||||
const name = localStorage.getItem('username') || 'Anonymous';
|
|
||||||
socket.emit('join', { room, sender: name });
|
|
||||||
|
|
||||||
// Fetch message history
|
|
||||||
fetch(`/api/room/${room}/messages`)
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => {
|
|
||||||
const chatBox = document.getElementById('chat-box');
|
|
||||||
if (data && Array.isArray(data)) {
|
|
||||||
data.forEach(msg => {
|
|
||||||
const msgElem = document.createElement('p');
|
|
||||||
msgElem.textContent = `${msg.sender}: ${msg.text}`;
|
|
||||||
chatBox.appendChild(msgElem);
|
|
||||||
});
|
|
||||||
chatBox.scrollTop = chatBox.scrollHeight;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Message listener
|
|
||||||
socket.on('message', (data) => {
|
|
||||||
const chatBox = document.getElementById('chat-box');
|
|
||||||
const msgElem = document.createElement('p');
|
|
||||||
|
|
||||||
msgElem.textContent = `${data.sender}: ${data.text}`;
|
|
||||||
if (data.sender === 'System') {
|
|
||||||
msgElem.style.fontStyle = 'italic';
|
|
||||||
msgElem.style.color = 'gray';
|
|
||||||
}
|
|
||||||
|
|
||||||
chatBox.appendChild(msgElem);
|
|
||||||
chatBox.scrollTop = chatBox.scrollHeight;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send message on button click
|
|
||||||
document.getElementById('send-button').onclick = () => {
|
|
||||||
const text = document.getElementById('chat-input').value;
|
|
||||||
const sender = localStorage.getItem('username') || 'Anonymous';
|
|
||||||
|
|
||||||
if (text.trim()) {
|
|
||||||
socket.emit('message', {
|
|
||||||
room,
|
|
||||||
text,
|
|
||||||
sender
|
|
||||||
});
|
|
||||||
document.getElementById('chat-input').value = '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send message on Enter key press
|
|
||||||
document.getElementById('chat-input').addEventListener('keydown', (event) => {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
document.getElementById('send-button').click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='js/room.js') }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -6,15 +6,20 @@
|
|||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||||
<link rel="icon" href="data:,">
|
<link rel="icon" href="data:,">
|
||||||
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
||||||
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||||
|
<meta http-equiv="Pragma" content="no-cache">
|
||||||
|
<meta http-equiv="Expires" content="0">
|
||||||
|
|
||||||
<title>{% block title %}{% endblock %}</title>
|
<title>{% block title %}{% endblock %}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<script src="{{ url_for('static', filename='js/base.js') }}"></script>
|
||||||
{% include 'navbar.html' %}
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
23
resource/template/settings.html
Normal file
23
resource/template/settings.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block title %}Echo | Settings{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>Settings</h1>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="setting-label">
|
||||||
|
<input type="checkbox" id="dark-mode-toggle" class="setting-toggle">
|
||||||
|
<span><strong>(EXPERIMENTAL)</strong> Enable Dark Mode</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<label for="notification-setting" class="setting-label">Enable Notifications:</label>
|
||||||
|
<select id="notification-setting" class="setting-select">
|
||||||
|
<option value="off">Off</option>
|
||||||
|
<option value="system">System Notifications</option>
|
||||||
|
<option value="tab">Tab Indicator</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='js/settings.js') }}"></script>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user