${escapeHtml(thread.title)}
By ${escapeHtml(thread.authorDisplayName)}
${formatTimestamp(thread.createdAt)}
${thread.replyCount || 0} replies
`;
threadList.appendChild(threadElement);
});
}
function selectThread(thread) {
selectedThread = thread;
loadPosts();
showThreadView();
}
async function loadPosts() {
if (!selectedThread || !db) return;
const postsCollectionPath = `artifacts/${appId}/public/data/threads/${selectedThread.id}/posts`;
const q = window.Firebase.query(
window.Firebase.collection(db, postsCollectionPath),
window.Firebase.orderBy(“createdAt”, “asc”)
);
window.Firebase.onSnapshot(q, (querySnapshot) => {
posts = [];
querySnapshot.forEach((doc) => {
posts.push({ id: doc.id, …doc.data() });
});
renderPosts();
}, (error) => console.error(“Error fetching posts:”, error));
}
function renderPosts() {
postsList.innerHTML = ”;
posts.forEach(post => {
const postElement = document.createElement(‘div’);
postElement.className = ‘bg-white p-4 rounded-lg shadow-sm border border-gray-200’;
postElement.innerHTML = `
${escapeHtml(post.authorDisplayName)}
${formatTimestamp(post.createdAt)}
${escapeHtml(post.content)}
`; postsList.appendChild(postElement); }); // Scroll to bottom setTimeout(() => { postsList.scrollIntoView({ behavior: ‘smooth’, block: ‘end’ }); }, 100); } async function createThread(title, content) { if (!db || !user) return; const threadsCollectionPath = `artifacts/${appId}/public/data/threads`; try { await window.Firebase.addDoc(window.Firebase.collection(db, threadsCollectionPath), { title: title, content: content, authorId: user.uid, authorDisplayName: user.displayName || “Anonymous”, createdAt: window.Firebase.serverTimestamp(), replyCount: 0, }); showThreadList(); initialThreadTitle = ”; } catch (error) { console.error(“Error creating thread:”, error); } } async function createPost(content) { if (!db || !user || !selectedThread) return; const postsCollectionPath = `artifacts/${appId}/public/data/threads/${selectedThread.id}/posts`; const threadDocRef = window.Firebase.doc(db, `artifacts/${appId}/public/data/threads`, selectedThread.id); try { await window.Firebase.addDoc(window.Firebase.collection(db, postsCollectionPath), { content: content, authorId: user.uid, authorDisplayName: user.displayName || “Anonymous”, createdAt: window.Firebase.serverTimestamp(), }); const threadDoc = await window.Firebase.getDoc(threadDocRef); const currentReplyCount = threadDoc.data().replyCount || 0; await window.Firebase.setDoc(threadDocRef, { replyCount: currentReplyCount + 1 }, { merge: true }); // Clear the form document.getElementById(‘post-content’).value = ”; } catch (error) { console.error(“Error creating post:”, error); } } // UI Navigation function showThreadList() { mainTitle.textContent = ‘Forum Threads’; newThreadBtn.style.display = ‘flex’; threadList.style.display = ‘block’; threadView.style.display = ‘none’; createThreadForm.style.display = ‘none’; if (initialThreadTitle) { showCreateThreadForm(); } } function showThreadView() { mainTitle.textContent = ‘Discussion’; newThreadBtn.style.display = ‘none’; threadList.style.display = ‘none’; threadView.style.display = ‘block’; createThreadForm.style.display = ‘none’; emptyState.style.display = ‘none’; // Render thread details threadDetails.innerHTML = `${escapeHtml(selectedThread.title)}
By ${escapeHtml(selectedThread.authorDisplayName)}
${formatTimestamp(selectedThread.createdAt)}
${escapeHtml(selectedThread.content)}
`; } function showCreateThreadForm() { mainTitle.textContent = ‘Forum Threads’; newThreadBtn.style.display = ‘none’; threadList.style.display = ‘none’; threadView.style.display = ‘none’; createThreadForm.style.display = ‘block’; emptyState.style.display = ‘none’; // Set initial title if provided document.getElementById(‘thread-title’).value = initialThreadTitle; } // Event Listeners authToggle.addEventListener(‘click’, toggleAuthMode); authForm.addEventListener(‘submit’, handleAuth); signOutBtn.addEventListener(‘click’, handleSignOut); newThreadBtn.addEventListener(‘click’, showCreateThreadForm); backToThreads.addEventListener(‘click’, () => { selectedThread = null; posts = []; showThreadList(); }); cancelThread.addEventListener(‘click’, () => { showThreadList(); initialThreadTitle = ”; }); threadForm.addEventListener(‘submit’, (e) => { e.preventDefault(); const title = document.getElementById(‘thread-title’).value.trim(); const content = document.getElementById(‘thread-content’).value.trim(); if (title && content) { createThread(title, content); document.getElementById(‘thread-title’).value = ”; document.getElementById(‘thread-content’).value = ”; } }); postForm.addEventListener(‘submit’, (e) => { e.preventDefault(); const content = document.getElementById(‘post-content’).value.trim(); if (content) { createPost(content); } }); // Utility Functions function formatTimestamp(timestamp) { if (!timestamp) return ‘Just now’; const date = timestamp.toDate(); const now = new Date(); const secondsPast = (now.getTime() – date.getTime()) / 1000; if (secondsPast < 60) return `${Math.round(secondsPast)}s ago`; if (secondsPast < 3600) return `${Math.round(secondsPast / 60)}m ago`; if (secondsPast <= 86400) return `${Math.round(secondsPast / 3600)}h ago`; const day = date.getDate(); const month = date.toLocaleString('default', { month: 'short' }); const year = date.getFullYear(); if(year === now.getFullYear()) return `${month} ${day}`; return `${month} ${day}, ${year}`; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Initialize the app initializeApp();