Add a New Research Idea
Loading your research...
No research notes yet
Get started by adding a new research idea above.
import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { initializeApp } from 'firebase/app'; import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from 'firebase/auth'; import { getFirestore, collection, doc, addDoc, setDoc, deleteDoc, onSnapshot, query, where, getDocs, writeBatch } from 'firebase/firestore'; // --- Helper Functions & Constants --- const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {}; const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-phd-notes'; // --- Main App Component --- export default function App() { const [auth, setAuth] = useState(null); const [db, setDb] = useState(null); const [userId, setUserId] = useState(null); const [isAuthReady, setIsAuthReady] = useState(false); const [notes, setNotes] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [newNoteContent, setNewNoteContent] = useState(''); // --- Firebase Initialization --- useEffect(() => { try { const app = initializeApp(firebaseConfig); const authInstance = getAuth(app); const dbInstance = getFirestore(app); setAuth(authInstance); setDb(dbInstance); const unsubscribe = onAuthStateChanged(authInstance, async (user) => { if (user) { setUserId(user.uid); } else { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(authInstance, __initial_auth_token); } else { await signInAnonymously(authInstance); } } catch (authError) { console.error("Authentication Error:", authError); setError("Failed to authenticate. Please refresh the page."); } } setIsAuthReady(true); }); return () => unsubscribe(); } catch (e) { console.error("Firebase initialization failed:", e); setError("Could not connect to the database. Please check your connection or configuration."); setLoading(false); } }, []); // --- Data Fetching (Notes) --- useEffect(() => { if (!isAuthReady || !db || !userId) return; setLoading(true); const notesCollectionPath = `artifacts/${appId}/users/${userId}/notes`; const q = query(collection(db, notesCollectionPath)); const unsubscribe = onSnapshot(q, (snapshot) => { const notesData = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); setNotes(notesData); setLoading(false); }, (err) => { console.error("Firestore Snapshot Error:", err); setError("Failed to load notes. Please try again later."); setLoading(false); }); return () => unsubscribe(); }, [isAuthReady, db, userId]); // --- Data Transformation (Build Tree) --- const noteTree = useMemo(() => { const noteMap = new Map(notes.map(note => [note.id, { ...note, children: [] }])); const tree = []; noteMap.forEach(note => { if (note.parentId && noteMap.has(note.parentId)) { const parent = noteMap.get(note.parentId); parent.children.push(note); } else { tree.push(note); } }); return tree; }, [notes]); // --- Core Firestore Actions --- const handleAddNote = useCallback(async (content, parentId = null) => { if (!db || !userId || !content.trim()) return; try { const notesCollectionPath = `artifacts/${appId}/users/${userId}/notes`; await addDoc(collection(db, notesCollectionPath), { content: content.trim(), parentId, createdAt: new Date(), userId, }); if (!parentId) { setNewNoteContent(''); } } catch (err) { console.error("Error adding note:", err); setError("Could not add note."); } }, [db, userId]); const handleUpdateNote = useCallback(async (id, newContent) => { if (!db || !userId || !newContent.trim()) return; try { const noteDocPath = `artifacts/${appId}/users/${userId}/notes/${id}`; await setDoc(doc(db, noteDocPath), { content: newContent.trim() }, { merge: true }); } catch (err) { console.error("Error updating note:", err); setError("Could not update note."); } }, [db, userId]); const handleDeleteNote = useCallback(async (noteIdToDelete) => { if (!db || !userId) return; try { const batch = writeBatch(db); const notesCollectionPath = `artifacts/${appId}/users/${userId}/notes`; // Find all descendants const descendants = new Set([noteIdToDelete]); let searchQueue = [noteIdToDelete]; while(searchQueue.length > 0){ const currentId = searchQueue.shift(); const q = query(collection(db, notesCollectionPath), where("parentId", "==", currentId)); const snapshot = await getDocs(q); snapshot.forEach(doc => { if (!descendants.has(doc.id)) { descendants.add(doc.id); searchQueue.push(doc.id); } }); } // Delete the note and all its descendants descendants.forEach(id => { const noteDocPath = `artifacts/${appId}/users/${userId}/notes/${id}`; batch.delete(doc(db, noteDocPath)); }); await batch.commit(); } catch (err) { console.error("Error deleting note and its children:", err); setError("Could not delete the note branch."); } }, [db, userId]); // --- Render Logic --- if (error) { return
Error
{error}
Loading your research...
Get started by adding a new research idea above.
{note.content}
)}