Introdução ao Firebase para Web

1. Visão geral

Neste codelab, você vai aprender alguns conceitos básicos do Firebase para criar aplicativos da Web interativos. Você vai criar um app de bate-papo para confirmação de presença em eventos e livro de visitas usando vários produtos do Firebase.

Captura de tela desta etapa

O que você aprenderá

  • Autentique usuários com o Firebase Authentication e o FirebaseUI.
  • Sincronizar dados usando o Cloud Firestore.
  • Crie regras de segurança do Firebase para proteger um banco de dados.

O que é necessário

  • Um navegador da sua escolha, como o Chrome.
  • Acesso a stackblitz.com (não é necessário ter uma conta nem fazer login).
  • Uma Conta do Google, como uma conta do Gmail. Recomendamos usar a conta de e-mail que você já usa na sua conta do GitHub. Isso permite usar recursos avançados no StackBlitz.
  • O exemplo de código do codelab. Confira a próxima etapa para saber como receber o código.

2. Fazer o download do código inicial

Neste codelab, você vai criar um app usando o StackBlitz, um editor on-line com vários fluxos de trabalho do Firebase integrados. O StackBlitz não exige instalação de software nem uma conta especial.

O StackBlitz permite compartilhar projetos com outras pessoas. Outras pessoas que têm o URL do seu projeto do StackBlitz podem ver seu código e criar um fork do projeto, mas não podem editá-lo.

  1. Acesse este URL para conferir o código inicial: https://stackblitz.com/edit/firebase-gtk-web-start
  2. Na parte de cima da página do StackBlitz, clique em Fork:

Captura de tela desta etapa

Agora você tem uma cópia do código inicial como seu próprio projeto do StackBlitz, que tem um nome e um URL exclusivos. Todos os seus arquivos e mudanças são salvos nesse projeto do StackBlitz.

3. Editar informações do evento

Os materiais iniciais deste codelab fornecem alguma estrutura para o app da Web, incluindo algumas folhas de estilo e alguns contêineres HTML para o app. Mais adiante neste codelab, você vai conectar esses contêineres ao Firebase.

Para começar, vamos conhecer um pouco mais a interface do StackBlitz.

  1. No StackBlitz, abra o arquivo index.html.
  2. Localize event-details-container e description-container e tente editar alguns detalhes do evento.

À medida que você edita o texto, a atualização automática da página no StackBlitz mostra os novos detalhes do evento. Legal, né?

<!-- ... -->

<div id="app">
  <img src="..." />

  <section id="event-details-container">
     <h1>Firebase Meetup</h1>

     <p><i class="material-icons">calendar_today</i> October 30</p>
     <p><i class="material-icons">location_city</i> San Francisco</p>

  </section>

  <hr>

  <section id="firebaseui-auth-container"></section>

  <section id="description-container">
     <h2>What we'll be doing</h2>
     <p>Join us for a day full of Firebase Workshops and Pizza!</p>
  </section>
</div>

<!-- ... -->

A prévia do app vai ficar assim:

Visualização do app

Captura de tela desta etapa

4. Criar e configurar um projeto do Firebase

Mostrar as informações do evento é ótimo para os convidados, mas apenas mostrar os eventos não é muito útil para ninguém. Vamos adicionar algumas funcionalidades dinâmicas a esse app. Para isso, você precisa conectar o Firebase ao app. Para começar a usar o Firebase, crie e configure um projeto do Firebase.

Criar um projeto do Firebase

  1. Faça login no console do Firebase usando sua Conta do Google.
  2. Clique no botão para criar um projeto e insira um nome (por exemplo, Firebase-Web-Codelab).
  3. Clique em Continuar.
  4. Se solicitado, leia e aceite os Termos do Firebase e clique em Continuar.
  5. (Opcional) Ative a assistência de IA no console do Firebase (chamada de "Gemini no Firebase").
  6. Neste codelab, você não precisa do Google Analytics. Portanto, desative a opção do Google Analytics.
  7. Clique em Criar projeto, aguarde o provisionamento e clique em Continuar.

Para saber mais sobre os projetos do Firebase, consulte Noções básicas sobre projetos do Firebase.

Ativar e configurar produtos do Firebase no console

O app que você está criando usa vários produtos do Firebase disponíveis para apps da Web:

  • Firebase Authentication e Firebase UI para permitir que os usuários façam login facilmente no app.
  • Cloud Firestore: é usado para salvar dados estruturados na nuvem e receber notificações instantâneas quando os dados são alterados.
  • Regras de segurança do Firebase para proteger seu banco de dados.

Alguns desses produtos precisam de configuração especial ou precisam ser ativados usando o Console do Firebase.

Ativar o login por e-mail para o Firebase Authentication

Para permitir que os usuários façam login no app da Web, use o método de login E-mail/Senha neste codelab:

  1. No painel à esquerda do console do Firebase, clique em Build > Authentication. Em seguida, clique em Começar. Agora você está no painel "Autenticação", onde pode conferir os usuários inscritos, configurar provedores de login e gerenciar configurações.

    Captura de tela desta etapa

  2. Selecione a guia Método de login ou clique aqui para acessar a guia diretamente.

    Captura de tela desta etapa

  3. Clique em E-mail/senha nas opções de provedor, mude a chave para Ativar e clique em Salvar.

    Captura de tela desta etapa

Configurar o Cloud Firestore

O app da Web usa o Cloud Firestore para salvar e receber mensagens de chat.

Veja como configurar o Cloud Firestore no seu projeto do Firebase:

  1. No painel à esquerda do console do Firebase, expanda Build e selecione Banco de dados do Firestore.
  2. Clique em Criar banco de dados.
  3. Deixe o ID do banco de dados definido como (default).
  4. Selecione um local para o banco de dados e clique em Próxima.
    No caso de apps reais, escolha um local próximo aos usuários.
  5. Clique em Iniciar no modo de teste. Leia o aviso sobre as regras de segurança.
    Mais adiante neste codelab, você vai adicionar regras de segurança para proteger seus dados. Não distribua ou exponha um aplicativo publicamente sem adicionar regras de segurança ao seu banco de dados.
  6. Clique em Criar.

5. Adicionar e configurar o Firebase

Agora que você criou seu projeto do Firebase e ativou alguns serviços, é necessário informar ao código que você quer usar o Firebase e qual projeto usar.

Adicionar as bibliotecas do Firebase

Para que seu app use o Firebase, é necessário adicionar as bibliotecas do Firebase a ele. Há várias maneiras de fazer isso, conforme descrito na documentação do Firebase. Por exemplo, é possível adicionar as bibliotecas da CDN do Google ou instalá-las localmente usando o npm e depois empacotá-las no seu app se você estiver usando o Browserify.

O StackBlitz oferece agrupamento automático. Assim, você pode adicionar as bibliotecas do Firebase usando instruções de importação. Você vai usar as versões modulares (v9) das bibliotecas, que ajudam a reduzir o tamanho geral da página da Web por um processo chamado "tree shaking". Saiba mais sobre os SDKs modulares na documentação.

Para criar esse app, você usa as bibliotecas do Firebase Authentication, FirebaseUI e Cloud Firestore. Neste codelab, as instruções de importação a seguir já estão incluídas na parte de cima do arquivo index.js. Vamos importar mais métodos de cada biblioteca do Firebase à medida que avançamos:

// Import stylesheets
import './style.css';

// Firebase App (the core Firebase SDK) is always required
import { initializeApp } from 'firebase/app';

// Add the Firebase products and methods that you want to use
import {} from 'firebase/auth';
import {} from 'firebase/firestore';

import * as firebaseui from 'firebaseui';

Adicionar um app da Web do Firebase ao seu projeto do Firebase

  1. De volta ao Console do Firebase, navegue até a página de visão geral do projeto clicando em Visão geral do projeto no canto superior esquerdo.
  2. No centro da página de visão geral do projeto, clique no ícone da Web ícone do web app para criar um app da Web do Firebase.

    Captura de tela desta etapa

  3. Registre o app com o apelido Web App.
  4. Para este codelab, NÃO marque a caixa ao lado de Também configurar o Firebase Hosting para este app. Por enquanto, você vai usar o painel de visualização do StackBlitz.
  5. Clique em Registrar app.

    Captura de tela desta etapa

  6. Copie o objeto de configuração do Firebase para a área de transferência.

    Captura de tela desta etapa

  7. Clique em Continuar no console e adicione o objeto de configuração do Firebase ao app:
  8. De volta ao StackBlitz, acesse o arquivo index.js.
  9. Localize a linha de comentário Add Firebase project configuration object here e cole o snippet de configuração logo abaixo dela.
  10. Adicione a chamada de função initializeApp para configurar o Firebase usando a configuração exclusiva do projeto.
    // ...
    // Add Firebase project configuration object here
    const firebaseConfig = {
      apiKey: "random-unique-string",
      authDomain: "your-projectId.firebaseapp.com",
      databaseURL: "https://your-projectId.firebaseio.com",
      projectId: "your-projectId",
      storageBucket: "your-projectId.firebasestorage.app",
      messagingSenderId: "random-unique-string",
      appId: "random-unique-string",
    };
    
    // Initialize Firebase
    initializeApp(firebaseConfig);
    

6. Adicionar login do usuário (RSVP)

Agora que você adicionou o Firebase ao app, é possível configurar um botão de confirmação de presença que registra pessoas usando o Firebase Authentication.

Autenticar usuários com login por e-mail e FirebaseUI

Você vai precisar de um botão de confirmação de presença que peça ao usuário para fazer login com o endereço de e-mail dele. Para isso, conecte a FirebaseUI a um botão de confirmação de presença.A FirebaseUI é uma biblioteca que oferece uma interface pré-criada com base no Firebase Auth.

O FirebaseUI exige uma configuração (consulte as opções na documentação) que faz duas coisas:

  • Informa à FirebaseUI que você quer usar o método de login por e-mail/senha.
  • Processa o callback de um login bem-sucedido e retorna "false" para evitar um redirecionamento. Você não quer que a página seja atualizada porque está criando um web app de página única.

Adicionar o código para inicializar o FirebaseUI Auth

  1. No StackBlitz, acesse o arquivo index.js.
  2. Na parte de cima, localize a instrução de importação firebase/auth e adicione getAuth e EmailAuthProvider, assim:
    // ...
    // Add the Firebase products and methods that you want to use
    import { getAuth, EmailAuthProvider } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
  3. Salve uma referência ao objeto de autenticação logo após initializeApp, assim:
    initializeApp(firebaseConfig);
    auth = getAuth();
    
  4. Observe que a configuração do FirebaseUI já está disponível no código inicial. Ele já está configurado para usar o provedor de autenticação por e-mail.
  5. Na parte de baixo da função main() em index.js, adicione a instrução de inicialização da FirebaseUI desta forma:
    async function main() {
      // ...
    
      // Initialize the FirebaseUI widget using Firebase
      const ui = new firebaseui.auth.AuthUI(auth);
    }
    main();
    
    

Adicionar um botão de confirmação de presença ao HTML

  1. No StackBlitz, acesse o arquivo index.html.
  2. Adicione o HTML de um botão de confirmação de presença dentro do event-details-container, conforme mostrado no exemplo abaixo.

    Use os mesmos valores de id mostrados abaixo porque, para este codelab, já existem hooks para esses IDs específicos no arquivo index.js.

    No arquivo index.html, há um contêiner com o ID firebaseui-auth-container. Esse é o ID que você vai transmitir para o FirebaseUI manter seu login.
    <!-- ... -->
    
    <section id="event-details-container">
        <!-- ... -->
        <!-- ADD THE RSVP BUTTON HERE -->
        <button id="startRsvp">RSVP</button>
    </section>
    <hr>
    <section id="firebaseui-auth-container"></section>
    <!-- ... -->
    
    Visualização do app

    Captura de tela desta etapa

  3. Configure um listener no botão "Confirmar presença" e chame a função de início do FirebaseUI. Isso informa à FirebaseUI que você quer ver a janela de login.

    Adicione o código a seguir à parte de baixo da função main() em index.js:
    async function main() {
      // ...
    
      // Listen to RSVP button clicks
      startRsvpButton.addEventListener("click",
       () => {
            ui.start("#firebaseui-auth-container", uiConfig);
      });
    }
    main();
    

Teste o login no app

  1. Na janela de visualização do StackBlitz, clique no botão "RSVP" para fazer login no app.
    • Neste codelab, você pode usar qualquer endereço de e-mail, até mesmo um falso, já que não está configurando uma etapa de verificação de e-mail.
    • Se você encontrar uma mensagem de erro informando auth/operation-not-allowed ou The given sign-in provider is disabled for this Firebase project, verifique se ativou E-mail/senha como um provedor de login no console do Firebase.
    Visualização do app

    Captura de tela desta etapa

  2. Acesse o painel Autenticação no console do Firebase. Na guia Usuários, você vai encontrar as informações da conta inseridas para fazer login no app.

    Captura de tela desta etapa

Adicionar o estado de autenticação à interface

Em seguida, verifique se a interface reflete o fato de você ter feito login.

Você vai usar o callback do listener de estado do Firebase Authentication, que é notificado sempre que o status de login do usuário muda. Se houver um usuário conectado, o app vai mudar o botão "Confirmar presença" para "Sair".

  1. No StackBlitz, acesse o arquivo index.js.
  2. Na parte de cima, localize a instrução de importação firebase/auth e adicione signOut e onAuthStateChanged, assim:
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getAuth,
      EmailAuthProvider,
      signOut,
      onAuthStateChanged
    } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
  3. Adicione o código a seguir na parte de baixo da função main():
    async function main() {
      // ...
    
      // Listen to the current Auth state
      onAuthStateChanged(auth, user => {
        if (user) {
          startRsvpButton.textContent = 'LOGOUT';
        } else {
          startRsvpButton.textContent = 'RSVP';
        }
      });
    }
    main();
    
  4. No listener do botão, verifique se há um usuário atual e faça logout dele. Para fazer isso, substitua o startRsvpButton.addEventListener atual pelo seguinte:
    // ...
    // Called when the user clicks the RSVP button
    startRsvpButton.addEventListener('click', () => {
      if (auth.currentUser) {
        // User is signed in; allows user to sign out
        signOut(auth);
      } else {
        // No user is signed in; allows user to sign in
        ui.start('#firebaseui-auth-container', uiConfig);
      }
    });
    

Agora, o botão no app vai mostrar SAIR e vai voltar para CONFIRMAR PRESENÇA quando for clicado.

Visualização do app

Captura de tela desta etapa

7. Grave mensagens no Cloud Firestore

Saber que os usuários estão chegando é ótimo, mas vamos dar aos convidados algo mais para fazer no app. E se eles pudessem deixar mensagens em um livro de visitas? Eles podem compartilhar por que estão animados para ir ou quem esperam encontrar.

Para armazenar as mensagens de chat que os usuários escrevem no app, você vai usar o Cloud Firestore.

Modelo de dados

O Cloud Firestore é um banco de dados NoSQL, e os dados armazenados nele são divididos em coleções, documentos, campos e subcoleções. Você vai armazenar cada mensagem do chat como um documento em uma coleção de nível superior chamada guestbook.

Gráfico do modelo de dados do Firestore mostrando uma coleção de livro de visitas com vários documentos de mensagens

Adicionar mensagens ao Firestore

Nesta seção, você vai adicionar a funcionalidade para que os usuários escrevam novas mensagens no banco de dados. Primeiro, adicione o HTML para os elementos da interface (campo de mensagem e botão "Enviar"). Em seguida, adicione o código que conecta esses elementos ao banco de dados.

Para adicionar os elementos da interface de um campo de mensagem e um botão de envio:

  1. No StackBlitz, acesse o arquivo index.html.
  2. Localize o guestbook-container e adicione o seguinte HTML para criar um formulário com o campo de entrada de mensagem e o botão de envio.
    <!-- ... -->
    
     <section id="guestbook-container">
       <h2>Discussion</h2>
    
       <form id="leave-message">
         <label>Leave a message: </label>
         <input type="text" id="message">
         <button type="submit">
           <i class="material-icons">send</i>
           <span>SEND</span>
         </button>
       </form>
    
     </section>
    
    <!-- ... -->
    

Visualização do app

Captura de tela desta etapa

Quando o usuário clica no botão ENVIAR, o snippet de código abaixo é acionado. Ele adiciona o conteúdo do campo de entrada de mensagem à coleção guestbook do banco de dados. Especificamente, o método addDoc adiciona o conteúdo da mensagem a um novo documento (com um ID gerado automaticamente) na coleção guestbook.

  1. No StackBlitz, acesse o arquivo index.js.
  2. Na parte de cima, localize a declaração de importação firebase/firestore e adicione getFirestore, addDoc e collection, assim:
    // ...
    
    // Add the Firebase products and methods that you want to use
    import {
      getAuth,
      EmailAuthProvider,
      signOut,
      onAuthStateChanged
    } from 'firebase/auth';
    
    import {
      getFirestore,
      addDoc,
      collection
    } from 'firebase/firestore';
    
  3. Agora vamos salvar uma referência ao objeto db do Firestore logo após initializeApp:
    initializeApp(firebaseConfig);
    auth = getAuth();
    db = getFirestore();
    
  4. Na parte de baixo da função main(), adicione o seguinte código.

    Observe que auth.currentUser.uid é uma referência ao ID exclusivo gerado automaticamente que o Firebase Authentication fornece para todos os usuários conectados.
    async function main() {
      // ...
    
      // Listen to the form submission
      form.addEventListener('submit', async e => {
        // Prevent the default form redirect
        e.preventDefault();
        // Write a new message to the database collection "guestbook"
        addDoc(collection(db, 'guestbook'), {
          text: input.value,
          timestamp: Date.now(),
          name: auth.currentUser.displayName,
          userId: auth.currentUser.uid
        });
        // clear message input field
        input.value = '';
        // Return false to avoid redirect
        return false;
      });
    }
    main();
    

Mostrar o livro de visitas apenas para usuários conectados

Você não quer que qualquer pessoa veja a conversa dos convidados. Uma das coisas que você pode fazer para proteger o chat é permitir que apenas usuários conectados vejam o livro de visitas. No entanto, para seus próprios apps, também é recomendável proteger o banco de dados com as regras de segurança do Firebase. Há mais informações sobre regras de segurança mais adiante neste codelab.

  1. No StackBlitz, acesse o arquivo index.js.
  2. Edite o listener onAuthStateChanged para ocultar e mostrar o livro de visitas.
    // ...
    
    // Listen to the current Auth state
    onAuthStateChanged(auth, user => {
      if (user) {
        startRsvpButton.textContent = 'LOGOUT';
        // Show guestbook to logged-in users
        guestbookContainer.style.display = 'block';
      } else {
        startRsvpButton.textContent = 'RSVP';
        // Hide guestbook for non-logged-in users
        guestbookContainer.style.display = 'none';
      }
    });
    

Teste o envio de mensagens

  1. Confira se você fez login no app.
  2. Digite uma mensagem como "Olá!" e clique em ENVIAR.

Essa ação grava a mensagem no seu banco de dados do Cloud Firestore. No entanto, você ainda não vai ver a mensagem no seu app da Web real, porque ainda é necessário implementar a recuperação de dados. Você vai fazer isso em seguida.

Mas você pode ver a mensagem recém-adicionada no Console do Firebase.

No console do Firebase, no painel do banco de dados do Firestore, você vai encontrar a coleção guestbook com a mensagem recém-adicionada. Se você continuar enviando mensagens, sua coleção de livros de visitas vai conter muitos documentos, como este:

Console do Firebase

Captura de tela desta etapa

8. Leia mensagens

Sincronizar mensagens

É ótimo que os convidados possam escrever mensagens no banco de dados, mas elas ainda não aparecem no app.

Para mostrar mensagens, adicione listeners que são acionados quando os dados mudam e crie um elemento da interface que mostre novas mensagens.

Você vai adicionar um código que detecta mensagens recém-adicionadas do app. Primeiro, adicione uma seção no HTML para mostrar mensagens:

  1. No StackBlitz, acesse o arquivo index.html.
  2. Em guestbook-container, adicione uma nova seção com o ID guestbook.
    <!-- ... -->
    
      <section id="guestbook-container">
       <h2>Discussion</h2>
    
       <form><!-- ... --></form>
    
       <section id="guestbook"></section>
    
     </section>
    
    <!-- ... -->
    

Em seguida, registre o listener que detecta as mudanças feitas nos dados:

  1. No StackBlitz, acesse o arquivo index.js.
  2. Na parte de cima, localize a declaração de importação firebase/firestore e adicione query, orderBy e onSnapshot, assim:
    // ...
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot
    } from 'firebase/firestore';
    
  3. Na parte de baixo da função main(), adicione o código a seguir para fazer um loop em todos os documentos (mensagens do livro de visitas) no banco de dados. Para saber mais sobre o que está acontecendo nesse código, leia as informações abaixo do snippet.
    async function main() {
      // ...
    
      // Create query for messages
      const q = query(collection(db, 'guestbook'), orderBy('timestamp', 'desc'));
      onSnapshot(q, snaps => {
        // Reset page
        guestbook.innerHTML = '';
        // Loop through documents in database
        snaps.forEach(doc => {
          // Create an HTML entry for each document and add it to the chat
          const entry = document.createElement('p');
          entry.textContent = doc.data().name + ': ' + doc.data().text;
          guestbook.appendChild(entry);
        });
      });
    }
    main();
    

Para detectar as mensagens no banco de dados, você criou uma consulta em uma coleção específica usando a função collection. O código acima detecta as mudanças na coleção guestbook, que é onde as mensagens do chat são armazenadas. As mensagens também são ordenadas por data, usando orderBy('timestamp', 'desc') para mostrar as mais recentes na parte de cima.

A função onSnapshot usa dois parâmetros: a consulta a ser usada e uma função de callback. A função de callback é acionada quando há mudanças nos documentos que correspondem à consulta. Essas alterações podem ser uma mensagem excluída, modificada ou adicionada. Para mais informações, consulte a documentação do Cloud Firestore.

Testar a sincronização de mensagens

O Cloud Firestore sincroniza dados de forma automática e instantânea com clientes inscritos no banco de dados.

  • As mensagens que você criou antes no banco de dados vão aparecer no app. Você pode escrever novas mensagens, que vão aparecer instantaneamente.
  • Se você abrir seu espaço de trabalho em várias janelas ou guias, as mensagens serão sincronizadas em tempo real entre elas.
  • (Opcional) Tente excluir, modificar ou adicionar manualmente novas mensagens diretamente na seção Banco de dados do console do Firebase. Todas as mudanças vão aparecer na interface.

Parabéns! Você está lendo documentos do Cloud Firestore no seu app.

Visualização do app

Captura de tela desta etapa

9. Configurar regras básicas de segurança

Inicialmente, você configurou o Cloud Firestore para usar o modo de teste, o que significa que seu banco de dados está aberto para leituras e gravações. No entanto, use o modo de teste apenas nas fases iniciais do desenvolvimento. Como prática recomendada, configure regras de segurança para seu banco de dados ao desenvolver o app. A segurança precisa ser parte integrante da estrutura e do comportamento do app.

Com as regras de segurança, é possível controlar o acesso a documentos e coleções no banco de dados. Com a sintaxe de regras flexíveis, é possível criar regras que correspondam a qualquer tipo de operação, desde todas as gravações no banco de dados inteiro até operações em um documento específico.

É possível escrever regras de segurança para o Cloud Firestore no console do Firebase:

  1. Na seção Build do Console do Firebase, clique em Firestore Database e selecione a guia Rules (ou clique aqui para acessar diretamente a guia Rules).
  2. Você vai encontrar as seguintes regras de segurança padrão, com um limite de tempo de acesso público de algumas semanas a partir de hoje.

Captura de tela desta etapa

Identificar coleções

Primeiro, identifique as coleções em que o app grava dados.

  1. Exclua a cláusula match /{document=**} atual para que suas regras fiquem assim:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
      }
    }
    
  2. Em match /databases/{database}/documents, identifique a coleção que você quer proteger:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
         // You'll add rules here in the next step.
      }
    }
    

Adicionar regras de segurança

Como você usou o UID de autenticação como um campo em cada documento do livro de visitas, é possível receber o UID de autenticação e verificar se qualquer pessoa que tenta gravar no documento tem um UID de autenticação correspondente.

  1. Adicione as regras de leitura e gravação ao conjunto de regras, conforme mostrado abaixo:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
          allow read: if request.auth.uid != null;
          allow create:
            if request.auth.uid == request.resource.data.userId;
        }
      }
    }
    
  2. Clique em Publicar para implantar as novas regras.Agora, no livro de visitas, apenas usuários conectados podem ler mensagens (qualquer mensagem!), mas você só pode criar uma mensagem usando seu ID de usuário. Também não permitimos que as mensagens sejam editadas ou excluídas.

Adicionar regras de validação

  1. Adicione a validação de dados para garantir que todos os campos esperados estejam presentes no documento:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
          allow read: if request.auth.uid != null;
          allow create:
          if request.auth.uid == request.resource.data.userId
              && "name" in request.resource.data
              && "text" in request.resource.data
              && "timestamp" in request.resource.data;
        }
      }
    }
    
  2. Clique em Publicar para implantar as novas regras.

Redefinir listeners

Como agora seu app só permite que usuários autenticados façam login, mova a consulta firestore do livro de visitas para dentro do listener de autenticação. Caso contrário, vão ocorrer erros de permissão, e o app será desconectado quando o usuário sair.

  1. No StackBlitz, acesse o arquivo index.js.
  2. Extraia o listener onSnapshot da coleção do livro de visitas para uma nova função chamada subscribeGuestbook. Além disso, atribua os resultados da função onSnapshot à variável guestbookListener.

    O listener onSnapshot do Firestore retorna uma função de cancelamento de inscrição que você poderá usar para cancelar o listener de snapshot mais tarde.
    // ...
    // Listen to guestbook updates
    function subscribeGuestbook() {
      const q = query(collection(db, 'guestbook'), orderBy('timestamp', 'desc'));
      guestbookListener = onSnapshot(q, snaps => {
        // Reset page
        guestbook.innerHTML = '';
        // Loop through documents in database
        snaps.forEach(doc => {
          // Create an HTML entry for each document and add it to the chat
          const entry = document.createElement('p');
          entry.textContent = doc.data().name + ': ' + doc.data().text;
          guestbook.appendChild(entry);
        });
      });
    }
    
  3. Adicione uma nova função abaixo chamada unsubscribeGuestbook. Verifique se a variável guestbookListener não é nula e chame a função para cancelar o listener.
    // ...
    // Unsubscribe from guestbook updates
    function unsubscribeGuestbook() {
      if (guestbookListener != null) {
        guestbookListener();
        guestbookListener = null;
      }
    }
    

Por fim, adicione as novas funções ao callback onAuthStateChanged.

  1. Adicione subscribeGuestbook() na parte de baixo de if (user).
  2. Adicione unsubscribeGuestbook() na parte de baixo da instrução else.
    // ...
    // Listen to the current Auth state
    onAuthStateChanged(auth, user => {
      if (user) {
        startRsvpButton.textContent = 'LOGOUT';
        // Show guestbook to logged-in users
        guestbookContainer.style.display = 'block';
        // Subscribe to the guestbook collection
        subscribeGuestbook();
      } else {
        startRsvpButton.textContent = 'RSVP';
        // Hide guestbook for non-logged-in users
        guestbookContainer.style.display = 'none';
        // Unsubscribe from the guestbook collection
        unsubscribeGuestbook();
      }
    });
    

10. Etapa bônus: pratique o que você aprendeu

Registrar o status de confirmação de presença de um participante

No momento, seu app só permite que as pessoas iniciem uma conversa se tiverem interesse no evento. Além disso, a única maneira de saber se alguém está chegando é se a pessoa postar no chat. Vamos nos organizar e informar às pessoas quantas pessoas vão comparecer.

Você vai adicionar uma chave para registrar as pessoas que querem participar do evento e coletar uma contagem de quantas pessoas vão comparecer.

  1. No StackBlitz, acesse o arquivo index.html.
  2. Em guestbook-container, adicione um conjunto de botões YES e NO, assim:
    <!-- ... -->
      <section id="guestbook-container">
       <h2>Are you attending?</h2>
         <button id="rsvp-yes">YES</button>
         <button id="rsvp-no">NO</button>
    
       <h2>Discussion</h2>
    
       <!-- ... -->
    
     </section>
    <!-- ... -->
    

Visualização do app

Captura de tela desta etapa

Em seguida, registre o listener para cliques de botão. Se o usuário clicar em SIM, use o UID de autenticação dele para salvar a resposta no banco de dados.

  1. No StackBlitz, acesse o arquivo index.js.
  2. Na parte de cima, localize a declaração de importação firebase/firestore e adicione doc, setDoc e where, assim:
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot,
      doc,
      setDoc,
      where
    } from 'firebase/firestore';
    
  3. Na parte de baixo da função main(), adicione o seguinte código para ouvir o status de confirmação de presença:
    async function main() {
      // ...
    
      // Listen to RSVP responses
      rsvpYes.onclick = async () => {
      };
      rsvpNo.onclick = async () => {
      };
    }
    main();
    
    
  4. Em seguida, crie uma coleção chamada attendees e registre uma referência de documento se um dos botões de confirmação de presença for clicado. Defina essa referência como true ou false, dependendo do botão clicado.

    Primeiro, para rsvpYes:
    // ...
    // Listen to RSVP responses
    rsvpYes.onclick = async () => {
      // Get a reference to the user's document in the attendees collection
      const userRef = doc(db, 'attendees', auth.currentUser.uid);
    
      // If they RSVP'd yes, save a document with attendi()ng: true
      try {
        await setDoc(userRef, {
          attending: true
        });
      } catch (e) {
        console.error(e);
      }
    };
    
    Em seguida, faça o mesmo para rsvpNo, mas com o valor false:
    rsvpNo.onclick = async () => {
      // Get a reference to the user's document in the attendees collection
      const userRef = doc(db, 'attendees', auth.currentUser.uid);
    
      // If they RSVP'd yes, save a document with attending: true
      try {
        await setDoc(userRef, {
          attending: false
        });
      } catch (e) {
        console.error(e);
      }
    };
    

Atualize suas regras de segurança

Como você já tem algumas regras configuradas, os novos dados adicionados com os botões serão rejeitados.

Permitir adições à coleção attendees

Você precisará atualizar as regras para permitir a adição à coleção attendees.

  1. Para a coleção attendees, como você usou o UID de autenticação como nome do documento, é possível extrair e verificar se o uid do remetente é o mesmo do documento que ele está escrevendo. Você vai permitir que todos leiam a lista de participantes (já que não há dados particulares nela), mas apenas o criador poderá atualizá-la.
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // ... //
        match /attendees/{userId} {
          allow read: if true;
          allow write: if request.auth.uid == userId;
        }
      }
    }
    
  2. Clique em Publicar para implantar as novas regras.

Adicionar regras de validação

  1. Adicione algumas regras de validação de dados para garantir que todos os campos esperados estejam presentes no documento:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // ... //
        match /attendees/{userId} {
          allow read: if true;
          allow write: if request.auth.uid == userId
              && "attending" in request.resource.data;
    
        }
      }
    }
    
  2. Não se esqueça de clicar em Publicar para implantar suas regras.

(Opcional) Agora você pode conferir os resultados de clicar nos botões. Acesse o painel do Cloud Firestore no Console do Firebase.

Ler o status de confirmação de presença

Agora que você gravou as respostas, vamos ver quem vai comparecer e refletir isso na interface.

  1. No StackBlitz, acesse o arquivo index.html.
  2. Em description-container, adicione um novo elemento com o ID number-attending.
    <!-- ... -->
    
     <section id="description-container">
         <!-- ... -->
         <p id="number-attending"></p>
     </section>
    
    <!-- ... -->
    

Em seguida, registre o listener para a coleção attendees e conte o número de respostas YES:

  1. No StackBlitz, acesse o arquivo index.js.
  2. Na parte de baixo da função main(), adicione o seguinte código para detectar o status do RSVP e contar os cliques em SIM.
    async function main() {
      // ...
    
      // Listen for attendee list
      const attendingQuery = query(
        collection(db, 'attendees'),
        where('attending', '==', true)
      );
      const unsubscribe = onSnapshot(attendingQuery, snap => {
        const newAttendeeCount = snap.docs.length;
        numberAttending.innerHTML = newAttendeeCount + ' people going';
      });
    }
    main();
    

Por fim, vamos destacar o botão correspondente ao status atual.

  1. Crie uma função que verifique se o UID de autenticação atual tem uma entrada na coleção attendees e defina a classe do botão como clicked.
    // ...
    // Listen for attendee list
    function subscribeCurrentRSVP(user) {
      const ref = doc(db, 'attendees', user.uid);
      rsvpListener = onSnapshot(ref, doc => {
        if (doc && doc.data()) {
          const attendingResponse = doc.data().attending;
    
          // Update css classes for buttons
          if (attendingResponse) {
            rsvpYes.className = 'clicked';
            rsvpNo.className = '';
          } else {
            rsvpYes.className = '';
            rsvpNo.className = 'clicked';
          }
        }
      });
    }
    
  2. Além disso, vamos criar uma função para cancelar a inscrição. Isso será usado quando o usuário sair.
    // ...
    function unsubscribeCurrentRSVP() {
      if (rsvpListener != null) {
        rsvpListener();
        rsvpListener = null;
      }
      rsvpYes.className = '';
      rsvpNo.className = '';
    }
    
  3. Chame as funções do listener de autenticação.
    // ...
    // Listen to the current Auth state
      // Listen to the current Auth state
      onAuthStateChanged(auth, user => {
        if (user) {
          startRsvpButton.textContent = 'LOGOUT';
          // Show guestbook to logged-in users
          guestbookContainer.style.display = 'block';
    
          // Subscribe to the guestbook collection
          subscribeGuestbook();
          // Subscribe to the user's RSVP
          subscribeCurrentRSVP(user);
        } else {
          startRsvpButton.textContent = 'RSVP';
          // Hide guestbook for non-logged-in users
          guestbookContainer.style.display = 'none'
          ;
          // Unsubscribe from the guestbook collection
          unsubscribeGuestbook();
          // Unsubscribe from the guestbook collection
          unsubscribeCurrentRSVP();
        }
      });
    
  4. Tente fazer login como vários usuários e veja a contagem aumentar a cada clique adicional no botão SIM.

Visualização do app

Captura de tela desta etapa

11. Parabéns!

Você usou o Firebase para criar um aplicativo da Web interativo em tempo real.

O que vimos

  • Firebase Authentication
  • FirebaseUI
  • Cloud Firestore
  • Regras de segurança do Firebase

Próximas etapas

  • Quer saber mais sobre o fluxo de trabalho do desenvolvedor do Firebase? Confira o codelab do emulador do Firebase para saber como testar e executar seu app completamente local.
  • Quer saber mais sobre outros produtos do Firebase? Talvez você queira armazenar arquivos de imagem que os usuários enviam. Ou enviar notificações para seus usuários? Confira o codelab da Web do Firebase para saber mais sobre vários produtos do Firebase para a Web.
  • Quer saber mais sobre o Cloud Firestore? Talvez você queira saber mais sobre subcoleções e transações. Acesse o codelab da Web do Cloud Firestore para saber mais sobre o Cloud Firestore. Ou confira esta série do YouTube para conhecer o Cloud Firestore.

Saiba mais

Como foi?

Adoraríamos receber seu feedback. Preencha um formulário (bem) curto aqui.