diff --git a/index.html b/index.html index 587f92c..1a9d1ad 100644 --- a/index.html +++ b/index.html @@ -1294,7 +1294,6 @@ async function handleVote(proposalId, voteType) { // TEMPORARILY DISABLED AEC VERIFICATION // Simply submit the vote directly regardless of vote type - submitVote(proposalId, voteType, voteType === 'upvote'); /* Original code commented out // Check if user needs to provide AEC details for petition signing @@ -1313,45 +1312,202 @@ async function handleVote(proposalId, voteType) { submitVote(proposalId, voteType, false); } */ -} - - // Submit vote with petition data if applicable - async function submitVote(proposalId, voteType, isPetition = false, petitionDetails = null) { - try { - const voteData = { - proposalId: proposalId, - userId: currentUser.id, - voteType: voteType - }; - - // Add petition data if this is a petition signature - if (isPetition && petitionDetails) { - voteData.isPetition = true; - voteData.petitionDetails = petitionDetails; - } - - const response = await fetch(`${API_BASE_URL}/votes`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(voteData), - mode: 'cors', - credentials: 'same-origin' - }); - - if (!response.ok) { - throw new Error('Failed to submit vote'); - } - - // Refresh proposals to show updated votes - fetchProposals(); - } catch (error) { - console.error('Error voting:', error); - showErrorMessage('Hmm, your vote didn\'t count. That\'s a bit suspicious, isn\'t it?'); + + // Find the proposal card + const proposalCards = document.querySelectorAll(`.proposal-card`); + let proposalCard = null; + + for (const card of proposalCards) { + if (card.querySelector(`[data-proposal-id="${proposalId}"]`)) { + proposalCard = card; + break; + } + } + + if (!proposalCard) return; + + // Get the buttons and count elements + const upvoteButton = proposalCard.querySelector(`[data-vote-type="upvote"]`); + const downvoteButton = proposalCard.querySelector(`[data-vote-type="downvote"]`); + const upvoteCount = upvoteButton.querySelector('span:nth-child(2)'); + const downvoteCount = downvoteButton.querySelector('span:nth-child(2)'); + const netVotesElement = proposalCard.querySelector('.net-votes'); + + // 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) { + // 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 net votes + const newUpvotes = parseInt(upvoteCount.textContent); + const newDownvotes = parseInt(downvoteCount.textContent); + const netVotes = newUpvotes - newDownvotes; + + netVotesElement.textContent = netVotes > 0 ? `+${netVotes}` : netVotes; + netVotesElement.className = `net-votes ${netVotes > 0 ? 'positive' : netVotes < 0 ? 'negative' : ''}`; + + // Also update the modal if it's open and showing this proposal + updateModalVoteUI(proposalId, voteType, wasUpvoted, wasDownvoted); + + // Submit vote to server in the background + try { + await submitVoteToServer(proposalId, voteType, voteType === 'upvote'); + } catch (error) { + console.error('Error voting:', error); + showToastMessage('Hmm, your vote didn\'t count. That\'s a bit suspicious, isn\'t it?', '#ff0099'); + // Revert UI changes + if (voteType === 'upvote') { + upvoteButton.classList.toggle('upvoted', wasUpvoted); + upvoteCount.textContent = upvotes; + } else if (voteType === 'downvote') { + downvoteButton.classList.toggle('downvoted', wasDownvoted); + downvoteCount.textContent = downvotes; + } + + // Restore net votes + const originalNetVotes = upvotes - downvotes; + netVotesElement.textContent = originalNetVotes > 0 ? `+${originalNetVotes}` : originalNetVotes; + netVotesElement.className = `net-votes ${originalNetVotes > 0 ? 'positive' : originalNetVotes < 0 ? 'negative' : ''}`; + } +} + +// New function to update vote UI in the modal (if open) +function updateModalVoteUI(proposalId, voteType, wasUpvoted, wasDownvoted) { + const modalUpvoteButton = document.getElementById('modal-upvote'); + const modalDownvoteButton = document.getElementById('modal-downvote'); + + if (!modalUpvoteButton || !modalDownvoteButton) return; + + // Make sure we're updating the right proposal + if (modalUpvoteButton.getAttribute('data-proposal-id') !== proposalId) return; + + const modalUpvoteCount = modalUpvoteButton.querySelector('span:nth-child(2)'); + const modalDownvoteCount = modalDownvoteButton.querySelector('span:nth-child(2)'); + const modalNetVotesElement = document.querySelector('.modal-body .net-votes'); + + if (!modalUpvoteCount || !modalDownvoteCount || !modalNetVotesElement) return; + + let upvotes = parseInt(modalUpvoteCount.textContent); + let downvotes = parseInt(modalDownvoteCount.textContent); + + // Update UI in the modal + if (voteType === 'upvote') { + if (wasUpvoted) { + modalUpvoteButton.classList.remove('upvoted'); + modalUpvoteCount.textContent = upvotes - 1; + } else { + modalUpvoteButton.classList.add('upvoted'); + modalUpvoteCount.textContent = upvotes + 1; + + if (wasDownvoted) { + modalDownvoteButton.classList.remove('downvoted'); + modalDownvoteCount.textContent = downvotes - 1; + } + } + } else if (voteType === 'downvote') { + if (wasDownvoted) { + modalDownvoteButton.classList.remove('downvoted'); + modalDownvoteCount.textContent = downvotes - 1; + } else { + modalDownvoteButton.classList.add('downvoted'); + modalDownvoteCount.textContent = downvotes + 1; + + if (wasUpvoted) { + modalUpvoteButton.classList.remove('upvoted'); + modalUpvoteCount.textContent = upvotes - 1; + } + } + } + + // Update net votes in modal + const newUpvotes = parseInt(modalUpvoteCount.textContent); + const newDownvotes = parseInt(modalDownvoteCount.textContent); + const netVotes = newUpvotes - newDownvotes; + + modalNetVotesElement.textContent = netVotes > 0 ? `+${netVotes}` : netVotes; + modalNetVotesElement.className = `net-votes ${netVotes > 0 ? 'positive' : netVotes < 0 ? 'negative' : ''}`; +} + +// New function to submit vote to server +async function submitVoteToServer(proposalId, voteType, isPetition = false, petitionDetails = null) { + const voteData = { + proposalId: proposalId, + userId: currentUser.id, + voteType: voteType + }; + + // Add petition data if this is a petition signature + if (isPetition && petitionDetails) { + voteData.isPetition = true; + voteData.petitionDetails = petitionDetails; + } + + const response = await fetch(`${API_BASE_URL}/votes`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(voteData), + mode: 'cors', + credentials: 'same-origin' + }); + + if (!response.ok) { + throw new Error('Failed to submit vote'); + } + + return await response.json(); +} + +// Replace the old submitVote function +async function submitVote(proposalId, voteType, isPetition = false, petitionDetails = null) { + try { + return await submitVoteToServer(proposalId, voteType, isPetition, petitionDetails); + } catch (error) { + console.error('Error voting:', error); + showToastMessage('Hmm, your vote didn\'t count. That\'s a bit suspicious, isn\'t it?', '#ff0099'); + throw error; + } +} // Format date function formatDate(timestamp) { const now = new Date();