Existe alguma forma de escutar o evento de login do usuário usando o componente Theme

Olá a todos,

Quero exibir a caixa de Popup usando o componente Theme quando o usuário fizer login pela primeira vez.

1. Tentei sobrescrever a ação de login do controlador de login usando o componente Theme para exibir a caixa de Popup após o login bem-sucedido do usuário.
Na ação de login, adicionei o código bootbox.alert(“Test Alert”); antes de hiddenLoginForm.submit();.

No entanto, o Popup é exibido apenas até que o hiddenLoginForm seja submetido e, em seguida, o usuário é redirecionado para a página inicial.

Minha necessidade é que a caixa de Popup seja exibida após o redirecionamento para a página inicial, após o login bem-sucedido.

2. Para verificar se o usuário fez login pela primeira vez, tentei verificar o valor da propriedade previous_visit_at do usuário atual.
Se previous_visit_at = null (ou seja, o usuário está fazendo login pela primeira vez).
Mas, mesmo aqui, meu teste falha, pois ao fazer login pela segunda vez, o valor da propriedade previous_visit_at continua sendo null.
Gostaria de saber exatamente quando o valor da propriedade previous_visit_at é atualizado.

Por favor, ajude-me a atender aos requisitos acima.

Obrigado,
Saurabh Khandelwal

Olá @Saurabh_Khandelwal :wave:

Recomendo evitar fazer isso dessa maneira; o login é uma das ações mais críticas, e qualquer sobrescrita nessa ação será frágil e propensa a falhar se fizermos alterações na área correspondente no núcleo.

Se você se lembra, quando se cadastrou pela primeira vez neste site, viu algo assim.

As condições que usamos para verificar isso são

!user.read_first_notification && !user.enforcedSecondFactor

Portanto, se você usar as mesmas condições em um inicializador, conseguirá renderizar um Bootbox na primeira visualização da página do usuário após o login.

Você pode tentar algo assim em seu componente de tema.

import { withPluginApi } from "discourse/lib/plugin-api";
import bootbox from "bootbox";

export default {
  name: "first-login-bootbox",
  initialize() {
    withPluginApi("0.8", api => {
      const user = api.getCurrentUser();
      if (!user) return;

      if (!user.read_first_notification && !user.enforcedSecondFactor) {
        const text = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor`;
        bootbox.alert(text);
      }
    });
  }
};

Isso resultará em algo assim:

Você pode usar HTML no texto do bootbox, se necessário. Assim que o usuário fechar o bootbox e clicar no avatar circulado, ele nunca mais verá o bootbox.

Observe que os bootboxes são destinados a diálogos ou confirmações simples. Se você precisar de algo um pouco mais complexo, provavelmente será melhor usar um modal com showModal().

Obrigado, @Johani, pelo seu conselho. Vou evitar fazer alterações na ação de login e tentar implementar conforme você mencionou.

[quote=“Johani, post:3, topic:170976”]
As condições que usamos para verificar isso são

!user.read_first_notification && !user.enforcedSecondFactor

Portanto, se você usar as mesmas condições em um inicializador, conseguirá renderizar um Bootbox na primeira visualização de página do usuário após o login.

Você pode tentar algo assim no seu componente de tema.
[/quote].

Olá, @Johani, adicionei um hiperlink simples (por exemplo, “/new-topic”) dentro da caixa de popup que abre em uma nova aba.

Quando clico nesse link, ele abre em uma nova aba, mas a caixa de popup é exibida novamente, pois verificamos as seguintes condições:

!user.read_first_notification && !user.enforcedSecondFactor

No meu caso, a caixa de popup não deve ser exibida após acessar o link.

É possível atualizar os valores dessas propriedades após a caixa de popup ser exibida pela primeira vez? Se sim, como podemos fazer isso?

Temor que isso não seja possível.

Essas propriedades não possuem setters; mesmo que tivessem, suas alterações só se aplicariam temporariamente na primeira janela. Assim que o usuário visitar a segunda aba, os dados serão baseados no que está armazenado no banco de dados. Os temas não têm acesso ao backend; eles só podem alterar o frontend.

O que você pode fazer é adicionar um hash ao seu link e verificá-lo da seguinte maneira.

import { withPluginApi } from "discourse/lib/plugin-api";
import bootbox from "bootbox";

export default {
  name: "first-login-bootbox",
  initialize() {
    withPluginApi("0.8", api => {
      const user = api.getCurrentUser();
      if (!user) return;

      if (
        !user.read_first_notification &&
        !user.enforcedSecondFactor &&
        !window.location.hash
      ) {
        const text = `Lorem ipsum dolor sit amet <a href="http://localhost:3000/new-topic#some-hash" target="_blank">Link</a>, consectetur adipiscing elit, sed do eiusmod tempor`;
        bootbox.alert(text);
      }
    });
  }
};

Não tenho certeza se vincular a “/new-topic” em sua postagem foi apenas um exemplo ou se é isso que você deseja fazer. Se for o resultado desejado, então você tem outro problema. Mesmo que o bootbox não seja exibido na página com o hash, eles ainda verão isso…

…e o compositor não será aberto, o que faz sentido, pois é muito inesperado para um usuário começar a digitar um tópico em sua primeira visualização de página imediatamente.

Posso perguntar o que você está tentando realizar aqui? Você está tentando informar o usuário de algo ou outro?

A maneira como vi isso feito em outros sites é editar a mensagem de boas-vindas, mas se essa for uma opção, existem alternativas.

Aqui está o que sugiro:

  1. crie um tópico e adicione todas as informações que deseja lá
  2. publique esse tópico
  3. vincule a esse tópico no bootbox e abra esse link em uma nova aba.

Dessa forma, quando o usuário clicar no link, verá algo assim (sem a sobreposição)

Assim que terminarem com essa página, podem voltar para a primeira aba, fechar o bootbox, ler a primeira notificação e continuar usando o site.

Dessa forma, você nem precisa adicionar/verificar um hash. Aqui está um exemplo de trecho

import { withPluginApi } from "discourse/lib/plugin-api";
import bootbox from "bootbox";

export default {
  name: "first-login-bootbox",
  initialize() {
    withPluginApi("0.8", api => {
      const user = api.getCurrentUser();
      if (!user) return;

      if (!user.read_first_notification && !user.enforcedSecondFactor) {
        const text = `Lorem ipsum dolor sit amet <a href="http://my.site.com/pub/bentley-flying-spur-s-production-milestone" target="_blank">Link</a>, consectetur adipiscing elit, sed do eiusmod tempor`;
        bootbox.alert(text);
      }
    });
  }
};

Estou tentando incluir alguns links como “/new-topic” e “/my/preferences” dentro do Popup.

Caso contrário, estava pensando em exibir o Popup apenas na página inicial, verificando a URL atual.
Assim, ao visitar outras páginas, o Popup não será exibido.

Mesmo que eu verifique a URL da página inicial após clicar no link “/new-topic”, o problema acima ocorrerá.

Olá Saurabh,
Sim, acho que verificar a URL atual e exibir o pop-up apenas na página inicial deve funcionar..

Podemos exibir a introdução para o novo usuário do Discobot após algum tempo, por exemplo, 2 minutos:

Olá, @Johani

Podemos criar um widget como o mostrado abaixo e chamar bootbox() dentro dele?
E após chamar bootbox, podemos chamar o método “skipNewUserTips()”, que também é usado pelo Discourse no widget header-notifications.

<script type="text/discourse-plugin" version="0.8">
 const { h } = require('virtual-dom');
 const { ajax } = require("discourse/lib/ajax").default;
 const DiscourseURL =  require("discourse/lib/url");
 const { userPath } = require("discourse/lib/url");

api.createWidget("welcome-popup", {
  tagName: "div.welcome-popup",
  html(attrs) {
      let user = this.currentUser;
      if (!user) return;
      if (
        !user.get("read_first_notification") &&
        !user.get("enforcedSecondFactor")
      ) {
          const title = `<div class="first-login-bootbox-title">Você é um membro. Bem-vindo a bordo!</div>`;
          const body = `<div class="first-login-bootbox-body">Agora você pode: </br> <ol class="user-suggestions"><li><a href="/new-topic" target="_blank">Faça uma pergunta ou inicie uma discussão</a></li> <li><a href="/my/preferences/tags" target="_blank">Configure suas preferências de notificação</a></li></ol></div>`;
          bootbox.alert({
                        title: title,
                        message: body
                    });
            this.skipNewUserTips();        
      }
      return null;
  },

  skipNewUserTips() {

    ajax(userPath(this.currentUser.username_lower), {
      type: "PUT",
      data: {
        skip_new_user_tips: true,
      },
    }).then(() => {
      this.currentUser.set("skip_new_user_tips", true);
    });
  },
});
</script>

<script
  type="text/x-handlebars"
  data-template-name="/connectors/below-site-header/welcome-popup"
>
  {{mount-widget widget="welcome-popup"}}
</script> 

Código do Discourse para o widget header-notifications:
https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/app/widgets/header.js#L101

Isso causará algum problema?

Isso ignora completamente esse overlay para todos os novos usuários. Então eles nunca o verão.

Se isso for aceitável para você, então, sim, deve funcionar.

Você precisa alterar a chamada do bootbox. Caso contrário, você receberá isso.

em vez de fazer isso

Você deve passar apenas um único argumento.

Essas opções foram adicionadas em versões mais recentes do Bootbox e não funcionarão com a versão que estamos usando atualmente no Discourse (estamos discutindo isso internamente, mas por enquanto, você não pode usá-las).

Além disso, como você está criando uma solicitação PUT, também pode ignorar isso.

this.currentUser.set("skip_new_user_tips", true);

Então, talvez algo assim.

  api.createWidget("welcome-popup", {
    tagName: "div.welcome-popup",
    html(attrs) {
      let user = this.currentUser;
      if (!user) return;
      if (
        !user.get("read_first_notification") &&
        !user.get("enforcedSecondFactor")
      ) {
        const body = `<h2 class="first-login-bootbox-title">Você é um membro. Bem-vindo a bordo!</h2>
                      <hr>
                      <div class="first-login-bootbox-body">
                        Agora você pode:
                        <br>
                        <ol class="user-suggestions">
                          <li>
                            <a href="/new-topic" target="_blank"
                              >Fazer uma pergunta ou iniciar uma discussão</a
                            >
                          </li>
                          <li>
                            <a href="/my/preferences/tags" target="_blank"
                              >Configurar suas preferências de notificação</a
                            >
                          </li>
                        </ol>
                      </div>`;
        bootbox.alert(body);
        this.skipNewUserTips();
      }
      return null;
    },

    skipNewUserTips() {
      ajax(userPath(this.currentUser.username_lower), {
        type: "PUT",
        data: {
          skip_new_user_tips: true
        }
      });
    }
  });

Olá @Johani,

Como definimos skip_new_user_tips como true, a notificação de boas-vindas do Discobot também é ignorada. Está tudo bem se a primeira máscara de sobreposição de notificação não for exibida, mas queremos que a notificação de boas-vindas do Discobot apareça.

Notificação do Discobot:

Para isso, alteramos o código para que a notificação de boas-vindas do Discobot seja carregada para todos os novos usuários.
Agora, adicionamos um novo método ao widget do cabeçalho chamado “closeFirstNotificationMask()”, onde definimos “ringBackdrop” como false e “userVisible” como o inverso dele. Em seguida, chamamos esse método após chamar o método bootbox no widget welcome-popup.

Nos baseamos no método “toggleUserMenu” do widget do cabeçalho, que é chamado quando o ícone do usuário é clicado.

<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.1/bootbox.min.js" integrity="sha512-eoo3vw71DUo5NRvDXP/26LFXjSFE1n5GQ+jZJhHz+oOTR4Bwt7QBCjsgGvuVMQUMMMqeEvKrQrNEI4xQMXp3uA==" crossorigin="anonymous"></script>

<script type="text/discourse-plugin" version="0.8">
 const { h } = require('virtual-dom');
 const { ajax } = require("discourse/lib/ajax").default;

/* Adicionando novo método ao widget header que será chamado após exibir o Welcome-popup */
api.reopenWidget("header",{
    closeFirstNotificationMask() {
        this.state.ringBackdrop = false;
        this.state.userVisible = !this.state.userVisible;
        this.toggleBodyScrolling(this.state.userVisible);
    }
});

/* Criando novo widget "welcome-popup", que será renderizado apenas quando o usuário fizer login pela primeira vez */
api.createWidget("welcome-popup", {
  tagName: "div.welcome-popup",
  html(attrs) {
      let user = this.currentUser;
      if (!user) return;
      if ( !user.get("read_first_notification") && !user.get("enforcedSecondFactor") ) {
          const title = `<div class="first-login-bootbox-title">Você é um membro. Bem-vindo a bordo!</div>`;
          const body = `<div class="first-login-bootbox-body">Agora você pode: </br> <ol class="user-suggestions"><li><a href="/new-topic" target="_blank">Fazer uma pergunta ou iniciar uma discussão</a></li> <li><a href="/my/preferences/tags" target="_blank">Configurar suas preferências de notificação</a></li></ol></div>`;
          
          bootbox.alert({
                        title: title,
                        message: body
                    });
            // Chamando o método de ação closeFirstNotificationMask do widget header
            this.sendWidgetAction("closeFirstNotificationMask", this.attrs);
        }
      return null;
  },
  
});

/* O código abaixo renderizará o widget "welcome-popup" após renderizar o widget "header-notifications" quando o usuário fizer login pela primeira vez */
api.decorateWidget("header-notifications:after", helper => { 
    if(!helper.attrs.active && helper.attrs.ringBackdrop){
        return helper.attach("welcome-popup", helper.attrs);     
    }else{
        return null;    
    }
});
</script>

Mais uma coisa: estamos usando a versão 5.4.1 do Bootbox, incluindo o script abaixo em nosso componente de tema, pois atualmente o Discourse não suporta o método bootbox com múltiplos parâmetros. Isso está correto?

<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.1/bootbox.min.js" integrity="sha512-eoo3vw71DUo5NRvDXP/26LFXjSFE1n5GQ+jZJhHz+oOTR4Bwt7QBCjsgGvuVMQUMMMqeEvKrQrNEI4xQMXp3uA==" crossorigin="anonymous"></script>

Por favor, dê sua opinião. Obrigado!

Isso parece bom :+1:

Testei localmente e não encontrei nenhum problema. Fiz algumas alterações menores.

<script
  src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.1/bootbox.min.js"
  integrity="sha512-eoo3vw71DUo5NRvDXP/26LFXjSFE1n5GQ+jZJhHz+oOTR4Bwt7QBCjsgGvuVMQUMMMqeEvKrQrNEI4xQMXp3uA=="
  crossorigin="anonymous"
></script>

<script type="text/discourse-plugin" version="0.8">
  const user = api.getCurrentUser();
  if (!user || user.read_first_notification || user.enforcedSecondFactor) {
    return;
  }

  const bootboxTitle = `<div class="first-login-bootbox-title">Você é um membro. Bem-vindo a bordo!</div>`;
  const bootboxBody = `<div class="first-login-bootbox-body">Agora você pode: </br> <ol class="user-suggestions"><li><a href="/new-topic" target="_blank">Fazer uma pergunta ou iniciar uma discussão</a></li> <li><a href="/my/preferences/tags" target="_blank">Configurar suas preferências de notificação</a></li></ol></div>`;

  /* Adicionando novo método ao widget do cabeçalho que será chamado após exibir o popup de boas-vindas */
  api.reopenWidget("header", {
    closeFirstNotificationMask() {
      this.state.ringBackdrop = false;
      this.state.userVisible = !this.state.userVisible;
    }
  });

  /* Criando novo widget "welcome-popup", que será renderizado apenas quando o usuário fizer login pela primeira vez */
  api.createWidget("welcome-popup", {
    tagName: "div.welcome-popup",
    html(attrs) {
      /* Chamando o método de ação closeFirstNotificationMask do widget do cabeçalho */
      this.sendWidgetAction("closeFirstNotificationMask", attrs);

      bootbox.alert({
        title: bootboxTitle,
        message: bootboxBody
      });
    }
  });

  /* O código abaixo renderizará o widget "welcome-popup" após renderizar o widget "header-notifications" quando o usuário fizer login pela primeira vez */
  api.decorateWidget("header-notifications:before", helper => {
    if (!helper.attrs.active && helper.attrs.ringBackdrop) {
      return helper.attach("welcome-popup", helper.attrs);
    }
  });
</script>

Sim, está tudo bem. O Discourse carrega a versão do bootbox que usamos como um shim, então carregar uma versão diferente no seu componente de tema não alterará nada no núcleo. A nova versão será usada apenas no seu componente de tema. A única desvantagem é que isso adiciona uma solicitação extra e cerca de 4kb ao carregamento inicial da página.

Olá @Johani, como você carregou o widget welcome-popup antes do widget header-notifications, parece que a notificação do Discobot não é carregada e, além disso, ao clicar nos links dentro do welcome-popup, ele é exibido novamente ao usuário.
Testei carregando o widget welcome-popup após o widget header-notifications e funcionou corretamente. Então, posso alterar para “header-notifications:after”?

Isso é estranho :thinking:

Acabei de testar o mesmo código novamente com 3 usuários novos diferentes e obtenho o resultado esperado todas as vezes. Vejo o popup na primeira visualização da página e a mensagem de saudação do Discobot é enviada. Nas visualizações subsequentes da página, o popup não aparece.

Claro, se funcionar para você, então a localização do decorador não deve importar.

Obrigado @Johani, usaremos o decorator after.