From 880647b9fd9fc89b7d2aab46f9527151a86d9db2 Mon Sep 17 00:00:00 2001 From: Omar Najjar Date: Wed, 12 Mar 2025 02:31:19 +1100 Subject: [PATCH] Revert "x" This reverts commit 695cc9df6c34106e1fe87916b10277b9f3b63361. --- index.html | 367 ++++++++++++++++++++++++----------------------------- styles.css | 119 +++++++---------- 2 files changed, 211 insertions(+), 275 deletions(-) diff --git a/index.html b/index.html index d4773ee..8d9ea76 100644 --- a/index.html +++ b/index.html @@ -1674,77 +1674,63 @@ function renderModalComments(container, comments) { container.innerHTML = commentsHtml; } -// Add function to handle comment votes +// 4. Add function to handle comment votes async function handleCommentVote(commentId, voteType) { if (!commentId || !voteType) return; - // Find all instances of this comment vote buttons (may be in modal and in main feed) - const upvoteButtons = document.querySelectorAll(`.comment-vote-button[data-comment-id="${commentId}"][data-vote-type="upvote"]`); - const downvoteButtons = document.querySelectorAll(`.comment-vote-button[data-comment-id="${commentId}"][data-vote-type="downvote"]`); + // Find all instances of this comment (may be in modal and in main feed) + const commentItems = document.querySelectorAll(`.comment-item[data-comment-id="${commentId}"]`); + if (commentItems.length === 0) return; - if (upvoteButtons.length === 0 || downvoteButtons.length === 0) return; - - // Track the state for all instances - let wasUpvoted = false; - let wasDownvoted = false; - let upvotes = 0; - let downvotes = 0; - - // Get current state from the first button set - wasUpvoted = upvoteButtons[0].classList.contains('upvoted'); - wasDownvoted = downvoteButtons[0].classList.contains('downvoted'); - upvotes = parseInt(upvoteButtons[0].querySelector('.comment-vote-count').textContent); - downvotes = parseInt(downvoteButtons[0].querySelector('.comment-vote-count').textContent); - - // Calculate new states - let newUpvotes = upvotes; - let newDownvotes = downvotes; - let newUpvotedClass = wasUpvoted; - let newDownvotedClass = wasDownvoted; - - if (voteType === 'upvote') { - if (wasUpvoted) { - // Toggle off upvote - newUpvotedClass = false; - newUpvotes = upvotes - 1; - } else { - // Add upvote - newUpvotedClass = true; - newUpvotes = upvotes + 1; - - // If was downvoted, remove downvote - if (wasDownvoted) { - newDownvotedClass = false; - newDownvotes = downvotes - 1; - } - } - } else if (voteType === 'downvote') { - if (wasDownvoted) { - // Toggle off downvote - newDownvotedClass = false; - newDownvotes = downvotes - 1; - } else { - // Add downvote - newDownvotedClass = true; - newDownvotes = downvotes + 1; - - // If was upvoted, remove upvote + // Get the buttons and counts for all instances + commentItems.forEach(commentItem => { + const upvoteButton = commentItem.querySelector(`.comment-vote-button[data-vote-type="upvote"]`); + const downvoteButton = commentItem.querySelector(`.comment-vote-button[data-vote-type="downvote"]`); + const upvoteCount = upvoteButton.querySelector('.comment-vote-count'); + const downvoteCount = downvoteButton.querySelector('.comment-vote-count'); + + // Get current state + const wasUpvoted = upvoteButton.classList.contains('upvoted'); + const wasDownvoted = downvoteButton.classList.contains('downvoted'); + + // Get current counts + let upvotes = parseInt(upvoteCount.textContent); + let downvotes = parseInt(downvoteCount.textContent); + + // Update UI optimistically + if (voteType === 'upvote') { if (wasUpvoted) { - newUpvotedClass = false; - newUpvotes = upvotes - 1; + // Toggle off upvote + upvoteButton.classList.remove('upvoted'); + upvoteCount.textContent = upvotes - 1; + } else { + // Add upvote + upvoteButton.classList.add('upvoted'); + upvoteCount.textContent = upvotes + 1; + + // If was downvoted, remove downvote + if (wasDownvoted) { + downvoteButton.classList.remove('downvoted'); + downvoteCount.textContent = downvotes - 1; + } + } + } else if (voteType === 'downvote') { + if (wasDownvoted) { + // Toggle off downvote + downvoteButton.classList.remove('downvoted'); + downvoteCount.textContent = downvotes - 1; + } else { + // Add downvote + downvoteButton.classList.add('downvoted'); + downvoteCount.textContent = downvotes + 1; + + // If was upvoted, remove upvote + if (wasUpvoted) { + upvoteButton.classList.remove('upvoted'); + upvoteCount.textContent = upvotes - 1; + } } } - } - - // Update all instances - upvoteButtons.forEach(button => { - button.classList.toggle('upvoted', newUpvotedClass); - button.querySelector('.comment-vote-count').textContent = newUpvotes; - }); - - downvoteButtons.forEach(button => { - button.classList.toggle('downvoted', newDownvotedClass); - button.querySelector('.comment-vote-count').textContent = newDownvotes; }); // Submit vote to server @@ -1765,25 +1751,21 @@ async function handleCommentVote(commentId, voteType) { throw new Error('Failed to submit comment vote'); } - // Server response successful + const result = await response.json(); + + // If the server response indicates we should update the UI differently, we could handle that here + // But generally the optimistic UI update above should match the server result + } catch (error) { console.error('Error voting on comment:', error); - showToastMessage('Your vote didn\'t count. Please try again.', '#ff0099'); + showToastMessage('Hmm, your comment vote didn\'t count. Please try again.', '#ff0099'); - // Revert UI changes - upvoteButtons.forEach(button => { - button.classList.toggle('upvoted', wasUpvoted); - button.querySelector('.comment-vote-count').textContent = upvotes; - }); - - downvoteButtons.forEach(button => { - button.classList.toggle('downvoted', wasDownvoted); - button.querySelector('.comment-vote-count').textContent = downvotes; - }); + // Revert UI changes by reloading the comments + // This is a simple approach; you could be more sophisticated by reverting just the affected elements + await loadComments(currentProposalId); } } - function getShareableImageUrl(proposal) { // Base Cloudinary URL const cloudinaryUrl = "https://res.cloudinary.com/dh8apmjya/image/fetch"; @@ -1844,66 +1826,31 @@ function toggleComments(proposalId, button) { } // Function to load comments for a proposal -function loadComments(proposalId) { - const commentsList = document.getElementById(`comments-list-${proposalId}`); - if (!commentsList) return; +async function loadComments(proposalId) { + const commentsContainer = document.querySelector(`.comments-container[data-proposal-id="${proposalId}"]`); + const commentsContent = commentsContainer.querySelector('.comments-content'); - commentsList.innerHTML = '
Loading comments...
'; + // Show loading state + commentsContent.innerHTML = `
Loading comments...
`; - fetch(`${API_BASE_URL}/comments?proposalId=${proposalId}&userId=${currentUser.id}`) - .then(response => response.json()) - .then(comments => { - if (comments.length === 0) { - commentsList.innerHTML = '
No comments yet. Be the first to comment!
'; - return; - } - - commentsList.innerHTML = ''; - comments.forEach(comment => { - const upvotedClass = comment.userVote === 'upvote' ? 'upvoted' : ''; - const downvotedClass = comment.userVote === 'downvote' ? 'downvoted' : ''; - - const commentElement = document.createElement('div'); - commentElement.className = 'comment'; - commentElement.innerHTML = ` -
- ${comment.user_name} - ${formatDate(comment.timestamp)} -
-
${comment.comment_text}
-
- - -
- `; - - // Add event listeners for the vote buttons - const upvoteButton = commentElement.querySelector(`.comment-vote-button[data-vote-type="upvote"]`); - const downvoteButton = commentElement.querySelector(`.comment-vote-button[data-vote-type="downvote"]`); - - upvoteButton.addEventListener('click', function(e) { - e.stopPropagation(); // Prevent triggering parent elements - handleCommentVote(comment.id, 'upvote'); - }); - - downvoteButton.addEventListener('click', function(e) { - e.stopPropagation(); // Prevent triggering parent elements - handleCommentVote(comment.id, 'downvote'); - }); - - commentsList.appendChild(commentElement); - }); - }) - .catch(error => { - commentsList.innerHTML = '
Failed to load comments.
'; - console.error('Error loading comments:', error); - }); + try { + // Include the current user ID to get their votes + const response = await fetch(`${API_BASE_URL}/comments?proposalId=${proposalId}&userId=${currentUser.id}`); + + if (!response.ok) { + throw new Error('Failed to fetch comments'); + } + + const comments = await response.json(); + renderComments(commentsContent, comments); + } catch (error) { + console.error('Error fetching comments:', error); + commentsContent.innerHTML = ` +
+ Failed to load comments. Please try again. +
+ `; + } } // Function to render comments @@ -1965,34 +1912,70 @@ function formatCommentText(text) { } // Function to submit a new comment -function submitComment(proposalId, commentText) { - fetch(`${API_BASE_URL}/comments`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - proposalId: proposalId, - userId: currentUser.id, - commentText: commentText - }) - }) - .then(response => { - if (!response.ok) { - throw new Error('Failed to post comment'); - } - return response.json(); - }) - .then(data => { - // Reload comments to show the new one with votes - loadComments(proposalId); - updateCommentCount(proposalId); - showToastMessage('Comment posted!', '#11cc77'); - }) - .catch(error => { - console.error('Error posting comment:', error); - showToastMessage('Failed to post comment', '#ff0099'); +async function submitComment(proposalId, form) { + const commentInput = form.querySelector('.comment-input'); + const submitButton = form.querySelector('.comment-submit'); + + const commentText = commentInput.value.trim(); + + if (!commentText) return; + + // Disable form while submitting + commentInput.disabled = true; + submitButton.disabled = true; + + try { + const response = await fetch(`${API_BASE_URL}/comments`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + proposalId: proposalId, + userId: currentUser.id, + commentText: commentText + }) }); + + if (!response.ok) { + throw new Error('Failed to submit comment'); + } + + // Clear input + commentInput.value = ''; + + // Reload comments to show the new one + const commentsContainer = document.querySelector(`.comments-container[data-proposal-id="${proposalId}"]`); + const commentsContent = commentsContainer.querySelector('.comments-content'); + + // Add new comment to the top (optimistic update) + const newComment = await response.json(); + const tempCommentHtml = ` +
+
+
${newComment.userName}
+
just now
+
+
${formatCommentText(newComment.commentText)}
+
+ `; + + // If there are no comments yet, replace the "no comments" message + if (commentsContent.querySelector('.no-comments')) { + commentsContent.innerHTML = tempCommentHtml; + } else { + // Otherwise, prepend to existing comments + commentsContent.insertAdjacentHTML('afterbegin', tempCommentHtml); + } + + } catch (error) { + console.error('Error posting comment:', error); + showToastMessage('Failed to post comment. Please try again.', '#ff0099'); + } finally { + // Re-enable form + commentInput.disabled = false; + submitButton.disabled = false; + } } // Function to check if comment input is not empty @@ -2001,7 +1984,6 @@ function checkCommentInput(input, submitButton) { submitButton.disabled = !hasContent; } - // Addition to renderProposals function - Add comments section to each proposal card function addCommentsToProposal(proposalCard, proposalId) { const commentsSection = document.createElement('div'); @@ -2009,38 +1991,45 @@ function addCommentsToProposal(proposalCard, proposalId) { const commentsToggle = document.createElement('button'); commentsToggle.className = 'comments-toggle'; - commentsToggle.id = `comments-toggle-${proposalId}`; commentsToggle.setAttribute('aria-expanded', 'false'); - commentsToggle.innerHTML = ' 0 comments'; const commentsContainer = document.createElement('div'); commentsContainer.className = 'comments-container'; - commentsContainer.id = `comments-container-${proposalId}`; - commentsContainer.style.display = 'none'; commentsContainer.setAttribute('data-proposal-id', proposalId); - const commentsList = document.createElement('div'); - commentsList.className = 'comments-list'; - commentsList.id = `comments-list-${proposalId}`; - - const commentsForm = document.createElement('div'); + const commentsForm = document.createElement('form'); commentsForm.className = 'comment-form'; + commentsForm.onsubmit = (e) => { + e.preventDefault(); + submitComment(proposalId, commentsForm); + }; const commentInput = document.createElement('textarea'); commentInput.className = 'comment-input'; - commentInput.id = `comment-input-${proposalId}`; - commentInput.placeholder = 'Add your comment...'; + commentInput.placeholder = 'Add a comment...'; + commentInput.rows = 1; + commentInput.oninput = () => checkCommentInput(commentInput, commentSubmit); + + // Auto-resize textarea as user types + commentInput.addEventListener('input', function() { + this.style.height = 'auto'; + this.style.height = (this.scrollHeight) + 'px'; + }); const commentSubmit = document.createElement('button'); commentSubmit.className = 'comment-submit'; - commentSubmit.setAttribute('data-proposal-id', proposalId); - commentSubmit.textContent = 'Submit'; + commentSubmit.type = 'submit'; + commentSubmit.textContent = 'Defy'; + commentSubmit.disabled = true; commentsForm.appendChild(commentInput); commentsForm.appendChild(commentSubmit); - commentsContainer.appendChild(commentsList); + const commentsContent = document.createElement('div'); + commentsContent.className = 'comments-content'; + commentsContainer.appendChild(commentsForm); + commentsContainer.appendChild(commentsContent); commentsSection.appendChild(commentsToggle); commentsSection.appendChild(commentsContainer); @@ -2048,30 +2037,10 @@ function addCommentsToProposal(proposalCard, proposalId) { proposalCard.appendChild(commentsSection); // Add event listener to toggle button - commentsToggle.addEventListener('click', function() { - const isVisible = commentsContainer.style.display !== 'none'; - - if (isVisible) { - commentsContainer.style.display = 'none'; - this.querySelector('.comments-toggle-icon').textContent = '▶'; - } else { - commentsContainer.style.display = 'block'; - this.querySelector('.comments-toggle-icon').textContent = '▼'; - loadComments(proposalId); // This now includes proper vote UI - } - }); - - // Add event listener for comment submission - commentSubmit.addEventListener('click', function() { - const commentText = commentInput.value.trim(); - - if (commentText) { - submitComment(proposalId, commentText); - commentInput.value = ''; - } + commentsToggle.addEventListener('click', () => { + toggleComments(proposalId, commentsToggle); }); } - // Music player functionality document.addEventListener('DOMContentLoaded', () => { // Set up variables diff --git a/styles.css b/styles.css index 860daa1..a9fe940 100644 --- a/styles.css +++ b/styles.css @@ -1143,111 +1143,78 @@ a { color: unset; } -/* Styles for comments in the main feed */ -.comments-list { - margin-top: 10px; - } - - .comments-list .comment { - padding: 10px; - border-bottom: 1px solid var(--border); - font-size: 14px; - } - - .comments-list .comment:last-child { - border-bottom: none; - } - - .comments-list .comment-header { - display: flex; - justify-content: space-between; - font-size: 12px; - margin-bottom: 4px; - } - - .comments-list .comment-author { - font-weight: bold; - color: var(--text); - } - - .comments-list .comment-time { - color: var(--text-light); - } - - .comments-list .comment-body { - margin-bottom: 6px; - line-height: 1.4; - word-break: break-word; - color: var(--text); - } - - .comments-list .comment-vote-buttons { +/* Comment voting styles */ +.comment-vote-buttons { display: flex; align-items: center; - margin-top: 4px; + margin-top: 8px; } - .comments-list .comment-vote-button { + .comment-vote-button { background: none; border: none; cursor: pointer; - font-size: 12px; + font-size: 14px; color: var(--text-light); - padding: 2px 4px; + padding: 3px 6px; border-radius: 3px; - margin-right: 8px; + margin-right: 10px; transition: all 0.2s ease; display: flex; align-items: center; } - .comments-list .comment-vote-button:hover { + .comment-vote-button:hover { background-color: rgba(255, 255, 255, 0.05); } - .comments-list .comment-vote-button.upvoted { + .comment-vote-button.upvoted { color: var(--primary); } - .comments-list .comment-vote-button.downvoted { + .comment-vote-button.downvoted { color: var(--primary); } - .comments-list .comment-vote-icon { - font-size: 9px; - margin-right: 3px; + .comment-vote-icon { + font-size: 10px; + margin-right: 4px; } - .comments-list .comment-vote-count { - font-size: 11px; - min-width: 12px; + .comment-vote-count { + font-size: 12px; + min-width: 15px; + text-align: center; } - /* Make sure comments container has max-height and scrolling */ - .comments-container { - max-height: 400px; - overflow-y: auto; - background-color: var(--background); - border-top: 1px solid var(--border); + /* Adjust existing comment styles to accommodate voting */ + .comment-item { + padding: 12px; + border-bottom: 1px solid var(--border); + animation: fadeIn 0.3s ease; } - /* Ensure proposal card buttons don't conflict with comment vote buttons */ - .proposal-card .vote-button { - z-index: 5; + .comment-text { + margin: 6px 0; + font-size: 14px; + line-height: 1.4; + word-break: break-word; } - /* Stop propagation for clicks inside comments */ - .comments-container * { - pointer-events: auto; + .comment-footer { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 8px; } - - .comment-vote-button.upvoted .comment-vote-icon, -.comment-vote-button.downvoted .comment-vote-icon { - animation: pulse 0.3s ease-in-out; -} - -@keyframes pulse { - 0% { transform: scale(1); } - 50% { transform: scale(1.5); } - 100% { transform: scale(1); } -} \ No newline at end of file + + /* Mobile optimizations */ + @media (max-width: 768px) { + .comment-vote-buttons { + margin-top: 6px; + } + + .comment-vote-button { + padding: 2px 4px; + } + } \ No newline at end of file