<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>GC Forum</title>
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet" />
<style>
body{margin:0;padding:0;font-family:'VT323',monospace;background:#181818;color:#f8f8f8;font-size:1.25em;}
header{background:#005050;padding:22px 30px;color:#ffde54;font-size:2em;display:flex;justify-content:space-between;align-items:center;border-bottom:3px solid #000;}
nav{background:#002f2f;padding:12px;display:flex;gap:20px;justify-content:center;flex-wrap:wrap;border-bottom:2px solid #000;}
nav a{color:#ffde54;text-decoration:none;padding:7px 15px;border-radius:6px;background:#003838;transition:.2s;border:1px solid transparent;cursor:pointer;}
nav a.active,nav a:hover{background:#ffde54;color:#003d3d;border:1px solid #ffbb00;}
.hidden{display:none!important;}
main{max-width:960px;margin:30px auto;padding:30px 20px;background:#1e1e1e;border-radius:12px;box-shadow:0 4px 16px #0005;}
input,textarea,button{font-family:'VT323',monospace;font-size:1em;padding:10px;margin-bottom:18px;border-radius:5px;border:2px solid #ffde54;background:#23241f;color:#fff;width:100%;box-sizing:border-box;}
button{background:#ff4444;border:none;cursor:pointer;transition:background .2s;}
button:hover{background:#bb2222;}
#user-info{font-size:.8em;background:#203040;border-radius:7px;padding:6px 16px;color:#ffe;}
.page{display:none;}
.active{display:block;}
.card{background:#262c25;border:1px solid #ffde5480;padding:14px;border-radius:8px;margin-bottom:16px;color:#ffeeb8;cursor:pointer;transition:.2s;}
.card:hover{background:#2a361c;color:#ffde54;}
.author{font-size:.9em;color:#ffde54;opacity:0.8;}
.add-friend-btn{padding:6px 12px;background:#0088ff;border:none;border-radius:5px;margin-top:8px;cursor:pointer;font-size:1em;}
.add-friend-btn:hover{background:#0055aa;}
.message-bubble{padding:6px 10px;border-radius:7px;margin-bottom:6px;max-width:75%;word-wrap:break-word;}
.sent{background:#ffde54;color:#000;margin-left:auto;text-align:right;}
.received{background:#0066cc;color:white;margin-right:auto;}
#chat-friends-list .friend-card{background:#222;padding:8px 12px;border:1px solid #666;border-radius:6px;margin-bottom:6px;cursor:pointer;}
#chat-friends-list .friend-card:hover{background:#2a2a2a;border-color:#888;}
.friend-actions button{margin-right:10px;padding:6px 12px;background:#0066cc;color:white;border:none;border-radius:5px;font-family:'VT323',monospace;cursor:pointer;}
.friend-actions .reject{background:#bb2222;}
.friend-menu{background:#333;border:1px solid #666;padding:10px;border-radius:8px;margin-top:10px;}
.friend-menu button{margin-right:10px;padding:6px 12px;background:#0066cc;color:white;border:none;border-radius:5px;font-family:'VT323',monospace;cursor:pointer;}
.friend-menu .delete{background:#bb2222;}
</style>
</head>
<body>
<header>
<div>GC Forum</div>
<div id="user-info">Non connecté</div>
</header>
<nav>
<a href="#" data-page="forum">Accueil</a>
<a href="#" data-page="choice" class="tab-auth hidden">Créer</a>
<a href="#" data-page="my-wall" class="tab-auth hidden">Mon mur</a>
<a href="#" data-page="messages" class="tab-auth hidden">Messages</a>
<a href="#" data-page="friends" class="tab-auth hidden">Amis</a>
<a href="#" data-page="friend-requests" class="tab-auth hidden">Demandes</a>
<a href="#" data-page="search">Recherche</a>
<a href="#" id="logout-btn" class="hidden">Déconnexion</a>
</nav>
<main>
<section id="auth" class="page active">
<h2>Connexion / Inscription</h2>
<form id="form-register">
<input type="text" id="register-username" placeholder="Nom d'utilisateur" required>
<input type="email" id="register-email" placeholder="Email" required>
<input type="password" id="register-password" placeholder="Mot de passe" required>
<button>S'inscrire</button>
</form>
<form id="form-login">
<input type="email" id="login-email" placeholder="Email" required>
<input type="password" id="login-password" placeholder="Mot de passe" required>
<button>Se connecter</button>
</form>
</section>
<section id="choice" class="page">
<h2>Créer ou gérer</h2>
<button onclick="showPage('create-post')">Créer un post</button>
<button onclick="showPage('create-forum')">Créer un forum</button>
<button onclick="attemptDeleteAccount()">Supprimer mon compte</button>
</section>
<section id="create-post" class="page">
<h2>Nouveau post</h2>
<form id="form-create-post">
<input type="text" id="post-title" placeholder="Titre du post" required>
<textarea id="post-content" placeholder="Contenu du post" required></textarea>
<button>Publier</button>
</form>
</section>
<section id="create-forum" class="page">
<h2>Nouveau forum</h2>
<form id="form-create-forum">
<input type="text" id="forum-title" placeholder="Titre du forum" required>
<textarea id="forum-content" placeholder="Contenu ou objectif" required></textarea>
<button>Créer</button>
</form>
</section>
<section id="forum" class="page">
<h2>Tous les posts</h2><div id="posts"></div>
<h2>Tous les forums</h2><div id="forums"></div>
</section>
<section id="my-wall" class="page">
<h2>Mon mur</h2>
<div id="my-posts"></div>
<div id="my-forums"></div>
</section>
<section id="messages" class="page">
<h2>Messagerie privée</h2>
<div style="display:flex;gap:20px;flex-wrap:wrap;">
<div style="min-width:200px;"><strong>Amis</strong><div id="chat-friends-list">Chargement...</div></div>
<div style="flex:1;min-width:280px;">
<div id="chat-window" style="border:1px solid #666;padding:12px;height:280px;overflow-y:auto;background:#111;border-radius:6px;margin-bottom:10px;">
<em>Sélectionne un ami pour discuter</em>
</div>
<form id="chat-form" style="display:none;"><input type="text" id="chat-input" placeholder="Écris un message…"><button>Envoyer</button></form>
</div>
</div>
</section>
<section id="friends" class="page">
<h2>Mes amis</h2><div id="friends-list"><p>Chargement…</p></div>
</section>
<section id="search" class="page">
<h2>Recherche d'utilisateur</h2>
<input id="search-input" placeholder="Nom d'utilisateur..." autocomplete="off"/>
<div id="search-results"></div>
</section>
<section id="details" class="page">
<h2 id="details-title"></h2>
<div id="details-meta"></div>
<p id="details-content"></p>
<div id="details-add-friend"></div>
<button id="delete-btn" class="tab-auth hidden">Supprimer</button>
</section>
<section id="friend-requests" class="page tab-auth">
<h2>Demandes d'amis reçues</h2>
<div id="friend-requests-list"><p>Aucune demande</p></div>
</section>
<section id="friend-wall" class="page">
<h2 id="friend-wall-title"></h2>
<div id="friend-posts"></div>
<div id="friend-forums"></div>
</section>
</main>
<script type="module">
import {initializeApp} from "https://www.gstatic.com/firebasejs/10.12.2/firebase-app.js";
import {
getAuth,createUserWithEmailAndPassword,signInWithEmailAndPassword,
onAuthStateChanged,signOut,updateProfile,deleteUser,sendEmailVerification
} from "https://www.gstatic.com/firebasejs/10.12.2/firebase-auth.js";
import {
getFirestore,collection,addDoc,getDocs,deleteDoc,doc,getDoc,
query,where,orderBy,serverTimestamp,limit,setDoc,onSnapshot, updateDoc
} from "https://www.gstatic.com/firebasejs/10.12.2/firebase-firestore.js";
const firebaseConfig = {
apiKey: "AIzaSyDgODCpW4QruRHmotueAp9mlLFrpeZwsZI",
authDomain: "gc-forum-d5a39.firebaseapp.com",
projectId: "gc-forum-d5a39",
storageBucket: "gc-forum-d5a39.appspot.com",
messagingSenderId: "119713127399",
appId: "1:119713127399:web:b96c600270fa04cefbbb42"
};
const DELETE_PASSWORD = "Enzo1208";
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
function getCanonicalId(id1, id2) {
return [id1, id2].sort().join('_');
}
window.showPage = function(id){
document.querySelectorAll(".page").forEach(p=>p.classList.remove("active"));
const sel=document.getElementById(id);
if(sel)sel.classList.add("active");
document.querySelectorAll("nav a").forEach(a=>a.classList.remove("active"));
const navLink=document.querySelector(`nav a[data-page="${id}"]`);
if(navLink)navLink.classList.add("active");
if(id==="friend-requests")loadFriendRequests();
};
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll("nav a[data-page]").forEach(link=>{
link.addEventListener("click",e=>{
e.preventDefault();
showPage(link.dataset.page);
});
});
});
document.getElementById("form-register").onsubmit=async e=>{
e.preventDefault();
const email=document.getElementById("register-email").value;
const password=document.getElementById("register-password").value;
const username=document.getElementById("register-username").value.trim();
try{
const cred=await createUserWithEmailAndPassword(auth,email,password);
await updateProfile(cred.user,{displayName:username});
await setDoc(doc(db,"users",cred.user.uid),{
uid:cred.user.uid,email,username,username_lowercase:username.toLowerCase()
});
alert("Compte créé ! Vérifie ton email.");
}catch(err){alert(err.message);}
};
document.getElementById("form-login").onsubmit=async e=>{
e.preventDefault();
const email=document.getElementById("login-email").value;
const password=document.getElementById("login-password").value;
try{
await signInWithEmailAndPassword(auth,email,password);
}catch(err){alert("Erreur : "+err.message);}
};
document.getElementById("logout-btn").onclick=()=>signOut(auth);
onAuthStateChanged(auth,user=>{
const show=sel=>document.querySelectorAll(sel).forEach(e=>e.classList.remove("hidden"));
const hide=sel=>document.querySelectorAll(sel).forEach(e=>e.classList.add("hidden"));
if(user){
document.getElementById("user-info").textContent="Connecté : "+(user.displayName||user.email);
show(".tab-auth");hide(".tab-guest");document.getElementById("logout-btn").classList.remove("hidden");
showPage("forum");
loadPosts();loadForums();loadUserContent();loadFriends();loadFriendRequests();loadChatFriends();
}else{
document.getElementById("user-info").textContent="Non connecté";
hide(".tab-auth");show(".tab-guest");document.getElementById("logout-btn").classList.add("hidden");
showPage("auth");
}
});
document.getElementById("form-create-post").onsubmit=async e=>{
e.preventDefault();
const title=document.getElementById("post-title").value;
const content=document.getElementById("post-content").value;
const user=auth.currentUser;
await addDoc(collection(db,"posts"),{
title,content,authorId:user.uid,authorName:user.displayName||user.email,createdAt:serverTimestamp()
});
showPage("forum");loadPosts();
};
document.getElementById("form-create-forum").onsubmit=async e=>{
e.preventDefault();
const title=document.getElementById("forum-title").value;
const content=document.getElementById("forum-content").value;
const user=auth.currentUser;
await addDoc(collection(db,"forums"),{
title,content,authorId:user.uid,authorName:user.displayName||user.email,createdAt:serverTimestamp()
});
showPage("forum");loadForums();
};
async function loadPosts(){
const container=document.getElementById("posts");
container.innerHTML="";
const q=query(collection(db,"posts"),orderBy("createdAt","desc"),limit(20));
const snap=await getDocs(q);
snap.forEach(doc=>{
const p=doc.data();
const div=document.createElement("div");
div.className="card";
div.innerHTML=`<strong>${p.title}</strong>
<div class="author">par ${p.authorName}</div>`;
if(auth.currentUser && auth.currentUser.uid!==p.authorId){
div.innerHTML+=`<button class="add-friend-btn" style="float:right"
onclick="event.stopPropagation();sendFriendRequest('${p.authorId}')">
Ajouter ${p.authorName} en ami
</button>`;
}
div.onclick=()=>showDetails(doc.id,p,"posts");
container.appendChild(div);
});
}
async function loadForums(){
const container=document.getElementById("forums");
container.innerHTML="";
const q=query(collection(db,"forums"),orderBy("createdAt","desc"),limit(20));
const snap=await getDocs(q);
snap.forEach(doc=>{
const f=doc.data();
const div=document.createElement("div");
div.className="card";
div.innerHTML=`<strong>${f.title}</strong>
<div class="author">par ${f.authorName}</div>`;
if(auth.currentUser && auth.currentUser.uid!==f.authorId){
div.innerHTML+=`<button class="add-friend-btn" style="float:right"
onclick="event.stopPropagation();sendFriendRequest('${f.authorId}')">
Ajouter ${f.authorName} en ami
</button>`;
}
div.onclick=()=>showDetails(doc.id,f,"forums");
container.appendChild(div);
});
}
async function loadUserContent(){
const uid=auth.currentUser?.uid;
const postWrap=document.getElementById("my-posts");
const forumWrap=document.getElementById("my-forums");
postWrap.innerHTML="";forumWrap.innerHTML="";
const userPosts=query(collection(db,"posts"),where("authorId","==",uid));
const pSnap=await getDocs(userPosts);
pSnap.forEach(doc=>{
const p=doc.data();const div=document.createElement("div");
div.className="card";div.textContent=p.title;postWrap.appendChild(div);
});
const userForums=query(collection(db,"forums"),where("authorId","==",uid));
const fSnap=await getDocs(userForums);
fSnap.forEach(doc=>{
const f=doc.data();const div=document.createElement("div");
div.className="card";div.textContent=f.title;forumWrap.appendChild(div);
});
}
async function showDetails(id,data,collectionName){
showPage("details");
document.getElementById("details-title").textContent=data.title;
document.getElementById("details-meta").innerHTML=`<div class="author">par ${data.authorName}</div>`;
document.getElementById("details-content").textContent=data.content;
const addFriendDiv=document.getElementById("details-add-friend");
addFriendDiv.innerHTML="";
if(auth.currentUser && auth.currentUser.uid!==data.authorId){
addFriendDiv.innerHTML=`<button class="add-friend-btn"
onclick="sendFriendRequest('${data.authorId}')">Ajouter ${data.authorName} en ami</button>`;
}
const deleteBtn=document.getElementById("delete-btn");
const currentUser=auth.currentUser;
if(currentUser && currentUser.uid===data.authorId){
deleteBtn.classList.remove("hidden");
deleteBtn.onclick=async()=>{
const pwd=prompt("Mot de passe de suppression ?");
if(pwd===DELETE_PASSWORD){
await deleteDoc(doc(db,collectionName,id));
alert("Supprimé !");
showPage("forum");
loadPosts();
loadForums();
}else{
alert("Mot de passe incorrect");
}
};
}else{
deleteBtn.classList.add("hidden");
}
}
document.getElementById("search-input").addEventListener("input",async e=>{
const term=e.target.value.toLowerCase().trim();
const resultsDiv=document.getElementById("search-results");
resultsDiv.innerHTML="";
if(term.length<2)return;
const q=query(collection(db,"users"),where("username_lowercase",">=",term),limit(10));
const snap=await getDocs(q);
snap.forEach(doc=>{
const user=doc.data();
const div=document.createElement("div");
div.className="card";
div.innerHTML=`<b>${user.username}</b><br>
<button class="add-friend-btn">Ajouter</button>`;
div.querySelector("button").onclick=()=>sendFriendRequest(user.uid);
resultsDiv.appendChild(div);
});
});
window.sendFriendRequest=async function(uid){
const from=auth.currentUser.uid;
if(from===uid)return alert("Tu ne peux pas t’ajouter toi-même !");
try{
const docId = getCanonicalId(from, uid);
await setDoc(doc(db,"friend_requests", docId),{from,to:uid});
alert("Demande envoyée !");
}catch(e){alert("Erreur envoi demande: "+e.message);}
};
async function loadFriendRequests(){
const currentUser=auth.currentUser?.uid;
if(!currentUser)return;
const container=document.getElementById("friend-requests-list");
container.innerHTML="";
const q=query(collection(db,"friend_requests"),where("to","==",currentUser));
const snap=await getDocs(q);
if(snap.empty){
container.innerHTML="<p>Aucune demande</p>";
return;
}
snap.forEach(async docSnap=>{
const data=docSnap.data();
const fromDoc=await getDoc(doc(db,"users",data.from));
const fromUser=fromDoc.data();
const div=document.createElement("div");
div.className="card";
div.innerHTML=`
<b>${fromUser?.username||data.from}</b>
<div class="friend-actions">
<button onclick="acceptFriend('${data.from}')">Accepter</button>
<button class="reject" onclick="rejectFriend('${docSnap.id}')">Refuser</button>
</div>`;
container.appendChild(div);
});
}
window.acceptFriend=async function(otherId){
const uid=auth.currentUser.uid;
const docId = getCanonicalId(uid, otherId);
const requestDocId = getCanonicalId(otherId, uid);
try{
await deleteDoc(doc(db,"friend_requests", requestDocId));
await setDoc(doc(db,"friends", docId),{users:[uid,otherId]});
loadFriendRequests();loadFriends();loadChatFriends();
}catch(e){alert("Erreur acceptation: "+e.message);}
};
window.rejectFriend=async function(docId){
try{
await deleteDoc(doc(db,"friend_requests",docId));
loadFriendRequests();
}catch(e){alert("Erreur refus: "+e.message);}
};
async function loadFriends(){
const uid=auth.currentUser?.uid;
if(!uid)return;
const q=query(collection(db,"friends"),where("users","array-contains",uid));
const container=document.getElementById("friends-list");
container.innerHTML="";
const snap=await getDocs(q);
if(snap.empty){container.innerHTML="<p>Ajoute des amis pour les voir ici !</p>";return;}
snap.forEach(async docSnap=>{
const data = docSnap.data();
const users=data.users;
const friendId=users.find(id=>id!==uid);
if(friendId){
const friendDoc=await getDoc(doc(db,"users",friendId));
const friendData=friendDoc.data();
const customName = data.customNames?.[uid];
const displayName = customName || friendData?.username || "Utilisateur inconnu";
const div=document.createElement("div");
div.className="card friend-card";
div.innerHTML=`<b>${displayName}</b>
<div class="friend-menu hidden">
<button onclick="viewFriendWall('${friendId}','${friendData?.username}')">Mur</button>
<button onclick="renameFriend('${friendId}')">Renommer</button>
<button class="delete" onclick="deleteFriend('${friendId}')">Supprimer</button>
</div>`;
div.onclick=()=>{
const menu=div.querySelector(".friend-menu");
menu.classList.toggle("hidden");
};
container.appendChild(div);
}
});
}
window.deleteFriend=async function(otherId){
const uid=auth.currentUser.uid;
const docId = getCanonicalId(uid, otherId);
try{
await deleteDoc(doc(db,"friends",docId));
loadFriends();
alert("Ami supprimé.");
}catch(e){alert("Erreur: "+e.message);}
}
window.renameFriend = async function(friendId) {
const uid = auth.currentUser.uid;
const newName = prompt("Entrez un nouveau nom pour cet ami:");
if (newName === null || newName.trim() === "") return;
const docId = getCanonicalId(uid, friendId);
try {
const friendDoc = await getDoc(doc(db, "friends", docId));
if (friendDoc.exists()) {
await updateDoc(doc(db, "friends", docId), {
[`customNames.${uid}`]: newName.trim()
});
alert("Nom mis à jour !");
loadFriends();
} else {
alert("Erreur: Amitié non trouvée.");
}
} catch (e) {
alert("Erreur lors du renommage: " + e.message);
}
};
window.viewFriendWall=async function(friendId,friendName){
showPage("friend-wall");
document.getElementById("friend-wall-title").textContent=`Mur de ${friendName}`;
const postWrap=document.getElementById("friend-posts");
const forumWrap=document.getElementById("friend-forums");
postWrap.innerHTML="<h4>Posts</h4>";forumWrap.innerHTML="<h4>Forums</h4>";
const friendPosts=query(collection(db,"posts"),where("authorId","==",friendId));
const pSnap=await getDocs(friendPosts);
pSnap.forEach(doc=>{
const p=doc.data();const div=document.createElement("div");
div.className="card";div.textContent=p.title;postWrap.appendChild(div);
});
const friendForums=query(collection(db,"forums"),where("authorId","==",friendId));
const fSnap=await getDocs(friendForums);
fSnap.forEach(doc=>{
const f=doc.data();const div=document.createElement("div");
div.className="card";div.textContent=f.title;forumWrap.appendChild(div);
});
}
async function loadChatFriends(){
const uid=auth.currentUser?.uid;
if(!uid)return;
const container=document.getElementById("chat-friends-list");
container.innerHTML="";
const q=query(collection(db,"friends"),where("users","array-contains",uid));
const snap=await getDocs(q);
if(snap.empty){container.innerHTML="<p>Pas d'amis pour discuter</p>";return;}
snap.forEach(async docSnap=>{
const data = docSnap.data();
const users=data.users;
const friendId=users.find(id=>id!==uid);
if(friendId){
const friendDoc=await getDoc(doc(db,"users",friendId));
const friendData=friendDoc.data();
const customName = data.customNames?.[uid];
const displayName = customName || friendData?.username || "Utilisateur inconnu";
const div=document.createElement("div");
div.className="friend-card";
div.textContent=displayName;
div.onclick=()=>startChat(friendId,displayName);
container.appendChild(div);
}
});
}
let currentChatId=null;
let unsubscribeChat=null;
async function startChat(friendId,friendName){
document.querySelectorAll("#chat-friends-list .friend-card").forEach(el=>el.classList.remove("active"));
document.querySelector(`#chat-friends-list .friend-card[data-id='${friendId}']`)?.classList.add("active");
const uid=auth.currentUser.uid;
const chatIds = getCanonicalId(uid, friendId);
currentChatId=chatIds;
document.getElementById("chat-form").style.display="block";
const chatWindow=document.getElementById("chat-window");
chatWindow.innerHTML=`<em>Conversation avec ${friendName}...</em>`;
if(unsubscribeChat)unsubscribeChat();
const q=query(collection(db,"chats",chatIds,"messages"),orderBy("createdAt"));
unsubscribeChat=onSnapshot(q,snapshot=>{
snapshot.docChanges().forEach(change=>{
if(change.type==="added"){
const msg=change.doc.data();
const msgDiv=document.createElement("div");
msgDiv.className=`message-bubble ${msg.senderId===uid?"sent":"received"}`;
msgDiv.textContent=msg.text;
chatWindow.appendChild(msgDiv);
chatWindow.scrollTop=chatWindow.scrollHeight;
}
});
});
}
document.getElementById("chat-form").onsubmit=async e=>{
e.preventDefault();
const input=document.getElementById("chat-input");
const text=input.value.trim();
if(!text||!currentChatId)return;
await addDoc(collection(db,"chats",currentChatId,"messages"),{
text,senderId:auth.currentUser.uid,createdAt:serverTimestamp()
});
input.value="";
};
window.attemptDeleteAccount=async function(){
const user=auth.currentUser;
if(!user)return;
const confirmPwd=prompt("Entre ton mot de passe pour confirmer la suppression de ton compte:");
try{
await signInWithEmailAndPassword(auth,user.email,confirmPwd);
await deleteUser(user);
alert("Compte supprimé avec succès.");
}catch(e){
alert("Erreur lors de la suppression du compte: "+e.message);
}
};
</script>
</body>
</html>