This commit is contained in:
Omar Najjar 2025-03-08 13:06:27 +11:00
parent c9db062d0f
commit adea5f4a36

View file

@ -1,13 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RADICAL - Australia's Democratic Revolution</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RADICAL - Australia's Democratic Revolution</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500;600;700&display=swap"
rel="stylesheet">
<style>
:root {
--primary: #ff0099;
--primary-light: #ff66c4;
@ -648,55 +650,169 @@
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;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>RADICAL</h1>
<div class="tagline">Vibe, Vote, Veto</div>
<p>Australia's direct democracy movement. Share policy ideas, vote on proposals, take back control.</p>
</header>
<section class="new-proposal">
<textarea id="proposal-input" class="proposal-input" placeholder="Your idea to revolutionize Australia? (280 character limit)" maxlength="280"></textarea>
<div id="char-counter" class="char-counter">280 characters remaining</div>
<button id="post-button" class="post-button" disabled>Post Proposal</button>
<div style="clear: both;"></div>
</section>
<section class="proposals">
<div class="filter-bar">
<div class="filter-title">Democracy Goes Brrr</div>
<select id="sort-select" class="sort-select">
<option value="newest">Latest Ideas</option>
<option value="popular">Most Supported</option>
<option value="controversial">Most Debated</option>
<option value="petitions">Petition Progress</option>
</select>
</div>
<div id="proposals-container">
<div class="loading-indicator">
<div class="loading-spinner"></div>
<p>Loading democracy...</p>
</div>
</div>
</section>
<footer>
<p>RADICAL - Direct Democracy Movement 🇦🇺 &copy; 2025</p>
<div class="footer-links">
<a href="#about">About Us</a>
<a href="#join">Join The Movement</a>
<a href="#events">Events</a>
<a href="#contact">Contact</a>
</div>
</footer>
</div>
</head>
<body>
<div class="container">
<header>
<h1>RADICAL</h1>
<div class="tagline">Vibe, Vote, Veto</div>
<p>Australia's direct democracy movement. Share policy ideas,
vote on proposals, take back control.</p>
</header>
<script>
<section class="new-proposal">
<textarea id="proposal-input" class="proposal-input"
placeholder="Your idea to revolutionize Australia? (280 character limit)"
maxlength="280"></textarea>
<div id="char-counter" class="char-counter">280 characters
remaining</div>
<button id="post-button" class="post-button" disabled>Post
Proposal</button>
<div style="clear: both;"></div>
</section>
<section class="proposals">
<div class="filter-bar">
<div class="filter-title">Democracy Goes Brrr</div>
<select id="sort-select" class="sort-select">
<option value="newest">Latest Ideas</option>
<option value="popular">Most Supported</option>
<option value="controversial">Most Debated</option>
<option value="petitions">Petition Progress</option>
</select>
</div>
<div id="proposals-container">
<div class="loading-indicator">
<div class="loading-spinner"></div>
<p>Loading democracy...</p>
</div>
</div>
</section>
<footer>
<p>RADICAL - Direct Democracy Movement 🇦🇺 &copy; 2025</p>
<div class="footer-links">
<a href="#about">About Us</a>
<a href="#join">Join The Movement</a>
<a href="#events">Events</a>
<a href="#contact">Contact</a>
</div>
</footer>
</div>
<script>
// Configuration
const API_BASE_URL = 'https://radical.omar-c29.workers.dev/api'; // Replace with your actual worker URL
@ -722,6 +838,8 @@
document.addEventListener('DOMContentLoaded', async () => {
// Initialize variables for DOM elements
initializeApp();
// Initialize meme upload
initMemeUpload();
});
// Initialize application
@ -1159,6 +1277,11 @@
<span class="petition-count">${proposal.petition_signatures || proposal.upvotes}</span>
<span>verified signatures</span>
</div>` : '';
const memeHtml = proposal.meme_url ?
`<div class="proposal-meme">
<img src="${proposal.meme_url}" alt="Meme for petition" />
</div>` : '';
proposalCard.innerHTML = `
${badgeHtml}
@ -1200,6 +1323,247 @@
}
});
}
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
};
}
// 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%';
// 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);
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!');
}
}
});
}
}
// Display the modal for petition verification
function showVerificationModal(proposalId) {
@ -1436,5 +1800,5 @@
}
}
</script>
</body>
</body>
</html>