This commit is contained in:
2025-09-27 17:07:58 +01:00
commit bfdcee8602
2663 changed files with 517832 additions and 0 deletions

199
static/js/donate.js Normal file
View File

@@ -0,0 +1,199 @@
(async () => {
const cfg = await fetch("/config").then(r => r.json());
const stripe = Stripe(cfg.publishableKey);
const form = document.getElementById("donation-form");
const result = document.getElementById("result");
const amountInput = document.getElementById("amount");
const currencyInput = document.getElementById("currency");
let elements, expressCheckoutElement, cardElement, paymentIntent;
// Initialize payment elements
async function initializePayment() {
const amount = parseFloat(amountInput.value) || 10;
const currency = currencyInput.value;
const supporterName = document.getElementById('supporter-name').value;
// Create payment intent
const res = await fetch("/create-payment-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ amount, currency, supporterName })
});
const { clientSecret } = await res.json();
// Create elements with appearance customization
elements = stripe.elements({
clientSecret,
appearance: {
theme: document.documentElement.getAttribute('data-theme') === 'dark' ? 'night' : 'stripe'
}
});
// Create and mount express checkout element (Google Pay, Apple Pay, etc.)
expressCheckoutElement = elements.create("expressCheckout", {
buttonType: {
googlePay: "donate",
applePay: "donate"
},
paymentMethods: {
link: "never" // Disable Link payment method
}
});
expressCheckoutElement.mount("#express-checkout-element");
// Listen for express checkout readiness
expressCheckoutElement.on('ready', (event) => {
const paymentDivider = document.querySelector('.payment-divider');
if (event.availablePaymentMethods && Object.keys(event.availablePaymentMethods).length > 0) {
// Express methods are available, show the divider
paymentDivider.style.display = 'block';
} else {
// No express methods available, hide the divider on desktop but keep on mobile
if (window.innerWidth <= 768) {
paymentDivider.style.display = 'block';
paymentDivider.querySelector('span').textContent = 'Pay with card';
} else {
paymentDivider.style.display = 'none';
}
}
});
// Create and mount card element
cardElement = elements.create("card", {
style: {
base: {
fontSize: '16px',
color: getComputedStyle(document.documentElement).getPropertyValue('--text-primary'),
'::placeholder': {
color: getComputedStyle(document.documentElement).getPropertyValue('--text-secondary'),
},
},
}
});
cardElement.mount("#card-element");
return clientSecret;
}
// Handle express checkout (Google Pay, Apple Pay, etc.)
async function handleExpressPayment() {
result.textContent = "Processing...";
try {
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: window.location.href,
},
redirect: "if_required"
});
if (error) {
result.textContent = "❌ " + error.message;
} else {
result.textContent = "✅ Thank you! Support received successfully.";
showSuccessAnimation();
addToSupporterWall();
}
} catch (err) {
result.textContent = "❌ Payment failed. Please try again.";
}
}
// Handle card payment
async function handleCardPayment(e) {
e.preventDefault();
result.textContent = "Processing...";
try {
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: window.location.href,
},
redirect: "if_required"
});
if (error) {
result.textContent = "❌ " + error.message;
} else {
result.textContent = "✅ Thank you! Support received successfully.";
showSuccessAnimation();
addToSupporterWall();
}
} catch (err) {
result.textContent = "❌ Payment failed. Please try again.";
}
}
// Success animation
function showSuccessAnimation() {
result.style.background = 'linear-gradient(135deg, #28a745, #20c997)';
result.style.color = 'white';
result.style.padding = '12px';
result.style.borderRadius = '8px';
result.style.animation = 'bounce 0.6s ease-out';
}
// Add to supporter wall
async function addToSupporterWall() {
const supporterName = document.getElementById('supporter-name').value.trim();
const amount = parseFloat(amountInput.value) || 0;
const currency = currencyInput.value;
if (supporterName && window.supporterWall) {
await window.supporterWall.addSupporter(supporterName, amount * 100, currency);
}
}
// Reinitialize when amount or currency changes
async function reinitialize() {
if (elements) {
elements.destroy();
}
const amount = parseFloat(amountInput.value);
if (amount && amount > 0) {
await initializePayment();
// Set up event listeners
if (expressCheckoutElement) {
expressCheckoutElement.on('click', handleExpressPayment);
}
}
}
// Event listeners
form.addEventListener("submit", handleCardPayment);
amountInput.addEventListener("blur", reinitialize);
currencyInput.addEventListener("change", reinitialize);
// Initialize on page load
await initializePayment();
// Set up express checkout event listener
if (expressCheckoutElement) {
expressCheckoutElement.on('click', handleExpressPayment);
}
// Handle responsive payment divider visibility
window.addEventListener('resize', () => {
const paymentDivider = document.querySelector('.payment-divider');
if (window.innerWidth <= 768) {
// Always show on mobile
paymentDivider.style.display = 'block';
}
});
// Update elements theme when theme changes
if (window.themeManager) {
const originalSetTheme = window.themeManager.setTheme;
window.themeManager.setTheme = function(theme) {
originalSetTheme.call(this, theme);
// Reinitialize elements with new theme
setTimeout(reinitialize, 100);
};
}
})();