salomonsky commited on
Commit
8dabf76
·
verified ·
1 Parent(s): a3af96b

Update cointube.jsx

Browse files
Files changed (1) hide show
  1. cointube.jsx +39 -4
cointube.jsx CHANGED
@@ -13,6 +13,7 @@ import {
13
  onSnapshot,
14
  setDoc,
15
  updateDoc,
 
16
  collection,
17
  addDoc,
18
  increment,
@@ -49,6 +50,7 @@ export default function App() {
49
  const coinIntervalRef = useRef(null);
50
  const localCoinBufferRef = useRef(0);
51
 
 
52
  useEffect(() => {
53
  const initFirebase = async () => {
54
  try {
@@ -72,6 +74,7 @@ export default function App() {
72
  initFirebase();
73
  }, []);
74
 
 
75
  useEffect(() => {
76
  if (!firebaseReady || !auth) return;
77
  const unsubscribeAuth = onAuthStateChanged(auth, (user) => {
@@ -88,6 +91,7 @@ export default function App() {
88
  return () => unsubscribeAuth();
89
  }, [firebaseReady]);
90
 
 
91
  useEffect(() => {
92
  if (!firebaseReady || !userId || !db) return;
93
  const profileRef = doc(db, 'users', userId);
@@ -107,9 +111,10 @@ export default function App() {
107
  return () => unsubscribeProfile();
108
  }, [userId, user, firebaseReady]);
109
 
 
110
  useEffect(() => {
111
  if (!firebaseReady || !db) return;
112
- const q = query(collection(db, 'videos'), orderBy('createdAt', 'desc'), limit(20));
113
  const unsubscribe = onSnapshot(q, (snapshot) => {
114
  const fetchedVideos = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
115
  if (fetchedVideos.length > 0) {
@@ -121,6 +126,7 @@ export default function App() {
121
  return () => unsubscribe();
122
  }, [firebaseReady]);
123
 
 
124
  useEffect(() => {
125
  if (!firebaseReady || !db) return;
126
  const q = query(collection(db, 'users'), orderBy('coins', 'desc'), limit(20));
@@ -131,6 +137,7 @@ export default function App() {
131
  return () => unsubscribe();
132
  }, [firebaseReady]);
133
 
 
134
  useEffect(() => {
135
  if (!window.YT) {
136
  const tag = document.createElement('script');
@@ -143,6 +150,7 @@ export default function App() {
143
  }
144
  }, []);
145
 
 
146
  const saveCoinBuffer = useCallback(() => {
147
  if (localCoinBufferRef.current > 0 && userId && db) {
148
  const coinsToSave = localCoinBufferRef.current;
@@ -210,6 +218,19 @@ export default function App() {
210
  if (playerRef.current) playerRef.current.loadVideoById(video.videoId);
211
  };
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  const handleGoogleLogin = async () => {
214
  if (!auth) return;
215
  const provider = new GoogleAuthProvider();
@@ -312,19 +333,33 @@ export default function App() {
312
  <h3 className="text-gray-400 text-sm uppercase font-bold mb-2">Lista de Reproducción</h3>
313
  <div className="overflow-y-auto flex-1 pr-2 custom-scrollbar space-y-2">
314
  {videos.map((video) => (
315
- <button key={video.id} onClick={() => loadVideo(video)} className={`w-full text-left p-2 rounded ${video.videoId === currentVideoId ? 'bg-blue-900 border-l-2 border-blue-500' : 'bg-gray-700 hover:bg-gray-600'}`}>
316
- <div className="text-sm text-white truncate">{video.title}</div>
 
 
 
 
 
 
 
 
 
 
 
317
  </button>
318
  ))}
319
  </div>
320
  </div>
 
 
321
  <div className="bg-gray-800 rounded p-4 flex-1 flex flex-col min-h-[200px]">
322
  <h3 className="text-yellow-500 text-sm uppercase font-bold mb-2">Ranking Global</h3>
323
  <div className="overflow-y-auto flex-1 pr-2 custom-scrollbar space-y-2">
324
  {leaderboard.map((u, i) => (
325
  <div key={u.id || i} className="flex justify-between p-2 bg-gray-700 rounded text-sm">
 
326
  <span className="text-white text-xs md:text-sm truncate mr-2" title={u.email}>
327
- #{i+1} {u.email}
328
  </span>
329
  <span className="text-yellow-400 font-mono whitespace-nowrap">{Math.floor(u.coins)}</span>
330
  </div>
 
13
  onSnapshot,
14
  setDoc,
15
  updateDoc,
16
+ deleteDoc, // <--- IMPORTANTE: Agregado para borrar
17
  collection,
18
  addDoc,
19
  increment,
 
50
  const coinIntervalRef = useRef(null);
51
  const localCoinBufferRef = useRef(0);
52
 
53
+ // --- 1. INIT ---
54
  useEffect(() => {
55
  const initFirebase = async () => {
56
  try {
 
74
  initFirebase();
75
  }, []);
76
 
77
+ // --- 2. AUTH ---
78
  useEffect(() => {
79
  if (!firebaseReady || !auth) return;
80
  const unsubscribeAuth = onAuthStateChanged(auth, (user) => {
 
91
  return () => unsubscribeAuth();
92
  }, [firebaseReady]);
93
 
94
+ // --- 3. PROFILE ---
95
  useEffect(() => {
96
  if (!firebaseReady || !userId || !db) return;
97
  const profileRef = doc(db, 'users', userId);
 
111
  return () => unsubscribeProfile();
112
  }, [userId, user, firebaseReady]);
113
 
114
+ // --- 4. VIDEOS ---
115
  useEffect(() => {
116
  if (!firebaseReady || !db) return;
117
+ const q = query(collection(db, 'videos'), orderBy('createdAt', 'desc'), limit(50));
118
  const unsubscribe = onSnapshot(q, (snapshot) => {
119
  const fetchedVideos = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
120
  if (fetchedVideos.length > 0) {
 
126
  return () => unsubscribe();
127
  }, [firebaseReady]);
128
 
129
+ // --- 5. RANKING ---
130
  useEffect(() => {
131
  if (!firebaseReady || !db) return;
132
  const q = query(collection(db, 'users'), orderBy('coins', 'desc'), limit(20));
 
137
  return () => unsubscribe();
138
  }, [firebaseReady]);
139
 
140
+ // --- 6. YOUTUBE API ---
141
  useEffect(() => {
142
  if (!window.YT) {
143
  const tag = document.createElement('script');
 
150
  }
151
  }, []);
152
 
153
+ // --- LOGICA DE MONEDAS ---
154
  const saveCoinBuffer = useCallback(() => {
155
  if (localCoinBufferRef.current > 0 && userId && db) {
156
  const coinsToSave = localCoinBufferRef.current;
 
218
  if (playerRef.current) playerRef.current.loadVideoById(video.videoId);
219
  };
220
 
221
+ // --- FUNCION NUEVA: BORRAR VIDEO (SOLO ADMIN) ---
222
+ const handleDeleteVideo = async (e, videoId) => {
223
+ e.stopPropagation(); // Evita que al borrar se reproduzca el video
224
+ if (!confirm("¿Estás seguro de que quieres ELIMINAR este video?")) return;
225
+
226
+ try {
227
+ await deleteDoc(doc(db, "videos", videoId));
228
+ } catch (error) {
229
+ console.error("Error borrando:", error);
230
+ alert("No tienes permisos para borrar este video.");
231
+ }
232
+ };
233
+
234
  const handleGoogleLogin = async () => {
235
  if (!auth) return;
236
  const provider = new GoogleAuthProvider();
 
333
  <h3 className="text-gray-400 text-sm uppercase font-bold mb-2">Lista de Reproducción</h3>
334
  <div className="overflow-y-auto flex-1 pr-2 custom-scrollbar space-y-2">
335
  {videos.map((video) => (
336
+ <button key={video.id} onClick={() => loadVideo(video)} className={`w-full text-left p-2 rounded flex justify-between items-center group ${video.videoId === currentVideoId ? 'bg-blue-900 border-l-2 border-blue-500' : 'bg-gray-700 hover:bg-gray-600'}`}>
337
+ <div className="text-sm text-white truncate flex-1">{video.title}</div>
338
+
339
+ {/* BOTÓN DE BORRAR (SOLO ADMIN) */}
340
+ {user?.email === ADMIN_EMAIL && (
341
+ <span
342
+ onClick={(e) => handleDeleteVideo(e, video.id)}
343
+ className="ml-2 text-red-500 hover:text-red-300 font-bold px-2 py-1 rounded bg-red-900/50 hover:bg-red-900"
344
+ title="Borrar Video"
345
+ >
346
+ X
347
+ </span>
348
+ )}
349
  </button>
350
  ))}
351
  </div>
352
  </div>
353
+
354
+ {/* RANKING CORREGIDO */}
355
  <div className="bg-gray-800 rounded p-4 flex-1 flex flex-col min-h-[200px]">
356
  <h3 className="text-yellow-500 text-sm uppercase font-bold mb-2">Ranking Global</h3>
357
  <div className="overflow-y-auto flex-1 pr-2 custom-scrollbar space-y-2">
358
  {leaderboard.map((u, i) => (
359
  <div key={u.id || i} className="flex justify-between p-2 bg-gray-700 rounded text-sm">
360
+ {/* Extrae el nombre antes del @ */}
361
  <span className="text-white text-xs md:text-sm truncate mr-2" title={u.email}>
362
+ #{i+1} {u.email ? u.email.split('@')[0] : 'Usuario'}
363
  </span>
364
  <span className="text-yellow-400 font-mono whitespace-nowrap">{Math.floor(u.coins)}</span>
365
  </div>