中央テーマヘッダー ユーザーアイコン

こんにちは。メタのセントラルテーマのようなヘッダーメニューとユーザー、通知の分離を再現しようとしています。テーマのスレッドで質問しましたが、返信がありませんでした。

もし同じことをしたい場合、どのファイルを編集する必要がありますか?また、ファイルをテーマに配置するのと、テーマコンポーネントで再現するのとでは、どちらが良いでしょうか?

「いいね!」 1

ページをインスペクトして「Sources」タブを確認すると、どのように作成されたかを見ることができます。

基本的に:

  • 新しいユーザーメニューは、api.addToHeaderIcons で追加されたウィジェットで作成されています。
  • 通知メニュー:
    • アバターは、header-notifications ウィジェットの内容を置き換えることで、ベルアイコンに置き換えられています。
    • ユーザーメニューのクイックアクセスアイコンは、CSS で非表示にされています。
「いいね!」 5

いつものご協力ありがとうございます。作業を進めるためにステージング開発環境を作成します。改めて感謝いたします。

「いいね!」 1

こんにちは @digitaldominica さん!まもなく次のアップデートをリリースするため、しばらくスレッドを確認できていませんでした。申し訳ありません。皆さんのフィードバックは非常に貴重ですが、もし離れずにいたら、次のアップデートではなく現在のリストに追加してしまう傾向があります。:sweat_smile:

@Arkshine さんのおっしゃる通りです。これはテーマ内で動作するかなりハックな方法です(Central の多くは、私がまずデザイナーであり、次に開発者であるため、プロトタイピングにすぎません)。

Central でラフなプロトタイプをどのように達成したか、ステップバイステップのガイドを提供します。ただし、注意点として、ユーザーメニューのコアアップデートを行った場合、このソリューションは将来性がない可能性があり、コンポーネントを作成するためのより最適な方法があるかもしれません。


ステップ 1: カスタムユーザーメニューコンポーネントを作成する

これらのファイルを作成します。

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

現時点ではCSSスタイリングはご自身で行っていただくことになりますが、トグル効果のためにこの部分を含めることが重要です。

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

ステップ 2: コンポーネントをウィジェットとして登録する

このファイルを作成します。

: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 />`)];
  },
});

ステップ 3: ウィジェットをヘッダーに追加し、既存のユーザーアイコンを通知ベルアイコンに置き換える

このファイルを作成します。

: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");
      }
    });
  },
};

主な変更点は、avatarImg(…)iconNode に置き換えて、既存の header-notifications ウィジェットを編集(または「再オープン」)して、ユーザーアバターではなく通知アイコンを使用するようにしたことです。

参考として、元の header-notifications コードはこちらです: discourse/app/assets/javascripts/discourse/app/widgets/header.js at 9bc78625af1d54693bc4f1bad3eaa9161ae030b6 · discourse/discourse · GitHub

そして、@Arkshine さんが言及したように、CSS で通知メニューのユーザーセクションを非表示にしました。

.d-header .panel {
  .user-menu.revamped .bottom-tabs, #user-menu-button-profile {
    display: none;
  }
}
「いいね!」 7

こんにちは。ユーザーのアバター画像をベルアイコンに置き換えることができましたが、

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

ユーザーのアバターをドロップダウンで表示する代わりに、ヘッダー全体が削除されてしまいます。

「いいね!」 2

私の見落としでした。そのコードにタイプミスがありました。以下のように修正する必要があります。

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

ウィジェットはステップ2で header-user-new として登録されているためです。

「いいね!」 3

うまくいきました。ありがとうございます。

別の質問をしてもよろしいでしょうか。

中央トピックリストテンプレートは、ユーザー投稿とユーザー返信をどのように切り替えるのですか?

{{#if topic.replies}} や {{#if topic.posters.[0]}} を試しましたが、うまくいきませんでした。

問題ありません!探しているのは topic.posters.1.user です(最初の返信)。それが存在する場合はそれを使用し、存在しない場合は OP をデフォルトにします。

これは、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>
    投稿しました
    <a href={{topic.lastPostUrl}}>
      {{format-date topic.bumpedAt format="medium" leaveAgo="true"}}
    </a>
  </span>
{{/if}}
「いいね!」 1

topic.posters.1.user は投稿の作成者として機能しますか?

ああ、topic.posters.0.user が投稿者で、topic.posters.1.user が(存在する場合)最初の返信者です。:laughing:

「いいね!」 3

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