Curso de Firebase - Proyecto Final

Portada del Curso de Firebase

Proyecto Final: Red Social con Firebase

Propuesta de Aplicación: "SocialFire"

Características principales:

  1. App tipo red social con perfiles de usuario y sistema de seguimiento
  2. Publicaciones con imágenes usando Firebase Storage
  3. Tiempo real con Firestore
  4. Hosting en Firebase para despliegue

Estructura del Proyecto

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SocialFire - Red Social con Firebase</title>
    <script src="https://www.gstatic.com/firebasejs/9.0.0/firebase-app-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.0.0/firebase-auth-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore-compat.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.0.0/firebase-storage-compat.js"></script>
    <style>
        /* Estilos CSS para la aplicación */
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 0;
            padding: 0;
            background-color: #f5f5f5;
        }
        .header {
            background-color: #4285f4;
            color: white;
            padding: 15px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .post-container {
            max-width: 600px;
            margin: 20px auto;
            background: white;
            border-radius: 8px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.12);
            padding: 15px;
        }
        .post-image {
            width: 100%;
            border-radius: 4px;
            margin-top: 10px;
        }
        .new-post {
            display: flex;
            flex-direction: column;
            margin-bottom: 20px;
        }
        textarea {
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 10px;
            margin-bottom: 10px;
            resize: vertical;
        }
        button {
            background-color: #4285f4;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 4px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>SocialFire</h1>
        <div id="auth-container"></div>
    </div>

    <div class="container">
        <div id="new-post-container" class="post-container" style="display: none;">
            <h2>Crear nueva publicación</h2>
            <div class="new-post">
                <textarea id="post-text" placeholder="¿Qué estás pensando?"></textarea>
                <input type="file" id="post-image" accept="image/*">
                <button id="post-button">Publicar</button>
            </div>
        </div>

        <div id="posts-container"></div>
    </div>

    <script>
        // Configuración de Firebase
        const firebaseConfig = {
            apiKey: "TU_API_KEY",
            authDomain: "TU_PROYECTO.firebaseapp.com",
            projectId: "TU_PROYECTO",
            storageBucket: "TU_PROYECTO.appspot.com",
            messagingSenderId: "TU_SENDER_ID",
            appId: "TU_APP_ID"
        };

        // Inicializar Firebase
        firebase.initializeApp(firebaseConfig);
        const auth = firebase.auth();
        const db = firebase.firestore();
        const storage = firebase.storage();

        // Elementos del DOM
        const authContainer = document.getElementById('auth-container');
        const newPostContainer = document.getElementById('new-post-container');
        const postsContainer = document.getElementById('posts-container');
        const postButton = document.getElementById('post-button');
        const postText = document.getElementById('post-text');
        const postImage = document.getElementById('post-image');

        // Manejar autenticación
        auth.onAuthStateChanged(user => {
            if (user) {
                // Usuario logueado
                authContainer.innerHTML = `
                    <span>Hola, ${user.displayName || user.email}</span>
                    <button id="logout-button">Cerrar sesión</button>
                `;
                document.getElementById('logout-button').addEventListener('click', () => auth.signOut());
                newPostContainer.style.display = 'block';
                loadPosts();
            } else {
                // Usuario no logueado
                authContainer.innerHTML = `
                    <button id="login-button">Iniciar sesión</button>
                    <button id="signup-button">Registrarse</button>
                `;
                document.getElementById('login-button').addEventListener('click', () => {
                    auth.signInWithEmailAndPassword(
                        prompt("Email:"),
                        prompt("Contraseña:")
                    );
                });
                document.getElementById('signup-button').addEventListener('click', () => {
                    auth.createUserWithEmailAndPassword(
                        prompt("Email:"),
                        prompt("Contraseña:")
                    );
                });
                newPostContainer.style.display = 'none';
                postsContainer.innerHTML = '<p>Inicia sesión para ver las publicaciones</p>';
            }
        });

        // Cargar publicaciones en tiempo real
        function loadPosts() {
            db.collection("posts").orderBy("timestamp", "desc").onSnapshot(snapshot => {
                postsContainer.innerHTML = '';
                snapshot.forEach(doc => {
                    const post = doc.data();
                    const postElement = document.createElement('div');
                    postElement.className = 'post-container';
                    postElement.innerHTML = `
                        <h3>${post.authorName || 'Anónimo'}</h3>
                        <p>${post.text}</p>
                        ${post.imageUrl ? `<img src="${post.imageUrl}" class="post-image">` : ''}
                        <small>${new Date(post.timestamp?.toDate()).toLocaleString()}</small>
                    `;
                    postsContainer.appendChild(postElement);
                });
            });
        }

        // Crear nueva publicación
        postButton.addEventListener('click', async () => {
            const text = postText.value.trim();
            const file = postImage.files[0];
            
            if (!text && !file) return;

            const user = auth.currentUser;
            if (!user) return;

            try {
                let imageUrl = null;
                
                // Subir imagen si existe
                if (file) {
                    const storageRef = storage.ref(`posts/${user.uid}/${Date.now()}_${file.name}`);
                    await storageRef.put(file);
                    imageUrl = await storageRef.getDownloadURL();
                }

                // Crear publicación en Firestore
                await db.collection("posts").add({
                    text: text,
                    imageUrl: imageUrl,
                    authorId: user.uid,
                    authorName: user.displayName || user.email,
                    timestamp: firebase.firestore.FieldValue.serverTimestamp()
                });

                // Limpiar formulario
                postText.value = '';
                postImage.value = '';
            } catch (error) {
                console.error("Error al publicar:", error);
                alert("Error al publicar. Intenta nuevamente.");
            }
        });
    </script>
</body>
</html>

Implementación Paso a Paso

1. Configuración de Firebase

  1. Crea un proyecto en Firebase Console
  2. Registra una aplicación web en tu proyecto
  3. Copia la configuración y reemplázala en el código

2. Estructura de Datos en Firestore

Crea una colección llamada "posts" con los siguientes campos:

  • text (string): Contenido de la publicación
  • imageUrl (string): URL de la imagen (opcional)
  • authorId (string): ID del usuario que creó la publicación
  • authorName (string): Nombre/email del autor
  • timestamp (timestamp): Fecha y hora de creación

3. Reglas de Seguridad

Configura las reglas en Firestore y Storage:

Firestore Rules:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{post} {
      allow read: if true;
      allow create: if request.auth != null;
      allow update, delete: if request.auth != null && request.auth.uid == resource.data.authorId;
    }
  }
}

Storage Rules:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /posts/{userId}/{allPaths=**} {
      allow read: if true;
      allow write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

4. Despliegue en Firebase Hosting

  1. Instala Firebase CLI:
npm install -g firebase-tools
  1. Inicializa tu proyecto:
firebase init
  • Selecciona Hosting y Firestore
  • Configura como proyecto público el directorio donde está tu HTML
  1. Despliega tu aplicación:
firebase deploy

Características Avanzadas para Implementar (Opcional)

  1. Sistema de likes/comentarios: Añade subcolecciones a los posts
  2. Seguimiento de usuarios: Crea una colección "users" con seguidores/seguidos
  3. Notificaciones en tiempo real: Usa Firebase Cloud Messaging
  4. Búsqueda de publicaciones: Implementa Algolia o Firebase Search
  5. Edición de perfil: Permite subir foto de perfil y cambiar nombre

Evaluación del Proyecto

Este proyecto cumple con todos los requisitos:

  • Red social básica con autenticación y publicaciones
  • Publicaciones con imágenes usando Firebase Storage
  • Tiempo real con listeners de Firestore
  • Hosting en Firebase para despliegue profesional

Además, incluye características adicionales como:

  • Sistema de autenticación por email
  • Diseño responsive básico
  • Reglas de seguridad para proteger los datos
  • Actualizaciones en tiempo real sin recargar la página

Publicar un comentario

0 Comentarios