Loading Source Connection...

${username}'s Profile

User ID: ${userIdDisplay}

Bio

${bio}

Social Media

`; } // --- NEW: Function to open public profile in a new window --- async function openPublicProfile(userId) { if (!dbInstance) { console.error("Firestore not initialized."); return; } let profileData = profilesCache[userId]; if (!profileData) { try { const profileRef = doc(dbInstance, `artifacts/${appId}/users/${userId}/profiles/main`); const docSnap = await getDoc(profileRef); if (docSnap.exists()) { profileData = docSnap.data(); profilesCache[userId] = profileData; // Cache it for future use } else { profileData = { username: `Anonymous${userId.substring(0, 6)}`, bio: 'No bio set for this user.', instagram: '', tiktok: '', facebook: '', isSilenced: false }; } } catch (error) { console.error("Error fetching profile for public view:", error); profileData = { username: `Anonymous${userId.substring(0, 6)}`, bio: 'Error loading bio.', instagram: '', tiktok: '', facebook: '', isSilenced: false }; } } // Add userId to profileData for the template profileData.userId = userId; const htmlContent = generatePublicProfileHtml(profileData); const dataUrl = 'data:text/html;charset=utf-8,' + encodeURIComponent(htmlContent); window.open(dataUrl, '_blank'); } // --- Authentication Functions --- async function handleLogin() { const email = document.getElementById('authEmail').value; const password = document.getElementById('authPassword').value; authError = ''; // Clear previous errors if (!email || !password) { authError = 'Email and password cannot be empty.'; renderAppStructure(); // Re-render to show error return; } try { await signInWithEmailAndPassword(authInstance, email, password); console.log('User logged in successfully!'); } catch (error) { console.error("Login error:", error); authError = `Login failed: ${error.message}`; renderAppStructure(); // Re-render to show error } } async function handleRegister() { const email = document.getElementById('authEmail').value; const password = document.getElementById('authPassword').value; authError = ''; // Clear previous errors if (!email || !password) { authError = 'Email and password cannot be empty.'; renderAppStructure(); // Re-render to show error return; } try { await createUserWithEmailAndPassword(authInstance, email, password); console.log('User registered successfully!'); } catch (error) { console.error("Registration error:", error); authError = `Registration failed: ${error.message}`; renderAppStructure(); // Re-render to show error } } async function handleLogout() { try { unsubscribeAllListeners(); // Clear all listeners before logging out await signOut(authInstance); console.log('User logged out.'); // onAuthStateChanged will handle renderAppStructure() for logged out state } catch (error) { console.error("Logout error:", error); } } // --- Profile Functions --- async function handleSaveProfile() { const newUsername = document.getElementById('usernameInput').value.trim(); const newBio = document.getElementById('bioInput').value.trim(); const newInstagram = document.getElementById('instagramInput').value.trim(); const newTiktok = document.getElementById('tiktokInput').value.trim(); const newFacebook = document.getElementById('facebookInput').value.trim(); if (!dbInstance || !currentUserId) { console.error("Firestore or User ID not available."); return; } if (!newUsername) { console.log("Username cannot be empty."); // Optionally show a message to the user return; } try { const userProfileRef = doc(dbInstance, `artifacts/${appId}/users/${currentUserId}/profiles/main`); await setDoc(userProfileRef, { username: newUsername, bio: newBio, instagram: newInstagram, tiktok: newTiktok, facebook: newFacebook }, { merge: true }); // Update local state and cache currentUsername = newUsername; userBio = newBio; instagramHandle = newInstagram; tiktokHandle = newTiktok; facebookHandle = newFacebook; profilesCache[currentUserId] = { ...profilesCache[currentUserId], // Preserve isSilenced if it exists username: currentUsername, bio: userBio, instagram: instagramHandle, tiktok: tiktokHandle, facebook: facebookHandle }; // No need to call renderAppStructure here, onSnapshot for profile will handle it console.log("Profile saved successfully!"); } catch (error) { console.error("Error saving profile:", error); } } // Removed fetchAndSetProfileForDisplay as it's no longer needed for internal sidebar view // --- Chat Functions --- async function handleSendMessage(event) { event.preventDefault(); // Prevent page reload const newMessageInput = document.getElementById('newMessageInput'); const messageText = newMessageInput.value.trim(); if (!dbInstance || !currentUserId || !currentUsername || !messageText) { console.log("Cannot send empty message or without a username."); return; } // Prevent sending message if current user is silenced if (isCurrentUserSilenced) { console.log("Cannot send message: You are silenced."); // Optionally provide visual feedback to the user that they are silenced return; } try { const chatCollectionRef = collection(dbInstance, `artifacts/${appId}/public/data/chatMessages`); await addDoc(chatCollectionRef, { userId: currentUserId, username: currentUsername, message: messageText, timestamp: serverTimestamp(), type: 'chat' // Explicitly mark as a chat message }); newMessageInput.value = ''; console.log("Message sent successfully!"); } catch (error) { console.error("Error sending message:", error); } } // --- Admin Functions --- async function handleToggleSilence(targetUserId, currentSilencedStatus) { if (!dbInstance || !currentUserId || !ADMIN_UIDS.includes(currentUserId)) { console.log("Permission denied: Not an admin or not logged in."); return; } if (targetUserId === currentUserId) { console.log("Cannot silence yourself."); return; } try { const targetUserProfileRef = doc(dbInstance, `artifacts/${appId}/users/${targetUserId}/profiles/main`); await setDoc(targetUserProfileRef, { isSilenced: !currentSilencedStatus }, { merge: true }); // Update local cache immediately to reflect change if (profilesCache[targetUserId]) { profilesCache[targetUserId].isSilenced = !currentSilencedStatus; } console.log(`User ${targetUserId} silence status toggled to: ${!currentSilencedStatus}`); // Re-render relevant sections to reflect changes renderChatMessages(); // Re-render chat to hide/show messages renderActiveUsers(); // Re-render active users to show (Silenced) tag } catch (error) { console.error("Error toggling silence status:", error); } } // --- Listener Management Functions --- function unsubscribeAllListeners() { console.log("Unsubscribing all listeners and clearing intervals."); if (profileUnsubscribe) { profileUnsubscribe(); profileUnsubscribe = null; } if (chatUnsubscribe) { chatUnsubscribe(); chatUnsubscribe = null; } if (presenceUnsubscribe) { presenceUnsubscribe(); presenceUnsubscribe = null; } if (presenceInterval) { clearInterval(presenceInterval); presenceInterval = null; } } // Sets up all real-time Firestore listeners for a logged-in user async function setupFirebaseListeners(userId) { console.log("Setting up Firebase listeners for user:", userId); unsubscribeAllListeners(); // Ensure clean slate before setting new listeners // Listener for current user's profile (for username, bio, isSilenced etc.) profileUnsubscribe = onSnapshot(doc(dbInstance, `artifacts/${appId}/users/${userId}/profiles/main`), (docSnap) => { if (docSnap.exists()) { const data = docSnap.data(); currentUsername = data.username || ''; userBio = data.bio || ''; instagramHandle = data.instagram || ''; tiktokHandle = data.tiktok || ''; facebookHandle = data.facebook || ''; isCurrentUserSilenced = data.isSilenced || false; // Get silenced status } else { // If no profile exists, set a default username and create a basic profile currentUsername = `Anonymous${userId.substring(0, 6)}`; isCurrentUserSilenced = false; // Default to not silenced // This initial setDoc ensures a profile exists for new users setDoc(doc(dbInstance, `artifacts/${appId}/users/${userId}/profiles/main`), { username: currentUsername, isSilenced: false }, { merge: true }) .then(() => console.log("Default profile created for new user.")) .catch(e => console.error("Error creating default profile:", e)); userBio = ''; instagramHandle = ''; tiktokHandle = ''; facebookHandle = ''; } profilesCache[userId] = { // Update cache for current user username: currentUsername, bio: userBio, instagram: instagramHandle, tiktok: tiktokHandle, facebook: facebookHandle, isSilenced: isCurrentUserSilenced // Store silenced status in cache }; profileLoaded = true; console.log('Current user profile data loaded/updated:', { currentUsername, userBio, isCurrentUserSilenced }); renderAppStructure(); // Re-render main UI to update username/silenced status in header/profile page/chat input }, (error) => { console.error("Error fetching current user profile:", error); profileLoaded = true; renderAppStructure(); }); // Listener for chat messages const chatCollectionRef = collection(dbInstance, `artifacts/${appId}/public/data/chatMessages`); const qChat = query(chatCollectionRef, orderBy('timestamp', 'asc')); chatUnsubscribe = onSnapshot(qChat, (snapshot) => { chatMessages = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); console.log('Chat messages updated.'); if (currentPage === 'chat') { // Only update chat display if on chat page renderChatMessages(); } }, (error) => { console.error("Error fetching chat messages:", error); }); // Setup presence update interval updatePresence(); // Initial presence update presenceInterval = setInterval(updatePresence, 15000); // Update every 15 seconds // Listener for active users presence const presenceCollectionRef = collection(dbInstance, `artifacts/${appId}/public/data/presence`); const qPresence = query(presenceCollectionRef); presenceUnsubscribe = onSnapshot(qPresence, async (snapshot) => { const now = Date.now(); const fetchedActiveUsers = snapshot.docs.filter(doc => { const data = doc.data(); // Consider a user active if their lastSeen is within the last 30 seconds return data.lastSeen && (now - data.lastSeen.toDate().getTime() < 30000); }).map(doc => ({ userId: doc.id, username: doc.data().username || doc.id.substring(0,6) // Use username from presence or truncated ID })); // Fetch profiles for active users not yet in cache or if their silenced status might have changed for (const user of fetchedActiveUsers) { // Only fetch if not in cache or if we don't have isSilenced status if (!profilesCache[user.userId] || profilesCache[user.userId].isSilenced === undefined) { try { const profileRef = doc(dbInstance, `artifacts/${appId}/users/${user.userId}/profiles/main`); const docSnap = await getDoc(profileRef); if (docSnap.exists()) { profilesCache[user.userId] = docSnap.data(); } else { profilesCache[user.userId] = { username: `Anonymous${user.userId.substring(0, 6)}`, bio: 'No bio set.', instagram: '', tiktok: '', facebook: '', isSilenced: false // Default to not silenced }; } } catch (e) { console.error("Error fetching profile for active user:", user.userId, e); profilesCache[user.userId] = { username: `Anonymous${user.userId.substring(0, 6)}`, bio: 'Error loading bio.', instagram: '', tiktok: '', facebook: '', isSilenced: false // Default to not silenced on error }; } } } activeUsers = fetchedActiveUsers; // Update the global activeUsers array console.log('Active users updated.'); if (currentPage === 'chat') { // Only update if on chat page renderActiveUsers(); // Update active users list renderChatMessages(); // Re-render chat messages to reflect any silence status changes } }, (error) => { console.error("Error fetching active users:", error); }); } async function updatePresence() { if (!dbInstance || !currentUserId) return; const userPresenceRef = doc(dbInstance, `artifacts/${appId}/public/data/presence`, currentUserId); try { await setDoc(userPresenceRef, { username: currentUsername || `Anonymous${currentUserId.substring(0, 6)}`, // Use currentUsername lastSeen: serverTimestamp() }, { merge: true }); console.log("Presence updated for:", currentUserId); } catch (error) { console.error("Error updating presence:", error); } } // --- Initialize Firebase and App --- document.addEventListener('DOMContentLoaded', async () => { appInstance = initializeApp(firebaseConfig); dbInstance = getFirestore(appInstance); authInstance = getAuth(appInstance); // Ensure persistent login await setPersistence(authInstance, browserLocalPersistence); console.log("Auth persistence set to LOCAL."); // This is the primary entry point for UI updates based on authentication state onAuthStateChanged(authInstance, async (user) => { if (user) { currentUserId = user.uid; currentUserEmail = user.email; console.log("User authenticated:", currentUserId); // setupFirebaseListeners will fetch profile and trigger renderAppStructure setupFirebaseListeners(currentUserId); } else { console.log("No user is signed in. Showing login/register."); currentUserId = null; currentUserEmail = null; currentUsername = ''; isCurrentUserSilenced = false; // Reset silenced status on logout profileLoaded = false; unsubscribeAllListeners(); renderAppStructure(); // Render login/register form } }); // Initial render call for the loading state or auth form // This ensures something is displayed immediately renderAppStructure(); });