Ich versuche gerade, ein Video zu bestimmten Benutzerprofilseiten hinzuzufügen, sodass alle unsere Gönner einen bestimmten Hintergrundvideo auf ihrem Profil haben. (Bevor Sie sich aufregen, es wäre nur eine Schleifenanimation, kein vollwertiges Video – es sollte ziemlich gut aussehen, ähnlich wie bei Steam-Profilhintergründen.)
Der folgende HTML- und CSS-Code funktioniert für alle Benutzer – aber das ist offensichtlich nicht wirklich das, was wir wollen:
// Dies kommt in den "Header"-Tab
<video playsinline autoplay muted loop id="myVideo" poster="[LINK EINFÜGEN]">
<source src="[LINK EINFÜGEN]" type="video/webm">
<source src="[LINK EINFÜGEN]" type="video/mp4">
</video>
Im Gegensatz zur Verwendung von body.category-general, um ein Bild nur auf Seiten in der Kategorie “allgemein” hinzuzufügen, scheinen keine spezifischen Slugs für Profilseiten von Benutzern in einer bestimmten Gruppe oder mit einem bestimmten Benutzernamen zugewiesen zu sein. Wir sind hier ziemlich neu und haben hauptsächlich Erfahrung mit CSS und nicht mit direkter Arbeit mit HTML, und sind uns daher nicht sicher, ob es eine einfache/bequeme Möglichkeit gibt, dies wie gewünscht zu realisieren.
Wir stellen uns vor, dass der beste Ansatz darin besteht, einen ähnlichen Slug für Benutzerprofile basierend auf ihrer Gruppe hinzuzufügen, aber wir sind uns nicht sicher, wie wir dies erreichen und wie wir sicherstellen können, dass das Video nur auf Seiten mit dem richtigen Inhalt angezeigt wird. Wir sind auch nicht darauf festgelegt, genau diesen Ansatz zu verwenden, wenn eine andere, einfachere Methode existiert.
Zum Beispiel wären wir auch offen für die Idee, dies pro Benutzer und nicht pro Gruppe zu tun, wenn das irgendwie einfacher ist.
Wir möchten das Video nur nicht auf jeder Seite hartcodieren, sodass es nur geladen wird, wenn Sie sich auf den spezifischen Benutzer(n) in Frage befinden.
Bearbeiten: Ich sollte wahrscheinlich erwähnen, dass wir auf dem stabilen Branch sind, falls das etwas ändert.
Unser aktueller Ansatz besteht darin, zu prüfen, ob wir uns einfach auf der Seite eines bestimmten Benutzers befinden, indem wir den kanonischen Link auslesen. Wenn ja, wenden wir das Video an. Daher haben wir Folgendes:
<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>
Dies scheint jedoch nur bei einem vollständigen Refresh zu funktionieren – aus irgendeinem Grund aktualisiert das Klicken von Seite zu Seite nicht die Eigenschaft --currUsername, und anstatt eine zufällige Hintergrundfarbe auf Benutzerseiten anzuwenden, wird eine zufällige Hintergrundfarbe auf alle Seiten angewendet, wenn zuletzt F5 auf einer Benutzerseite gedrückt wurde, während nichts auf irgendeiner Seite angewendet wird, wenn zuletzt F5 auf einer Nicht-Benutzerseite gedrückt wurde.
Ich bin ehrlich gesagt nicht erfahren genug mit JavaScript, um zu wissen, warum das so sein könnte – es scheint mir, dass die Funktion bei einer Seitenänderung ausgelöst werden sollte (was sie tut), wodurch die Variable pageURL aktualisiert wird, und dies sollte die Eigenschaft --currUsername beim Laden einer Seite aktualisieren. Dies geschieht jedoch nur bei einem vollständigen Refresh, andernfalls scheinen sich die Variablen nicht zu ändern.
Es scheint, dass dies daran liegt, dass sich die kanonische URL nicht aktualisiert, während die Eigenschaft “og:url” dies tut.
Das einzige Problem ist, dass die Verwendung von var pageURL = document.querySelector("meta[property='og:url']").getAttribute("content");bevor das Meta-Tag selbst aktualisiert wird, aktualisiert wird – d. h. dieser Code gibt mir die URL der vorherigen Seite und nicht die aktuelle.
Wenn du einer bestimmten Seite Inhalte hinzufügen möchtest, ist ein Plugin-Outlet deine beste Option. Kurz gesagt: Plugin-Outlets sind in Discourse-Templates reservierte Bereiche, die du nutzen kannst, um neue Inhalte einzufügen.
Als Erstes musst du herausfinden, ob auf der Seite, die du ansprechen möchtest, ein Plugin-Outlet existiert. Dafür gibt es eine Theme-Komponente, die du installieren kannst.
Sobald du diese Komponente installiert und aktiviert hast, gehe zur gewünschten Seite und prüfe, was dir zur Verfügung steht. In deinem Fall existiert ein solches Plugin-Outlet bereits (grün hervorgehoben)
Angenommen, es gäbe es nicht… was dann? Die beste Option wäre in diesem Fall, die Hinzufügung zu beantragen oder einen PR einzureichen, um es in den Core aufzunehmen. In den meisten Fällen wird dies angenommen, wenn dein Anwendungsfall sinnvoll ist.
Wie auch immer, wie gesagt, existiert es in diesem Fall bereits. Schauen wir uns also an, wie du Markup hinzufügen kannst. Für den Rest dieses Beitrags brauchst du die oben genannte Komponente nicht mehr, du kannst sie also jetzt deaktivieren, da du bereits den Namen des Plugin-Outlets hast.
Alles, was du tun musst, ist, etwas Ähnliches im Reiter „Header“ deines Themes hinzuzufügen.
<script type="text/x-handlebars" data-template-name="/connectors/OUTLET_NAME/SOME_NAME">
Dein Markup geht hier rein...
</script>
Du musst OUTLET_NAME durch den Namen des gewünschten Outlets ersetzen. Ändere dann SOME_NAME in den Namen, den du dieser Anpassung geben möchtest. Der Name kann beliebig sein, aber versuche, wenn möglich, ihn beschreibend zu wählen. Das ist gute Praxis. So landen wir bei folgendem Code:
<script type="text/x-handlebars" data-template-name="/connectors/above-user-profile/add-profile-videos">
Dein Markup geht hier rein... wie
<h1>Hallo Welt!!</h1>
</script>
Lass uns das ausprobieren und sehen, was passiert… denk dran, das Snippet oben gehört in den Reiter common > header deines Themes.
Du möchtest deine Videos nicht auf jedem Profil anzeigen, sondern nur unter bestimmten Bedingungen. Wie machst du das? Du brauchst dafür zwei Dinge: einige Daten zum Auswerten und ein wenig JavaScript.
Lass uns die Daten finden. Erinnerst du dich, als ich sagte, Plugin-Outlets sind reservierte Bereiche? Was bringt es denn, sie ohne Kontext zu haben? Deshalb übergibt Discourse die relevanten Kontextteile an jedes Plugin-Outlet… aber zuerst einen Schritt zurück. Wenn du folgendes hinzufügst:
<script type="text/x-handlebars" data-template-name="/connectors/above-user-profile/add-profile-videos">
Dein Markup geht hier rein, wie...
<h1>Hallo Welt!!</h1>
</script>
Sieht das zwar wie HTML aus – und die Script-Tags sind es auch –, aber der Inhalt darin wird als Handlebars-Code behandelt.
Das bedeutet, du kannst stattdessen etwas wie Folgendes tun:
Ist das hier hilfreich? Ja… aber im Moment nicht. Wir kommen darauf zurück. Lass uns einen weiteren Schritt zurückgehen und sehen, wie Discourse den Kontext an das Outlet übergibt. Wenn du den Outlet-Namen auf GitHub (oder lokal) suchst, erhältst du dies:
Du kannst durch diese Daten blättern und prüfen, ob sie das enthalten, was du brauchst. Das sollte der Fall sein, da sie alle Daten über den Benutzer enthalten, die auch in anderen Elementen auf dieser Seite verwendet werden. Es ist das „Modell“ für die Benutzerseite dieses bestimmten Benutzers.
Eine der verfügbaren Eigenschaften dort ist… Trommelwirbel … die Gruppen, denen der Benutzer angehört.
Wenn du also:
{{log args.model.groups}}
dienst, erhältst du alle Gruppen, denen der Benutzer angehört, in der Konsole.
Okay, jetzt haben wir die benötigten Daten, also bleibt nur noch, einige Bedingungen basierend darauf hinzuzufügen.
Du bist vielleicht versucht zu denken, dass wir das im selben Snippet tun können, aber leider können wir das nicht. Handlebars ist eine Template-Sprache. Sie bietet sehr, sehr grundlegende Unterstützung für Logik – nichts weiter als einfache true/false-Bedingungen und Schleifen. Vergleiche und ähnliches sind nicht möglich.
Wo genau kannst du das also tun? In einer Connector-Klasse, klingt fancy… ich weiß.
Kurz gesagt ist eine Connector-Klasse im Wesentlichen ein Stück JavaScript, das an das Outlet gebunden ist. Es ist viel nuancierter als das, aber das ist alles, was du vorerst wirklich wissen musst.
Innerhalb unserer Connector-Klasse können wir etwas tun… aber… wir müssen bedenken, dass es nicht einfach eine beliebige JavaScript-Datei ist. Aus Mangel an einer besseren Beschreibung… betrachte es als ein Ember-Komponente auf Diät. Das weiter auszuführen, würde hier den Rahmen sprengen, also machen wir weiter.
Standardmäßig sind vier Methoden damit verbunden:
actions ermöglicht es dir, Aktionen wie folgt zu definieren:
api.registerConnectorClass("above-user-profile", "add-profile-videos", {
actions: {
myAction() {
// etwas tun
}
}
});
Du kannst diese Aktion dann innerhalb des Outlets aufrufen, z. B. wenn ein Button gedrückt wird. Wir brauchen das hier nicht, also machen wir weiter.
api.registerConnectorClass("above-user-profile", "add-profile-videos", {
shouldRender(args, component) {
// hier true oder false zurückgeben
}
});
Auch diese werden wir nicht verwenden, da das Outlet nur auf Profilseiten gerendert wird und wir vorerst keine weiteren Anforderungen haben. Du kannst dies jedoch nutzen, um Bedingungen hinzuzufügen, die vor dem Rendern des Outlets geprüft werden sollen. Zum Beispiel die Vertrauensebene des aktuellen Benutzers oder ähnliches. Weiter…
api.registerConnectorClass("above-user-profile", "add-profile-videos", {
setupComponent(args, component) {
// etwas tun
}
});
Das ist die Methode, auf die wir uns konzentrieren wollen. Welche JavaScript-Bedingungen oder Variablen du setzen möchtest, gehören hierhin. Bevor wir tiefer darauf eingehen, decken wir der Vollständigkeit halber zuerst die letzte Methode ab:
api.registerConnectorClass("above-user-profile", "add-profile-videos", {
teardownComponent(args, component) {
// etwas tun
}
});
Dies wird ausgelöst, wenn das Outlet entfernt wird. Du kannst also Aufräumarbeiten durchführen, z. B. Event-Listener entfernen und so weiter.
Okay, zurück zu setupComponent:
api.registerConnectorClass("above-user-profile", "add-profile-videos", {
setupComponent(args, component) {
// etwas tun
}
});
Du siehst, dass zwei Dinge übergeben werden: Erstens args und zweitens component.
args hier ist dasselbe, was wir früher betrachtet haben. Es sind die Kontextdaten, die Discourse an das Outlet übergibt. Wenn du also:
machst, siehst du dieselben Informationen in der Browserkonsole wie zuvor: die Gruppen, denen der Profilinhaber angehört. Hier beginnt der Spaß: Du hast jetzt die Daten und den richtigen Hook. Du kannst also hier alles Mögliche tun. Wenn ich also möchte, dass das Video nur auf den Profilen von Mitgliedern angezeigt wird, die einer bestimmten Gruppe angehören, kann ich Folgendes tun:
Wenn du dies auf einer Profilseite eines Benutzers in der Gruppe staff ausprobierst, wird true in der Konsole ausgegeben. Also bleibt uns nur noch, dies an das Outlet-Template weiterzugeben. So geht’s:
component, das an setupComponent übergeben wird, wird zwischen Connector und Outlet geteilt. Du kannst Dinge an das Outlet übergeben, indem du sie als Eigenschaften auf der Komponente setzt, wie folgt:
Prüfe dann eine Profilseite eines Staff-Benutzers. Du wirst sehen, dass das Video geladen wird.
Sobald du die Profilseite des Staff-Mitglieds verlässt, verschwindet das Video. Das Video wird nicht auf Profilen von Benutzern angezeigt, die nicht der Gruppe staff angehören.
Lass uns das alles zusammenfassen. Das ist dasselbe wie oben.
Hier ist das CSS, das ich verwendet habe. Reiter common > css:
#myVideo {
position: fixed;
top: var(--header-offset);
min-height: 100vh;
left: 0;
z-index: -1;
}
.user-content {
background: none;
}
.user-main {
padding: 0.5em;
background: rgba(var(--secondary-rgb), 0.8);
}
// wenn du es auch auf Mobile haben willst
.mobile-view {
body[class*="user-"] {
background: none;
.user-main,
.user-content {
padding: 0.5em;
background: rgba(var(--secondary-rgb), 0.8);
}
}
}
HTML / JavaScript / Handlebars. Dies gehört in den Reiter common > header deines Themes:
Ändere TARGET_GROUP in den Namen der Gruppe, die du ansprechen möchtest, und füge die src-Attribute für deine Videos hinzu.
Dieser Beitrag war etwas länger… lass dich davon nicht abschrecken. Sobald du das Konzept verstanden hast, kann alles, was wir oben gemacht haben, in weniger als 3–5 Minuten erledigt werden.
Das Schöne daran ist, dass alles, worüber wir gesprochen haben, für jedes Plugin-Outlet im Wesentlichen gleich ist. Einzige Änderung ist der Name. Das gilt also für alle Plugin-Outlet-Anpassungen, die du in Zukunft vornehmen möchtest.
Das ist unglaublich detailliert und ich werde es mir nächste Woche ansehen, wenn ich etwas Zeit habe. Aber oberflächlich betrachtet scheint es viel besser zu sein als meine aktuelle Implementierung (das Video auf jeder einzelnen Seite einbetten und es nur auf dem Benutzerprofil anzeigen, was ich mit einem Skript erreicht habe, das dem Body einer Benutzerseite ein Tag hinzufügt, wenn sein Kontoname etwas Bestimmtes ist). Danke für die ausführliche Erklärung, ich kann es kaum erwarten, mich damit zu beschäftigen!
Allerdings stoßen wir auf ein Problem mit unserem CSS – wir möchten einige Änderungen daran vornehmen, wie die Benutzerseite nur für diese Benutzer aussieht.
Derzeit erreichen wir dies über denselben Code wie zuvor:
<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>
Dies ermöglicht es uns, den “User1”-Code einfach für jeden neuen Benutzer zu kopieren und einzufügen, basiert jedoch auf einer Verzögerung von 50 ms nach jedem Seitenaufruf, bevor er ausgelöst wird, was für den Endbenutzer sichtbar ist (und wenn er entfernt wird, aus irgendeinem Grund nicht funktioniert).
Gibt es eine Möglichkeit, diese Hinzufügung einer Klasse zum Body auch in den von Ihnen bereitgestellten Code einzuhaken, damit wir sie verwenden können, um Seiten mit Videos anders zu gestalten als solche ohne?