diff --git a/api/get_latest_tips.php b/api/get_latest_tips.php new file mode 100644 index 0000000..d13a56a --- /dev/null +++ b/api/get_latest_tips.php @@ -0,0 +1,23 @@ +prepare("SELECT * FROM dj_tips WHERE created_at > (NOW() - INTERVAL 15 SECOND) ORDER BY created_at DESC LIMIT 5"); + $stmt->execute(); + $tips = $stmt->fetchAll(); + + // Also get current DJ + $stmt = $pdo->query("SELECT setting_value FROM settings WHERE setting_key = 'current_dj'"); + $dj = $stmt->fetchColumn() ?: "Lili"; + + echo json_encode([ + "success" => true, + "tips" => $tips, + "current_dj" => $dj + ]); +} catch (Exception $e) { + echo json_encode(["success" => false, "error" => $e->getMessage()]); +} diff --git a/api/send_tip.php b/api/send_tip.php new file mode 100644 index 0000000..777399a --- /dev/null +++ b/api/send_tip.php @@ -0,0 +1,51 @@ + false, "error" => "Inicia sesión para enviar flores"]); + exit; +} + +try { + $pdo->beginTransaction(); + + // Get current DJ + $stmt = $pdo->query("SELECT setting_value FROM settings WHERE setting_key = 'current_dj'"); + $dj = $stmt->fetchColumn() ?: "Lili"; + + // Check user points + $stmt = $pdo->prepare("SELECT points FROM fans WHERE name = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(); + + if (!$user || $user["points"] < $amount) { + echo json_encode(["success" => false, "error" => "No tienes suficientes puntos ($amount requeridos)"]); + $pdo->rollBack(); + exit; + } + + // Deduct points + $stmt = $pdo->prepare("UPDATE fans SET points = points - ? WHERE name = ?"); + $stmt->execute([$amount, $username]); + + // Log tip + $stmt = $pdo->prepare("INSERT INTO dj_tips (sender_name, dj_name, amount) VALUES (?, ?, ?)"); + $stmt->execute([$username, $dj, $amount]); + + $pdo->commit(); + + echo json_encode([ + "success" => true, + "message" => "¡Has enviado flores a $dj!", + "dj" => $dj + ]); + +} catch (Exception $e) { + $pdo->rollBack(); + echo json_encode(["success" => false, "error" => $e->getMessage()]); +} diff --git a/db/migrations/20260217_dj_flores.sql b/db/migrations/20260217_dj_flores.sql new file mode 100644 index 0000000..7d330ab --- /dev/null +++ b/db/migrations/20260217_dj_flores.sql @@ -0,0 +1,11 @@ +-- Migration: DJ Flores and Current DJ settings +INSERT INTO settings (setting_key, setting_value) VALUES ('current_dj', 'Lili') ON DUPLICATE KEY UPDATE setting_value = setting_value; + +CREATE TABLE IF NOT EXISTS dj_tips ( + id INT AUTO_INCREMENT PRIMARY KEY, + sender_name VARCHAR(255) NOT NULL, + dj_name VARCHAR(255) NOT NULL, + amount INT NOT NULL, + message VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); diff --git a/index.php b/index.php index be6ed47..2402890 100644 --- a/index.php +++ b/index.php @@ -1360,39 +1360,53 @@ $twitter_link = "https://twitter.com/"; font-size: 1rem; color: white; } - .shop-item-desc { - font-size: 0.75rem; - opacity: 0.6; - } - .shop-item-price { - font-weight: 800; - color: #facc15; - display: flex; - align-items: center; - gap: 4px; - } - .pinned-message-container { - background: linear-gradient(90deg, rgba(56, 189, 248, 0.2), rgba(0, 200, 83, 0.2)); - border: 1px solid var(--primary-color); - border-radius: 12px; - padding: 0.8rem; - margin-bottom: 1rem; - display: none; - animation: fadeIn 0.5s ease; - position: relative; - } - .pinned-label { - font-size: 0.6rem; - font-weight: 800; - color: var(--primary-color); - text-transform: uppercase; - letter-spacing: 1px; - margin-bottom: 4px; - display: flex; - align-items: center; - gap: 4px; - } - + .shop-item-desc { + font-size: 0.75rem; + opacity: 0.7; + } + + /* Celebration & Flowers */ + #celebration-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 9999; + overflow: hidden; + } + + .flower-particle { + position: absolute; + font-size: 2rem; + user-select: none; + animation: fall-and-sway var(--duration) linear forwards; + opacity: 0; + } + + @keyframes fall-and-sway { + 0% { transform: translateY(-10vh) translateX(0) rotate(0deg); opacity: 0; } + 10% { opacity: 1; } + 90% { opacity: 1; } + 100% { transform: translateY(110vh) translateX(var(--sway)) rotate(360deg); opacity: 0; } + } + + .dj-badge { + background: linear-gradient(135deg, #f472b6, #db2777); + color: white; + padding: 4px 12px; + border-radius: 20px; + font-size: 0.7rem; + font-weight: 800; + display: inline-flex; + align-items: center; + gap: 5px; + margin-bottom: 8px; + box-shadow: 0 4px 12px rgba(236, 72, 153, 0.3); + animation: pulse 2s infinite; + } +
@@ -1447,6 +1461,9 @@ $twitter_link = "https://twitter.com/"; Cover
+
+
LILI
+
ESTÁS ESCUCHANDO:
Cargando stream...
Lili Records Radio
@@ -1480,6 +1497,10 @@ $twitter_link = "https://twitter.com/"; +
@@ -1880,6 +1901,9 @@ $twitter_link = "https://twitter.com/";
+ +
+ @@ -3264,6 +3288,106 @@ $twitter_link = "https://twitter.com/"; setInterval(fetchActivePerks, 10000); fetchActivePerks(); // --- End Radio Shop Functionality --- + + // --- DJ Flores Functionality --- + let lastCelebrationId = 0; + const shownTips = new Set(); + + async function sendFlowersToDJ() { + const userName = document.getElementById('user-name').value.trim(); + if (!userName) { + alert('Por favor, ingresa tu nombre arriba para regalar flores.'); + document.getElementById('user-name').focus(); + return; + } + + if (!confirm(`¿Quieres enviarle un ramo de flores al DJ por 100 puntos?`)) return; + + try { + const formData = new FormData(); + formData.append('username', userName); + formData.append('amount', 100); + + const response = await fetch('api/send_tip.php', { + method: 'POST', + body: formData + }); + const data = await response.json(); + + if (data.success) { + alert(data.message); + launchFlowerCelebration(); // Local trigger + fetchLeaderboard(); // Update points + } else { + alert(data.error); + } + } catch (e) { + console.error('Error sending flowers:', e); + } + } + + async function pollCelebrations() { + try { + const response = await fetch('api/get_latest_tips.php'); + const data = await response.json(); + + if (data.success) { + // Update DJ Name + if (data.current_dj) { + document.getElementById('current-dj-name').innerText = data.current_dj.toUpperCase(); + } + + // Check for new tips + data.tips.forEach(tip => { + if (!shownTips.has(tip.id)) { + shownTips.add(tip.id); + launchFlowerCelebration(); + console.log(`Celebration for tip from ${tip.sender_name}`); + } + }); + + // Cleanup set to keep it small + if (shownTips.size > 50) { + const it = shownTips.values(); + shownTips.delete(it.next().value); + } + } + } catch (e) { + console.error('Error polling celebrations:', e); + } + } + + function launchFlowerCelebration() { + const container = document.getElementById('celebration-overlay'); + const emojis = ['🌸', '🌹', '🌺', '🌻', '🌼', '🌷', '✨', '💖']; + const count = 30; + + for (let i = 0; i < count; i++) { + setTimeout(() => { + const flower = document.createElement('div'); + flower.className = 'flower-particle'; + flower.innerText = emojis[Math.floor(Math.random() * emojis.length)]; + + const left = Math.random() * 100; + const sway = (Math.random() - 0.5) * 200; + const duration = 3 + Math.random() * 4; + + flower.style.left = `${left}%`; + flower.style.setProperty('--sway', `${sway}px`); + flower.style.setProperty('--duration', `${duration}s`); + flower.style.fontSize = `${1.5 + Math.random() * 2}rem`; + + container.appendChild(flower); + + setTimeout(() => flower.remove(), duration * 1000); + }, i * 100); + } + } + + // Initialize polling + setInterval(pollCelebrations, 5000); + pollCelebrations(); + \ No newline at end of file