Estoy intentando añadir un vídeo a páginas de perfil de usuario específicas, de modo que todos nuestros mecenas tengan un determinado vídeo de fondo en su perfil. (Antes de que os pongáis todos nerviosos, sería solo una animación en bucle, no un vídeo completo; debería quedar bastante bien, similar a los fondos de perfil de Steam.)
El siguiente código HTML y CSS funciona para todos los usuarios, pero obviamente eso no es lo que buscamos:
// Esto va en la pestaña "Header"
<video playsinline autoplay muted loop id="myVideo" poster="[INSERTAR ENLACE]">
<source src="[INSERTAR ENLACE]" type="video/webm">
<source src="[INSERTAR ENLACE]" type="video/mp4">
</video>
A diferencia de usar body.category-general para añadir una imagen solo a las páginas de la categoría “general”, no parecen existir “slugs” específicos asignados a las páginas de perfil de usuarios de un grupo específico o de un nombre de usuario específico. Somos bastante nuevos en esto y tenemos más experiencia con CSS que con el trabajo directo con HTML, por lo que no estamos seguros de si existe una forma fácil y conveniente de hacer que esto funcione como nos gustaría.
Imaginamos que el mejor enfoque sería añadir un “slug” similar para los perfiles de usuario basado en su grupo, pero no estamos seguros de cómo hacerlo y de cómo hacer que el vídeo solo se muestre en las páginas con el contenido correcto, y tampoco estamos comprometidos a usar específicamente este enfoque si existe otro método más fácil.
Por ejemplo, también estaríamos abiertos a la idea de hacerlo por usuario en lugar de por grupo, si eso es de alguna manera más fácil.
Solo preferiríamos no tener que codificar el vídeo en cada página, para que solo se cargue cuando estés en el usuario o usuarios específicos en cuestión.
Edición: Probablemente debería mencionar que estamos en la rama estable, en caso de que eso cambie algo.
Nuestro enfoque actual es ver si podemos simplemente detectar que estamos en la página de cierto usuario a través del enlace canónico y, si es así, aplicar el video. Como tal, tenemos lo siguiente:
<script type="text/discourse-plugin" version="0.8">
api.onPageChange(() => {
determineUser();
});
function determineUser() {
var pageURL = document.querySelector("link[rel='canonical']").getAttribute("href");
var isUserPage = pageURL.includes("https://www.fortressoflies.com/u/");
document.documentElement.style.setProperty('--currUsername', pageURL);
if(isUserPage)
{
document.documentElement.style.setProperty('--lastUsername', pageURL);
$('body').css('background-color', '#'+(Math.random()*0xFFFFFF<<0).toString(16));
}
}
</script>
Sin embargo, esto solo parece funcionar en una actualización completa; por alguna razón, hacer clic de una página a otra no actualiza la propiedad --currUsername, y en lugar de aplicar un fondo de color aleatorio a las páginas de usuario, aplica un fondo de color aleatorio a todas las páginas si se presionó F5 por última vez en una página de usuario, mientras que no aplica nada a ninguna página si se presionó F5 por última vez en una página que no es de usuario.
Francamente, no tengo suficiente experiencia con JavaScript para saber por qué sería así; me parece que, al cambiar de página, la función debería ejecutarse (lo cual hace), haciendo que la variable pageURL se actualice, y esto debería hacer que la propiedad --currUsername se actualice al cargar una página. Sin embargo, esto solo ocurre en una actualización completa, de lo contrario, las variables no parecen cambiar.
Parece que esto se debe a que la URL canónica no se actualiza, mientras que la propiedad “og:url” sí lo hace.
El único problema es que usar var pageURL = document.querySelector("meta[property='og:url']").getAttribute("content"); se actualiza antes de que la etiqueta meta se actualice, es decir, este código me da la URL de la página anterior, no la actual.
Si quieres agregar contenido a una página específica, tu mejor opción es un plugin-outlet. En pocas palabras, los plugin-outlets son espacios reservados en las plantillas de Discourse que puedes usar para añadir nuevo contenido.
Lo primero que necesitas hacer es averiguar si existe un plugin-outlet en la página que quieres modificar. Hay un componente de tema que puedes instalar para ayudarte con eso.
Una vez que instales ese componente, actívalo, ve a la página que quieres modificar y revisa qué tienes disponible. En tu caso, existe un plugin-outlet (resaltado en verde)
Así que, el que nos interesa es above-user-profile
Supongamos que no existiera… ¿qué hacer? En ese caso, la mejor opción es solicitar que se añada o enviar una PR para incluirlo en el núcleo. La mayoría de las veces será aceptado si tu caso de uso tiene sentido.
De todos modos, como ya mencioné, en este caso ya existe. Así que veamos cómo puedes agregar marcado a él. No necesitarás el componente anterior para el resto de esto, así que puedes desactivarlo ahora que ya tienes el nombre del plugin outlet.
Todo lo que necesitas hacer es agregar algo como esto en la pestaña de encabezado de tu tema.
<script type="text/x-handlebars" data-template-name="/connectors/OUTLET_NAME/SOME_NAME">
Tu marcado va aquí...
</script>
Debes cambiar OUTLET_NAME por el nombre del outlet que quieres modificar. Luego cambia SOME_NAME por el nombre que quieras darle a esta personalización. El nombre puede ser cualquier cosa, pero intenta ser descriptivo si es posible. Es una buena práctica. Así que terminamos con esto.
<script type="text/x-handlebars" data-template-name="/connectors/above-user-profile/add-profile-videos">
Tu marcado va aquí... como
<h1>¡Hola Mundo!!</h1>
</script>
Probémoslo y veamos qué sucede… recuerda, el fragmento anterior va en la pestaña common > header de tu tema.
No quieres que tus videos se muestren en todos los perfiles, sino solo bajo ciertas condiciones. Entonces, ¿cómo lo haces? Necesitarás dos cosas: algunos datos para consumir y un poco de JavaScript.
Encontrémos los datos. ¿Recuerdas cuando dije que los plugin-outlets son espacios reservados? ¿Cuál sería el punto de tenerlos sin contexto? Por eso Discourse pasa los fragmentos relevantes de contexto a cada plugin outlet… pero primero, retrocedamos un paso. Cuando agregas esto
<script type="text/x-handlebars" data-template-name="/connectors/above-user-profile/add-profile-videos">
Tu marcado va aquí, como...
<h1>¡Hola Mundo!!</h1>
</script>
Parece HTML —y las etiquetas script lo son—, pero lo que hay dentro se trata como código Handlebars.
Eso significa que puedes hacer algo como esto en su lugar:
Ahora, ¿algo de esto es útil? Sí… pero no por el momento. Volvemos a esto más adelante. Retrocedamos otro paso y veamos cómo Discourse pasa el contexto al outlet. Si buscas el nombre del outlet en GitHub —o localmente— obtendrás esto:
Puedes explorar esos datos y ver si tienen lo que necesitas. Deberían, ya que contienen toda la información sobre el usuario utilizada en otros elementos de esa página. Es el “modelo” para la página de usuario de ese usuario en particular.
Una de las propiedades disponibles allí es… redoble de tambores … los grupos a los que pertenece el usuario.
Así que, si haces:
{{log args.model.groups}}
obtendrás todos los grupos a los que pertenece el usuario en la consola.
Bien, ahora tenemos los datos que necesitamos, así que lo único que queda es agregar alguna(s) condición(es) basada(s) en ellos.
Podrías pensar que podemos hacer eso en el mismo fragmento, pero, desafortunadamente, no podemos. Handlebars es un lenguaje de plantillas. Tiene soporte muy, muy básico para lógica: nada más allá de condiciones simples verdadero/falso y bucles. No puedes hacer comparaciones ni otras cosas como esa.
Entonces, ¿dónde exactamente puedes hacer eso? En una clase de conector, suena sofisticado… lo sé.
En pocas palabras, una clase de conector es esencialmente un poco de JavaScript adjunto al outlet. Es mucho más matizado que eso, pero eso es todo lo que realmente necesitas saber por ahora.
Dentro de nuestra clase de conector, podemos hacer algo… pero… debemos tener en cuenta que no es como cualquier archivo de JavaScript. Por falta de una mejor descripción… piénsalo como un componente Ember a dieta. Expandir esto está un poco fuera del alcance aquí, así que sigamos adelante.
Tampoco usaremos esta, ya que el outlet solo se renderiza en páginas de perfil y por ahora no tenemos otros requisitos. Sin embargo, puedes usarla para agregar cualquier condición que quieras probar antes de que se renderice el outlet. Por ejemplo, el nivel de confianza del usuario actual o cosas así. Sigamos…
Esta es la que queremos enfocarnos. Cualquier condición o variable de JavaScript que quieras establecer va aquí. Antes de profundizar en esta, cubramos el último método primero por completitud:
Esto se ejecuta cuando el outlet va a ser eliminado. Así que te permite realizar cualquier limpieza necesaria, como eliminar escuchadores de eventos, etc.
verás la misma información en la consola del navegador que vimos antes. Los grupos a los que pertenece el propietario del perfil. Aquí es donde comienza la diversión; ahora tienes los datos y el gancho correcto. Así que puedes hacer lo que quieras aquí. Entonces, si quiero que el video solo se muestre en los perfiles de miembros que pertenecen a un grupo determinado, puedo hacer esto:
Si pruebas esto en una página de perfil que pertenece a un usuario del grupo staff, imprimirá true en la consola. Así que ahora lo único que nos queda es pasar eso a la plantilla del outlet. Así es como puedes hacerlo.
component pasado a setupComponent aquí se comparte entre el conector y el outlet. Puedes pasar cosas al outlet estableciéndolas como propiedades en el componente así:
luego revisa una página de perfil de un usuario de staff. Verás que carga el video.
Una vez que navegas fuera del perfil del miembro del staff, el video desaparece. El video no se mostrará en los perfiles de usuarios que no estén en el grupo staff.
Así que, pongamos todo esto junto. Esto es lo mismo que vimos arriba.
Cambia TARGET_GROUP por el nombre del grupo que quieras modificar y agrega los atributos src para tus videos.
Esta publicación fue un poco larga… no te desanimes por eso. Una vez que comprendas el concepto, todo lo que hicimos arriba se puede hacer en menos de 3-5 minutos.
Lo bueno aquí es que todo lo que hablamos es prácticamente lo mismo para cualquier plugin outlet. Lo único que cambia es el nombre. Así que esto se aplica a cualquier modificación de plugin outlet que quieras hacer en el futuro.
Eso es increíblemente profundo y me aseguraré de revisarlo cuando tenga tiempo la próxima semana, pero basta decir que, de un vistazo, parece mucho mejor que mi implementación actual (incrustar el video en cada página y solo mostrarlo en el perfil del usuario, lo que logré con un script que agrega una etiqueta al cuerpo de la página de un usuario si el nombre de su cuenta es algo específico). ¡Gracias por la explicación detallada, ¡no puedo esperar a ponerme a ello!
Sin embargo, nos encontramos con un problema con nuestro CSS: deseamos realizar algunos cambios en la apariencia de la página de usuario solo para estos usuarios.
En este momento, lo estamos logrando a través del mismo código que antes:
<script type="text/discourse-plugin" version="0.8">
api.onPageChange(() =>{
window.onload = determineUser();
});
async function determineUser() {
await sleep(50);
var pageURL = document.querySelector("meta[property='og:url']").getAttribute("content");
var isUserPage = pageURL.includes("https://www.siteurl.com/u/");
var isUser1 = pageURL.includes("u/User1/");
document.body.className = document.body.className.replace(" user-page-animated","");
if(isUserPage)
{
if(isUser1)
{
document.body.className += ' user-page-animated';
}
}
}
function sleep(ms)
{
return new Promise(resolve => setTimeout(resolve, ms));
}
</script>
Esto nos permite simplemente copiar y pegar el código de “User1” para cada nuevo usuario, pero depende de un retraso de 50 ms después de cada carga de página antes de activarse, lo que es visible para el usuario final (y, si se elimina, no funciona por alguna razón).
¿Hay alguna manera de vincular también esta adición de una clase al cuerpo al código que proporcionaste, para que podamos usarla para estilizar páginas con videos de manera diferente a las que no los tienen?