x
This commit is contained in:
parent
e99e31d85d
commit
695cc9df6c
2 changed files with 277 additions and 213 deletions
367
index.html
367
index.html
|
|
@ -1674,63 +1674,77 @@ function renderModalComments(container, comments) {
|
|||
container.innerHTML = commentsHtml;
|
||||
}
|
||||
|
||||
// 4. Add function to handle comment votes
|
||||
// Add function to handle comment votes
|
||||
async function handleCommentVote(commentId, voteType) {
|
||||
if (!commentId || !voteType) return;
|
||||
|
||||
// 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;
|
||||
// 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"]`);
|
||||
|
||||
// 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) {
|
||||
// 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 (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) {
|
||||
// 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;
|
||||
}
|
||||
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
|
||||
if (wasUpvoted) {
|
||||
newUpvotedClass = false;
|
||||
newUpvotes = 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
|
||||
|
|
@ -1751,21 +1765,25 @@ async function handleCommentVote(commentId, voteType) {
|
|||
throw new Error('Failed to submit comment vote');
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// Server response successful
|
||||
} catch (error) {
|
||||
console.error('Error voting on comment:', error);
|
||||
showToastMessage('Hmm, your comment vote didn\'t count. Please try again.', '#ff0099');
|
||||
showToastMessage('Your vote didn\'t count. Please try again.', '#ff0099');
|
||||
|
||||
// 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);
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getShareableImageUrl(proposal) {
|
||||
// Base Cloudinary URL
|
||||
const cloudinaryUrl = "https://res.cloudinary.com/dh8apmjya/image/fetch";
|
||||
|
|
@ -1826,31 +1844,66 @@ function toggleComments(proposalId, button) {
|
|||
}
|
||||
|
||||
// Function to load comments for a proposal
|
||||
async function loadComments(proposalId) {
|
||||
const commentsContainer = document.querySelector(`.comments-container[data-proposal-id="${proposalId}"]`);
|
||||
const commentsContent = commentsContainer.querySelector('.comments-content');
|
||||
function loadComments(proposalId) {
|
||||
const commentsList = document.getElementById(`comments-list-${proposalId}`);
|
||||
if (!commentsList) return;
|
||||
|
||||
// Show loading state
|
||||
commentsContent.innerHTML = `<div class="comments-loading">Loading comments...</div>`;
|
||||
commentsList.innerHTML = '<div class="comments-loading">Loading comments...</div>';
|
||||
|
||||
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 = `
|
||||
<div class="no-comments">
|
||||
Failed to load comments. Please try again.
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
fetch(`${API_BASE_URL}/comments?proposalId=${proposalId}&userId=${currentUser.id}`)
|
||||
.then(response => response.json())
|
||||
.then(comments => {
|
||||
if (comments.length === 0) {
|
||||
commentsList.innerHTML = '<div class="no-comments">No comments yet. Be the first to comment!</div>';
|
||||
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 = `
|
||||
<div class="comment-header">
|
||||
<span class="comment-author">${comment.user_name}</span>
|
||||
<span class="comment-time">${formatDate(comment.timestamp)}</span>
|
||||
</div>
|
||||
<div class="comment-body">${comment.comment_text}</div>
|
||||
<div class="comment-vote-buttons">
|
||||
<button class="comment-vote-button ${upvotedClass}" data-comment-id="${comment.id}" data-vote-type="upvote">
|
||||
<span class="comment-vote-icon">▲</span>
|
||||
<span class="comment-vote-count">${comment.upvotes || 0}</span>
|
||||
</button>
|
||||
<button class="comment-vote-button ${downvotedClass}" data-comment-id="${comment.id}" data-vote-type="downvote">
|
||||
<span class="comment-vote-icon">▼</span>
|
||||
<span class="comment-vote-count">${comment.downvotes || 0}</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 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 = '<div class="comments-error">Failed to load comments.</div>';
|
||||
console.error('Error loading comments:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to render comments
|
||||
|
|
@ -1912,70 +1965,34 @@ function formatCommentText(text) {
|
|||
}
|
||||
|
||||
// Function to submit a new comment
|
||||
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
|
||||
})
|
||||
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');
|
||||
});
|
||||
|
||||
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 = `
|
||||
<div class="comment-item" style="animation: fadeIn 0.5s;">
|
||||
<div class="comment-header">
|
||||
<div class="comment-author">${newComment.userName}</div>
|
||||
<div class="comment-time">just now</div>
|
||||
</div>
|
||||
<div class="comment-text">${formatCommentText(newComment.commentText)}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 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
|
||||
|
|
@ -1984,6 +2001,7 @@ 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');
|
||||
|
|
@ -1991,45 +2009,38 @@ 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 = '<span class="comments-toggle-icon">▶</span> 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 commentsForm = document.createElement('form');
|
||||
const commentsList = document.createElement('div');
|
||||
commentsList.className = 'comments-list';
|
||||
commentsList.id = `comments-list-${proposalId}`;
|
||||
|
||||
const commentsForm = document.createElement('div');
|
||||
commentsForm.className = 'comment-form';
|
||||
commentsForm.onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
submitComment(proposalId, commentsForm);
|
||||
};
|
||||
|
||||
const commentInput = document.createElement('textarea');
|
||||
commentInput.className = 'comment-input';
|
||||
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';
|
||||
});
|
||||
commentInput.id = `comment-input-${proposalId}`;
|
||||
commentInput.placeholder = 'Add your comment...';
|
||||
|
||||
const commentSubmit = document.createElement('button');
|
||||
commentSubmit.className = 'comment-submit';
|
||||
commentSubmit.type = 'submit';
|
||||
commentSubmit.textContent = 'Defy';
|
||||
commentSubmit.disabled = true;
|
||||
commentSubmit.setAttribute('data-proposal-id', proposalId);
|
||||
commentSubmit.textContent = 'Submit';
|
||||
|
||||
commentsForm.appendChild(commentInput);
|
||||
commentsForm.appendChild(commentSubmit);
|
||||
|
||||
const commentsContent = document.createElement('div');
|
||||
commentsContent.className = 'comments-content';
|
||||
|
||||
commentsContainer.appendChild(commentsList);
|
||||
commentsContainer.appendChild(commentsForm);
|
||||
commentsContainer.appendChild(commentsContent);
|
||||
|
||||
commentsSection.appendChild(commentsToggle);
|
||||
commentsSection.appendChild(commentsContainer);
|
||||
|
|
@ -2037,10 +2048,30 @@ function addCommentsToProposal(proposalCard, proposalId) {
|
|||
proposalCard.appendChild(commentsSection);
|
||||
|
||||
// Add event listener to toggle button
|
||||
commentsToggle.addEventListener('click', () => {
|
||||
toggleComments(proposalId, commentsToggle);
|
||||
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 = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Music player functionality
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Set up variables
|
||||
|
|
|
|||
123
styles.css
123
styles.css
|
|
@ -1143,78 +1143,111 @@ a {
|
|||
color: unset;
|
||||
}
|
||||
|
||||
/* Comment voting styles */
|
||||
.comment-vote-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
/* Styles for comments in the main feed */
|
||||
.comments-list {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.comment-vote-button {
|
||||
.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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.comments-list .comment-vote-button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
padding: 3px 6px;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
margin-right: 10px;
|
||||
margin-right: 8px;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.comment-vote-button:hover {
|
||||
.comments-list .comment-vote-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.comment-vote-button.upvoted {
|
||||
.comments-list .comment-vote-button.upvoted {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.comment-vote-button.downvoted {
|
||||
.comments-list .comment-vote-button.downvoted {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.comment-vote-icon {
|
||||
font-size: 10px;
|
||||
margin-right: 4px;
|
||||
.comments-list .comment-vote-icon {
|
||||
font-size: 9px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.comment-vote-count {
|
||||
font-size: 12px;
|
||||
min-width: 15px;
|
||||
text-align: center;
|
||||
.comments-list .comment-vote-count {
|
||||
font-size: 11px;
|
||||
min-width: 12px;
|
||||
}
|
||||
|
||||
/* Adjust existing comment styles to accommodate voting */
|
||||
.comment-item {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
animation: fadeIn 0.3s ease;
|
||||
/* 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);
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
margin: 6px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
/* Ensure proposal card buttons don't conflict with comment vote buttons */
|
||||
.proposal-card .vote-button {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.comment-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
/* Stop propagation for clicks inside comments */
|
||||
.comments-container * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* Mobile optimizations */
|
||||
@media (max-width: 768px) {
|
||||
.comment-vote-buttons {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.comment-vote-button {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.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); }
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue