Menu
Accueil
Forum Débats
Forum Vie quotidienne
Forum culture
Actualités
Forums
Discussions sans réponses
Membres
Quoi de neuf
Nouveaux messages
Nouveaux médias
Nouveaux commentaires (Médias)
Nouveaux articles
Dernières activités
Médias
Nouveaux médias
Nouveaux commentaires
Rechercher un média
Blogs
Recherche de blogs
💵 0 Fofocoins
🛒 Boutique
Quoi de neuf ?
Rechercher par titre uniquement
Par:
Discussions sans réponses
Membres
Menu
Connexion
S'inscrire
Accueil
Forums
Discussions générales et communauté
Forum de discussion libre sur tous les sujets
Je tente de créer un forum de discussions (à titre éducatif et pas en concurrence)
JavaScript est désactivé. Pour une meilleure expérience, s'il vous plaît activer JavaScript dans votre navigateur avant de continuer.
Vous utilisez un navigateur non à jour ou ancien. Il ne peut pas afficher ce site ou d'autres sites correctement.
Vous devez le mettre à jour ou utiliser un
navigateur alternatif
.
Répondre à la discussion
Message
[QUOTE="𝑪𝑨𝑷𝑰𝑻𝑨𝑰𝑵𝑬 𝑱𝑨𝑪𝑲, post: 643442, member: 3039"] [HEADING=1]RECTIFIE AVEC ZERO ERREUR EN ENREGISTREMENT [/HEADING] <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html> <html b:version='2' class='v2' expr:dir='data:blog.languageDirection' xmlns='[URL]http://www.w3.org/1999/xhtml[/URL]' xmlns:b='[URL]http://www.google.com/2005/gml/b[/URL]' xmlns:data='[URL]http://www.google.com/2005/gml/data[/URL]' xmlns:expr='[URL]http://www.google.com/2005/gml/expr[/URL]'> <head> <meta charset='UTF-8'/> <meta content='width=device-width, initial-scale=1' name='viewport'/> <title><data:blog.pageTitle/></title> <b:skin><![CDATA[ ]]></b:skin> </head> <body> <div id='site-header'> <div> <h1 id='site-title'>💬 ForumFrance</h1> <p id='header-sub'>Chargement…</p> </div> <div id='header-user'></div> </div> <div id='forum-root'> <b:section id='main' name='Main' showaddelement='no'/> </div> <div id='site-footer'>ForumFrance © 2025</div> <script type='module'> //<![CDATA[ // ── CSS injecte via JS ────────────────────────────────────────── const st = document.createElement('style'); st.textContent = ` *,*::before,*::after{box-sizing:border-box;margin:0;padding:0} body{font-family:system-ui,sans-serif;background:#f4f4f8;min-height:100vh} #site-header{background:linear-gradient(135deg,#7F77DD,#D4537E 50%,#BA7517);padding:1.2rem 1.5rem;color:#fff;display:flex;justify-content:space-between;align-items:center;position:sticky;top:0;z-index:100;box-shadow:0 2px 12px rgba(0,0,0,.15)} #site-title{font-size:22px;font-weight:700} #header-sub{font-size:12px;opacity:.8;margin-top:2px} #header-user{display:flex;align-items:center;gap:10px} #forum-root{max-width:780px;margin:0 auto;padding:1.5rem 1rem 3rem} #site-footer{text-align:center;color:#bbb;font-size:12px;padding:2rem 0 1rem} .fc{background:#fff;border-radius:14px;border:1px solid #ececec;padding:16px;margin-bottom:12px;box-shadow:0 1px 4px rgba(0,0,0,.04)} .fi,.fta,.fse{width:100%;padding:9px 12px;border-radius:10px;border:1.5px solid #e0e0e0;font-size:14px;outline:none;font-family:inherit;background:#fff} .fta{resize:vertical} .fb{padding:9px 20px;border-radius:10px;border:none;font-weight:700;font-size:14px;cursor:pointer} .fbg{background:linear-gradient(90deg,#7F77DD,#D4537E);color:#fff} .fbo{background:#f0f0f0;color:#666} .fbsm{padding:5px 14px;font-size:13px} .fbgl{background:#fff;color:#333;border:1.5px solid #ddd;display:flex;align-items:center;gap:8px;justify-content:center;width:100%;margin-top:10px;padding:9px 20px;border-radius:10px;font-weight:700;font-size:14px;cursor:pointer} .ft{padding:8px 20px;border-radius:10px;border:none;font-weight:700;font-size:14px;cursor:pointer} .ft.on{background:#7F77DD;color:#fff} .ft.off{background:#f0f0f0;color:#888} .fcb{padding:5px 14px;border-radius:20px;font-size:13px;cursor:pointer;border:1.5px solid #ddd;background:#fff;color:#888} .fcb.on{font-weight:700} .ftag{display:inline-block;padding:2px 10px;border-radius:20px;font-size:11px;font-weight:600;margin-right:6px} .fpin{display:inline-flex;align-items:center;gap:4px;background:#FAEEDA;color:#BA7517;font-size:11px;font-weight:700;padding:2px 8px;border-radius:20px;margin-right:6px} .fav{border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:700;flex-shrink:0;overflow:hidden} .fav img{width:100%;height:100%;object-fit:cover} .flk{display:flex;align-items:center;gap:4px;padding:4px 12px;border-radius:20px;border:1.5px solid #eee;background:#fafafa;color:#aaa;font-size:13px;font-weight:600;cursor:pointer} .flk.on{border-color:#D4537E;background:#FBEAF0;color:#D4537E} .ferr{color:#c0392b;font-size:13px;margin-top:6px} .fsh{background:#EEEDFE;border-radius:10px;padding:10px 12px;font-size:12px;color:#7F77DD;margin-bottom:12px} .flbl{font-size:13px;font-weight:600;color:#555;display:block;margin-bottom:5px;margin-top:10px} .fmu{color:#aaa;font-size:12px} .frw{display:flex;align-items:center;gap:8px} .fbk{background:none;border:none;color:#7F77DD;font-weight:700;font-size:14px;cursor:pointer;padding:8px 0} .ftb{display:flex;gap:8px;align-items:center;margin-bottom:12px} .fcts{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px} .fst{background:#fafafa;border-radius:10px;padding:12px;text-align:center} .fg2{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:16px} .faw{max-width:420px;margin:2rem auto} .tpc:hover{box-shadow:0 4px 16px rgba(127,119,221,.12);border-color:#d0cef8;transition:all .15s} `; document.head.appendChild(st); // ── Firebase ──────────────────────────────────────────────────── import { initializeApp } from "[URL]https://www.gstatic.com/firebasejs/10.12.0/firebase-app.js[/URL]"; import { getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, signInWithPopup, GoogleAuthProvider, signOut, onAuthStateChanged, updateProfile } from "[URL]https://www.gstatic.com/firebasejs/10.12.0/firebase-auth.js[/URL]"; import { getFirestore, collection, addDoc, doc, updateDoc, increment, query, orderBy, onSnapshot, serverTimestamp, getDoc, setDoc, getDocs } from "[URL]https://www.gstatic.com/firebasejs/10.12.0/firebase-firestore.js[/URL]"; const firebaseConfig = { apiKey: "AIzaSyAGAlQFAO2z1K5iQA5h6JHPeX_zaMOkb5Q", authDomain: "forumfrance-6de67.firebaseapp.com", projectId: "forumfrance-6de67", storageBucket: "forumfrance-6de67.firebasestorage.app", messagingSenderId: "246371695638", appId: "1:246371695638:web:f48296c3cb65953cf88937" }; const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const gProvider = new GoogleAuthProvider(); // ── Categories ────────────────────────────────────────────────── const CATS = [ { id:"all", label:"Tout", color:"#7F77DD", bg:"#EEEDFE" }, { id:"actu", label:"Actualite", color:"#185FA5", bg:"#E6F1FB" }, { id:"culture", label:"Culture", color:"#D4537E", bg:"#FBEAF0" }, { id:"tech", label:"Tech", color:"#0F6E56", bg:"#E1F5EE" }, { id:"humour", label:"Humour", color:"#BA7517", bg:"#FAEEDA" }, { id:"sport", label:"Sport", color:"#A32D2D", bg:"#FCEBEB" }, ]; const catOf = id => CATS.find(c => c.id === id) || CATS[0]; const ts2str = ts => { if (!ts) return ""; const d = ts.toDate ? ts.toDate() : new Date(ts); return d.toLocaleDateString("fr-FR", { day:"2-digit", month:"short", hour:"2-digit", minute:"2-digit" }); }; // ── State ─────────────────────────────────────────────────────── let S = { screen:"loading", authTab:"login", user:null, topics:[], replies:{}, liked:{}, activeCat:"all", search:"", view:"list", activeTopic:null, activeAuthor:null, showForm:false, lEmail:"", lPass:"", rPseudo:"", rEmail:"", rPass:"", rPass2:"", nTitle:"", nContent:"", nCat:"actu", replyText:"", error:"", loading:false, }; const root = document.getElementById("forum-root"); const hSub = document.getElementById("header-sub"); const hUser = document.getElementById("header-user"); function setState(patch, skip) { S = Object.assign({}, S, patch); if (!skip) render(); } // ── Avatar ────────────────────────────────────────────────────── function av(user, size) { size = size || 36; if (user && user.photoURL) return '<div class="fav" style="width:' + size + 'px;height:' + size + 'px"><img src="' + user.photoURL + '" referrerpolicy="no-referrer"></div>'; const colors = ["#7F77DD","#D4537E","#0F6E56","#BA7517","#185FA5","#A32D2D"]; const bgs = ["#EEEDFE","#FBEAF0","#E1F5EE","#FAEEDA","#E6F1FB","#FCEBEB"]; const name = (user && (user.displayName || user.pseudo)) || "?"; const idx = name.charCodeAt(0) % colors.length; return '<div class="fav" style="width:' + size + 'px;height:' + size + 'px;background:' + bgs[idx] + ';color:' + colors[idx] + ';font-size:' + (size > 40 ? 20 : 14) + 'px">' + name[0].toUpperCase() + '</div>'; } // ── Render ────────────────────────────────────────────────────── function render() { updateHeader(); if (S.screen === "loading") { root.innerHTML = '<div style="text-align:center;padding:60px 0;color:#aaa"><div style="font-size:40px">💬</div><p style="margin-top:12px">Chargement…</p></div>'; return; } if (S.screen === "auth") { renderAuth(); return; } if (S.view === "list") { renderList(); return; } if (S.view === "topic") { renderTopic(); return; } if (S.view === "profile") { renderProfile(); return; } } function updateHeader() { if (!S.user) { hSub.textContent = "Connectez-vous pour participer"; hUser.innerHTML = ""; return; } hSub.textContent = S.topics.length + " sujets · Bonjour, " + (S.user.displayName || "?") + " !"; hUser.innerHTML = '<div id="hav" style="cursor:pointer">' + av(S.user, 38) + '</div><button class="fb fbo fbsm" id="hlo" style="font-size:12px">Deconnexion</button>'; document.getElementById("hav")?.addEventListener("click", () => setState({ view:"profile", activeAuthor:S.user.uid })); document.getElementById("hlo")?.addEventListener("click", () => signOut(auth)); } // ── Google icon ───────────────────────────────────────────────── function gico() { return '<svg width="18" height="18" viewBox="0 0 48 48"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/></svg>'; } // ── AUTH ──────────────────────────────────────────────────────── function renderAuth() { const login = S.authTab === "login"; root.innerHTML = '<div class="faw"><div class="fc">' + '<div class="frw" style="margin-bottom:20px;gap:8px">' + '<button class="ft ' + (login?"on":"off") + '" id="tl">Connexion</button>' + '<button class="ft ' + (!login?"on":"off") + '" id="tr">Inscription</button>' + '</div>' + (login ? '<label class="flbl">Email</label>' + '<input class="fi" id="le" type="email" placeholder="[email]vous@exemple.com[/email]" value="' + S.lEmail + '">' + '<label class="flbl">Mot de passe</label>' + '<input class="fi" id="lp" type="password" placeholder="••••••" value="' + S.lPass + '">' + (S.error ? '<div class="ferr">⚠ ' + S.error + '</div>' : '') + '<button class="fb fbg" id="bli" style="width:100%;margin-top:14px">' + (S.loading?"Connexion…":"Se connecter") + '</button>' + '<button class="fbgl" id="bgl">' + gico() + ' Continuer avec Google</button>' + '<p style="text-align:center;margin-top:12px;font-size:13px;color:#aaa">Pas de compte ? <a href="#" id="gor" style="color:#7F77DD;font-weight:600">S\'inscrire</a></p>' : '<label class="flbl">Pseudo (3-20 car.)</label>' + '<input class="fi" id="rp" placeholder="MonPseudo" value="' + S.rPseudo + '">' + '<label class="flbl">Email</label>' + '<input class="fi" id="re" type="email" placeholder="[email]vous@exemple.com[/email]" value="' + S.rEmail + '">' + '<label class="flbl">Mot de passe (6 car. min.)</label>' + '<input class="fi" id="rpa" type="password" placeholder="••••••" value="' + S.rPass + '">' + '<label class="flbl">Confirmer</label>' + '<input class="fi" id="rp2" type="password" placeholder="••••••" value="' + S.rPass2 + '">' + '<div class="fsh" style="margin-top:12px">🛡 Protection anti-abus activee.</div>' + (S.error ? '<div class="ferr">⚠ ' + S.error + '</div>' : '') + '<button class="fb fbg" id="brg" style="width:100%;margin-top:10px">' + (S.loading?"Inscription…":"Creer mon compte") + '</button>' + '<button class="fbgl" id="bgr">' + gico() + ' Continuer avec Google</button>' + '<p style="text-align:center;margin-top:12px;font-size:13px;color:#aaa">Deja membre ? <a href="#" id="gol" style="color:#7F77DD;font-weight:600">Se connecter</a></p>' ) + '</div></div>'; document.getElementById("tl")?.addEventListener("click", () => setState({ authTab:"login", error:"" })); document.getElementById("tr")?.addEventListener("click", () => setState({ authTab:"register", error:"" })); document.getElementById("gol")?.addEventListener("click", e => { e.preventDefault(); setState({ authTab:"login", error:"" }); }); document.getElementById("gor")?.addEventListener("click", e => { e.preventDefault(); setState({ authTab:"register", error:"" }); }); document.getElementById("bli")?.addEventListener("click", doLogin); document.getElementById("brg")?.addEventListener("click", doRegister); document.getElementById("bgl")?.addEventListener("click", doGoogle); document.getElementById("bgr")?.addEventListener("click", doGoogle); const bind = (id, key) => document.getElementById(id)?.addEventListener("input", e => setState({ [key]: e.target.value }, true)); bind("le","lEmail"); bind("lp","lPass"); bind("rp","rPseudo"); bind("re","rEmail"); bind("rpa","rPass"); bind("rp2","rPass2"); } // ── Auth actions ──────────────────────────────────────────────── function valPseudo(p) { if (!p || p.length < 3 || p.length > 20) return "Pseudo : 3 a 20 caracteres."; if (!/^[a-zA-Z0-9_\-]+$/.test(p)) return "Caracteres invalides."; if (["admin","moderateur","root","system"].some(w => p.toLowerCase().includes(w))) return "Pseudo reserve."; return null; } async function doLogin() { const email = document.getElementById("le")?.value.trim(); const pass = document.getElementById("lp")?.value; if (!email || !pass) { setState({ error:"Remplissez tous les champs." }); return; } setState({ loading:true, error:"" }); try { await signInWithEmailAndPassword(auth, email, pass); } catch(e) { const msg = e.code === "auth/invalid-credential" ? "Email ou mot de passe incorrect." : e.code === "auth/too-many-requests" ? "Trop de tentatives. Reessayez plus tard." : e.message; setState({ loading:false, error:msg }); } } async function doRegister() { const pseudo = document.getElementById("rp")?.value.trim(); const email = document.getElementById("re")?.value.trim(); const pass = document.getElementById("rpa")?.value; const pass2 = document.getElementById("rp2")?.value; const err = valPseudo(pseudo); if (err) { setState({ error:err }); return; } if (!email) { setState({ error:"Email requis." }); return; } if (pass.length < 6){ setState({ error:"Mot de passe : 6 caracteres minimum." }); return; } if (pass !== pass2) { setState({ error:"Les mots de passe ne correspondent pas." }); return; } setState({ loading:true, error:"" }); try { const cred = await createUserWithEmailAndPassword(auth, email, pass); await updateProfile(cred.user, { displayName: pseudo }); await cred.user.getIdToken(true); await setDoc(doc(db, "users", cred.user.uid), { pseudo, email, joined: new Date().toLocaleDateString("fr-FR", { month:"long", year:"numeric" }), posts:0, role:"member", }); } catch(e) { const msg = e.code === "auth/email-already-in-use" ? "Cet email est deja utilise." : e.message; setState({ loading:false, error:msg }); } } async function doGoogle() { try { const result = await signInWithPopup(auth, gProvider); const u = result.user; const ref = doc(db, "users", u.uid); const snap = await getDoc(ref); if (!snap.exists()) { await setDoc(ref, { pseudo: u.displayName || "Utilisateur", email: u.email, joined: new Date().toLocaleDateString("fr-FR", { month:"long", year:"numeric" }), posts:0, role:"member", }); } } catch(e) { setState({ error:"Connexion Google annulee ou bloquee." }); } } // ── LIST ──────────────────────────────────────────────────────── function renderList() { const filtered = S.topics .filter(t => (S.activeCat === "all" || t.category === S.activeCat) && (!S.search || t.title.toLowerCase().includes(S.search.toLowerCase()))) .sort((a,b) => (b.pinned?1:0) - (a.pinned?1:0)); let thtml = filtered.length === 0 ? '<p style="text-align:center;color:#bbb;padding:2rem 0">Aucun sujet trouve.</p>' : filtered.map(function(t) { const cat = catOf(t.category); return '<div class="fc tpc" data-id="' + t.id + '" style="cursor:pointer">' + '<div class="frw" style="align-items:flex-start;gap:10px">' + '<div class="alnk" data-uid="' + t.authorId + '" style="cursor:pointer">' + av({displayName:t.author, photoURL:t.photoURL}) + '</div>' + '<div style="flex:1;min-width:0">' + (t.pinned ? '<span class="fpin">📌</span>' : '') + '<span class="ftag" style="background:' + cat.bg + ';color:' + cat.color + '">' + cat.label + '</span>' + '<div style="font-size:15px;font-weight:600;color:#222;margin:4px 0 2px;line-height:1.3">' + t.title + '</div>' + '<div class="fmu">par <b style="color:#888;cursor:pointer">' + t.author + '</b> · ' + ts2str(t.createdAt) + '</div>' + '</div></div>' + '<div class="frw" style="margin-top:10px">' + '<button class="flk' + (S.liked[t.id]?" on":"") + ' lkb" data-id="' + t.id + '">❤ ' + (t.likes||0) + '</button>' + '<span class="fmu" style="margin-left:8px">💬 ' + (t.replyCount||0) + '</span>' + '</div></div>'; }).join(""); let form = ""; if (S.showForm) { form = '<div class="fc" style="border:1.5px solid #e0e0e0;margin-bottom:16px">' + '<div style="font-size:16px;font-weight:700;color:#333;margin-bottom:10px">Creer un sujet</div>' + '<label class="flbl">Titre</label><input class="fi" id="nt" placeholder="Titre du sujet" value="' + S.nTitle + '">' + '<label class="flbl">Categorie</label>' + '<select class="fse" id="nc" style="margin-top:5px">' + CATS.filter(c => c.id !== "all").map(c => '<option value="' + c.id + '"' + (c.id === S.nCat?" selected":"") + '>' + c.label + '</option>').join("") + '</select>' + '<label class="flbl">Contenu</label><textarea class="fta" id="nco" rows="4" placeholder="Decrivez votre sujet">' + S.nContent + '</textarea>' + '<div class="frw" style="margin-top:10px;gap:8px">' + '<button class="fb fbg" id="bpt">Publier</button>' + '<button class="fb fbo" id="bcl">Annuler</button>' + '</div></div>'; } root.innerHTML = '<div class="ftb">' + '<input class="fi" id="sr" placeholder="🔍 Rechercher" value="' + S.search + '" style="flex:1">' + '<button class="fb fbg" id="bnt">+ Nouveau sujet</button>' + '</div>' + form + '<div class="fcts">' + CATS.map(function(c) { const on = S.activeCat === c.id; return '<button class="fcb' + (on?" on":"") + '" data-cat="' + c.id + '" style="' + (on?"border-color:"+c.color+";background:"+c.bg+";color:"+c.color+";font-weight:700":"") + '">' + c.label + '</button>'; }).join("") + '</div>' + thtml; document.getElementById("sr")?.addEventListener("input", e => setState({ search:e.target.value })); document.getElementById("bnt")?.addEventListener("click", () => setState({ showForm:!S.showForm })); document.querySelectorAll(".fcb").forEach(b => b.addEventListener("click", () => setState({ activeCat:b.dataset.cat }))); document.querySelectorAll(".tpc").forEach(el => el.addEventListener("click", function() { const t = S.topics.find(x => x.id === el.dataset.id); if (t) setState({ view:"topic", activeTopic:t }); })); document.querySelectorAll(".alnk").forEach(el => el.addEventListener("click", function(e) { e.stopPropagation(); setState({ view:"profile", activeAuthor:el.dataset.uid }); })); document.querySelectorAll(".lkb").forEach(btn => btn.addEventListener("click", async function(e) { e.stopPropagation(); const id = btn.dataset.id; const on = !S.liked[id]; const liked = Object.assign({}, S.liked); liked[id] = on; setState({ liked:liked }); await updateDoc(doc(db,"topics",id), { likes:increment(on?1:-1) }); })); if (S.showForm) { document.getElementById("nt")?.addEventListener("input", e => setState({ nTitle:e.target.value }, true)); document.getElementById("nco")?.addEventListener("input", e => setState({ nContent:e.target.value }, true)); document.getElementById("nc")?.addEventListener("change", e => setState({ nCat:e.target.value }, true)); document.getElementById("bcl")?.addEventListener("click", () => setState({ showForm:false })); document.getElementById("bpt")?.addEventListener("click", doPost); } } async function doPost() { const title = document.getElementById("nt")?.value.trim(); const content = document.getElementById("nco")?.value.trim(); const cat = document.getElementById("nc")?.value; if (!title || !content) return; const u = S.user; await addDoc(collection(db,"topics"), { title, content, category:cat, author:u.displayName||"Anonyme", authorId:u.uid, photoURL:u.photoURL||null, likes:0, replyCount:0, pinned:false, createdAt:serverTimestamp(), }); setState({ showForm:false, nTitle:"", nContent:"", nCat:"actu" }); } // ── TOPIC ─────────────────────────────────────────────────────── function renderTopic() { const t = S.activeTopic; if (!t) { setState({ view:"list" }); return; } const cat = catOf(t.category); const reps = S.replies[t.id] || []; root.innerHTML = '<button class="fbk" id="bk">← Retour</button>' + '<div class="fc">' + '<span class="ftag" style="background:' + cat.bg + ';color:' + cat.color + '">' + cat.label + '</span>' + (t.pinned ? '<span class="fpin">📌 Epingle</span>' : '') + '<div style="font-size:20px;font-weight:700;color:#222;margin:8px 0 6px">' + t.title + '</div>' + '<div class="frw" style="margin-bottom:10px">' + av({displayName:t.author, photoURL:t.photoURL}, 28) + '<span class="fmu">par <b style="color:#555">' + t.author + '</b> · ' + ts2str(t.createdAt) + '</span>' + '</div>' + '<div style="font-size:15px;color:#444;line-height:1.6;margin-bottom:12px">' + t.content + '</div>' + '<div class="frw">' + '<button class="flk' + (S.liked[t.id]?" on":"") + '" id="tlk">❤ ' + (t.likes||0) + '</button>' + '<span class="fmu" style="margin-left:8px">💬 ' + reps.length + ' reponse(s)</span>' + '</div></div>' + '<div style="font-size:14px;font-weight:700;color:#555;margin:12px 0 8px">Reponses (' + reps.length + ')</div>' + (reps.length === 0 ? '<p style="color:#bbb;font-size:13px;margin-bottom:12px">Aucune reponse. Soyez le premier !</p>' : '') + reps.map(function(r) { return '<div class="fc" style="background:#fafafa">' + '<div class="frw" style="margin-bottom:6px">' + av({displayName:r.author, photoURL:r.photoURL}, 28) + '<b style="font-size:13px;color:#555">' + r.author + '</b>' + '<span class="fmu">· ' + ts2str(r.createdAt) + '</span>' + '</div><div style="font-size:14px;color:#333;line-height:1.5">' + r.content + '</div></div>'; }).join("") + '<div style="margin-top:14px">' + '<textarea class="fta" id="rt" rows="3" placeholder="Ecrire une reponse">' + S.replyText + '</textarea>' + '<button class="fb fbg" id="bry" style="margin-top:8px">Envoyer</button>' + '</div>'; document.getElementById("bk")?.addEventListener("click", () => setState({ view:"list", activeTopic:null })); document.getElementById("tlk")?.addEventListener("click", async function() { const on = !S.liked[t.id]; const liked = Object.assign({}, S.liked); liked[t.id] = on; setState({ liked:liked }); await updateDoc(doc(db,"topics",t.id), { likes:increment(on?1:-1) }); }); document.getElementById("rt")?.addEventListener("input", e => setState({ replyText:e.target.value }, true)); document.getElementById("bry")?.addEventListener("click", doReply); } async function doReply() { const content = document.getElementById("rt")?.value.trim(); if (!content) return; const u = S.user; const t = S.activeTopic; await addDoc(collection(db,"topics",t.id,"replies"), { content, author:u.displayName||"Anonyme", authorId:u.uid, photoURL:u.photoURL||null, createdAt:serverTimestamp(), }); await updateDoc(doc(db,"topics",t.id), { replyCount:increment(1) }); const snap = await getDocs(query(collection(db,"topics",t.id,"replies"), orderBy("createdAt"))); const reps = snap.docs.map(d => Object.assign({}, d.data(), { id:d.id })); const replies = Object.assign({}, S.replies); replies[t.id] = reps; setState({ replies:replies, replyText:"" }); } // ── PROFILE ───────────────────────────────────────────────────── async function renderProfile() { const uid = S.activeAuthor; const isMe = uid === S.user?.uid; let uData = { pseudo:S.user?.displayName||"?", joined:"?" }; try { const d = await getDoc(doc(db,"users",uid)); if (d.exists()) uData = d.data(); } catch(e) {} const userTopics = S.topics.filter(t => t.authorId === uid); root.innerHTML = '<button class="fbk" id="bk">← Retour</button>' + '<div class="fc" style="text-align:center;padding:24px">' + '<div style="margin:0 auto 12px;width:64px">' + av({displayName:uData.pseudo, photoURL:isMe?S.user.photoURL:null}, 64) + '</div>' + '<div style="font-size:20px;font-weight:700;color:#222">' + uData.pseudo + '</div>' + (isMe ? '<div style="display:inline-block;padding:2px 10px;border-radius:20px;background:#EEEDFE;color:#7F77DD;font-size:12px;font-weight:700;margin-top:6px">👤 Vous</div>' : '') + '<div class="fmu" style="margin-top:6px">Membre depuis ' + (uData.joined||"?") + '</div>' + '<div class="fg2">' + '<div class="fst"><b style="display:block;font-size:22px;font-weight:700;color:#7F77DD">' + userTopics.length + '</b><span class="fmu">Sujets</span></div>' + '<div class="fst"><b style="display:block;font-size:22px;font-weight:700;color:#D4537E">' + userTopics.reduce(function(a,t){return a+(t.likes||0);},0) + '</b><span class="fmu">Likes recus</span></div>' + '</div>' + (isMe ? '<button class="fb fbo" id="blo" style="width:100%;margin-top:16px">Se deconnecter</button>' : '') + (userTopics.length > 0 ? '<div style="text-align:left;margin-top:18px">' + '<div style="font-size:14px;font-weight:700;color:#555;margin-bottom:8px">Sujets de ' + uData.pseudo + '</div>' + userTopics.map(function(t){ const cat = catOf(t.category); return '<div class="fc tpc" data-id="' + t.id + '" style="cursor:pointer">' + '<span class="ftag" style="background:' + cat.bg + ';color:' + cat.color + '">' + cat.label + '</span>' + '<div style="font-size:15px;font-weight:600;color:#222;margin-top:4px">' + t.title + '</div></div>'; }).join("") + '</div>' : '') + '</div>'; document.getElementById("bk")?.addEventListener("click", () => setState({ view:"list", activeAuthor:null })); document.getElementById("blo")?.addEventListener("click", () => signOut(auth)); document.querySelectorAll(".tpc").forEach(el => el.addEventListener("click", function() { const t = S.topics.find(x => x.id === el.dataset.id); if (t) setState({ view:"topic", activeTopic:t }); })); } // ── Seed demo ─────────────────────────────────────────────────── async function seedDemo() { const snap = await getDocs(collection(db,"topics")); if (!snap.empty) return; const DEMO = [ { title:"Quels sont vos films preferes de 2025 ?", category:"culture", content:"Je cherche des recommandations pour mes soirees du week-end !", pinned:true, likes:47, replyCount:24 }, { title:"L'IA va-t-elle remplacer les developpeurs ?", category:"tech", content:"Un debat qui revient souvent. Vos avis ?", pinned:false, likes:102, replyCount:58 }, { title:"Blague du jour - partagez les votres !", category:"humour", content:"Pourquoi les plongeurs plongent-ils toujours en arriere ?", pinned:false, likes:213, replyCount:89 }, { title:"Resultats de la Ligue des Champions", category:"sport", content:"Quelle soiree ! Vos reactions ?", pinned:false, likes:88, replyCount:132 }, { title:"Elections europeennes - analyse et perspectives", category:"actu", content:"Un decryptage des resultats et de leurs implications.", pinned:false, likes:54, replyCount:76 }, ]; for (let i = 0; i < DEMO.length; i++) { await addDoc(collection(db,"topics"), Object.assign({}, DEMO[i], { author:"Equipe Forum", authorId:"demo", photoURL:null, createdAt:serverTimestamp() })); } } // ── Firebase listener ─────────────────────────────────────────── onAuthStateChanged(auth, async function(user) { if (user) { setState({ user:user, screen:"forum" }, true); await seedDemo(); onSnapshot(query(collection(db,"topics"), orderBy("createdAt","desc")), function(snap) { const topics = snap.docs.map(d => Object.assign({}, d.data(), { id:d.id })); setState({ topics:topics }); }); } else { setState({ user:null, screen:"auth", view:"list", topics:[] }); } }); //]]> </script> </body> </html>[/i] [/QUOTE]
Nom d'utilisateur
🤖 Vérification anti robots
Poster une réponse
Accueil
Forums
Discussions générales et communauté
Forum de discussion libre sur tous les sujets
Je tente de créer un forum de discussions (à titre éducatif et pas en concurrence)