トピック/返信アラートをクリックしてもトピックリストが更新されない理由を見つけるのを手伝ってください

テーマ作成者サイトで、無視したユーザーのトピックをトピックリストから削除しようとしています。以下が私のコードです。

api.modifyClass("component:topic-list", {
      @on("didReceiveAttrs")
      removeIgnoredUsers() {
        this.topics = this.topics.filter(
          topic => !ignored.includes(topic.posters[0].user.username)
        );
      }
});

これはモバイルとデスクトップの両方で期待どおりに動作しますが、新しいトピック/返信アラートをクリックした後にトピックリストが更新されない原因にもなっています。

このバグを再現するスクリーンレコードがあります。

ビデオでは、デスクトップでモバイルビューを見ており、携帯電話から「test new topic alert 3」に新しい返信を作成し、デスクトップでアラートをクリックしています。通常の場合、更新されたトピックがトピックの最上部に表示されるはずですが、この場合は表示されません。

上記で使用したメソッドが誤ってトリガーされていると思いましたが、トピックアラートをクリックしても呼び出されていません。

このバグが発生する理由について、何か提案はありますか?よろしくお願いします。

更新:スクロール時に「さらにトピックを自動読み込み」がモバイルビューで機能しなくなる原因にもなっていることがわかりました。

「いいね!」 2

上記のコードを削除すると、無視されたユーザーが表示されなくなり、問題は解決しますか?もし解決するなら、コードがすべてのユーザー(または無視されるべきではないユーザーよりも多くのユーザー)のトピックをフィルタリングしているため、表示されなくなっていると推測します。

ignored がどのように設定されているかはわかりませんが、アラートを一番上に表示した新しいトピックを作成したユーザーのユーザー名が設定されている可能性があります。無視されるべきではないユーザーが設定されているのではないでしょうか? ignored の値を出力できますか?

「いいね!」 2

これはモバイル/モバイルビューでのみ発生した問題なので、そうではないと思います。(更新されず、さらに読み込めない)

無視機能を使用できる信頼レベルに達していないか、無効になっていると仮定して、テーマクリエーターサイトでテストするために手動で const ignored = ['david', 'pekka_gaiser', 'sam']; を設定しました。

そして、私が更新したトピックは私が作成したトピックです。

「いいね!」 1

奇妙ですね。問題がそのコードに特有のものでない限り。コードを次のように変更した場合:

api.modifyClass("component:topic-list", {
      @on("didReceiveAttrs")
      removeIgnoredUsers() {
        this.topics = this.topics.filter(() => true);
      }
});

問題は解決しませんか?もし解決しない場合、問題はおそらく他の場所にあるでしょう。

「いいね!」 2

いいえ、ブロックを提示されたものに変更すると、バグはなくなります。フィルター機能がトピックデータの更新サイクルを壊しているのではないかと思いましたが、このバグはモバイルでのみ発生し、デスクトップとモバイルの違いの原因を特定できません…

「いいね!」 1

それなら、私が以前言ったように、ignored 変数が誤って設定されているとしか考えられません。どこかで値が変更されているのかもしれません。コードを次のように変更してみてください。

api.modifyClass("component:topic-list", {
      @on("didReceiveAttrs")
      removeIgnoredUsers() {
        const ignored2 = ['david', 'pekka_gaiser', 'sam'];
        this.topics = this.topics.filter(
          topic => !ignored2.includes(topic.posters[0].user.username)
        );
      }
});

上記のコードでは、ignored 変数をフィルターが適用されているのと同じスコープに配置し、変数名を変更して、配列の値が意図せず変更されるのを回避しました(たとえば、どこかで ignored.push(...) が呼び出された場合、他の場所の値が変更される可能性があります)。

「いいね!」 1

When you do something like this

this.topics = something_else

You’re redefining that property to something else. This is fine if nothing else is watching that property, but in this case, several things are watching the topics array.

For example

If you do this on a development installation, Ember will throw errors in the console like so.

.

So, the key takeaway here is to do something like this instead.

this.set("topics", something_else)

until the @tracked decorator lands in Discourse.

So, if you try something like this

api.modifyClass("component:topic-list", {
  @on("didReceiveAttrs")
  removeIgnoredUsers() {
    const filtered = this.topics.filter(
      topic => !ignored.includes(topic.posters[0].user.username)
    );
    this.set("topics", filtered);
  }
});

It should work on both desktop and mobile on theme creator.

However…

If you try that on development installation, you’ll see another error.

So this should tell you something. The topics array is being used in many different places. So it’s not a good idea to muck around with it - especially since your use case is primarily visual and there are no security implications involved.

Here’s what I suggest: Take a step back and try a different approach. Instead of trying to modify the array, do something much simpler. If an ignored user created the topic, add a CSS class and hide it with CSS.

You do that with something like this - I left some comments if you want to follow along but you can delete them when you’re ready to use it.

This goes in the initilizer file:

import { apiInitializer } from "discourse/lib/api";
import discourseComputed from "discourse-common/utils/decorators";

export default apiInitializer("0.11.1", api => {
  // set an id for your modifications
  const PLUGIN_ID = "hide-ignored-op-topics";

  // The class name you want to add. The space at the start is required
  const IGNORED_TOPIC_CLASS_STRING = " ignored-op-topic";

  // get current user
  const user = api.getCurrentUser();

  // not logged in, bail
  if (!user) {
    return;
  }

  // get a list of ignored users
  const ignored = user.ignored_users;

  // helper function to avoid duplicating code
  const addIgnoredTopicClass = context => {
    // get classes from core / other plugins and themes
    let classList = context._super(...arguments);

    // create your condition
    const shouldAddClass = ignored.includes(
      context.topic.posters[0].user.username
    );

    // add ignored class if condition is true
    if (shouldAddClass) {
      classList += IGNORED_TOPIC_CLASS_STRING;
    }

    // return the classList plus the modifications if any
    return classList;
  };

  // add the class to the default topic list like on the "latest" page
  api.modifyClass("component:topic-list-item", {
    pluginId: PLUGIN_ID,
    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass(this);
    }
  });

  // do the same for the categories page topic list
  api.modifyClass("component:latest-topic-list-item", {
    pluginId: PLUGIN_ID,
    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass(this);
    }
  });
});

and this goes in /common/common.css

// we don't use display: none; here because we don't want to mess with load-more
.ignored-op-topic {
  height: 0;
  width: 0;
  position: fixed;
  bottom: 0;
}
「いいね!」 2

ありがとうございます。2番目の方法でうまくいきました。

コンポーネントの子要素に余分なクラスを追加することは可能でしょうか?

例えば、トピックタイトルの右側にあるデスクトップのアバターリストで、無視したユーザーのアバターを非表示にしたいのですが、トピックリストアイテムにクラスを追加することで、アバターコンポーネントにクラスを追加することはできますか?

上記で使用した unboundClassNames() メソッドは、コンポーネント要素に追加されるクラスを担当します。言い換えると、.topic-list-item または .latest-topic-list-item の HTML 要素です。これはここで確認できます。

そしてここで

Ember は、そのメソッドが返す文字列を取得し、それを要素自体の class 属性として追加します。
その要素の子要素にクラスを追加したい場合は、別の方法を使用する必要があります。

posters プロパティの各ポスターには extras というプロパティがあります。このプロパティは、レンダリング時にアバターに追加される追加クラスのフラグとして使用されます。

これはここで設定され

ここで使用されます

したがって、条件に基づいてトピックリストのアバターにクラスを追加するには、そのプロパティを拡張する必要があります。
コンポーネントが属性を受け取るときに @on デコレータを使用して、それを実行するメソッドを呼び出すことができます。すでにこれらのコンポーネントクラスを変更しているので、上記のコードにその新しい動作を組み込むことができます。

結果は次のようになります。

イニシャライザで

import { apiInitializer } from "discourse/lib/api";
import discourseComputed, { on } from "discourse-common/utils/decorators";

export default apiInitializer("0.11.1", (api) => {
  const PLUGIN_ID = "hide-ignored-op-topics";
  const IGNORED_TOPIC_CLASS_STRING = " ignored-op-topic";
  const IGNORED_AVATAR_CLASS_STRING = " ignored-user-avatar";

  const user = api.getCurrentUser();

  if (!user) {
    return;
  }

  const ignoredUsers = user.ignored_users;

  function isIgnoredUser(poster) {
    return ignoredUsers.includes(poster.user.username);
  }

  function addIgnoredTopicClass() {
    let classList = this._super(...arguments);
    
    const topicCreator = this.topic.posters[0];

    if (isIgnoredUser(topicCreator)) {
      classList += IGNORED_TOPIC_CLASS_STRING;
    }

    return classList;
  }

  function addIgnoredAvatarClass() {
    this.topic.posters.forEach((poster) => {
      if (isIgnoredUser(poster)) {
        // default raw topic-lists
        poster.extras += IGNORED_AVATAR_CLASS_STRING;

        // categories page topic lists
        poster.user.set("extras", IGNORED_AVATAR_CLASS_STRING);
      }
    });
  }

  api.modifyClass("component:topic-list-item", {
    pluginId: PLUGIN_ID,

    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass.call(this);
    },

    @on("didReceiveAttrs")
    ignoredAvatarClass() {
      addIgnoredAvatarClass.call(this);
    },
  });

  api.modifyClass("component:latest-topic-list-item", {
    pluginId: PLUGIN_ID,

    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass.call(this);
    },

    @on("didReceiveAttrs")
    ignoredAvatarClass() {
      addIgnoredAvatarClass.call(this);
    },
  });
});

これにより、無視されたユーザーによって開始されたトピックリストアイテムに ignored-op-topic CSS クラスが追加され、ポスター列の無視されたユーザーアバターすべてに ignored-user-avatar CSS クラスが追加されます。

.ignored-op-topic の CSS はすでに上記にあります。

// load-more に影響を与えたくないため、ここでは display: none; は使用しません
.ignored-op-topic {
  height: 0;
  width: 0;
  position: fixed;
  bottom: 0;
}

次に、ポスター列の無視されたユーザーアバターを非表示にしたいとします。

そうしないでください。これは多くの混乱を引き起こします。
無視されたユーザーがトピックに返信してそれが更新された場合、アバターが非表示になっているとしたらどうなりますか?誰かがトピックを更新したように見えてしまいます。
また、カテゴリページにはトピックタイトルの横にアバターが 1 つしかありません。最後の返信が無視されたユーザーによるものだった場合はどうなりますか?アバターなし?
このようなケースがユーザーにとって不快な体験を生み出す可能性があることがわかります。

無視されたユーザーのアバターを非表示にする代わりに、SVG アイコンに置き換えることができます。すべての無視されたユーザーは同じアバターを持つことになります。これは CSS で行うことができます。

.ignored-user-avatar {
  background: white;
  border: 1px solid transparent;
  box-sizing: border-box;
  opacity: 0.5;
  content: svg-uri(
    '\u003csvg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"\u003e\u003cpath d="m256 0c141.385 0 256 114.615 256 256s-114.615 256-256 256-256-114.615-256-256 114.615-256 256-256zm0 105c-83.262 0-151 67.74-151 151s67.737 151 151 151 151-67.736 151-151-67.74-151-151-151zm-52.816 130.621a22.119 22.119 0 1 0 0-44.237 22.119 22.119 0 0 0 0 44.237zm127.749-22.121a22.116 22.116 0 0 0 -22.12-22.12 22.119 22.119 0 1 0 22.12 22.12zm-40.233 70.79a9.439 9.439 0 0 0 -13.35-13.347l-21.35 21.357-21.352-21.357a9.438 9.438 0 1 0 -13.348 13.347l21.352 21.352-21.352 21.358a9.438 9.438 0 1 0 13.347 13.347l21.353-21.355 21.351 21.351a9.439 9.439 0 0 0 13.349-13.343l-21.352-21.354z"/\u003e\u003c/svg\u003e'\n  );
}

そして、このようにレンダリングされます。

そして、latest-topic-list-item でも同様です。SVG は、使用したいアイコンに変更してください。

それを脇に置いておくと…

トピックリストのカスタマイズ方法と、そのカスタマイズ方法について説明する良い機会なので、質問に答えました。しかし、ユースケースには多くの懸念があります。無視されたユーザーのアバターを非表示にする必要性は、根本的な問題を示しています。「この人は私が興味のないトピックについて書いています。ノイズを減らすために無視します」と言うのは一つのことです。しかし、「この人のアバターを見るだけで感情的な反応を引き起こします。彼のアバターを二度と見たくありません」と言うのは全く別のことです。

コミュニティのことを誰よりも知っていると思いますが…調べる価値のあることでしょう。

「いいね!」 4

承知いたしました。MastodonやTwitterのようなブロック機能を模倣しようとしていました。ユーザーを無視すると、そのユーザーからのコンテンツは二度と表示されなくなります。ほとんどのコミュニティではこのような機能は必要ないかもしれませんが、私のユーザーが求めているので、最善を尽くしたいと思います。

「いいね!」 1