add live page
This commit is contained in:
parent
114bcca24e
commit
eb8c42d0c0
19 changed files with 2759 additions and 199 deletions
|
@ -108,6 +108,9 @@ button:hover {
|
|||
|
||||
.nav-links {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
|
@ -120,6 +123,99 @@ button:hover {
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Navigation container */
|
||||
.nav-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Hamburger menu for mobile */
|
||||
.menu-toggle {
|
||||
display: none;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
color: #0078ff;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
/* Responsive styles */
|
||||
@media (max-width: 768px) {
|
||||
.nav-container {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
display: block;
|
||||
align-self: flex-end;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-links.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Collapsible panel styles */
|
||||
.collapsible-panel {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.collapsible-header {
|
||||
background-color: #f8f9fa;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.collapsible-header h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.collapsible-header .toggle-icon {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.collapsible-header.active .toggle-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.collapsible-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease;
|
||||
border-left: 1px solid #e9ecef;
|
||||
border-right: 1px solid #e9ecef;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
border-radius: 0 0 5px 5px;
|
||||
}
|
||||
|
||||
.collapsible-content.active {
|
||||
max-height: 1000px;
|
||||
}
|
||||
|
||||
/* Authentication section styles */
|
||||
.auth-section {
|
||||
background-color: #f8f9fa;
|
||||
|
@ -523,4 +619,10 @@ select:invalid {
|
|||
.add-event-btn{
|
||||
float: left;
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
button{
|
||||
padding: 1rem 0.5rem;
|
||||
border-radius: 5px;
|
||||
background-color: #79a2d1;
|
||||
}
|
1
oedb/resources/demo/static/pouet.mp3
Normal file
1
oedb/resources/demo/static/pouet.mp3
Normal file
|
@ -0,0 +1 @@
|
|||
# Ce fichier est un MP3 binaire qui contiendra le son 'pouet pouet'.
|
750
oedb/resources/demo/static/social.js
Normal file
750
oedb/resources/demo/static/social.js
Normal file
|
@ -0,0 +1,750 @@
|
|||
// Fonctionnalités sociales pour OEDB
|
||||
|
||||
class OEDBSocial {
|
||||
constructor() {
|
||||
this.socket = null;
|
||||
this.position = null;
|
||||
this.username = '';
|
||||
this.friends = [];
|
||||
this.markers = {};
|
||||
this.map = null;
|
||||
this.lastPouetTime = 0;
|
||||
this.showOnlyFriends = false;
|
||||
|
||||
// Charger les amis depuis le localStorage
|
||||
this.loadFriends();
|
||||
|
||||
// Boutons pour l'interface sociale
|
||||
this.createSocialUI();
|
||||
}
|
||||
|
||||
// Initialiser la connexion WebSocket
|
||||
init(map, username) {
|
||||
this.map = map;
|
||||
this.username = username || localStorage.getItem('oedb_social_username') || '';
|
||||
|
||||
if (!this.username) {
|
||||
this.promptForUsername();
|
||||
} else {
|
||||
localStorage.setItem('oedb_social_username', this.username);
|
||||
}
|
||||
|
||||
// Créer la connexion WebSocket
|
||||
// Utiliser l'URL relative au serveur actuel
|
||||
const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||
const wsUrl = `${wsProtocol}${window.location.host}/ws`;
|
||||
this.socket = new WebSocket(wsUrl);
|
||||
|
||||
this.socket.onopen = () => {
|
||||
console.log('Connexion WebSocket établie');
|
||||
this.startSendingPosition();
|
||||
};
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
this.handleSocketMessage(data);
|
||||
};
|
||||
|
||||
this.socket.onclose = () => {
|
||||
console.log('Connexion WebSocket fermée');
|
||||
// Tentative de reconnexion après 5 secondes
|
||||
setTimeout(() => this.init(this.map, this.username), 5000);
|
||||
};
|
||||
|
||||
this.socket.onerror = (error) => {
|
||||
console.error('Erreur WebSocket:', error);
|
||||
this.showToast(`Erreur de connexion au serveur WebSocket. Vérifiez que le serveur est en cours d'exécution sur le port 8765.`, 'error');
|
||||
};
|
||||
}
|
||||
|
||||
// Demander le pseudo à l'utilisateur
|
||||
promptForUsername() {
|
||||
// Créer une boîte de dialogue modale pour demander le pseudo
|
||||
const modalOverlay = document.createElement('div');
|
||||
modalOverlay.className = 'modal-overlay';
|
||||
modalOverlay.style.position = 'fixed';
|
||||
modalOverlay.style.top = '0';
|
||||
modalOverlay.style.left = '0';
|
||||
modalOverlay.style.width = '100%';
|
||||
modalOverlay.style.height = '100%';
|
||||
modalOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
|
||||
modalOverlay.style.zIndex = '1000';
|
||||
modalOverlay.style.display = 'flex';
|
||||
modalOverlay.style.justifyContent = 'center';
|
||||
modalOverlay.style.alignItems = 'center';
|
||||
|
||||
const modalContent = document.createElement('div');
|
||||
modalContent.className = 'modal-content';
|
||||
modalContent.style.backgroundColor = '#fff';
|
||||
modalContent.style.padding = '20px';
|
||||
modalContent.style.borderRadius = '5px';
|
||||
modalContent.style.maxWidth = '400px';
|
||||
modalContent.style.width = '80%';
|
||||
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = 'Choisissez un pseudo';
|
||||
title.style.marginBottom = '15px';
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const input = document.getElementById('username-input');
|
||||
const username = input.value.trim();
|
||||
if (username) {
|
||||
this.username = username;
|
||||
localStorage.setItem('oedb_social_username', username);
|
||||
document.body.removeChild(modalOverlay);
|
||||
this.startSendingPosition();
|
||||
}
|
||||
};
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.id = 'username-input';
|
||||
input.placeholder = 'Votre pseudo';
|
||||
input.style.width = '100%';
|
||||
input.style.padding = '8px';
|
||||
input.style.marginBottom = '15px';
|
||||
input.style.borderRadius = '4px';
|
||||
input.style.border = '1px solid #ddd';
|
||||
|
||||
const button = document.createElement('button');
|
||||
button.type = 'submit';
|
||||
button.textContent = 'Valider';
|
||||
button.style.padding = '8px 15px';
|
||||
button.style.backgroundColor = '#0078ff';
|
||||
button.style.color = 'white';
|
||||
button.style.border = 'none';
|
||||
button.style.borderRadius = '4px';
|
||||
button.style.cursor = 'pointer';
|
||||
|
||||
form.appendChild(input);
|
||||
form.appendChild(button);
|
||||
|
||||
modalContent.appendChild(title);
|
||||
modalContent.appendChild(form);
|
||||
modalOverlay.appendChild(modalContent);
|
||||
|
||||
document.body.appendChild(modalOverlay);
|
||||
|
||||
input.focus();
|
||||
}
|
||||
|
||||
// Commencer à envoyer sa position
|
||||
startSendingPosition() {
|
||||
if (!this.username || !this.socket) return;
|
||||
|
||||
// Obtenir la position actuelle
|
||||
this.getCurrentPosition();
|
||||
|
||||
// Mettre à jour la position toutes les 5 secondes
|
||||
setInterval(() => {
|
||||
this.getCurrentPosition();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Obtenir la position GPS actuelle
|
||||
getCurrentPosition() {
|
||||
if (navigator.geolocation && this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
this.position = {
|
||||
lat: position.coords.latitude,
|
||||
lng: position.coords.longitude
|
||||
};
|
||||
|
||||
// Envoyer la position au serveur WebSocket
|
||||
this.socket.send(JSON.stringify({
|
||||
type: 'position',
|
||||
username: this.username,
|
||||
position: this.position,
|
||||
timestamp: new Date().toISOString(),
|
||||
showOnlyToFriends: this.showOnlyFriends
|
||||
}));
|
||||
},
|
||||
(error) => {
|
||||
console.error('Erreur lors de la récupération de la position:', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Traiter les messages reçus par WebSocket
|
||||
handleSocketMessage(data) {
|
||||
switch (data.type) {
|
||||
case 'position':
|
||||
this.updateUserPosition(data);
|
||||
break;
|
||||
case 'pouet':
|
||||
this.receivePouet(data);
|
||||
break;
|
||||
case 'friendRequest':
|
||||
this.receiveFriendRequest(data);
|
||||
break;
|
||||
case 'users':
|
||||
this.updateAllUsers(data.users);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour la position d'un utilisateur sur la carte
|
||||
updateUserPosition(data) {
|
||||
// Ignorer les mises à jour de notre propre position
|
||||
if (data.username === this.username) return;
|
||||
|
||||
// Vérifier si l'utilisateur est visible uniquement pour ses amis
|
||||
if (data.showOnlyToFriends && !this.friends.includes(data.username)) return;
|
||||
|
||||
// Supprimer l'ancien marqueur s'il existe
|
||||
if (this.markers[data.username]) {
|
||||
this.markers[data.username].remove();
|
||||
}
|
||||
|
||||
// Créer un élément HTML personnalisé pour le marqueur
|
||||
const el = document.createElement('div');
|
||||
el.className = 'user-marker';
|
||||
|
||||
// Styles de base pour le marqueur
|
||||
el.style.width = '40px';
|
||||
el.style.height = '40px';
|
||||
el.style.borderRadius = '50%';
|
||||
el.style.backgroundColor = this.friends.includes(data.username) ? '#4CAF50' : '#0078ff';
|
||||
el.style.border = '2px solid white';
|
||||
el.style.boxShadow = '0 0 5px rgba(0,0,0,0.3)';
|
||||
el.style.display = 'flex';
|
||||
el.style.justifyContent = 'center';
|
||||
el.style.alignItems = 'center';
|
||||
el.style.color = 'white';
|
||||
el.style.fontWeight = 'bold';
|
||||
el.style.fontSize = '12px';
|
||||
el.style.cursor = 'pointer';
|
||||
|
||||
// Ajouter les initiales de l'utilisateur
|
||||
const initials = data.username.substring(0, 2).toUpperCase();
|
||||
el.textContent = initials;
|
||||
|
||||
// Ajouter un tooltip avec le nom complet
|
||||
el.title = data.username;
|
||||
|
||||
// Créer le popup
|
||||
const popupContent = `
|
||||
<div class="user-popup" style="padding: 10px; max-width: 200px;">
|
||||
<h3 style="margin-top: 0;">${data.username}</h3>
|
||||
<p style="margin-bottom: 10px;">Position mise à jour: ${this.formatTimestamp(data.timestamp)}</p>
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<button class="pouet-btn" style="padding: 5px 10px; background-color: #FFC107; border: none; border-radius: 3px; cursor: pointer;">Pouet Pouet!</button>
|
||||
${!this.friends.includes(data.username) ? `
|
||||
<button class="add-friend-btn" style="padding: 5px 10px; background-color: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">Ajouter ami</button>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const popup = new maplibregl.Popup({
|
||||
closeButton: true,
|
||||
closeOnClick: true
|
||||
}).setHTML(popupContent);
|
||||
|
||||
// Ajouter des gestionnaires d'événements au popup
|
||||
popup.on('open', () => {
|
||||
// Gérer le clic sur le bouton Pouet Pouet
|
||||
setTimeout(() => {
|
||||
const pouetBtn = document.querySelector('.pouet-btn');
|
||||
if (pouetBtn) {
|
||||
pouetBtn.addEventListener('click', () => {
|
||||
this.sendPouet(data.username);
|
||||
});
|
||||
}
|
||||
|
||||
// Gérer le clic sur le bouton Ajouter ami
|
||||
const addFriendBtn = document.querySelector('.add-friend-btn');
|
||||
if (addFriendBtn) {
|
||||
addFriendBtn.addEventListener('click', () => {
|
||||
this.sendFriendRequest(data.username);
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Créer le marqueur et l'ajouter à la carte
|
||||
const marker = new maplibregl.Marker(el)
|
||||
.setLngLat([data.position.lng, data.position.lat])
|
||||
.setPopup(popup)
|
||||
.addTo(this.map);
|
||||
|
||||
// Stocker le marqueur pour pouvoir le supprimer plus tard
|
||||
this.markers[data.username] = marker;
|
||||
}
|
||||
|
||||
// Mettre à jour tous les utilisateurs actifs
|
||||
updateAllUsers(users) {
|
||||
// Supprimer les marqueurs des utilisateurs qui ne sont plus actifs
|
||||
Object.keys(this.markers).forEach(username => {
|
||||
if (!users.find(user => user.username === username)) {
|
||||
this.markers[username].remove();
|
||||
delete this.markers[username];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Envoyer un pouet à un utilisateur
|
||||
sendPouet(username) {
|
||||
const now = Date.now();
|
||||
|
||||
// Vérifier si on peut envoyer un pouet (limité à 1 toutes les 10 secondes)
|
||||
if (now - this.lastPouetTime < 10000) {
|
||||
const remainingTime = Math.ceil((10000 - (now - this.lastPouetTime)) / 1000);
|
||||
this.showToast(`Merci d'attendre encore ${remainingTime} secondes avant d'envoyer un autre pouet!`, 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||
this.socket.send(JSON.stringify({
|
||||
type: 'pouet',
|
||||
from: this.username,
|
||||
to: username,
|
||||
timestamp: new Date().toISOString()
|
||||
}));
|
||||
|
||||
this.lastPouetTime = now;
|
||||
this.showToast(`Pouet pouet envoyé à ${username}!`, 'success');
|
||||
}
|
||||
}
|
||||
|
||||
// Recevoir un pouet
|
||||
receivePouet(data) {
|
||||
this.showToast(`${data.from} vous a envoyé un pouet pouet!`, 'info');
|
||||
|
||||
// Jouer un son
|
||||
const audio = new Audio('/static/pouet.mp3');
|
||||
audio.play().catch(e => console.log('Erreur lors de la lecture du son:', e));
|
||||
}
|
||||
|
||||
// Envoyer une demande d'ami
|
||||
sendFriendRequest(username) {
|
||||
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||
this.socket.send(JSON.stringify({
|
||||
type: 'friendRequest',
|
||||
from: this.username,
|
||||
to: username,
|
||||
timestamp: new Date().toISOString()
|
||||
}));
|
||||
|
||||
this.showToast(`Demande d'ami envoyée à ${username}!`, 'success');
|
||||
}
|
||||
}
|
||||
|
||||
// Recevoir une demande d'ami
|
||||
receiveFriendRequest(data) {
|
||||
// Créer une boîte de dialogue modale pour la demande d'ami
|
||||
const modalOverlay = document.createElement('div');
|
||||
modalOverlay.className = 'modal-overlay';
|
||||
modalOverlay.style.position = 'fixed';
|
||||
modalOverlay.style.top = '0';
|
||||
modalOverlay.style.left = '0';
|
||||
modalOverlay.style.width = '100%';
|
||||
modalOverlay.style.height = '100%';
|
||||
modalOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
|
||||
modalOverlay.style.zIndex = '1000';
|
||||
modalOverlay.style.display = 'flex';
|
||||
modalOverlay.style.justifyContent = 'center';
|
||||
modalOverlay.style.alignItems = 'center';
|
||||
|
||||
const modalContent = document.createElement('div');
|
||||
modalContent.className = 'modal-content';
|
||||
modalContent.style.backgroundColor = '#fff';
|
||||
modalContent.style.padding = '20px';
|
||||
modalContent.style.borderRadius = '5px';
|
||||
modalContent.style.maxWidth = '400px';
|
||||
modalContent.style.width = '80%';
|
||||
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = 'Demande d\'ami';
|
||||
title.style.marginBottom = '15px';
|
||||
|
||||
const message = document.createElement('p');
|
||||
message.textContent = `${data.from} souhaite vous ajouter à sa liste d'amis.`;
|
||||
message.style.marginBottom = '20px';
|
||||
|
||||
const buttonsContainer = document.createElement('div');
|
||||
buttonsContainer.style.display = 'flex';
|
||||
buttonsContainer.style.justifyContent = 'space-between';
|
||||
|
||||
const acceptButton = document.createElement('button');
|
||||
acceptButton.textContent = 'Accepter';
|
||||
acceptButton.style.padding = '8px 15px';
|
||||
acceptButton.style.backgroundColor = '#4CAF50';
|
||||
acceptButton.style.color = 'white';
|
||||
acceptButton.style.border = 'none';
|
||||
acceptButton.style.borderRadius = '4px';
|
||||
acceptButton.style.cursor = 'pointer';
|
||||
acceptButton.onclick = () => {
|
||||
this.addFriend(data.from);
|
||||
document.body.removeChild(modalOverlay);
|
||||
};
|
||||
|
||||
const rejectButton = document.createElement('button');
|
||||
rejectButton.textContent = 'Refuser';
|
||||
rejectButton.style.padding = '8px 15px';
|
||||
rejectButton.style.backgroundColor = '#f44336';
|
||||
rejectButton.style.color = 'white';
|
||||
rejectButton.style.border = 'none';
|
||||
rejectButton.style.borderRadius = '4px';
|
||||
rejectButton.style.cursor = 'pointer';
|
||||
rejectButton.onclick = () => {
|
||||
document.body.removeChild(modalOverlay);
|
||||
};
|
||||
|
||||
buttonsContainer.appendChild(acceptButton);
|
||||
buttonsContainer.appendChild(rejectButton);
|
||||
|
||||
modalContent.appendChild(title);
|
||||
modalContent.appendChild(message);
|
||||
modalContent.appendChild(buttonsContainer);
|
||||
modalOverlay.appendChild(modalContent);
|
||||
|
||||
document.body.appendChild(modalOverlay);
|
||||
}
|
||||
|
||||
// Ajouter un ami à la liste d'amis
|
||||
addFriend(username) {
|
||||
if (!this.friends.includes(username)) {
|
||||
this.friends.push(username);
|
||||
this.saveFriends();
|
||||
this.showToast(`${username} a été ajouté à votre liste d'amis!`, 'success');
|
||||
|
||||
// Mettre à jour le marqueur de cet ami s'il est visible
|
||||
if (this.markers[username]) {
|
||||
const position = this.markers[username].getLngLat();
|
||||
this.markers[username].remove();
|
||||
delete this.markers[username];
|
||||
|
||||
// Simuler une mise à jour de position pour recréer le marqueur
|
||||
this.updateUserPosition({
|
||||
username: username,
|
||||
position: {
|
||||
lng: position.lng,
|
||||
lat: position.lat
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sauvegarder la liste d'amis dans le localStorage
|
||||
saveFriends() {
|
||||
localStorage.setItem('oedb_social_friends', JSON.stringify(this.friends));
|
||||
}
|
||||
|
||||
// Charger la liste d'amis depuis le localStorage
|
||||
loadFriends() {
|
||||
try {
|
||||
const friendsJson = localStorage.getItem('oedb_social_friends');
|
||||
if (friendsJson) {
|
||||
this.friends = JSON.parse(friendsJson);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Erreur lors du chargement des amis:', e);
|
||||
this.friends = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Créer l'interface utilisateur pour les fonctionnalités sociales
|
||||
createSocialUI() {
|
||||
// Conteneur principal pour les contrôles sociaux
|
||||
const socialContainer = document.createElement('div');
|
||||
socialContainer.className = 'social-controls';
|
||||
socialContainer.style.position = 'absolute';
|
||||
socialContainer.style.top = '10px';
|
||||
socialContainer.style.right = '10px';
|
||||
socialContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.9)';
|
||||
socialContainer.style.padding = '10px';
|
||||
socialContainer.style.borderRadius = '5px';
|
||||
socialContainer.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
|
||||
socialContainer.style.zIndex = '10';
|
||||
socialContainer.style.display = 'flex';
|
||||
socialContainer.style.flexDirection = 'column';
|
||||
socialContainer.style.gap = '10px';
|
||||
|
||||
// Titre
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = 'Mode Social';
|
||||
title.style.margin = '0 0 10px 0';
|
||||
title.style.textAlign = 'center';
|
||||
|
||||
// Bouton pour activer/désactiver le mode social
|
||||
const toggleButton = document.createElement('button');
|
||||
toggleButton.className = 'toggle-social-btn';
|
||||
toggleButton.textContent = 'Activer le mode social';
|
||||
toggleButton.style.padding = '8px';
|
||||
toggleButton.style.backgroundColor = '#0078ff';
|
||||
toggleButton.style.color = 'white';
|
||||
toggleButton.style.border = 'none';
|
||||
toggleButton.style.borderRadius = '4px';
|
||||
toggleButton.style.cursor = 'pointer';
|
||||
toggleButton.style.fontWeight = 'bold';
|
||||
|
||||
let socialActive = false;
|
||||
|
||||
toggleButton.addEventListener('click', () => {
|
||||
socialActive = !socialActive;
|
||||
|
||||
if (socialActive) {
|
||||
toggleButton.textContent = 'Désactiver le mode social';
|
||||
toggleButton.style.backgroundColor = '#f44336';
|
||||
this.init(this.map);
|
||||
} else {
|
||||
toggleButton.textContent = 'Activer le mode social';
|
||||
toggleButton.style.backgroundColor = '#0078ff';
|
||||
|
||||
// Fermer la connexion WebSocket
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
this.socket = null;
|
||||
}
|
||||
|
||||
// Supprimer tous les marqueurs
|
||||
Object.values(this.markers).forEach(marker => marker.remove());
|
||||
this.markers = {};
|
||||
}
|
||||
|
||||
// Afficher/masquer les options supplémentaires
|
||||
optionsContainer.style.display = socialActive ? 'block' : 'none';
|
||||
});
|
||||
|
||||
// Conteneur pour les options supplémentaires
|
||||
const optionsContainer = document.createElement('div');
|
||||
optionsContainer.className = 'social-options';
|
||||
optionsContainer.style.display = 'none';
|
||||
|
||||
// Bouton pour changer de pseudo
|
||||
const changeUsernameBtn = document.createElement('button');
|
||||
changeUsernameBtn.textContent = 'Changer de pseudo';
|
||||
changeUsernameBtn.style.width = '100%';
|
||||
changeUsernameBtn.style.padding = '8px';
|
||||
changeUsernameBtn.style.backgroundColor = '#FFC107';
|
||||
changeUsernameBtn.style.border = 'none';
|
||||
changeUsernameBtn.style.borderRadius = '4px';
|
||||
changeUsernameBtn.style.marginBottom = '10px';
|
||||
changeUsernameBtn.style.cursor = 'pointer';
|
||||
|
||||
changeUsernameBtn.addEventListener('click', () => {
|
||||
this.promptForUsername();
|
||||
});
|
||||
|
||||
// Case à cocher pour la visibilité uniquement aux amis
|
||||
const visibilityContainer = document.createElement('div');
|
||||
visibilityContainer.style.display = 'flex';
|
||||
visibilityContainer.style.alignItems = 'center';
|
||||
visibilityContainer.style.marginBottom = '10px';
|
||||
|
||||
const visibilityCheckbox = document.createElement('input');
|
||||
visibilityCheckbox.type = 'checkbox';
|
||||
visibilityCheckbox.id = 'visibility-checkbox';
|
||||
visibilityCheckbox.checked = this.showOnlyFriends;
|
||||
|
||||
const visibilityLabel = document.createElement('label');
|
||||
visibilityLabel.htmlFor = 'visibility-checkbox';
|
||||
visibilityLabel.textContent = 'Visible uniquement par mes amis';
|
||||
visibilityLabel.style.marginLeft = '5px';
|
||||
|
||||
visibilityContainer.appendChild(visibilityCheckbox);
|
||||
visibilityContainer.appendChild(visibilityLabel);
|
||||
|
||||
visibilityCheckbox.addEventListener('change', () => {
|
||||
this.showOnlyFriends = visibilityCheckbox.checked;
|
||||
this.getCurrentPosition(); // Mettre à jour immédiatement avec le nouveau paramètre
|
||||
});
|
||||
|
||||
// Gestionnaire d'amis
|
||||
const friendsManager = document.createElement('div');
|
||||
friendsManager.style.marginTop = '10px';
|
||||
|
||||
const friendsTitle = document.createElement('h4');
|
||||
friendsTitle.textContent = 'Mes amis';
|
||||
friendsTitle.style.margin = '0 0 5px 0';
|
||||
|
||||
const friendsList = document.createElement('ul');
|
||||
friendsList.style.listStyle = 'none';
|
||||
friendsList.style.padding = '0';
|
||||
friendsList.style.margin = '0';
|
||||
friendsList.style.maxHeight = '150px';
|
||||
friendsList.style.overflowY = 'auto';
|
||||
friendsList.style.border = '1px solid #ddd';
|
||||
friendsList.style.borderRadius = '4px';
|
||||
friendsList.style.padding = '5px';
|
||||
|
||||
// Fonction pour mettre à jour la liste d'amis
|
||||
const updateFriendsList = () => {
|
||||
friendsList.innerHTML = '';
|
||||
|
||||
if (this.friends.length === 0) {
|
||||
const emptyItem = document.createElement('li');
|
||||
emptyItem.textContent = 'Aucun ami pour l\'instant';
|
||||
emptyItem.style.fontStyle = 'italic';
|
||||
emptyItem.style.padding = '5px';
|
||||
friendsList.appendChild(emptyItem);
|
||||
} else {
|
||||
this.friends.forEach(friend => {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.style.display = 'flex';
|
||||
listItem.style.justifyContent = 'space-between';
|
||||
listItem.style.alignItems = 'center';
|
||||
listItem.style.padding = '5px';
|
||||
listItem.style.borderBottom = '1px solid #eee';
|
||||
|
||||
const friendName = document.createElement('span');
|
||||
friendName.textContent = friend;
|
||||
|
||||
const removeBtn = document.createElement('button');
|
||||
removeBtn.textContent = 'X';
|
||||
removeBtn.style.backgroundColor = '#f44336';
|
||||
removeBtn.style.color = 'white';
|
||||
removeBtn.style.border = 'none';
|
||||
removeBtn.style.borderRadius = '50%';
|
||||
removeBtn.style.width = '20px';
|
||||
removeBtn.style.height = '20px';
|
||||
removeBtn.style.fontSize = '10px';
|
||||
removeBtn.style.cursor = 'pointer';
|
||||
removeBtn.style.display = 'flex';
|
||||
removeBtn.style.justifyContent = 'center';
|
||||
removeBtn.style.alignItems = 'center';
|
||||
|
||||
removeBtn.addEventListener('click', () => {
|
||||
this.friends = this.friends.filter(f => f !== friend);
|
||||
this.saveFriends();
|
||||
updateFriendsList();
|
||||
});
|
||||
|
||||
listItem.appendChild(friendName);
|
||||
listItem.appendChild(removeBtn);
|
||||
friendsList.appendChild(listItem);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Initialiser la liste d'amis
|
||||
updateFriendsList();
|
||||
|
||||
friendsManager.appendChild(friendsTitle);
|
||||
friendsManager.appendChild(friendsList);
|
||||
|
||||
// Ajouter tous les éléments au conteneur d'options
|
||||
optionsContainer.appendChild(changeUsernameBtn);
|
||||
optionsContainer.appendChild(visibilityContainer);
|
||||
optionsContainer.appendChild(friendsManager);
|
||||
|
||||
// Ajouter tous les éléments au conteneur principal
|
||||
socialContainer.appendChild(title);
|
||||
socialContainer.appendChild(toggleButton);
|
||||
socialContainer.appendChild(optionsContainer);
|
||||
|
||||
// Ajouter le conteneur au document après le chargement du DOM
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.body.appendChild(socialContainer);
|
||||
});
|
||||
}
|
||||
|
||||
// Afficher un toast (message flottant)
|
||||
showToast(message, type = 'info') {
|
||||
// Créer le conteneur de toast s'il n'existe pas
|
||||
let toastContainer = document.getElementById('toast-container');
|
||||
|
||||
if (!toastContainer) {
|
||||
toastContainer = document.createElement('div');
|
||||
toastContainer.id = 'toast-container';
|
||||
toastContainer.style.position = 'fixed';
|
||||
toastContainer.style.top = '20px';
|
||||
toastContainer.style.left = '50%';
|
||||
toastContainer.style.transform = 'translateX(-50%)';
|
||||
toastContainer.style.zIndex = '1000';
|
||||
toastContainer.style.display = 'flex';
|
||||
toastContainer.style.flexDirection = 'column';
|
||||
toastContainer.style.alignItems = 'center';
|
||||
toastContainer.style.gap = '10px';
|
||||
document.body.appendChild(toastContainer);
|
||||
}
|
||||
|
||||
// Créer le toast
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast toast-${type}`;
|
||||
toast.style.padding = '10px 15px';
|
||||
toast.style.borderRadius = '5px';
|
||||
toast.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.2)';
|
||||
toast.style.minWidth = '250px';
|
||||
toast.style.textAlign = 'center';
|
||||
toast.style.animation = 'fadeIn 0.3s, fadeOut 0.3s 2.7s';
|
||||
toast.style.opacity = '0';
|
||||
toast.style.maxWidth = '80vw';
|
||||
|
||||
// Définir la couleur en fonction du type
|
||||
switch (type) {
|
||||
case 'success':
|
||||
toast.style.backgroundColor = '#4CAF50';
|
||||
toast.style.color = 'white';
|
||||
break;
|
||||
case 'warning':
|
||||
toast.style.backgroundColor = '#FFC107';
|
||||
toast.style.color = 'black';
|
||||
break;
|
||||
case 'error':
|
||||
toast.style.backgroundColor = '#f44336';
|
||||
toast.style.color = 'white';
|
||||
break;
|
||||
default: // info
|
||||
toast.style.backgroundColor = '#0078ff';
|
||||
toast.style.color = 'white';
|
||||
}
|
||||
|
||||
toast.textContent = message;
|
||||
|
||||
// Ajouter le style d'animation s'il n'existe pas
|
||||
if (!document.getElementById('toast-style')) {
|
||||
const style = document.createElement('style');
|
||||
style.id = 'toast-style';
|
||||
style.textContent = `
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
@keyframes fadeOut {
|
||||
from { opacity: 1; transform: translateY(0); }
|
||||
to { opacity: 0; transform: translateY(-20px); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
// Ajouter le toast au conteneur
|
||||
toastContainer.appendChild(toast);
|
||||
|
||||
// Animer l'entrée
|
||||
setTimeout(() => {
|
||||
toast.style.opacity = '1';
|
||||
toast.style.transform = 'translateY(0)';
|
||||
}, 10);
|
||||
|
||||
// Supprimer le toast après 3 secondes
|
||||
setTimeout(() => {
|
||||
toast.style.opacity = '0';
|
||||
toast.style.transform = 'translateY(-20px)';
|
||||
|
||||
setTimeout(() => {
|
||||
toastContainer.removeChild(toast);
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Formatter un timestamp en heure locale
|
||||
formatTimestamp(timestamp) {
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleTimeString();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialiser l'objet social lorsque le DOM est chargé
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Créer l'instance sociale
|
||||
window.oedbSocial = new OEDBSocial();
|
||||
});
|
|
@ -7,6 +7,23 @@ let existingMarkers = [];
|
|||
const PANORAMAX_TOKEN_STORAGE_KEY = 'oedb_panoramax_token';
|
||||
let mediaStream = null;
|
||||
|
||||
// Fonction pour créer un marqueur personnalisé avec emoji
|
||||
function createCustomMarker(emoji, backgroundColor) {
|
||||
const markerElement = document.createElement('div');
|
||||
markerElement.className = 'custom-marker';
|
||||
markerElement.style.width = '30px';
|
||||
markerElement.style.height = '30px';
|
||||
markerElement.style.borderRadius = '50%';
|
||||
markerElement.style.backgroundColor = backgroundColor;
|
||||
markerElement.style.display = 'flex';
|
||||
markerElement.style.justifyContent = 'center';
|
||||
markerElement.style.alignItems = 'center';
|
||||
markerElement.style.fontSize = '16px';
|
||||
markerElement.style.boxShadow = '0 2px 4px rgba(0,0,0,0.3)';
|
||||
markerElement.innerHTML = emoji;
|
||||
return markerElement;
|
||||
}
|
||||
|
||||
function setDefaultDates() {
|
||||
const now = new Date();
|
||||
const nowISO = now.toISOString().slice(0, 16);
|
||||
|
@ -70,8 +87,22 @@ function fetchExistingTrafficEvents() {
|
|||
if (event.geometry && event.geometry.type === 'Point') {
|
||||
const coords = event.geometry.coordinates;
|
||||
const needsRealityCheck = checkIfNeedsRealityCheck(event);
|
||||
const markerColor = needsRealityCheck ? '#ff9800' : '#888888';
|
||||
const em = new maplibregl.Marker({ color: markerColor }).setLngLat(coords).addTo(map);
|
||||
let markerColor = needsRealityCheck ? '#ff9800' : '#888888';
|
||||
let markerOptions = { color: markerColor };
|
||||
|
||||
// Check if event title contains "vélo" or "travaux"
|
||||
const eventTitle = event.properties.label || '';
|
||||
if (eventTitle.toLowerCase().includes('vélo')) {
|
||||
markerOptions = {
|
||||
element: createCustomMarker('🚲', markerColor)
|
||||
};
|
||||
} else if (eventTitle.toLowerCase().includes('travaux')) {
|
||||
markerOptions = {
|
||||
element: createCustomMarker('🚧', markerColor)
|
||||
};
|
||||
}
|
||||
|
||||
const em = new maplibregl.Marker(markerOptions).setLngLat(coords).addTo(map);
|
||||
let popupContent = `\n<h3>${event.properties.label || 'Traffic Event'}</h3>\n<p>Type: ${event.properties.what || 'Unknown'}</p>\n<p>Start: ${event.properties.start || 'Unknown'}</p>\n<p>End: ${event.properties.stop || 'Unknown'}</p>`;
|
||||
if (needsRealityCheck) {
|
||||
popupContent += `\n<div class="reality-check">\n<p>Is this traffic event still present?</p>\n<div class="reality-check-buttons">\n<button class="confirm-btn" onclick="confirmEvent('${event.properties.id}', true)">Yes, still there</button>\n<button class="deny-btn" onclick="confirmEvent('${event.properties.id}', false)">No, it's gone</button>\n</div>\n</div>`;
|
||||
|
@ -590,11 +621,24 @@ function updateUserInfoDisplay() {
|
|||
userInfoPanel.innerHTML = `\n<h3>User Information</h3>\n<p>Username: <strong>${username}</strong></p>\n<p>Points: <span class="user-points">${points}</span></p>`;
|
||||
}
|
||||
|
||||
// Initialize collapsible panels
|
||||
function initCollapsiblePanels() {
|
||||
const headers = document.querySelectorAll('.collapsible-header');
|
||||
headers.forEach(header => {
|
||||
header.addEventListener('click', function() {
|
||||
this.classList.toggle('active');
|
||||
const content = this.nextElementSibling;
|
||||
content.classList.toggle('active');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setDefaultDates();
|
||||
initTabs();
|
||||
initMap();
|
||||
updateUserInfoDisplay();
|
||||
initCollapsiblePanels();
|
||||
});
|
||||
|
||||
// Contrôles Caméra
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue