diff --git a/index.html b/index.html
index 0fd8982..fc28951 100644
--- a/index.html
+++ b/index.html
@@ -2423,12 +2423,41 @@ function toggleComments(proposalId, button) {
}
// Function to load comments for a proposal (feed cards and modals)
-function loadComments(proposalId) {
+async function loadComments(proposalId) {
+ // Show loading state immediately in all matching containers (feed + modal)
document.querySelectorAll(`.comments-container[data-proposal-id="${proposalId}"]`).forEach(container => {
const content = container.querySelector('.comments-content');
- if (content) content.innerHTML = `
`;
+ if (content) content.innerHTML = ``;
});
- queueCommentLoad(proposalId);
+
+ const userId = currentUser?.id || 'anonymous';
+ try {
+ const res = await fetch(
+ `${API_BASE_URL}/comments?proposalId=${proposalId}&userId=${userId}`,
+ { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, mode: 'cors', credentials: 'same-origin' }
+ );
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ const data = await res.json();
+
+ document.querySelectorAll(`.comments-container[data-proposal-id="${proposalId}"]`).forEach(container => {
+ const content = container.querySelector('.comments-content');
+ if (content) renderComments(content, data);
+ });
+
+ // Update toggle arrow + count, preserving open/closed arrow direction
+ const toggle = document.getElementById(`comments-toggle-${proposalId}`);
+ if (toggle) {
+ const arrow = toggle.querySelector('.comments-toggle-icon')?.textContent || '▼';
+ const count = Array.isArray(data) ? data.length : 0;
+ toggle.innerHTML = ` ${count} ${count === 1 ? 'comment' : 'comments'}`;
+ }
+ } catch (err) {
+ console.error('Error loading comments:', err);
+ document.querySelectorAll(`.comments-container[data-proposal-id="${proposalId}"]`).forEach(container => {
+ const content = container.querySelector('.comments-content');
+ if (content) content.innerHTML = ``;
+ });
+ }
}
// Function to render comments
@@ -2489,138 +2518,57 @@ function formatCommentText(text) {
return withLinks;
}
-// Function to submit a new comment
async function submitComment(proposalId, commentText) {
- // If commentText is an element, extract the value (backward compatibility)
- if (typeof commentText !== 'string') {
- const form = commentText;
- const commentInput = form.querySelector('.comment-input');
- commentText = commentInput.value.trim();
-
- // Disable form while submitting
- commentInput.disabled = true;
- const submitButton = form.querySelector('.comment-submit');
- submitButton.disabled = true;
-
- // Enable form when done
- const enableForm = () => {
- commentInput.disabled = false;
- submitButton.disabled = false;
- };
-
- // Return early if empty
- if (!commentText) {
- enableForm();
- return;
- }
-
- // Clear input optimistically
- commentInput.value = '';
- }
-
- // Ensure we have text to submit
- if (!commentText || !commentText.trim()) {
- console.error('Empty comment text');
- return;
- }
-
- console.log(`Submitting comment for proposal ${proposalId}: ${commentText.substring(0, 20)}...`);
-
+ if (!commentText || !commentText.trim()) return;
+ if (!currentUser || !currentUser.id) await loadUserData();
+
+ // Optimistically render the comment immediately so it appears instant
+ const optimisticComment = {
+ id: 'optimistic_' + Date.now(),
+ user_name: currentUser.name || 'You',
+ comment_text: commentText,
+ timestamp: Date.now(),
+ upvotes: 0,
+ downvotes: 0,
+ userVote: null
+ };
+ document.querySelectorAll(`.comments-container[data-proposal-id="${proposalId}"]`).forEach(container => {
+ const content = container.querySelector('.comments-content');
+ if (!content) return;
+ const noComments = content.querySelector('.no-comments');
+ if (noComments) content.innerHTML = '';
+ const div = document.createElement('div');
+ div.className = 'comment-item optimistic';
+ div.style.opacity = '0.6';
+ div.innerHTML = `
+
+ `;
+ content.prepend(div);
+ });
+
try {
- console.log("Making POST request to comments endpoint");
-
- // Log the full API URL and request details
- const requestUrl = `${API_BASE_URL}/comments`;
- const requestData = {
- proposalId: proposalId,
- userId: currentUser?.id || 'anonymous',
- commentText: commentText
- };
-
- console.log("Request URL:", requestUrl);
- console.log("Request data:", requestData);
-
- // Ensure we have a user
- if (!currentUser || !currentUser.id) {
- console.error("No current user - creating anonymous");
- showToastMessage('Please wait, initializing user...', '#ff0099');
- await loadUserData(); // Reinitialize user
- }
-
- // Create a controller to manage request timeouts
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
-
- const response = await fetch(requestUrl, {
+ const res = await fetch(`${API_BASE_URL}/comments`, {
method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Accept': 'application/json'
- },
- body: JSON.stringify({
- proposalId: proposalId,
- userId: currentUser.id,
- commentText: commentText
- }),
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
+ body: JSON.stringify({ proposalId, userId: currentUser.id, commentText }),
mode: 'cors',
- credentials: 'same-origin',
- signal: controller.signal
+ credentials: 'same-origin'
});
-
- clearTimeout(timeoutId);
-
- console.log("Comment submission response status:", response.status);
- console.log("Response headers:", Object.fromEntries(response.headers.entries()));
-
- // Process response based on status code
- if (response.status === 200 || response.status === 201) {
- // Successfully created comment
- try {
- const data = await response.json();
- console.log("Comment created successfully:", data);
-
- loadComments(proposalId);
- updateCommentCount(proposalId);
- showToastMessage('Comment posted!', '#11cc77');
- return data;
- } catch (jsonError) {
- console.warn("Could not parse success response as JSON:", jsonError);
- // Still consider this a success if status code was good
- loadComments(proposalId);
- updateCommentCount(proposalId);
- showToastMessage('Comment posted!', '#11cc77');
- return { success: true };
- }
- } else {
- // Server returned an error
- let errorMessage;
- try {
- const errorData = await response.json();
- console.error("Error response data:", errorData);
- errorMessage = errorData.error || 'Unknown server error';
- } catch (e) {
- try {
- errorMessage = await response.text() || 'Failed to post comment';
- console.error("Error response text:", errorMessage);
- } catch (textError) {
- errorMessage = `HTTP error ${response.status}`;
- console.error("Could not read error response:", textError);
- }
- }
-
- console.error('Error posting comment:', errorMessage);
- showToastMessage('Failed to post comment', '#ff0099');
- throw new Error(errorMessage);
+ if (!res.ok) {
+ const err = await res.json().catch(() => ({}));
+ throw new Error(err.error || `HTTP ${res.status}`);
}
- } catch (error) {
- if (error.name === 'AbortError') {
- console.error('Comment request timed out');
- showToastMessage('Comment request timed out', '#ff0099');
- } else {
- console.error('Network or processing error:', error);
- showToastMessage('Failed to post comment', '#ff0099');
- }
- throw error;
+ // Replace optimistic with real data from server
+ loadComments(proposalId);
+ showToastMessage('Comment posted!', '#11cc77');
+ } catch (err) {
+ // Remove the optimistic comment on failure
+ document.querySelectorAll('.comment-item.optimistic').forEach(el => el.remove());
+ showToastMessage('Failed to post comment', '#ff0099');
+ throw err;
}
}
@@ -2890,80 +2838,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
- // Add these functions outside your renderProposals function
-
-function updateCommentCount(proposalId) {
- const commentsToggle = document.getElementById(`comments-toggle-${proposalId}`);
- if (!commentsToggle) return;
-
- safeFetch(`${API_BASE_URL}/comments?proposalId=${proposalId}`)
- .then(comments => {
- const commentCount = Array.isArray(comments) ? comments.length : 0;
- const commentText = commentCount === 1 ? 'comment' : 'comments';
- commentsToggle.innerHTML = ` ${commentCount} ${commentText}`;
- })
- .catch(() => {
- commentsToggle.innerHTML = ` 0 comments`;
- });
-}
-
-
-// Add these new functions for comment management
-const commentRequestQueue = new Set();
-let commentRequestTimeout = null;
-const MAX_BATCH_SIZE = 10;
-const COMMENT_REQUEST_DELAY = 1000; // 1 second delay between batches
-
-async function batchLoadComments() {
- commentRequestTimeout = null; // reset so the next queueCommentLoad can schedule again
- if (commentRequestQueue.size === 0) return;
-
- const batch = Array.from(commentRequestQueue).slice(0, MAX_BATCH_SIZE);
- commentRequestQueue.clear();
-
- const userId = currentUser?.id || 'anonymous';
-
- await Promise.all(batch.map(async proposalId => {
- try {
- const response = await fetch(
- `${API_BASE_URL}/comments?proposalId=${proposalId}&userId=${userId}`,
- { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, mode: 'cors', credentials: 'same-origin' }
- );
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
- const data = await response.json();
-
- // Update ALL containers for this proposal (covers both feed card and modal)
- document.querySelectorAll(`.comments-container[data-proposal-id="${proposalId}"]`).forEach(container => {
- const content = container.querySelector('.comments-content');
- if (content) renderComments(content, data);
- });
-
- // Update toggle count on feed card
- const toggle = document.getElementById(`comments-toggle-${proposalId}`);
- if (toggle) {
- const count = Array.isArray(data) ? data.length : 0;
- toggle.innerHTML = ` ${count} ${count === 1 ? 'comment' : 'comments'}`;
- }
- } catch (err) {
- console.error(`Failed to load comments for ${proposalId}:`, err);
- document.querySelectorAll(`.comments-container[data-proposal-id="${proposalId}"]`).forEach(container => {
- const content = container.querySelector('.comments-content');
- if (content) content.innerHTML = ``;
- });
- }
- }));
-
- if (commentRequestQueue.size > 0) {
- commentRequestTimeout = setTimeout(batchLoadComments, COMMENT_REQUEST_DELAY);
- }
-}
-
-function queueCommentLoad(proposalId) {
- commentRequestQueue.add(proposalId);
- if (!commentRequestTimeout) {
- commentRequestTimeout = setTimeout(batchLoadComments, COMMENT_REQUEST_DELAY);
- }
-}
// ─── STATS BAR ────────────────────────────────────────────────────────────
async function fetchStats() {