Intestazione Tema Centrale Icone Utente

Ciao, sto cercando di ricreare la separazione del menu dell’intestazione con l’utente e le notifiche simile al tema centrale su meta, ho chiesto nel thread del tema ma non ho ricevuto risposta.

Se volessi fare lo stesso, quali file dovrei modificare e è meglio posizionare il file nel mio tema o ricrearlo con un componente del tema?

1 Mi Piace

Se ispezioni una pagina e controlli la scheda Sorgenti, può vedere come è stata creata.

Essenzialmente:

  • Il nuovo menu utente è realizzato con un widget, aggiunto con api.addToHeaderIcons
  • Il menu delle notifiche:
    • L’avatar viene sostituito con un’icona a campana sostituendo il suo contenuto dal widget header-notifications.
    • L’icona di accesso rapido del menu utente viene nascosta con CSS.
5 Mi Piace

Grazie per l’assistenza come sempre, creerò uno staging dev per lavorarci. Grazie ancora.

1 Mi Piace

Ciao @digitaldominica! Mi scuso per non aver controllato il thread da un po’ poiché sto per rilasciare il prossimo aggiornamento. Tutti hanno feedback preziosi, ma se non distolgo lo sguardo, avrò la tendenza ad aggiungerlo alla mia lista attuale piuttosto che salvarlo per l’aggiornamento successivo. :sweat_smile:

@Arkshine ha ragione. È un metodo piuttosto grezzo che funziona nei limiti di un tema (gran parte di Central è solo prototipazione poiché sono un designer prima, secondo e terzo, sviluppatore quarto).

Fornirò una guida passo passo su come ho ottenuto il prototipo grezzo in Central. Ma disclaimer, questa soluzione potrebbe non essere a prova di futuro se dovessimo mai apportare un aggiornamento principale al menu utente, e potrebbe esserci un modo più ottimale per creare il componente.


Passaggio 1: Creare un componente personalizzato per il menu utente

Crea questi file:

:page_facing_up: /javascripts/discourse/components/header-user-new.js
:page_facing_up: /javascripts/discourse/components/header-user-new.hbs

import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";

export default class HeaderUserNew extends Component {
  @service currentUser;
  @tracked isActive = false;

  constructor() {
    super(...arguments);
    this.handleDocumentClick = this.handleDocumentClick.bind(this);
  }

  @action
  toggleDropdown() {
    this.isActive = !this.isActive;

    if (this.isActive) {
      setTimeout(() => {
        document.addEventListener("click", this.handleDocumentClick);
      }, 0);
    } else {
      document.removeEventListener("click", this.handleDocumentClick);
    }
  }

  handleDocumentClick(event) {
    const dropdown = document.querySelector(".header-user-new__menu");
    const isClickInside = dropdown.contains(event.target);

    if (!isClickInside) {
      this.isActive = false;
      document.removeEventListener("click", this.handleDocumentClick);
    }
  }

  willDestroy() {
    super.willDestroy();
    document.removeEventListener("click", this.handleDocumentClick);
  }
}
<div class={{concatClass "header-user-new" (if isActive "active")}}>
  <button class="header-user-new__button" type="button" {{on "click" this.toggleDropdown}}>
    {{avatar currentUser}}
  </button>

  <div class="header-user-new__menu">
    <LinkTo class="header-user-new__profile" @route="user.summary" @model={{this.currentUser}}>
      {{avatar currentUser}}
      <div class="header-user-new__profile-info">
        <span class="header-user-new__profile-name">
          {{#if currentUser.name}}
            {{currentUser.name}}
          {{else}}
            {{currentUser.username}}
          {{/if}}
        </span>
        <span class="header-user-new__profile-view">
          View Profile
        </span>
      </div>
    </LinkTo>

    <ul>
      <li>
        <LinkTo @route="userActivity.bookmarks" @model={{this.currentUser}}>
          {{d-icon "bookmark"}}
          <span>
            {{i18n "js.user.bookmarks"}}
          </span>
        </LinkTo>
      </li>
      <li>
        <LinkTo @route="preferences" @model={{this.currentUser}}>
          {{d-icon "cog"}}
          <span>
            {{i18n "user.preferences"}}
          </span>
        </LinkTo>
      </li>
      <li>
        <DButton @action={{route-action "logout"}}>
          {{d-icon "sign-out-alt"}}
          <span>
            {{i18n "user.log_out"}}
          </span>
        </DButton>
      </li>
    </ul>
  </div>
</div>

Per ora lascerò lo styling CSS a te, ma è importante includere questo pezzo per l’effetto toggle:

.header-user-new {
  &.active {
    .header-user-new__menu {
      display: flex;
    }
  }
  .header-user-new__menu {
    display: none;
  }
}

Passaggio 2: Registrare il componente come widget

Crea questo file:

:page_facing_up: /javascripts/discourse/widgets/header-user-new.js

import { hbs } from "ember-cli-htmlbars";
import RenderGlimmer from "discourse/widgets/render-glimmer";
import { createWidget } from "discourse/widgets/widget";

export default createWidget("header-user-new", {
  tagName: "li.header-dropdown-toggle.header-user-new",

  html() {
    return [new RenderGlimmer(this, "div", hbs`<HeaderUserNew />`)];
  },
});

Passaggio 3: Aggiungere il widget all’header, sostituire l’icona utente esistente con l’icona della campana di notifica

Crea questo file:

:page_facing_up: /javascripts/discourse/initializers/header-edit.js

import { h } from "virtual-dom";
import { withPluginApi } from "discourse/lib/plugin-api";
import { iconNode } from "discourse-common/lib/icon-library";
import I18n from "discourse-i18n";

export default {
  initialize() {
    withPluginApi("0.8", (api) => {
      api.reopenWidget("header-notifications", {
        html(attrs) {
          const { user } = attrs;

          let avatarAttrs = {
            template: user.get("avatar_template"),
            username: user.get("username"),
          };

          if (this.siteSettings.enable_names) {
            avatarAttrs.name = user.get("name");
          }

          const contents = [h("div", iconNode("bell"))];

          if (this.currentUser.status) {
            contents.push(this.attach("user-status-bubble", this.currentUser.status));
          }

          if (user.isInDoNotDisturb()) {
            contents.push(h("div.do-not-disturb-background", iconNode("moon")));
          } else {
            if (user.new_personal_messages_notifications_count) {
              contents.push(
                this.attach("link", {
                  action: attrs.action,
                  className: "badge-notification with-icon new-pms",
                  icon: "envelope",
                  omitSpan: true,
                  title: "notifications.tooltip.new_message_notification",
                  titleOptions: {
                    count: user.new_personal_messages_notifications_count,
                  },
                  attributes: {
                    "aria-label": I18n.t("notifications.tooltip.new_message_notification", {
                      count: user.new_personal_messages_notifications_count,
                    }),
                  },
                })
              );
            } else if (user.unseen_reviewable_count) {
              contents.push(
                this.attach("link", {
                  action: attrs.action,
                  className: "badge-notification with-icon new-reviewables",
                  icon: "flag",
                  omitSpan: true,
                  title: "notifications.tooltip.new_reviewable",
                  titleOptions: { count: user.unseen_reviewable_count },
                  attributes: {
                    "aria-label": I18n.t("notifications.tooltip.new_reviewable", {
                      count: user.unseen_reviewable_count,
                    }),
                  },
                })
              );
            } else if (user.all_unread_notifications_count) {
              contents.push(
                this.attach("link", {
                  action: attrs.action,
                  className: "badge-notification unread-notifications",
                  rawLabel: user.all_unread_notifications_count,
                  omitSpan: true,
                  title: "notifications.tooltip.regular",
                  titleOptions: { count: user.all_unread_notifications_count },
                  attributes: {
                    "aria-label": I18n.t("user.notifications"),
                  },
                })
              );
            }
          }
          return contents;
        },
      });

      const currentUser = api.container.lookup("service:current-user");
      if (currentUser !== null) {
        api.addToHeaderIcons("header-user-new");
      }
    });
  },
};

La modifica principale è stata la sostituzione di avatarImg(…) con iconNode per utilizzare l’icona di notifica anziché l’avatar dell’utente modificando (o “riaprendo”) il widget esistente header-notifications.
Come menzionato da @Arkshine, ho nascosto la sezione utente nel menu delle notifiche con CSS.

.d-header .panel {
  .user-menu.revamped .bottom-tabs, #user-menu-button-profile {
    display: none;
  }
}
7 Mi Piace

Ciao, sono riuscito a sostituire l’avatarImg dell’utente con l’icona della campana tuttavia il comando

api.addToHeaderIcons(“new-user-icon”);

rimuove l’intero header, invece di visualizzare l’avatar dell’utente con il menu a discesa.

2 Mi Piace

Mia svista, c’era un errore di battitura in quel codice, dovrebbe essere—

api.addToHeaderIcons("header-user-new");

Poiché il widget è registrato come header-user-new nel passaggio 2.

3 Mi Piace

Grazie, ha funzionato.

Se posso fare un’altra domanda su qualcosa di separato;

Come fa il modello di elenco degli argomenti centrali ad alternare tra
postato dall’utente e risposto dall’utente
?

Ho provato {{#if topic.replies}} e {{#if topic.posters.[0]}}
ma non funziona.

Nessun problema! topic.posters.1.user è quello che stai cercando (la prima risposta). Se esiste, usalo, altrimenti usa l’OP come predefinito.

Questo è come è attualmente strutturato in Central—

{{#if topic.posters.1.user}}
  <span class="tli__last-reply">
    {{d-icon "reply"}}
    <a href="{{topic.lastPoster.user.userPath}}" data-user-card="{{topic.lastPoster.user.username}}">
      {{~topic.lastPoster.user.username~}}
    </a>
    {{theme-i18n "topic_list_item.replied"}}
    <a href={{topic.lastPostUrl}}>
      {{format-date topic.bumpedAt format="medium" leaveAgo="true"}}
    </a>
  </span>
{{else}}
  <span class="tli__last-reply">
    {{d-icon "m-post_add"}}
    <a href="{{topic.posters.0.user.userPath}}" data-user-card="{{topic.posters.0.user.username}}">
      {{~topic.posters.0.user.username~}}
    </a>
    posted
    <a href={{topic.lastPostUrl}}>
      {{format-date topic.bumpedAt format="medium" leaveAgo="true"}}
    </a>
  </span>
{{/if}}
1 Mi Piace

Quindi topic.posters.1.user funge da autore del post?

Oh no topic.posters.0.user è l’autore, topic.posters.1.user è il primo utente che risponde (se esiste). :laughing:

3 Mi Piace

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.