Sticky: Benachrichtigung über neue oder aktualisierte Themen

Ja, das Hinzufügen einer URL führt zu einer Navigation, da es keine Logik gibt, die das Ereignis abfängt.

Der Link ruft eine Ember-Aktion auf. Alle Aktionen in Discourse sind erweiterbar. Sie wirken also gewissermaßen wie Anpassungshooks. Wie modifiziert man also eine Aktion? Nun, schauen wir uns zunächst an, was sie tut.

Suche auf GitHub oder lokal nach dem Namen der Aktion. Aktionen werden immer in JS-Dateien definiert und in Handlebars referenziert. Wir wollen die Definition sehen, also beschränken wir die Suche auf JS-Dateien.

Repository search results · GitHub

Du erhältst vier Dateien. Welche solltest du dir ansehen? Du möchtest die

discovery/topics.hbs

Vorlage anpassen. Also ist

discovery/topics.js

das, was du dir ansehen solltest.

Ist irgendeiner dieser Codeabschnitte hilfreich? Nein, aber jetzt wissen wir, wo die Aktion definiert ist. Also ändern wir sie.

discovery/topics.js ist eine Ember-Klasse. Du kannst Ember-Klassen mit einer Methode in der plugin-api ändern, die … modifyClass heißt :stuck_out_tongue:

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.js#L166-L195

Wir wissen bereits, welche Klasse wir ändern möchten. Es ist discovery/topics. Wir müssen wissen, um welche Art von Klasse es sich handelt. Prüfen wir also das Verzeichnis.

discourse/app/controllers/discovery/topics.js

Es ist ein Controller.

Wir wissen auch, dass wir die showInserted-Aktion in dieser Klasse ändern möchten. Also beginnen wir damit.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
      // lass uns etwas arbeiten
    }
  }
});

Anschließend kannst du beliebigen Code hinzufügen, um das Fenster zu scrollen, wenn der Benutzer auf das Banner „neue Themen" klickt. Ich habe mich für etwas wie das hier entschieden.

const listControls = document.querySelector(".list-controls");
listControls.scrollIntoView();

Weitere Informationen zu scrollIntoView() findest du hier.

Dann fügst du das wie folgt zur plugin-api-Methode hinzu.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
+     const listControls = document.querySelector(".list-controls");
+     listControls.scrollIntoView();
    }
  }
});

Sind wir also fertig? Nein. Das bricht Discourse, weil du die Aktion vollständig überschreibst. Beim Klicken auf den Link wird nun zum list-controls-Element gescrollt, aber die neuen Themen werden nicht geladen. Warum? Weil der Code im Kern nicht mehr ausgeführt wird. Ich meine, dieser Teil hier

Wie behebt man das also? Mit dieser einfachen Zeile

this._super(...arguments);

Du musst keinen Code aus dem Kern kopieren, wenn du ihn nur erweitern möchtest. Erledige deine Arbeit und füge dann diese Zeile hinzu. Alles, was sie bewirkt, ist, dass der Code aus dem Kern angewendet wird.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
     const listControls = document.querySelector(".list-controls");
     listControls.scrollIntoView();
+
+    this._super(...arguments);
    }
  }
});

Wenn du dies testest, wirst du sehen, dass fast alles gut funktioniert, außer … der Header überlappt mit list-controls. Warum? Weil der Header auf „sticky" gesetzt ist.

Du kannst dies auf verschiedene Arten in JS beheben – die Höhe berechnen, den Offset ermitteln, einen Helfer aus Discourse importieren usw. Ich werde darauf nicht näher eingehen.

Der einfachste Weg ist CSS mit scroll-margin-top, über das du hier mehr lesen kannst.

Also fügen wir Folgendes hinzu

.list-controls {
  scroll-margin-top: calc(var(--header-offset) * 2);
}

Auf Deutsch: Wenn auf den Link geklickt wird, zum oberen Rand von list-controls scrollen – 2 * die Headerhöhe, damit keine Überlappung entsteht und etwas Platz darunter bleibt.

Bringen wir also alles zusammen.

Common Header Tab

<script type="text/discourse-plugin" version="0.8">
api.modifyClass("controller:discovery/topics", {
  pluginId: "sticky-new-topics-banner",
  actions: {
    showInserted() {
      const listControls = document.querySelector(".list-controls");
      listControls.scrollIntoView();
      this._super(...arguments);
    }
  }
});
</script>

Common CSS

#list-area {
  // Mobile hat ein anderes Layout
  .alert-info,
  .show-more.has-topics {
    position: sticky;
    // Safari ist manchmal ohne das Präfix pingelig
    position: -webkit-sticky;
    top: var(--header-offset);
    // Banner sollte über dem Inhalt liegen
    z-index: z("header");
  }
}

.list-controls {
  scroll-margin-top: calc(var(--header-offset) * 2);
}