import React, { useState, useEffect, useRef, useCallback } from 'react'; import { initializeApp } from 'firebase/app'; import { getAuth, onAuthStateChanged, GoogleAuthProvider, signInWithPopup, signOut } from 'firebase/auth'; import { getFirestore, doc, onSnapshot, setDoc, updateDoc, deleteDoc, collection, addDoc, increment, serverTimestamp, query, orderBy, limit, writeBatch, getDocs } from 'firebase/firestore'; const firebaseConfig = { apiKey: "AIzaSyDJx6_b3JxctCW1RoDE-bm4zp7rrWT9lqA", authDomain: "cointube-f7695.firebaseapp.com", projectId: "cointube-f7695", storageBucket: "cointube-f7695.firebasestorage.app", messagingSenderId: "710177637377", appId: "1:710177637377:web:10605b43eea6eac446ac00" }; const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const ADMIN_EMAIL = "dimensionalpulsar@gmail.com"; const VIDEO_COST = 10000; // Datos de EmailJS const EMAILJS_SERVICE_ID = "service_v18p188"; const EMAILJS_TEMPLATE_ID = "template_wexg1ww"; const EMAILJS_PUBLIC_KEY = "XK6EoFoAqInBoDjIe"; export default function App() { const [videos, setVideos] = useState([]); const [leaderboard, setLeaderboard] = useState([]); const [user, setUser] = useState(null); const [userId, setUserId] = useState(null); const [authReady, setAuthReady] = useState(false); const [userProfile, setUserProfile] = useState(null); const [currentTab, setCurrentTab] = useState('ganar'); const [currentVideoId, setCurrentVideoId] = useState(null); const [currentVideoTitle, setCurrentVideoTitle] = useState(null); const [ytApiReady, setYtApiReady] = useState(false); const playerRef = useRef(null); const coinIntervalRef = useRef(null); const localCoinBufferRef = useRef(0); const initialLoadRef = useRef(true); useEffect(() => { const unsubscribeAuth = onAuthStateChanged(auth, (user) => { if (user) { setUser(user); setUserId(user.uid); } else { setUser(null); setUserId(null); setUserProfile(null); } setAuthReady(true); }); return () => unsubscribeAuth(); }, []); useEffect(() => { if (!userId) return; const profileRef = doc(db, 'users', userId); const unsubscribeProfile = onSnapshot(profileRef, (docSnap) => { if (docSnap.exists()) { setUserProfile(docSnap.data()); } else { const newProfile = { email: user.email, coins: 0 }; setDoc(profileRef, newProfile).catch(e => console.error(e)); setUserProfile(newProfile); } }, (error) => console.error(error)); return () => unsubscribeProfile(); }, [userId, user]); // Lógica de Reseteo Diario (Para el admin) useEffect(() => { if (user?.email !== ADMIN_EMAIL) return; const checkReset = async () => { const settingsRef = doc(db, 'settings', 'stats'); const snap = await getDocs(query(collection(db, 'settings'))); if (snap.empty) return; const data = snap.docs[0].data(); const lastReset = data.lastReset.toDate(); const now = new Date(); // 86,400,000 ms = 1 Día (Reseteo Diario) if (now - lastReset >= 86400000) { const batch = writeBatch(db); const usersSnap = await getDocs(collection(db, 'users')); usersSnap.forEach((userDoc) => { if (userDoc.data().email !== ADMIN_EMAIL) { batch.update(userDoc.ref, { coins: 0 }); } }); batch.update(settingsRef, { lastReset: serverTimestamp() }); await batch.commit(); alert("Reseteo Diario Completado: Las monedas de los usuarios se reiniciaron a 0."); } }; checkReset(); }, [user]); // Lista de videos (Randomizada y sin video por defecto) useEffect(() => { const q = query(collection(db, 'videos'), orderBy('createdAt', 'desc'), limit(50)); const unsubscribe = onSnapshot(q, (snapshot) => { let fetchedVideos = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); if (fetchedVideos.length > 0) { // Randomizar la lista de videos fetchedVideos = fetchedVideos.sort(() => Math.random() - 0.5); setVideos(fetchedVideos); if (initialLoadRef.current || !currentVideoId) { const latestVideo = fetchedVideos[0]; setCurrentVideoId(latestVideo.videoId); setCurrentVideoTitle(latestVideo.title); initialLoadRef.current = false; } } else { setVideos([]); if (initialLoadRef.current || !currentVideoId) { setCurrentVideoId(null); setCurrentVideoTitle("Sin videos disponibles"); initialLoadRef.current = false; } } }); return () => unsubscribe(); }, [currentVideoId]); useEffect(() => { const q = query(collection(db, 'users'), orderBy('coins', 'desc'), limit(100)); const unsubscribe = onSnapshot(q, (snapshot) => { let usersList = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); usersList = usersList.filter(u => u.email && u.email.endsWith('@gmail.com') ); setLeaderboard(usersList.slice(0, 20)); }); return () => unsubscribe(); }, []); useEffect(() => { if (!window.YT) { const tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; const firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); window.onYouTubeIframeAPIReady = () => setYtApiReady(true); } else { setYtApiReady(true); } }, []); const saveCoinBuffer = useCallback(() => { if (localCoinBufferRef.current > 0 && userId) { const coinsToSave = localCoinBufferRef.current; localCoinBufferRef.current = 0; const profileRef = doc(db, 'users', userId); updateDoc(profileRef, { coins: increment(coinsToSave) }) .catch(e => { localCoinBufferRef.current += coinsToSave; }); } }, [userId]); const startCoinInterval = useCallback(() => { if (coinIntervalRef.current) return; coinIntervalRef.current = setInterval(() => { localCoinBufferRef.current++; if (localCoinBufferRef.current >= 5) saveCoinBuffer(); }, 1000); }, [saveCoinBuffer]); const stopCoinInterval = useCallback(() => { if (coinIntervalRef.current) { clearInterval(coinIntervalRef.current); coinIntervalRef.current = null; saveCoinBuffer(); } }, [saveCoinBuffer]); const onPlayerReady = useCallback((event) => { playerRef.current = event.target; playerRef.current.playVideo(); }, []); const onPlayerStateChange = useCallback((event) => { const state = event.data; if (state === window.YT.PlayerState.PLAYING) startCoinInterval(); else stopCoinInterval(); }, [startCoinInterval, stopCoinInterval]); useEffect(() => { if (ytApiReady && userId && !playerRef.current && currentVideoId) { playerRef.current = new window.YT.Player('player', { height: '100%', width: '100%', videoId: currentVideoId, playerVars: { 'playsinline': 1, 'autoplay': 1, 'controls': 1, 'modestbranding': 1, 'rel': 0 }, events: { 'onReady': onPlayerReady, 'onStateChange': onPlayerStateChange } }); } if (!userId && playerRef.current) { stopCoinInterval(); playerRef.current = null; } }, [ytApiReady, userId, currentVideoId, onPlayerReady, onPlayerStateChange]); const loadVideo = (video) => { stopCoinInterval(); setCurrentVideoId(video.videoId); setCurrentVideoTitle(video.title); if (playerRef.current) playerRef.current.loadVideoById(video.videoId); }; const handleDeleteVideo = async (e, videoId) => { e.stopPropagation(); if (!confirm("¿Eliminar video?")) return; try { await deleteDoc(doc(db, "videos", videoId)); } catch (error) { console.error(error); } }; const handleGiveCoins = async (targetUserId, currentCoins) => { const amountStr = prompt("Cantidad de monedas a sumar (usa negativo para restar):", "1000"); if (!amountStr) return; const amount = parseInt(amountStr); if (isNaN(amount)) return; try { const userRef = doc(db, 'users', targetUserId); await updateDoc(userRef, { coins: increment(amount) }); alert("Monedas actualizadas."); } catch (error) { console.error("Error:", error); alert("Error al dar monedas. Revisa las reglas de Firebase."); } }; // Función para Email Masivo const handleMassEmail = async () => { if (!window.emailjs) return alert("EmailJS no está cargado. Revisa tu index.html."); if (!confirm("¿Enviar notificación de reseteo a todos los usuarios?")) return; const usersSnap = await getDocs(collection(db, 'users')); let count = 0; usersSnap.forEach((uDoc) => { const userData = uDoc.data(); if (userData.email) { window.emailjs.send(EMAILJS_SERVICE_ID, EMAILJS_TEMPLATE_ID, { to_email: userData.email, message: "¡Hola! Los puntos se han reseteado para una nueva ronda. ¡Vuelve a CoinTube y suma monedas viendo videos!" }, EMAILJS_PUBLIC_KEY); count++; } }); alert(`Correos en proceso de envío para ${count} usuarios.`); }; // Uso de Popup original restaurado const handleGoogleLogin = async () => { const provider = new GoogleAuthProvider(); try { await signInWithPopup(auth, provider); } catch (error) { console.error("Error Google:", error); } }; const handleLogout = async () => { stopCoinInterval(); await signOut(auth); }; if (!authReady) { return (
Cargando...
Descubre contenido increíble, gana monedas virtuales por cada minuto que ves y promociona tus propios videos.
¡Junta 10,000 monedas para subir tu video!
Al continuar, aceptas nuestros términos.
Necesitas {videoCost} monedas para subir un video.
{Math.floor(coins)} / {videoCost}
Deposita USDT para recargar al instante.
1 USD = 3000 Monedas
Dirección USDT (BEP20/TRC20):
Envía tu comprobante a: {adminEmail}