dimensionalpulsar commited on
Commit
eeab3e9
·
verified ·
1 Parent(s): 87d46e7

Update cointube.jsx

Browse files
Files changed (1) hide show
  1. cointube.jsx +35 -45
cointube.jsx CHANGED
@@ -23,7 +23,22 @@ import {
23
  limit
24
  } from 'firebase/firestore';
25
 
26
- let app, auth, db;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  const ADMIN_EMAIL = "dimensionalpulsar@gmail.com";
29
  const VIDEO_COST = 10000;
@@ -32,8 +47,10 @@ const DEFAULT_VIDEO_TITLE = "Cuentos de Terror";
32
 
33
  export default function App() {
34
 
35
- const [firebaseReady, setFirebaseReady] = useState(false);
36
- const [configError, setConfigError] = useState(null);
 
 
37
 
38
  const [videos, setVideos] = useState([]);
39
  const [leaderboard, setLeaderboard] = useState([]);
@@ -43,7 +60,6 @@ export default function App() {
43
  const [userProfile, setUserProfile] = useState(null);
44
  const [currentTab, setCurrentTab] = useState('ganar');
45
 
46
- // CAMBIO 1: Iniciamos en null para no mostrar el default mientras carga
47
  const [currentVideoId, setCurrentVideoId] = useState(null);
48
  const [currentVideoTitle, setCurrentVideoTitle] = useState(null);
49
 
@@ -54,31 +70,10 @@ export default function App() {
54
  const localCoinBufferRef = useRef(0);
55
  const initialLoadRef = useRef(true);
56
 
57
- useEffect(() => {
58
- const initFirebase = async () => {
59
- try {
60
- const response = await fetch('/api/config');
61
- if (!response.ok) throw new Error("Error de red al cargar config");
62
-
63
- const config = await response.json();
64
-
65
- if (!app) {
66
- app = initializeApp(config);
67
- auth = getAuth(app);
68
- db = getFirestore(app);
69
- }
70
- setFirebaseReady(true);
71
-
72
- } catch (error) {
73
- console.error(error);
74
- setConfigError("Error de conexión con el servidor.");
75
- }
76
- };
77
- initFirebase();
78
- }, []);
79
 
80
  useEffect(() => {
81
- if (!firebaseReady || !auth) return;
82
  const unsubscribeAuth = onAuthStateChanged(auth, (user) => {
83
  if (user) {
84
  setUser(user);
@@ -91,10 +86,10 @@ export default function App() {
91
  setAuthReady(true);
92
  });
93
  return () => unsubscribeAuth();
94
- }, [firebaseReady]);
95
 
96
  useEffect(() => {
97
- if (!firebaseReady || !userId || !db) return;
98
  const profileRef = doc(db, 'users', userId);
99
  const unsubscribeProfile = onSnapshot(profileRef, (docSnap) => {
100
  if (docSnap.exists()) {
@@ -110,18 +105,16 @@ export default function App() {
110
  }
111
  }, (error) => console.error(error));
112
  return () => unsubscribeProfile();
113
- }, [userId, user, firebaseReady]);
114
 
115
  useEffect(() => {
116
- if (!firebaseReady || !db) return;
117
- // La consulta ya ordena por fecha descendente (el más nuevo primero)
118
  const q = query(collection(db, 'videos'), orderBy('createdAt', 'desc'), limit(50));
119
  const unsubscribe = onSnapshot(q, (snapshot) => {
120
  const fetchedVideos = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
121
 
122
  if (fetchedVideos.length > 0) {
123
  setVideos(fetchedVideos);
124
- // CAMBIO 2: Si es la carga inicial O si no hay video seleccionado aún
125
  if (initialLoadRef.current || !currentVideoId) {
126
  const latestVideo = fetchedVideos[0];
127
  setCurrentVideoId(latestVideo.videoId);
@@ -129,7 +122,6 @@ export default function App() {
129
  initialLoadRef.current = false;
130
  }
131
  } else {
132
- // Solo usamos el default si NO hay videos en la base de datos
133
  setVideos([{ id: '1', title: DEFAULT_VIDEO_TITLE, videoId: DEFAULT_VIDEO_ID }]);
134
  if (initialLoadRef.current || !currentVideoId) {
135
  setCurrentVideoId(DEFAULT_VIDEO_ID);
@@ -139,10 +131,10 @@ export default function App() {
139
  }
140
  });
141
  return () => unsubscribe();
142
- }, [firebaseReady, currentVideoId]); // Agregamos currentVideoId a dependencias para el chequeo de seguridad
143
 
144
  useEffect(() => {
145
- if (!firebaseReady || !db) return;
146
  const q = query(collection(db, 'users'), orderBy('coins', 'desc'), limit(100));
147
  const unsubscribe = onSnapshot(q, (snapshot) => {
148
  let usersList = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
@@ -155,7 +147,7 @@ export default function App() {
155
  setLeaderboard(usersList.slice(0, 20));
156
  });
157
  return () => unsubscribe();
158
- }, [firebaseReady]);
159
 
160
  useEffect(() => {
161
  if (!window.YT) {
@@ -214,7 +206,6 @@ export default function App() {
214
  }, [startCoinInterval, stopCoinInterval, currentVideoId, userId]);
215
 
216
  useEffect(() => {
217
- // CAMBIO 3: Aseguramos que currentVideoId exista antes de inicializar el player
218
  if (ytApiReady && userId && !playerRef.current && currentVideoId) {
219
  playerRef.current = new window.YT.Player('player', {
220
  height: '100%',
@@ -275,20 +266,17 @@ export default function App() {
275
  await signOut(auth);
276
  };
277
 
278
- if (configError) {
279
- return <div className="flex justify-center items-center h-full text-red-500">{configError}</div>;
280
- }
281
-
282
- if (!firebaseReady || !authReady) {
283
  return (
284
  <div className="flex flex-col items-center justify-center h-full w-full">
285
  <div className="spinner mb-4"></div>
286
- <p className="text-lg text-gray-400">{!firebaseReady ? "Cargando..." : "Conectando..."}</p>
287
  </div>
288
  );
289
  }
290
 
291
  if (authReady && !user) {
 
292
  return (
293
  <div className="flex flex-col items-center justify-center h-full w-full bg-gray-900 p-4">
294
  <div className="max-w-md w-full bg-gray-800 rounded-2xl shadow-2xl p-8 text-center border border-gray-700">
@@ -322,6 +310,7 @@ export default function App() {
322
  );
323
  }
324
 
 
325
  return (
326
  <div className="h-full w-full max-w-7xl mx-auto flex flex-col p-4 md:p-6">
327
 
@@ -384,7 +373,6 @@ export default function App() {
384
  <main className={`${currentTab === 'ganar' ? 'flex' : 'hidden'} flex-1 flex-col lg:flex-row gap-6`}>
385
  <div className="lg:w-2/3">
386
  <div className="aspect-video bg-black rounded-lg overflow-hidden shadow-xl flex items-center justify-center">
387
- {/* CAMBIO 4: Renderizado condicional del player */}
388
  {currentVideoId ? (
389
  <div id="player" className="w-full h-full"></div>
390
  ) : (
@@ -451,6 +439,8 @@ export default function App() {
451
  );
452
  }
453
 
 
 
454
  function PromoCheck({ user, userProfile, userId, adminEmail, videoCost, db }) {
455
  const isAdmin = user?.email === adminEmail;
456
  const coins = userProfile?.coins || 0;
 
23
  limit
24
  } from 'firebase/firestore';
25
 
26
+ // --- CAMBIO IMPORTANTE: Inicialización Global ---
27
+ // Definimos la configuración usando las variables de entorno de Hugging Face (Vite)
28
+ const firebaseConfig = {
29
+ apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
30
+ authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
31
+ projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
32
+ storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
33
+ messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
34
+ appId: import.meta.env.VITE_FIREBASE_APP_ID
35
+ };
36
+
37
+ // Inicializamos Firebase inmediatamente
38
+ const app = initializeApp(firebaseConfig);
39
+ const auth = getAuth(app);
40
+ const db = getFirestore(app);
41
+ // ------------------------------------------------
42
 
43
  const ADMIN_EMAIL = "dimensionalpulsar@gmail.com";
44
  const VIDEO_COST = 10000;
 
47
 
48
  export default function App() {
49
 
50
+ // Ya no necesitamos 'configError' porque cargamos la config directa
51
+ // Pero mantenemos 'firebaseReady' para no romper tu lógica visual,
52
+ // aunque ahora siempre será true desde el inicio.
53
+ const [firebaseReady, setFirebaseReady] = useState(true);
54
 
55
  const [videos, setVideos] = useState([]);
56
  const [leaderboard, setLeaderboard] = useState([]);
 
60
  const [userProfile, setUserProfile] = useState(null);
61
  const [currentTab, setCurrentTab] = useState('ganar');
62
 
 
63
  const [currentVideoId, setCurrentVideoId] = useState(null);
64
  const [currentVideoTitle, setCurrentVideoTitle] = useState(null);
65
 
 
70
  const localCoinBufferRef = useRef(0);
71
  const initialLoadRef = useRef(true);
72
 
73
+ // --- NOTA: Eliminé el useEffect del 'fetch' porque ya inicializamos arriba ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
  useEffect(() => {
76
+ if (!auth) return;
77
  const unsubscribeAuth = onAuthStateChanged(auth, (user) => {
78
  if (user) {
79
  setUser(user);
 
86
  setAuthReady(true);
87
  });
88
  return () => unsubscribeAuth();
89
+ }, []); // Array vacío porque 'auth' ya es estable
90
 
91
  useEffect(() => {
92
+ if (!userId || !db) return;
93
  const profileRef = doc(db, 'users', userId);
94
  const unsubscribeProfile = onSnapshot(profileRef, (docSnap) => {
95
  if (docSnap.exists()) {
 
105
  }
106
  }, (error) => console.error(error));
107
  return () => unsubscribeProfile();
108
+ }, [userId, user]); // Quitamos firebaseReady de dependencias
109
 
110
  useEffect(() => {
111
+ if (!db) return;
 
112
  const q = query(collection(db, 'videos'), orderBy('createdAt', 'desc'), limit(50));
113
  const unsubscribe = onSnapshot(q, (snapshot) => {
114
  const fetchedVideos = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
115
 
116
  if (fetchedVideos.length > 0) {
117
  setVideos(fetchedVideos);
 
118
  if (initialLoadRef.current || !currentVideoId) {
119
  const latestVideo = fetchedVideos[0];
120
  setCurrentVideoId(latestVideo.videoId);
 
122
  initialLoadRef.current = false;
123
  }
124
  } else {
 
125
  setVideos([{ id: '1', title: DEFAULT_VIDEO_TITLE, videoId: DEFAULT_VIDEO_ID }]);
126
  if (initialLoadRef.current || !currentVideoId) {
127
  setCurrentVideoId(DEFAULT_VIDEO_ID);
 
131
  }
132
  });
133
  return () => unsubscribe();
134
+ }, [currentVideoId]);
135
 
136
  useEffect(() => {
137
+ if (!db) return;
138
  const q = query(collection(db, 'users'), orderBy('coins', 'desc'), limit(100));
139
  const unsubscribe = onSnapshot(q, (snapshot) => {
140
  let usersList = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
 
147
  setLeaderboard(usersList.slice(0, 20));
148
  });
149
  return () => unsubscribe();
150
+ }, []);
151
 
152
  useEffect(() => {
153
  if (!window.YT) {
 
206
  }, [startCoinInterval, stopCoinInterval, currentVideoId, userId]);
207
 
208
  useEffect(() => {
 
209
  if (ytApiReady && userId && !playerRef.current && currentVideoId) {
210
  playerRef.current = new window.YT.Player('player', {
211
  height: '100%',
 
266
  await signOut(auth);
267
  };
268
 
269
+ if (!authReady) {
 
 
 
 
270
  return (
271
  <div className="flex flex-col items-center justify-center h-full w-full">
272
  <div className="spinner mb-4"></div>
273
+ <p className="text-lg text-gray-400">Conectando...</p>
274
  </div>
275
  );
276
  }
277
 
278
  if (authReady && !user) {
279
+ // ... (TU UI DE LOGIN SE MANTIENE IGUAL)
280
  return (
281
  <div className="flex flex-col items-center justify-center h-full w-full bg-gray-900 p-4">
282
  <div className="max-w-md w-full bg-gray-800 rounded-2xl shadow-2xl p-8 text-center border border-gray-700">
 
310
  );
311
  }
312
 
313
+ // El resto del return (header, main, etc) sigue igual que en tu código original...
314
  return (
315
  <div className="h-full w-full max-w-7xl mx-auto flex flex-col p-4 md:p-6">
316
 
 
373
  <main className={`${currentTab === 'ganar' ? 'flex' : 'hidden'} flex-1 flex-col lg:flex-row gap-6`}>
374
  <div className="lg:w-2/3">
375
  <div className="aspect-video bg-black rounded-lg overflow-hidden shadow-xl flex items-center justify-center">
 
376
  {currentVideoId ? (
377
  <div id="player" className="w-full h-full"></div>
378
  ) : (
 
439
  );
440
  }
441
 
442
+ // Los componentes PromoCheck y PromoUnlocked se mantienen igual,
443
+ // solo asegúrate de que PromoUnlocked reciba 'db' como prop, lo cual ya haces.
444
  function PromoCheck({ user, userProfile, userId, adminEmail, videoCost, db }) {
445
  const isAdmin = user?.email === adminEmail;
446
  const coins = userProfile?.coins || 0;