Como adicionar notificações push a um app da Web

As mensagens push são uma maneira simples e eficaz de reengajar os usuários. Neste codelab, você vai aprender a adicionar notificações push ao seu app da Web.

O que você vai aprender

  • Como inscrever e cancelar a inscrição de um usuário para mensagens push
  • Como lidar com mensagens push recebidas
  • Como mostrar uma notificação
  • Como responder a cliques de notificação

O que é necessário

  • Chrome 52 ou mais recente
  • Servidor da Web para Chrome ou seu próprio servidor da Web
  • Um editor de texto
  • Conhecimento básico de HTML, CSS, JavaScript e Chrome DevTools
  • O exemplo de código (consulte "Preparar")

Baixe o exemplo de código

Você tem duas opções para receber o exemplo de código deste codelab:

  • Clone o repositório Git:
git clone https://github.com/GoogleChrome/push-notifications.git
  • Faça o download do arquivo ZIP:

Fazer o download do código-fonte

Se você baixar a fonte como um arquivo ZIP, ao descompactá-la, uma pasta raiz push-notifications-master será criada.

Instalar e verificar o servidor da Web

Embora você possa usar seu próprio servidor da Web, este codelab foi projetado para funcionar bem com o app Web Server for Chrome. Se você ainda não tiver esse app instalado, faça o download na Chrome Web Store:

Instalar o servidor da Web para Chrome

Depois de instalar o app Web Server para Chrome, clique no atalho Apps na barra de favoritos:

Na janela "Apps", clique no ícone do servidor da Web:

Você verá esta caixa de diálogo, que permite configurar seu servidor da Web local:

Clique no botão Escolher pasta e selecione a pasta app na pasta push-notifications que você baixou. Isso permite que seu trabalho seja detalhado pelo URL mostrado na seção URLs do servidor da Web da caixa de diálogo.

Em Opções, marque a caixa ao lado de Mostrar automaticamente index.html, conforme mostrado abaixo:

Em seguida, pare e reinicie o servidor deslizando a chave Servidor da Web: INICIADO para a esquerda e depois para a direita.

Clique no URL do servidor da Web para acessar seu site no navegador da Web. Você vai ver uma página parecida com esta, mas sua versão pode mostrar 127.0.0.1:8887 como endereço:

00-push-codelab.png

Sempre atualizar o service worker

Durante o desenvolvimento, é útil garantir que o service worker esteja sempre atualizado e com as mudanças mais recentes.

Para configurar isso no Chrome:

  1. Acesse a guia Codelab de push.
  2. Abra o DevTools: Ctrl-Shift-I no Windows e no Linux, Cmd-Option-I no macOS.
  3. Selecione o painel Application, clique na guia Service Workers e marque a caixa de seleção Update on Reload. Quando essa caixa de seleção está marcada, o service worker é atualizado à força sempre que a página é recarregada.

Código concluído

No diretório app, observe que há um arquivo vazio chamado sw.js. Esse arquivo será o service worker. Por enquanto, ele pode ficar vazio. Você vai adicionar código a ele mais tarde.

Primeiro, registre esse arquivo como seu service worker.

Sua página app/index.html é carregada scripts/main.js. Você registra o service worker nesse arquivo JavaScript.

Adicione o código a seguir a scripts/main.js:

if ('serviceWorker' in navigator && 'PushManager' in window) {
  console.log('Service Worker and Push are supported');

  navigator.serviceWorker.register('sw.js')
  .then(function(swReg) {
    console.log('Service Worker is registered', swReg);

    swRegistration = swReg;
  })
  .catch(function(error) {
    console.error('Service Worker Error', error);
  });
} else {
  console.warn('Push messaging is not supported');
  pushButton.textContent = 'Push Not Supported';
}

Esse código verifica se os service workers e as mensagens push são compatíveis com seu navegador. Se forem compatíveis, o código vai registrar seu arquivo sw.js.

Fazer um teste

Atualize a guia Push Codelab no navegador para verificar as mudanças.

Verifique o console no Chrome DevTools para um Service Worker is registered message, assim:

Receber chaves do servidor de aplicativos

Para trabalhar com este codelab, você precisa gerar chaves do servidor de aplicativos. Você pode fazer isso no site complementar: web-push-codelab.glitch.me

Aqui, você pode gerar um par de chaves pública e privada.

push-codelab-04-companion.png

Copie sua chave pública em scripts/main.js, substituindo o valor <Your Public Key>:

const applicationServerPublicKey = '<Your Public Key>';

Importante: nunca coloque sua chave privada no app da Web.

Código concluído

No momento, o botão Ativar do web app está desativado e não pode ser clicado. Isso porque é uma boa prática desativar o botão push por padrão e ativá-lo depois de saber que as mensagens push são compatíveis com o navegador e que é possível verificar se o usuário está inscrito ou não.

Você precisará criar duas funções em scripts/main.js:

  • initializeUI, para verificar se o usuário está inscrito no momento
  • updateBtn, para ativar o botão e mudar o texto dependendo se o usuário é assinante ou não.

Adicione uma função initializeUI a main.js desta forma:

function initializeUI() {
  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

Seu novo método usa o swRegistration da etapa anterior, recebe a propriedade pushManager dele e chama getSubscription() nele.

pushManager. getSubscription() retorna uma promessa que é resolvida com a assinatura atual, se houver uma. Caso contrário, ele retorna null. Assim, é possível verificar se o usuário já é assinante, definir o valor de isSubscribed e chamar updateBtn() para atualizar o botão.

Adicione a função updateBtn() a main.js:

function updateBtn() {
  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

Essa função ativa o botão e muda o texto dele dependendo se o usuário é inscrito ou não.

Por fim, chame initializeUI() quando o service worker for registrado em main.js:

navigator.serviceWorker.register('sw.js')
.then(function(swReg) {
  console.log('Service Worker is registered', swReg);

  swRegistration = swReg;
  initializeUI();
})

Fazer um teste

Atualize a guia Push Codelab. O botão Ativar mensagens push agora está ativado (você pode clicar nele), e User is NOT subscribed aparece no console.

Ao longo deste codelab, o texto do botão vai mudar sempre que você se inscrever ou cancelar a inscrição.

Código concluído

No momento, o botão Ativar mensagens push não faz muita coisa. Vamos corrigir isso.

Na função initializeUI(), adicione um listener de clique ao botão:

function initializeUI() {
  pushButton.addEventListener('click', function() {
    pushButton.disabled = true;
    if (isSubscribed) {
      // TODO: Unsubscribe user
    } else {
      subscribeUser();
    }
  });

  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    updateSubscriptionOnServer(subscription);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

Quando o usuário clicar no botão, desative-o para garantir que ele não possa clicar uma segunda vez, já que a assinatura de mensagens push pode levar algum tempo.

Em seguida, chame subscribeUser() se o usuário não estiver inscrito. Para isso, cole o seguinte código em scripts/main.js:

function subscribeUser() {
  const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
  swRegistration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: applicationServerKey
  })
  .then(function(subscription) {
    console.log('User is subscribed.');

    updateSubscriptionOnServer(subscription);

    isSubscribed = true;

    updateBtn();
  })
  .catch(function(error) {
    console.error('Failed to subscribe the user: ', error);
    updateBtn();
  });
}

Vamos analisar o que esse código está fazendo e como ele está inscrevendo o usuário para mensagens push.

Primeiro, pegue a chave pública do servidor de aplicativos, que é codificada em Base64 seguro para URLs, e converta em um UInt8Array, porque essa é a entrada esperada da chamada subscribe(). A função urlB64ToUint8Array() está na parte de cima de scripts/main.js.

Depois de converter o valor, chame o método subscribe() no pushManager do service worker, transmitindo a chave pública do servidor de aplicativos e o valor userVisibleOnly: true.

const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})

O parâmetro userVisibleOnly garante que uma notificação será mostrada sempre que uma mensagem push for enviada. No momento, esse valor é obrigatório e precisa ser "true".

Chamar subscribe() retorna uma promessa que será resolvida após as seguintes etapas:

  1. O usuário concedeu permissão para mostrar notificações.
  2. O navegador enviou uma solicitação de rede para um serviço de push e recebeu os dados necessários para gerar um PushSubscription.

A promessa subscribe() será resolvida com um PushSubscription se essas etapas forem concluídas. Se o usuário não conceder permissão ou se houver algum problema ao assinar, a promessa será rejeitada com um erro. Isso resulta na seguinte cadeia de promessas no seu codelab:

swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})
.then(function(subscription) {
  console.log('User is subscribed.');

  updateSubscriptionOnServer(subscription);

  isSubscribed = true;

  updateBtn();

})
.catch(function(err) {
  console.log('Failed to subscribe the user: ', err);
  updateBtn();
});

Com isso, você recebe uma assinatura e trata o usuário como assinante ou captura um erro e o registra no console. Em ambos os cenários, chame updateBtn() para garantir que o botão seja reativado e tenha o texto adequado.

Em um aplicativo real, a função updateSubscriptionOnServer() é onde você enviaria os dados de assinatura para um back-end, mas, para o codelab, basta mostrar a assinatura na interface. Adicione a seguinte função a scripts/main.js:

function updateSubscriptionOnServer(subscription) {
  // TODO: Send subscription to application server

  const subscriptionJson = document.querySelector('.js-subscription-json');
  const subscriptionDetails =
    document.querySelector('.js-subscription-details');

  if (subscription) {
    subscriptionJson.textContent = JSON.stringify(subscription);
    subscriptionDetails.classList.remove('is-invisible');
  } else {
    subscriptionDetails.classList.add('is-invisible');
  }
}

Fazer um teste

Acesse a guia Push Codelab, atualize a página e clique no botão. Você vai ver uma solicitação de permissão como esta:

Se você conceder a permissão, User is subscribed vai aparecer no console. O texto do botão vai mudar para Desativar mensagens push, e você poderá ver a inscrição como dados JSON na parte de baixo da página.

Código concluído

Uma coisa que você ainda não processou é o que acontece se o usuário bloquear a solicitação de permissão. Isso exige uma consideração exclusiva porque, se o usuário bloquear a permissão, o web app não poderá mostrar novamente a solicitação de permissão nem inscrever o usuário. É necessário desativar o botão de pressão para que o usuário saiba que ele não pode ser usado.

O lugar óbvio para lidar com esse cenário é a função updateBtn(). Basta verificar o valor de Notification.permission, assim:

function updateBtn() {
  if (Notification.permission === 'denied') {
    pushButton.textContent = 'Push Messaging Blocked';
    pushButton.disabled = true;
    updateSubscriptionOnServer(null);
    return;
  }

  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

Se a permissão for denied, o usuário não poderá se inscrever, e não há mais nada que você possa fazer. Portanto, desativar o botão permanentemente é a melhor abordagem.

Fazer um teste

Como você já concedeu permissão para seu web app na etapa anterior, clique no i em um círculo na barra de URL e mude a permissão de Notificações para Usar padrão global (perguntar).

Depois de mudar essa configuração, atualize a página, clique no botão Ativar mensagens push e selecione Bloquear na caixa de diálogo de permissão. O botão vai ficar desativado e mostrar o texto Mensagens push bloqueadas.

Com essa mudança, agora é possível inscrever o usuário, já tendo cuidado com os possíveis cenários de permissão.

Código concluído

Antes de aprender a enviar uma mensagem push do seu back-end, considere o que vai acontecer quando um usuário inscrito receber uma mensagem push.

Quando você aciona uma mensagem push, o navegador a recebe, descobre para qual service worker ela é, ativa esse service worker e envia um evento push. É necessário detectar esse evento e mostrar uma notificação como resultado.

Adicione o seguinte código ao arquivo sw.js:

self.addEventListener('push', function(event) {
  console.log('[Service Worker] Push Received.');
  console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);

  const title = 'Push Codelab';
  const options = {
    body: 'Yay it works.',
    icon: 'images/icon.png',
    badge: 'images/badge.png'
  };

  event.waitUntil(self.registration.showNotification(title, options));
});

Vamos analisar esse código. Você está detectando eventos push no service worker ao adicionar um listener de eventos:

self.addEventListener('push', ... );

A menos que você já tenha usado Web Workers, self provavelmente é novo. Em um arquivo de service worker, self faz referência ao próprio service worker.)

Quando uma mensagem push é recebida, o listener de eventos é chamado, e você cria uma notificação chamando showNotification() na propriedade registration do service worker. O showNotification() exige um title. Você também pode fornecer um objeto options para definir uma mensagem no corpo, um ícone e um selo. (O selo só é usado no Android no momento da redação.)

const title = 'Push Codelab';
const options = {
  body: 'Yay it works.',
  icon: 'images/icon.png',
  badge: 'images/badge.png'
};
self.registration.showNotification(title, options);

A última coisa a abordar no seu processamento de eventos push é event.waitUntil(). Esse método usa uma promessa para permitir que o navegador mantenha o service worker ativo e em execução até que a promessa transmitida seja resolvida.

Para facilitar um pouco a compreensão do código acima, você pode reescrevê-lo assim:

const notificationPromise = self.registration.showNotification(title, options);
event.waitUntil(notificationPromise);

Agora que você já passou pelo evento de push, vamos testar um.

Fazer um teste

Com o processamento de eventos push no service worker, você pode acionar um evento push falso para testar o que acontece quando uma mensagem é recebida.

No app da Web, inscreva-se para receber mensagens push e verifique se User IS subscribed aparece no console. No painel Application das DevTools, na guia Service Workers, clique no botão Push:

Depois de clicar em Enviar, você vai receber uma notificação como esta:

Observação: se esta etapa não funcionar, tente cancelar o registro do service worker com o link Cancelar registro no painel "Application" do DevTools. Aguarde a interrupção do service worker e recarregue a página.

Código concluído

Se você clicar em uma dessas notificações, nada vai acontecer. Você pode processar cliques em notificações detectando eventos notificationclick no service worker.

Comece adicionando um listener notificationclick em sw.js:

self.addEventListener('notificationclick', function(event) {
  console.log('[Service Worker] Notification click received.');

  event.notification.close();

  event.waitUntil(
    clients.openWindow('https://developers.google.com/web')
  );
});

Quando o usuário clicar na notificação, o listener de eventos notificationclick será chamado.

Primeiro, o código fecha a notificação clicada:

event.notification.close();

Em seguida, uma nova janela ou guia é aberta, carregando o URL https://developers.google.com/web. Você pode mudar isso.

event.waitUntil(
    clients.openWindow('https://developers.google.com/web/')
  );

event.waitUntil() garante que o navegador não encerre o service worker antes que a nova janela ou guia seja exibida.

Fazer um teste

Tente acionar uma mensagem push no DevTools novamente e clique na notificação. A notificação será fechada, e uma nova guia será aberta.

Você já viu que seu app da Web é capaz de mostrar uma notificação usando o DevTools e aprendeu a fechar a notificação com um clique. A próxima etapa é enviar uma mensagem push real.

Normalmente, isso exigiria o envio de uma assinatura de uma página da Web para um back-end. Em seguida, o back-end acionaria uma mensagem push fazendo uma chamada de API para o endpoint na assinatura.

Isso está fora do escopo deste codelab, mas você pode usar o site complementar (web-push-codelab.glitch.me) para acionar uma mensagem push real. Cole a assinatura na parte de baixo da página:

Em seguida, cole na área de texto Assinatura do Send To do site complementar:

Em Texto para enviar, adicione a string que você quer enviar com a mensagem push.

Clique no botão Enviar mensagem push.

Você vai receber uma mensagem push. O texto usado será registrado no console.

Isso vai permitir que você teste o envio e o recebimento de dados, além de manipular as notificações.

O app complementar é apenas um servidor de nós que usa a biblioteca web-push para enviar mensagens. Vale a pena analisar a organização web-push-libs no GitHub (link em inglês) para saber quais bibliotecas estão disponíveis para enviar mensagens push. Isso lida com muitos detalhes para acionar mensagens push.

Confira todo o código do site complementar aqui.

Código concluído

A única coisa que falta é a capacidade de cancelar a inscrição de um usuário nas notificações push. Para fazer isso, chame unsubscribe() em um PushSubscription.

De volta ao arquivo scripts/main.js, mude o listener de clique pushButton em initializeUI() para o seguinte:

pushButton.addEventListener('click', function() {
  pushButton.disabled = true;
  if (isSubscribed) {
    unsubscribeUser();
  } else {
    subscribeUser();
  }
});

Agora você vai chamar uma nova função unsubscribeUser(). Nessa função, você recebe a assinatura atual e chama unsubscribe() nela. Adicione o código a seguir à scripts/main.js:

function unsubscribeUser() {
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    if (subscription) {
      return subscription.unsubscribe();
    }
  })
  .catch(function(error) {
    console.log('Error unsubscribing', error);
  })
  .then(function() {
    updateSubscriptionOnServer(null);

    console.log('User is unsubscribed.');
    isSubscribed = false;

    updateBtn();
  });
}

Vamos analisar essa função.

Primeiro, chame getSubscription() para receber a assinatura atual:

swRegistration.pushManager.getSubscription()

Isso retorna uma promessa que é resolvida com um PushSubscription se houver um. Caso contrário, retorna null. Se houver uma assinatura, chame unsubscribe() nela, o que invalida o PushSubscription.

swRegistration.pushManager.getSubscription()
.then(function(subscription) {
  if (subscription) {
    // TODO: Tell application server to delete subscription
    return subscription.unsubscribe();
  }
})
.catch(function(error) {
  console.log('Error unsubscribing', error);
})

Chamar unsubscribe() retorna uma promessa, já que pode levar algum tempo para ser concluída. Você retorna essa promessa para que o próximo then() na cadeia aguarde a conclusão de unsubscribe(). Você também adiciona um manipulador de captura caso a chamada de unsubscribe() resulte em um erro. Depois disso, você pode atualizar a interface.

.then(function() {
  updateSubscriptionOnServer(null);

  console.log('User is unsubscribed.');
  isSubscribed = false;

  updateBtn();
})

Fazer um teste

Você poderá pressionar Ativar mensagens push ou Desativar mensagens push no seu web app, e os registros vão mostrar o usuário se inscrevendo e cancelando a inscrição.

Parabéns por concluir este codelab!

Este codelab mostrou como adicionar notificações push ao seu web app. Se quiser saber mais sobre o que as notificações da Web podem fazer, confira estes documentos.

Se você quiser implantar notificações push no seu site, talvez seja interessante adicionar suporte a navegadores mais antigos ou não compatíveis com padrões que usam o GCM. Saiba mais aqui.

Leia mais

Postagens relevantes do blog