需要帮助找出为什么点击新主题/回复警报后主题列表未更新

在主题创建者网站上,我试图从主题列表中删除被忽略用户的主题,这是我的代码

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 个赞

当你这样做时

this.topics = something_else

你正在将该属性重新定义为其他东西。如果没有任何东西在监视该属性,这没关系,但在这种情况下,有几个东西在监视 topics 数组。

例如

如果你在一个开发安装上这样做,Ember 会在控制台中抛出类似以下的错误。

所以,这里的关键要点是这样做。

this.set("topics", something_else)

直到 @tracked 装饰器在 Discourse 中可用。

所以,如果你尝试这样做

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);
  }
});

它应该可以在主题创建者上的桌面和移动设备上工作。

然而…

如果你在开发安装上尝试这样做,你会看到另一个错误。

所以这应该告诉你一些事情。topics 数组在许多不同的地方被使用。所以随意修改它不是一个好主意——尤其是因为你的用例主要是视觉上的,并且没有安全方面的考虑。

我的建议是:退一步,尝试另一种方法。不要试图修改数组,而是做一些更简单的事情。如果一个被忽略的用户创建了主题,添加一个 CSS 类并用 CSS 隐藏它。

你可以这样做——我留下了一些注释,如果你想跟着做,但你可以在准备使用时删除它们。

这放在初始化器文件中:

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

export default apiInitializer("0.11.1", api => {
  // 为你的修改设置一个 ID
  const PLUGIN_ID = "hide-ignored-op-topics";

  // 你想添加的类名。开头的空格是必需的
  const IGNORED_TOPIC_CLASS_STRING = " ignored-op-topic";

  // 获取当前用户
  const user = api.getCurrentUser();

  // 未登录,退出
  if (!user) {
    return;
  }

  // 获取被忽略用户的列表
  const ignored = user.ignored_users;

  // 辅助函数,避免代码重复
  const addIgnoredTopicClass = context => {
    // 从核心/其他插件和主题获取类
    let classList = context._super(...arguments);

    // 创建你的条件
    const shouldAddClass = ignored.includes(
      context.topic.posters[0].user.username
    );

    // 如果条件为真,则添加被忽略的类
    if (shouldAddClass) {
      classList += IGNORED_TOPIC_CLASS_STRING;
    }

    // 返回类列表以及任何修改
    return classList;
  };

  // 将类添加到默认主题列表,例如在“最新”页面上
  api.modifyClass("component:topic-list-item", {
    pluginId: PLUGIN_ID,
    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass(this);
    }
  });

  // 对类别页面主题列表做同样的事情
  api.modifyClass("component:latest-topic-list-item", {
    pluginId: PLUGIN_ID,
    @discourseComputed()
    unboundClassNames() {
      return addIgnoredTopicClass(this);
    }
  });
});

这放在 /common/common.css

// 我们不在这里使用 display: none; 因为我们不想干扰 load-more
.ignored-op-topic {
  height: 0;
  width: 0;
  position: fixed;
  bottom: 0;
}
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。

// 我们在这里不使用 display: none; 因为我们不想影响 load-more
.ignored-op-topic {
  height: 0;
  width: 0;
  position: fixed;
  bottom: 0;
}

现在,你想隐藏海报列中被忽略用户的头像。

不要这样做。这会造成很多混淆。

如果被忽略的用户回复了一个主题,并且该主题被顶上来了,但你隐藏了他们的头像怎么办?这会让人看起来像是别人顶了该主题。

另外,分类页面主题标题旁只有一个头像。如果最后回复是被忽略的用户,会发生什么?没有头像?

你可以看到这些情况会给你的用户带来不愉快的体验。

与其隐藏被忽略用户的头像,不如用 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'
  );
}

它将渲染如下:

并且在 latest-topic-list-item 上也是如此。将 SVG 更改为您想使用的任何图标。

言归正传……

我回答了你的问题,因为这是一个谈论如何自定义主题列表的好机会。然而,我对你的用例有很多保留。隐藏被忽略用户头像的需求表明存在潜在问题。说

“这个人写的东西我不敢兴趣。我会忽略他们以减少噪音。”

这是一种情况,但说

“即使看到这个人的头像也会引起我的情绪反应。我再也不想看到他们的头像了。”

这是完全不同的情况。

你比任何人都了解你的社区……但这可能值得深入研究。

4 个赞

明白了。我试图尽可能地模仿 Mastodon 或 Twitter 的屏蔽功能,这样当你屏蔽一个用户时,你就再也看不到来自该用户的内容了。我同意大多数社区可能永远不需要这种功能。由于我的用户要求,我想尽力而为。

1 个赞