¿Hay alguna forma de escuchar el evento de inicio de sesión del usuario usando el componente Theme?

Hola a todos,

Quiero mostrar el cuadro de diálogo (Popup) utilizando el componente Theme cuando el usuario inicia sesión por primera vez.

1. He intentado sobrescribir la acción de inicio de sesión del controlador de login usando el componente Theme para mostrar el cuadro de diálogo cuando el usuario inicia sesión correctamente.
En la acción de login, agregué el código bootbox.alert(“Test Alert”); antes de hiddenLoginForm.submit();.

Sin embargo, el Popup se muestra hasta que se envía el hiddenLoginForm y luego se redirige a la página de inicio.

Mi necesidad es que el cuadro de diálogo se muestre después de redirigir a la página de inicio tras un inicio de sesión exitoso.

2. Para verificar si el usuario inicia sesión por primera vez, intenté comprobar el valor de la propiedad previous_visit_at del usuario actual.
Si previous_visit_at = null (es decir, el usuario inicia sesión por primera vez).
Pero aquí también mi prueba falla, ya que al iniciar sesión por segunda vez, el valor de la propiedad previous_visit_at sigue siendo null.
Quisiera saber exactamente cuándo se actualiza el valor de la propiedad previous_visit_at.

Por favor, ayúdenme a cumplir con los requisitos mencionados anteriormente.

Gracias,
Saurabh Khandelwal

¡Hola @Saurabh_Khandelwal :wave:!

Te aconsejo que no lo hagas de esta manera; el inicio de sesión es una de las acciones más críticas, y cualquier sobrescritura de esa acción será frágil y propensa a fallar si realizamos cambios en esa área del núcleo.

Si recuerdas, cuando te registraste por primera vez en este sitio, viste algo así.

Las condiciones que usamos para verificar esto son:

!user.read_first_notification && !user.enforcedSecondFactor

Por lo tanto, si utilizas las mismas condiciones en un inicializador, podrás renderizar un Bootbox en la primera vista de página del usuario después del inicio de sesión.

Puedes probar algo así en tu 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);
      }
    });
  }
};

Y eso te dará algo como esto:

Puedes usar HTML en el texto del bootbox si es necesario. Una vez que el usuario cierre el bootbox y haga clic en el avatar circundante, nunca volverá a ver el bootbox.

Ten en cuenta que los bootboxes están pensados para diálogos o confirmaciones simples. Si necesitas algo un poco más complicado que eso, probablemente sea mejor usar un modal con showModal().

Gracias, @Johani, por tu consejo. Evitaré realizar cambios en la acción de inicio de sesión e intentaré implementarlo como mencionas.

.

Hola, @Johani, he añadido un hipervínculo sencillo (por ejemplo, “/new-topic”) dentro del cuadro emergente que se abre en una nueva pestaña.

Cuando hago clic en ese enlace, se abre en una nueva pestaña, pero vuelve a mostrar el cuadro emergente, ya que hemos verificado las siguientes condiciones:

!user.read_first_notification && !user.enforcedSecondFactor

En mi caso, el cuadro emergente no debería mostrarse después de visitar el enlace.

¿Podemos actualizar los valores de estas propiedades una vez que el cuadro emergente se ha mostrado? De ser así, ¿cómo podemos hacerlo?

Me temo que eso no es posible.

Esas propiedades no tienen setters; incluso si los tuvieran, tus cambios solo se aplicarían temporalmente en la primera ventana. Una vez que el usuario visite la segunda pestaña, los datos se basarán en lo que está almacenado en la base de datos. Los temas no tienen acceso al backend; solo pueden modificar el frontend.

Lo que puedes hacer es agregar un hash a tu enlace y verificarlo de la siguiente manera.

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">Enlace</a>, consectetur adipiscing elit, sed do eiusmod tempor`;
        bootbox.alert(text);
      }
    });
  }
};

No estoy seguro si enlazar a “/new-topic” en tu publicación era solo un ejemplo o si es lo que realmente quieres hacer. Si ese es el resultado deseado, entonces tienes otro problema. Incluso si el bootbox no se muestra en la página con el hash, el usuario aún verá esto…

…y el compositor no se abrirá, lo cual tiene sentido ya que es muy inesperado que un usuario comience a redactar un tema en su primera visita a la página.

¿Podría preguntarle qué intenta lograr aquí? ¿Está tratando de informar al usuario sobre algo en particular?

La forma en que he visto que se hace esto en otros sitios es editando el mensaje de bienvenida, pero si esa es una opción, hay alternativas.

Aquí está lo que sugiero:

  1. Crea un tema y agrega toda la información que desees allí.
  2. Publica ese tema.
  3. Enlaza a ese tema en el bootbox y abre ese enlace en una nueva pestaña.

De esta manera, cuando el usuario haga clic en el enlace, verá algo como esto (sin la superposición):

Una vez que termine en esa página, puede volver a la primera pestaña, cerrar el bootbox, leer la primera notificación y luego continuar usando el sitio.

De esta forma, ni siquiera necesitas agregar o verificar un hash. Aquí tienes un ejemplo de fragmento:

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">Enlace</a>, consectetur adipiscing elit, sed do eiusmod tempor`;
        bootbox.alert(text);
      }
    });
  }
};

Estoy intentando tener algunos enlaces como “/new-topic” o “/my/preferences” dentro del Popup.

De lo contrario, estaba pensando en mostrar el Popup solo para la página de inicio verificando la URL actual.
Así, al visitar otras páginas, el Popup no se mostrará.

Incluso si verifico la URL de la página de inicio después de hacer clic en el enlace “/new-topic”, el problema anterior se presentará.

Hola Saurabh,
Sí, creo que verificar la URL actual y mostrar el popup solo en la página de inicio debería funcionar..

Podemos mostrar la introducción para nuevos usuarios de Discobot después de un tiempo, por ejemplo, 2 minutos:

Hola, @Johani

¿Podemos crear un widget como el que se muestra a continuación y llamar a bootbox() dentro de él?
¿Y después de llamar a bootbox, podemos llamar al método “skipNewUserTips()”? Este método también es utilizado por Discourse en el 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">Eres un miembro. ¡Bienvenido a bordo!</div>`;
          const body = `<div class="first-login-bootbox-body">Ahora puedes: </br> <ol class="user-suggestions"><li><a href="/new-topic" target="_blank">Haz una pregunta o inicia una discusión</a></li> <li><a href="/my/preferences/tags" target="_blank">Configura tu ajustes de notificación</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 de Discourse para el widget header-notifications:
https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/app/widgets/header.js#L101

¿Esto causará algún problema?

Esto omite completamente este superposición para todos los nuevos usuarios. Por lo tanto, nunca lo verán.

Si eso te parece bien, entonces, sí, debería funcionar.

Necesitas cambiar la llamada a bootbox. De lo contrario, obtendrás esto.

en lugar de hacer esto

Solo debes pasar un único argumento.

Esas opciones se agregaron en versiones más recientes de Bootbox y no funcionarán con la versión que estamos usando actualmente en Discourse (lo estamos discutiendo internamente, pero por ahora, no puedes usarlas).

Además, como estás creando una solicitud PUT, también puedes omitir esto.

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

Así que, quizás algo como esto.

  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">¡Eres un miembro. ¡Bienvenido a bordo!</h2>
                      <hr>
                      <div class="first-login-bootbox-body">
                        Ahora puedes:
                        <br>
                        <ol class="user-suggestions">
                          <li>
                            <a href="/new-topic" target="_blank"
                              >Hacer una pregunta o iniciar una discusión</a
                            >
                          </li>
                          <li>
                            <a href="/my/preferences/tags" target="_blank"
                              >Configurar tu configuración de notificaciones</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
        }
      });
    }
  });

Hola @Johani,

Al establecer skip_new_user_tips en true, también se omite la notificación de saludo de Discobot. Está bien que no se muestre la primera máscara de superposición de notificaciones, pero sí queremos que aparezca la notificación de saludo de Discobot.

Notificación de Discobot:

Para ello, modificamos el código para que la notificación de saludo de Discobot se cargue para todos los nuevos usuarios.
Ahora hemos añadido un nuevo método al widget del encabezado llamado “closeFirstNotificationMask()”, donde establecemos “ringBackdrop” en false y “userVisible” en su inverso. Luego, llamamos a este método después de ejecutar el método bootbox en el widget welcome-popup.

Tomamos como referencia el método “toggleUserMenu” del widget del encabezado, que se llama cuando se hace clic en el icono de usuario.

<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;

/* Añadimos un nuevo método al widget del encabezado que se llamará después de mostrar el welcome-popup */
api.reopenWidget("header",{
    closeFirstNotificationMask() {
        this.state.ringBackdrop = false;
        this.state.userVisible = !this.state.userVisible;
        this.toggleBodyScrolling(this.state.userVisible);
    }
});

/* Creamos un nuevo widget "welcome-popup", este widget solo se renderizará cuando el usuario inicie sesión por primera 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">¡Eres miembro. ¡Bienvenido a bordo!</div>`;
          const body = `<div class="first-login-bootbox-body">Ahora puedes: </br> <ol class="user-suggestions"><li><a href="/new-topic" target="_blank">Hacer una pregunta o iniciar una discusión</a></li> <li><a href="/my/preferences/tags" target="_blank">Configurar tus preferencias de notificación</a></li></ol></div>`;
          
          bootbox.alert({
                        title: title,
                        message: body
                    });
            //Llamamos al método de acción closeFirstNotificationMask del widget del encabezado
            this.sendWidgetAction("closeFirstNotificationMask", this.attrs);
        }
      return null;
  },
  
});

/* El siguiente código renderizará el widget "welcome-popup" después de renderizar el widget "header-notifications" cuando el usuario inicie sesión por primera 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>

Una cosa más: estamos utilizando la versión 5.4.1 de Bootbox incluyendo el siguiente script en nuestro componente de tema, ya que actualmente Discourse no soporta el método bootbox con múltiples parámetros. ¿Está bien?

<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éjanos tu opinión. ¡Gracias!

Esto se ve bien :+1:

Lo probé localmente y no encontré ningún problema. Realicé algunos cambios 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">¡Eres miembro. ¡Bienvenido a bordo!</div>`;
  const bootboxBody = `<div class="first-login-bootbox-body">Ahora puedes: </br> <ol class="user-suggestions"><li><a href="/new-topic" target="_blank ">Haz una pregunta o inicia una discusión</a></li> <li><a href="/my/preferences/tags" target="_blank">Configura tu ajustes de notificación</a></li></ol></div>`;

  /* Agregando un nuevo método al widget de encabezado que se llamará después de mostrar el popup de bienvenida */
  api.reopenWidget("header", {
    closeFirstNotificationMask() {
      this.state.ringBackdrop = false;
      this.state.userVisible = !this.state.userVisible;
    }
  });

  /* Creando un nuevo widget "welcome-popup", este widget se renderizará solo cuando el usuario inicie sesión por primera vez */
  api.createWidget("welcome-popup", {
    tagName: "div.welcome-popup",
    html(attrs) {
      // Llamando al método de acción closeFirstNotificationMask del widget de encabezado
      this.sendWidgetAction("closeFirstNotificationMask", attrs);

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

  /* El siguiente código renderizará el widget "welcome-popup" después de renderizar el widget "header-notifications" cuando el usuario inicie sesión por primera vez */
  api.decorateWidget("header-notifications:before", helper => {
    if (!helper.attrs.active && helper.attrs.ringBackdrop) {
      return helper.attach("welcome-popup", helper.attrs);
    }
  });
</script>

Sí, está bien. Discourse carga la versión de bootbox que usamos como un shim, por lo que cargar una versión diferente en tu componente de tema no cambiará nada en el núcleo. La nueva versión solo se usará en tu componente de tema. La única desventaja es que agrega una solicitud extra y ~4kb a la carga inicial de la página.

Hola @Johani, como cargaste el widget welcome-popup antes que el widget header-notifications, parece que la notificación de Discobot no se carga. Además, al hacer clic en los enlaces dentro de welcome-popup, este vuelve a mostrarse al usuario.

Lo he probado cargando el widget welcome-popup después del widget header-notifications y funciona correctamente. ¿Puedo cambiarlo a “header-notifications:after”?

Eso es extraño :thinking:

Acabo de probar el mismo código nuevamente con 3 usuarios nuevos diferentes y obtengo el resultado esperado cada vez. Veo el popup en la primera vista de la página y se envía el mensaje de saludo de Discobot. Las vistas de página posteriores no tienen el popup.

Claro, si funciona para ti, entonces la ubicación del decorador no debería importar.

Gracias @Johani, usaremos el decorador after.