import React, { useState, useEffect } from 'react'; import { initializeApp } from 'firebase/app'; import { getAuth, signInWithCustomToken, signInAnonymously, onAuthStateChanged, signOut } from 'firebase/auth'; import { getFirestore, collection, addDoc, deleteDoc, updateDoc, doc, query, onSnapshot, serverTimestamp } from 'firebase/firestore'; import { Plus, Trash2, CheckCircle, Circle, Loader2, LogOut, AlertCircle } from 'lucide-react'; // --- Firebase Configuration --- const firebaseConfig = JSON.parse(__firebase_config); const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id'; // --- Main Component --- export default function App() { const [user, setUser] = useState(null); const [tasks, setTasks] = useState([]); const [newTask, setNewTask] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // 1. Authentication Effect useEffect(() => { let mounted = true; const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { await signInAnonymously(auth); } } catch (err) { console.error("Auth failed:", err); if (mounted) setError("Authentication failed. Please refresh."); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, (u) => { if (mounted) { setUser(u); // Only stop loading if we have a user (or auth failed previously) if (!u) setLoading(false); } }); return () => { mounted = false; unsubscribe(); }; }, []); // 2. Data Fetching Effect (Strictly dependent on 'user') useEffect(() => { if (!user) return; // <--- CRITICAL FIX: Stop execution if no user // Use a simplified query to avoid index errors (no orderBy/where initially) // We sort in memory instead. const q = query(collection(db, 'artifacts', appId, 'public', 'data', 'todos')); const unsubscribe = onSnapshot(q, (snapshot) => { const fetchedTasks = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); // In-memory sort: Incomplete first, then by creation time fetchedTasks.sort((a, b) => { if (a.completed === b.completed) { return (b.createdAt?.seconds || 0) - (a.createdAt?.seconds || 0); } return a.completed ? 1 : -1; }); setTasks(fetchedTasks); setLoading(false); setError(null); }, (err) => { console.error("Firestore error:", err); setError("Missing permissions or connection issue."); setLoading(false); } ); return () => unsubscribe(); }, [user]); // <--- Reruns only when user status changes // --- Handlers --- const handleAddTask = async (e) => { e.preventDefault(); if (!newTask.trim() || !user) return; try { await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'todos'), { text: newTask, completed: false, createdAt: serverTimestamp(), userId: user.uid, // Store who created it createdBy: user.isAnonymous ? 'Anonymous' : (user.displayName || 'User') }); setNewTask(''); } catch (err) { console.error("Error adding task:", err); setError("Failed to add task."); } }; const toggleTask = async (task) => { try { await updateDoc(doc(db, 'artifacts', appId, 'public', 'data', 'todos', task.id), { completed: !task.completed }); } catch (err) { console.error("Error updating task:", err); } }; const deleteTask = async (taskId) => { try { await deleteDoc(doc(db, 'artifacts', appId, 'public', 'data', 'todos', taskId)); } catch (err) { console.error("Error deleting task:", err); } }; // --- Render --- if (loading) { return (
Shared list • {user ? (user.isAnonymous ? 'Anonymous Guest' : 'Signed In') : 'Offline'}
{error}
Start by adding a new task above.
{task.text}
Data is stored in collection: public/data/todos