Create README.md
Browse filesimport React, { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
// Firebase imports
import { initializeApp } from 'firebase/app';
import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from 'firebase/auth';
import { getFirestore, doc, getDoc, setDoc, onSnapshot, collection, query, orderBy, addDoc } from 'firebase/firestore';
// Shadcn/ui imports
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './components/ui/card';
import { Button } from './components/ui/button';
import { Input } from './components/ui/input';
import { Textarea } from './components/ui/textarea';
import { useToast, Toast, ToastProvider, ToastViewport } from './components/ui/toast';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from './components/ui/dialog';
// Lucide-react icons
import { Rss, MessageCircle, User, Feather, Send, Edit, X } from 'lucide-react';
const __app_id = "your-app-id-here"; // This will be automatically replaced by the system
const __firebase_config = "{}"; // This will be automatically replaced by the system
const __initial_auth_token = "your-auth-token-here"; // This will be automatically replaced by the system
const firebaseConfig = JSON.parse(__firebase_config);
// Component for the Toast notification system
const Toaster = () => {
const { toasts } = useToast();
return createPortal(
<ToastProvider>
{toasts.map(({ id, title, description, action, ...props }) => (
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && <ToastDescription>{description}</ToastDescription>}
</div>
{action}
<ToastClose />
</Toast>
))}
<ToastViewport />
</ToastProvider>,
document.body
);
};
// Component for a custom modal dialog
const CustomModal = ({ isOpen, title, description, onClose, children }) => {
if (!isOpen) return null;
return createPortal(
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div className="w-11/12 max-w-md rounded-lg bg-white p-6 shadow-lg dark:bg-gray-800">
<div className="flex items-center justify-between">
<h2 className="text-xl font-bold">{title}</h2>
<Button variant="ghost" onClick={onClose}>
<X size={24} />
</Button>
</div>
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">{description}</p>
<div className="mt-4">{children}</div>
</div>
</div>,
document.body
);
};
// Helper function to create a unique ID for users not yet in Firestore
const createUserId = () => {
return 'user_' + crypto.randomUUID().slice(0, 8);
};
// Main application component
const App = () => {
const [view, setView] = useState('feed'); // 'feed', 'messages', 'profile'
const [db, setDb] = useState(null);
const [auth, setAuth] = useState(null);
const [userId, setUserId] = useState(null);
const [isAuthReady, setIsAuthReady] = useState(false);
const [userProfile, setUserProfile] = useState(null);
const [posts, setPosts] = useState([]);
const [messages, setMessages] = useState([]);
const [newPostContent, setNewPostContent] = useState('');
const [newMessageContent, setNewMessageContent] = useState('');
const [profileBio, setProfileBio] = useState('');
const [profileUsername, setProfileUsername] = useState('');
const { toast } = useToast();
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';
// State for the custom modal
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalType, setModalType] = useState(null); // 'edit-profile', 'create-post'
// Initialize Firebase and set up authentication
useEffect(() => {
try {
const app = initializeApp(firebaseConfig);
const firestore = getFirestore(app);
const authInstance = getAuth(app);
setDb(firestore);
setAuth(authInstance);
onAuthStateChanged(authInstance, async (user) => {
if (user) {
setUserId(user.uid);
setIsAuthReady(true);
} else {
// If no user, sign in anonymously
await signInAnonymously(authInstance);
}
});
if (typeof __initial_auth_token !== 'undefined') {
signInWithCustomToken(authInstance, __initial_auth_token);
}
} catch (error) {
console.error("Error initializing Firebase:", error);
toast({
title: "Initialization Error",
description: "Failed to initialize Firebase. Check console for details.",
variant: "destructive"
});
}
}, []);
// Fetch or create user profile when auth is ready
useEffect(() => {
if (db && userId) {
const userDocRef = doc(db, `/artifacts/${appId}/users/${userId}/profile/info`);
const unsubscribe = onSnapshot(userDocRef, (docSnap) => {
if (docSnap.exists()) {
const profileData = docSnap.data();
setUserProfile(profileData);
setProfileBio(profileData.bio || '');
setProfileUsername(profileData.username || createUserId());
} else {
// Create a new profile if it doesn't exist
const newProfile = {
username: createUserId(),
profilePictureUrl: `https://placehold.co/100x100/3b82f6/ffffff?text=${createUserId().slice(-4)}`,
bio: 'Hello, this is my new profile!',
};
setDoc(userDocRef, newProfile).then(() => {
setUserProfile(newProfile);
setProfileBio(newProfile.bio);
setProfileUsername(newProfile.username);
}).catch(console.error);
}
}, console.error);
return () => unsubscribe();
}
}, [db, userId]);
// Fetch posts and messages when auth is ready
useEffect(() => {
if (db && isAuthReady) {
// Set up real-time listener for posts
const postsCollectionRef = collection(db, `/artifacts/${appId}/public/data/posts`);
const postsQuery = query(postsCollectionRef);
const unsubscribePosts = onSnapshot(postsQuery, (querySnapshot) => {
const fetchedPosts = querySnapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
// Ensure a default username is used if not present
username: doc.data().username || `user_${doc.data().userId?.slice(-4)}`,
// Ensure a default profile picture is used
profilePictureUrl: doc.data().profilePictureUrl || `https://placehold.co/100x100/3b82f6/ffffff?text=${doc.data().username?.slice(-4) || '??'}`,
}));
setPosts(fetchedPosts.sort((a, b) => b.timestamp - a.timestamp));
}, console.error);
// Set up real-time listener for messages
const messagesCollectionRef = collection(db, `/artifacts/${appId}/public/data/messages`);
const messagesQuery = query(messagesCollectionRef);
const unsubscribeMessages = onSnapshot(messagesQuery, (querySnapshot) => {
const fetchedMessages = querySnapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}));
setMessages(fetchedMessages.sort((a, b) => b.timestamp - a.timestamp));
}, console.error);
return () => {
unsubscribePosts();
unsubscribeMessages();
};
}
}, [db, isAuthReady]);
// Handle post creation
const handleCreatePost = async () => {
if (!newPostContent.trim()) {
toast({
title: "Content Missing",
description: "Post content cannot be empty.",
variant: "destructive"
});
return;
}
if (!userId || !userProfile) {
toast({
title: "Error",
description: "User not authenticated. Please try again.",
variant: "destructive"
});
return;
}
try {
const postsCollectionRef = collection(db, `/artifacts/${appId}/public/data/posts`);
await addDoc(postsCollectionRef, {
userId: userId,
username: userProfile.username,
profilePictureUrl: userProfile.profilePictureUrl,
content: newPostContent,
timestamp: Date.now(),
likes: 0,
});
setNewPostContent('');
setIsModalOpen(false);
toast({
title: "Success!",
description: "Your new post has been published.",
});
} catch (e) {
console.error("Error adding document: ", e);
toast({
title: "Post Error",
description: "Failed to publish post. Check console for details.",
variant: "destructive"
});
}
};
// Handle message sending
const handleSendMessage = async (e) => {
e.preventDefault();
if (!newMessageContent.trim()) {
toast({
title: "Content Missing",
description: "Message content cannot be empty.",
variant: "destructive"
});
return;
}
if (!userId || !userProfile) {
toast({
title: "Error",
description: "User not authenticated. Please try again.",
variant: "destructive"
});
return;
}
try {
const messagesCollectionRef = collection(db, `/artifacts/${appId}/public/data/messages`);
await addDoc(messagesCollectionRef, {
senderId: userId,
username: userProfile.username,
content: newMessageContent,
timestamp: Date.now(),
});
setNewMessageContent('');
} catch (e) {
console.error("Error adding document: ", e);
toast({
title: "Message Error",
description: "Failed to send message. Check console for details.",
variant: "destructive"
});
}
};
// Handle profile update
const handleUpdateProfile = async () => {
if (!profileBio.trim() || !profileUsername.trim()) {
toast({
title: "Input Error",
description: "Username and bio cannot be empty.",
variant: "destructive"
});
return;
}
if (!userId) {
toast({
title: "Error",
description: "User not authenticated. Please try again.",
variant: "destructive"
});
return;
}
t