diff --git a/index.html b/index.html index 52a770a..336090a 100644 --- a/index.html +++ b/index.html @@ -247,7 +247,7 @@ } .proposal-text { - margin-bottom: 25px; + margin-bottom: 15px; word-wrap: break-word; font-size: 1.1rem; line-height: 1.7; @@ -626,6 +626,120 @@ padding-left: 10px; } + /* Meme Upload Styles */ + .meme-upload { + margin: 15px 0; + padding: 15px; + border: 1px solid var(--border); + background-color: rgba(0, 0, 0, 0.3); + } + + .meme-upload-label { + display: inline-flex; + align-items: center; + gap: 10px; + background-color: var(--darker); + padding: 10px 15px; + border: 1px dashed var(--border); + cursor: pointer; + transition: all 0.3s ease; + width: 100%; + } + + .meme-upload-label:hover { + border-color: var(--primary); + background-color: rgba(255, 0, 153, 0.1); + } + + .meme-upload-input { + display: none; + } + + .meme-upload-icon { + color: var(--primary); + font-size: 1.5rem; + } + + .meme-upload-text { + font-size: 0.9rem; + color: var(--text-muted); + } + + .meme-preview { + width: 100%; + max-height: 300px; + display: none; + flex-direction: column; + justify-content: center; + align-items: center; + overflow: hidden; + position: relative; + border: 1px solid var(--border); + background-color: var(--darker); + margin-top: 15px; + padding: 10px; + } + + .meme-preview img { + max-width: 100%; + max-height: 250px; + object-fit: contain; + margin: 10px 0; + } + + .meme-remove { + position: absolute; + top: 10px; + right: 10px; + background-color: rgba(0, 0, 0, 0.7); + color: var(--light); + border: none; + width: 30px; + height: 30px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + font-size: 1rem; + transition: all 0.3s ease; + } + + .meme-remove:hover { + background-color: var(--primary); + } + + .meme-upload-progress { + margin-top: 10px; + height: 6px; + width: 100%; + background-color: var(--darker); + overflow: hidden; + border-radius: 3px; + } + + .meme-upload-progress-bar { + height: 100%; + background-color: var(--primary); + width: 0; + transition: width 0.3s ease; + } + + .proposal-meme { + margin: 15px 0; + border: 1px solid var(--border); + padding: 10px; + background-color: rgba(0, 0, 0, 0.3); + text-align: center; + } + + .proposal-meme img { + max-width: 100%; + max-height: 300px; + object-fit: contain; + display: inline-block; + } + @media (max-width: 600px) { .container { padding: 20px; @@ -650,117 +764,8 @@ max-height: 90vh; overflow-y: auto; } - - /* Meme Upload Styles */ -.meme-upload { - margin-top: 15px; - margin-bottom: 15px; - display: flex; - flex-direction: column; - gap: 10px; -} - -.meme-upload-label { - display: inline-flex; - align-items: center; - gap: 10px; - background-color: var(--darker); - padding: 10px 15px; - border: 1px dashed var(--border); - cursor: pointer; - transition: all 0.3s ease; -} - -.meme-upload-label:hover { - border-color: var(--primary); - background-color: rgba(255, 0, 153, 0.1); -} - -.meme-upload-input { - display: none; -} - -.meme-upload-icon { - color: var(--primary); - font-size: 1.5rem; -} - -.meme-upload-text { - font-size: 0.9rem; - color: var(--text-muted); -} - -.meme-preview { - width: 100%; - max-height: 300px; - display: flex; - justify-content: center; - align-items: center; - overflow: hidden; - position: relative; - border: 1px solid var(--border); - background-color: var(--darker); -} - -.meme-preview img { - max-width: 100%; - max-height: 300px; - object-fit: contain; -} - -.meme-remove { - position: absolute; - top: 10px; - right: 10px; - background-color: rgba(0, 0, 0, 0.7); - color: var(--light); - border: none; - width: 30px; - height: 30px; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - font-size: 1rem; - transition: all 0.3s ease; -} - -.meme-remove:hover { - background-color: var(--primary); -} - -.meme-upload-progress { - height: 4px; - width: 100%; - background-color: var(--darker); - overflow: hidden; -} - -.meme-upload-progress-bar { - height: 100%; - background-color: var(--primary); - width: 0; - transition: width 0.3s ease; -} - -.proposal-meme { - margin-top: 15px; - margin-bottom: 15px; - border: 1px solid var(--border); - padding: 0; - overflow: hidden; -} - -.proposal-meme img { - width: 100%; - max-height: 300px; - object-fit: contain; - display: block; -} - } - +
@@ -838,8 +843,8 @@ document.addEventListener('DOMContentLoaded', async () => { // Initialize variables for DOM elements initializeApp(); - // Initialize meme upload - initMemeUpload(); + // Initialize meme upload + initMemeUpload(); }); // Initialize application @@ -953,54 +958,10 @@ // Enable/disable post button postButton.disabled = proposalInput.value.trim().length === 0; }); - } - - // Post new proposal - if (postButton) { - postButton.addEventListener('click', async () => { - const text = proposalInput.value.trim(); - - if (text) { - try { - const trending = Math.random() > 0.8; // Randomly mark some as trending for effect - - const response = await fetch(`${API_BASE_URL}/proposals`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - authorId: currentUser.id, - text: text, - trending: trending - }) - }); - - if (!response.ok) { - throw new Error('Failed to create proposal'); - } - - // Clear input - proposalInput.value = ''; - charCounter.textContent = `${CHAR_LIMIT} characters remaining`; - postButton.disabled = true; - - // Show success animation - animatePostSuccess(); - - // Refresh proposals - fetchProposals(); - } catch (error) { - console.error('Error creating proposal:', error); - showErrorMessage('Failed to post your petition. Parliament might be onto us!'); - } - } - }); - } - - // Sort proposals when sort option changes - if (sortSelect) { - sortSelect.addEventListener('change', fetchProposals); + // Sort proposals when sort option changes + if (sortSelect) { + sortSelect.addEventListener('change', fetchProposals); + } } } @@ -1193,16 +1154,14 @@ } const response = await fetch(`${API_BASE_URL}/votes`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(voteData), - mode: 'cors', - credentials: 'same-origin' -}); - - + 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'); @@ -1239,7 +1198,7 @@ } } - // Render proposals + // Render proposals - FIXED VERSION function renderProposals(proposals) { const proposalsContainer = document.getElementById('proposals-container'); if (!proposalsContainer) return; @@ -1277,16 +1236,18 @@ ${proposal.petition_signatures || proposal.upvotes} verified signatures
` : ''; - - const memeHtml = proposal.meme_url ? - `
- Meme for petition -
` : ''; + + // Add meme if available - FIXED: This is now correctly included in the layout + const memeHtml = proposal.meme_url ? + `
+ Meme for petition +
` : ''; proposalCard.innerHTML = ` ${badgeHtml}
${proposal.author_name}
${proposal.text}
+ ${memeHtml} ${petitionInfo}
@@ -1324,246 +1285,246 @@ }); } + // Create meme upload elements function createMemeUpload() { - const uploadContainer = document.createElement('div'); - uploadContainer.className = 'meme-upload'; - - const uploadLabel = document.createElement('label'); - uploadLabel.className = 'meme-upload-label'; - uploadLabel.setAttribute('for', 'meme-upload-input'); - - const uploadIcon = document.createElement('span'); - uploadIcon.className = 'meme-upload-icon'; - uploadIcon.textContent = '🖼️'; - - const uploadText = document.createElement('span'); - uploadText.className = 'meme-upload-text'; - uploadText.textContent = 'Add a meme to your petition (optional)'; - - const uploadInput = document.createElement('input'); - uploadInput.className = 'meme-upload-input'; - uploadInput.id = 'meme-upload-input'; - uploadInput.type = 'file'; - uploadInput.accept = 'image/jpeg, image/png, image/gif, image/webp'; - - uploadLabel.appendChild(uploadIcon); - uploadLabel.appendChild(uploadText); - uploadLabel.appendChild(uploadInput); - - uploadContainer.appendChild(uploadLabel); - - const previewContainer = document.createElement('div'); - previewContainer.className = 'meme-preview'; - previewContainer.style.display = 'none'; - - const previewImage = document.createElement('img'); - previewImage.id = 'meme-preview-image'; - - const removeButton = document.createElement('button'); - removeButton.className = 'meme-remove'; - removeButton.innerHTML = '×'; - removeButton.setAttribute('aria-label', 'Remove meme'); - - const progressContainer = document.createElement('div'); - progressContainer.className = 'meme-upload-progress'; - progressContainer.style.display = 'none'; - - const progressBar = document.createElement('div'); - progressBar.className = 'meme-upload-progress-bar'; - - progressContainer.appendChild(progressBar); - previewContainer.appendChild(previewImage); - previewContainer.appendChild(removeButton); - - uploadContainer.appendChild(previewContainer); - uploadContainer.appendChild(progressContainer); - - return { - container: uploadContainer, - input: uploadInput, - preview: previewContainer, - image: previewImage, - removeBtn: removeButton, - progress: progressContainer, - progressBar: progressBar - }; -} + const uploadContainer = document.createElement('div'); + uploadContainer.className = 'meme-upload'; + + const uploadLabel = document.createElement('label'); + uploadLabel.className = 'meme-upload-label'; + uploadLabel.setAttribute('for', 'meme-upload-input'); + + const uploadIcon = document.createElement('span'); + uploadIcon.className = 'meme-upload-icon'; + uploadIcon.textContent = '🖼️'; + + const uploadText = document.createElement('span'); + uploadText.className = 'meme-upload-text'; + uploadText.textContent = 'Add a meme to your petition (optional)'; + + const uploadInput = document.createElement('input'); + uploadInput.className = 'meme-upload-input'; + uploadInput.id = 'meme-upload-input'; + uploadInput.type = 'file'; + uploadInput.accept = 'image/jpeg, image/png, image/gif, image/webp'; + + uploadLabel.appendChild(uploadIcon); + uploadLabel.appendChild(uploadText); + uploadLabel.appendChild(uploadInput); + + uploadContainer.appendChild(uploadLabel); + + const previewContainer = document.createElement('div'); + previewContainer.className = 'meme-preview'; + previewContainer.style.display = 'none'; + + const previewImage = document.createElement('img'); + previewImage.id = 'meme-preview-image'; + + const removeButton = document.createElement('button'); + removeButton.className = 'meme-remove'; + removeButton.innerHTML = '×'; + removeButton.setAttribute('aria-label', 'Remove meme'); + + const progressContainer = document.createElement('div'); + progressContainer.className = 'meme-upload-progress'; + progressContainer.style.display = 'none'; + + const progressBar = document.createElement('div'); + progressBar.className = 'meme-upload-progress-bar'; + + progressContainer.appendChild(progressBar); + previewContainer.appendChild(previewImage); + previewContainer.appendChild(removeButton); + + uploadContainer.appendChild(previewContainer); + uploadContainer.appendChild(progressContainer); + + return { + container: uploadContainer, + input: uploadInput, + preview: previewContainer, + image: previewImage, + removeBtn: removeButton, + progress: progressContainer, + progressBar: progressBar + }; + } -// Initialize meme upload -function initMemeUpload() { - const newProposalSection = document.querySelector('.new-proposal'); - if (!newProposalSection) return; - - const charCounter = document.getElementById('char-counter'); - if (!charCounter) return; - - // Create meme upload elements - const memeUpload = createMemeUpload(); - - // Insert after character counter, before post button - charCounter.after(memeUpload.container); - - // Variable to store the selected file - let selectedMeme = null; - - // Handle file selection - memeUpload.input.addEventListener('change', (e) => { - const file = e.target.files[0]; - if (!file) return; - - // Validate file type - const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; - if (!validTypes.includes(file.type)) { - showToastMessage('⚠️ Invalid file type. Only JPEG, PNG, GIF, and WebP images are allowed.', '#ff0099'); - return; - } - - // Validate file size (limit to 2MB) - const MAX_SIZE = 2 * 1024 * 1024; // 2MB - if (file.size > MAX_SIZE) { - showToastMessage('⚠️ File too large. Maximum size is 2MB.', '#ff0099'); - return; - } - - // Store the file - selectedMeme = file; - - // Show preview - const reader = new FileReader(); - reader.onload = (event) => { - memeUpload.image.src = event.target.result; - memeUpload.preview.style.display = 'flex'; - }; - reader.readAsDataURL(file); - }); - - // Handle remove button - memeUpload.removeBtn.addEventListener('click', () => { - selectedMeme = null; - memeUpload.input.value = ''; - memeUpload.preview.style.display = 'none'; - memeUpload.image.src = ''; - }); - - // Modify post button click handler to include meme upload - const postButton = document.getElementById('post-button'); - if (postButton) { - // Store the original click event handler - const originalClickHandler = postButton.onclick; - - // Remove the original handler - postButton.onclick = null; - - // Add new handler - postButton.addEventListener('click', async () => { - const proposalInput = document.getElementById('proposal-input'); - const text = proposalInput?.value.trim(); - - if (text) { - try { - const trending = Math.random() > 0.8; // Randomly mark some as trending - - // Create the proposal first - const createResponse = await fetch(`${API_BASE_URL}/proposals`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - authorId: currentUser.id, - text: text, - trending: trending - }) - }); - - if (!createResponse.ok) { - throw new Error('Failed to create proposal'); - } - - const proposalData = await createResponse.json(); - const proposalId = proposalData.id; - - // If there's a meme, upload it - if (selectedMeme) { - // Show progress bar - memeUpload.progress.style.display = 'block'; - memeUpload.progressBar.style.width = '0%'; + // Initialize meme upload + function initMemeUpload() { + const newProposalSection = document.querySelector('.new-proposal'); + if (!newProposalSection) return; - // Create form data for file upload - const formData = new FormData(); - formData.append('proposalId', proposalId); - formData.append('meme', selectedMeme); + const charCounter = document.getElementById('char-counter'); + if (!charCounter) return; - // Track upload progress - const xhr = new XMLHttpRequest(); - xhr.open('POST', `${API_BASE_URL}/memes`); + // Create meme upload elements + const memeUpload = createMemeUpload(); - xhr.upload.addEventListener('progress', (event) => { - if (event.lengthComputable) { - const percent = (event.loaded / event.total) * 100; - memeUpload.progressBar.style.width = percent + '%'; - } + // Insert after character counter, before post button + charCounter.after(memeUpload.container); + + // Variable to store the selected file + let selectedMeme = null; + + // Handle file selection + memeUpload.input.addEventListener('change', (e) => { + const file = e.target.files[0]; + if (!file) return; + + // Validate file type + const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; + if (!validTypes.includes(file.type)) { + showToastMessage('⚠️ Invalid file type. Only JPEG, PNG, GIF, and WebP images are allowed.', '#ff0099'); + return; + } + + // Validate file size (limit to 2MB) + const MAX_SIZE = 2 * 1024 * 1024; // 2MB + if (file.size > MAX_SIZE) { + showToastMessage('⚠️ File too large. Maximum size is 2MB.', '#ff0099'); + return; + } + + // Store the file + selectedMeme = file; + + // Show preview + const reader = new FileReader(); + reader.onload = (event) => { + memeUpload.image.src = event.target.result; + memeUpload.preview.style.display = 'flex'; + }; + reader.readAsDataURL(file); }); - xhr.addEventListener('load', () => { - if (xhr.status >= 200 && xhr.status < 300) { - // Upload successful - memeUpload.progress.style.display = 'none'; - - // Clear the meme upload + // Handle remove button + memeUpload.removeBtn.addEventListener('click', () => { selectedMeme = null; memeUpload.input.value = ''; memeUpload.preview.style.display = 'none'; memeUpload.image.src = ''; - - // Show success message - animatePostSuccess(); - - // Clear input - proposalInput.value = ''; - charCounter.textContent = `${CHAR_LIMIT} characters remaining`; - postButton.disabled = true; - - // Refresh proposals - fetchProposals(); - } else { - // Upload failed - memeUpload.progress.style.display = 'none'; - console.error('Meme upload failed:', xhr.responseText); - showErrorMessage('Failed to upload meme. Please try again.'); - } }); - xhr.addEventListener('error', () => { - memeUpload.progress.style.display = 'none'; - console.error('Meme upload network error'); - showErrorMessage('Failed to upload meme. Please check your connection.'); - }); - - // Send the upload - xhr.send(formData); - } else { - // No meme to upload, just show success and refresh - // Clear input - proposalInput.value = ''; - charCounter.textContent = `${CHAR_LIMIT} characters remaining`; - postButton.disabled = true; - - // Show success animation - animatePostSuccess(); - - // Refresh proposals - fetchProposals(); - } - } catch (error) { - console.error('Error creating proposal:', error); - showErrorMessage('Failed to post your petition. Parliament might be onto us!'); + // Modify post button click handler to include meme upload + const postButton = document.getElementById('post-button'); + if (postButton) { + // Store the original click event handler + const originalClickHandler = postButton.onclick; + + // Remove the original handler + postButton.onclick = null; + + // Add new handler + postButton.addEventListener('click', async () => { + const proposalInput = document.getElementById('proposal-input'); + const text = proposalInput?.value.trim(); + + if (text) { + try { + const trending = Math.random() > 0.8; // Randomly mark some as trending + + // Create the proposal first + const createResponse = await fetch(`${API_BASE_URL}/proposals`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + authorId: currentUser.id, + text: text, + trending: trending + }) + }); + + if (!createResponse.ok) { + throw new Error('Failed to create proposal'); + } + + const proposalData = await createResponse.json(); + const proposalId = proposalData.id; + + // If there's a meme, upload it + if (selectedMeme) { + // Show progress bar + memeUpload.progress.style.display = 'block'; + memeUpload.progressBar.style.width = '0%'; + + // Create form data for file upload + const formData = new FormData(); + formData.append('proposalId', proposalId); + formData.append('meme', selectedMeme); + + // Track upload progress + const xhr = new XMLHttpRequest(); + xhr.open('POST', `${API_BASE_URL}/memes`); + + xhr.upload.addEventListener('progress', (event) => { + if (event.lengthComputable) { + const percent = (event.loaded / event.total) * 100; + memeUpload.progressBar.style.width = percent + '%'; + } + }); + + xhr.addEventListener('load', () => { + if (xhr.status >= 200 && xhr.status < 300) { + // Upload successful + memeUpload.progress.style.display = 'none'; + + // Clear the meme upload + selectedMeme = null; + memeUpload.input.value = ''; + memeUpload.preview.style.display = 'none'; + memeUpload.image.src = ''; + + // Show success message + animatePostSuccess(); + + // Clear input + proposalInput.value = ''; + charCounter.textContent = `${CHAR_LIMIT} characters remaining`; + postButton.disabled = true; + + // Refresh proposals + fetchProposals(); + } else { + // Upload failed + memeUpload.progress.style.display = 'none'; + console.error('Meme upload failed:', xhr.responseText); + showToastMessage('Failed to upload meme. Please try again.', '#ff0099'); + } + }); + + xhr.addEventListener('error', () => { + memeUpload.progress.style.display = 'none'; + console.error('Meme upload network error'); + showToastMessage('Failed to upload meme. Please check your connection.', '#ff0099'); + }); + + // Send the upload + xhr.send(formData); + } else { + // No meme to upload, just show success and refresh + // Clear input + proposalInput.value = ''; + charCounter.textContent = `${CHAR_LIMIT} characters remaining`; + postButton.disabled = true; + + // Show success animation + animatePostSuccess(); + + // Refresh proposals + fetchProposals(); + } + } catch (error) { + console.error('Error creating proposal:', error); + showErrorMessage('Failed to post your petition. Parliament might be onto us!'); + } + } + }); + } } - } - }); - } -} - // Display the modal for petition verification function showVerificationModal(proposalId) { @@ -1775,8 +1736,8 @@ function initMemeUpload() { userData: userData }; - // Save verification data to localStorage (in real app, this would be in your database) - localStorage.setItem('radical_verified_users', JSON.stringify(verifiedUsers)); + // Save verification data to localStorage (in real app, this would be in your database) + localStorage.setItem('radical_verified_users', JSON.stringify(verifiedUsers)); // Remove the modal const modal = document.querySelector('.modal-overlay'); @@ -1799,6 +1760,6 @@ function initMemeUpload() { } } } - + \ No newline at end of file