解决林木木老师哔哔广场中同一Memos实例下多用户头像昵称显示错误问题

目录

问题描述

非常感谢 林木木 老师编写的 哔哔广场 ,日常用于调取Memos网站的内容,并实现了同步订阅其他Memos用户及发表 memos 等功能。
但是,我这里有一个较为小众的需求,就是在使用 Memos 广场功能时,如下图界面:

当多个用户来自同一个 Memos 实例时,其动态内容能够正常显示,但用户信息(头像和昵称)却全部显示为同一个用户的信息。

例如,在我的订阅列表中:

  • 小十 (ID: 1, 头像: avatar.png)
  • 泥鳅胡子 (ID: 3, 头像: myh.jpg)

上述两个用户都使用同一个 Memos 实例 https://memos.xiaoten.com,但在广场模式下,所有动态都显示为泥鳅胡子的头像和昵称,即使这些动态实际上是小十发布的。

原因分析

主要原因

用户信息映射逻辑未覆盖这种特殊情况,在原始的代码实现中:

  1. 获取用户动态时,代码使用了同一个 Memos 实例下的第一个用户信息来覆盖所有用户的动态信息。

  2. 最初使用 creatorName 作为映射键,但当多个用户有相同的显示名时会导致冲突。

  3. 在合并用户信息和动态数据时,属性覆盖的顺序问题,会使用户信息被动态数据中的空值覆盖。

技术细节

在 Promise 内部处理动态数据时,原始代码使用了 matchedMemo 而不是当前用户 u 的信息:

// 原因
for (let key in matchedMemo) {
  if (matchedMemo.hasOwnProperty(key)) {
    item[key] = matchedMemo[key];
  }
}

这会使同一个 Memos 实例下的所有用户都使用了第一个匹配的用户信息。

解决方式

1. 修改 Promise 内部的用户信息赋值

在获取每个用户的动态时,使用当前用户 u 的信息而不是 matchedMemo

memosData.forEach(item => {
  if (matchedV1 && item.createTime) {
    item.createdTs = Math.floor(new Date(item.createTime).getTime() / 1000);
  }
  // 使用当前用户 u 的信息,避免覆盖核心属性
  for (let key in u) {
    if (u.hasOwnProperty(key) && key !== 'createdTs' && key !== 'id' && key !== 'content') {
      item[key] = u[key];
    }
  }
});

2. 创建覆盖这种特殊需求的用户映射表

使用 link + creatorId 组合作为唯一键来创建用户映射表:

const userMap = {};
memoList.forEach(user => {
  const key = `${user.link}-${user.creatorId}`;
  userMap[key] = user;
});

这样,对于同一个 Memos 实例下的不同用户,可以通过不同的 creatorId 来区分:

  • 小十:https://memos.xiaoten.com-1
  • 泥鳅胡子:https://memos.xiaoten.com-3

3. 属性覆盖顺序

在合并用户信息和动态数据时,让用户信息优先,动态的核心属性得到保留:

memoData = memoData.map(memo => {
  if (!memo || !memo.creatorId || !memo.link) {
    return memo;
  }
  
  const memoKey = `${memo.link}-${memo.creatorId}`;
  const correctUser = userMap[memoKey];
  
  if (correctUser) {
    return {
      ...correctUser,           // 用户信息(头像、昵称等)
      id: memo.id,              // 保留动态ID
      content: memo.content,    // 保留动态内容
      createdTs: memo.createdTs, // 保留创建时间
      // ... 其他核心属性
    };
  }
  
  return memo;
});

完整代码实现

以下是修改后的 getMemos 函数:

async function getMemos(search) {
  // ... 初始化代码
  
  if(search && search != "" && search != null ){
    // ... 搜索逻辑
  }else{
    results = await Promise.allSettled(memoList.map(async(u) => {
      // ... 获取动态数据的逻辑
      
      memosData.forEach(item => {
        if (matchedV1 && item.createTime) {
          item.createdTs = Math.floor(new Date(item.createTime).getTime() / 1000);
        }
        // 使用当前用户 u 的信息
        for (let key in u) {
          if (u.hasOwnProperty(key) && key !== 'createdTs' && key !== 'id' && key !== 'content') {
            item[key] = u[key];
          }
        }
      });
      return memosData;
    }));
  }
  
  // ... 处理结果
  
  // 创建用户映射表
  const userMap = {};
  memoList.forEach(user => {
    const key = `${user.link}-${user.creatorId}`;
    userMap[key] = user;
  });

  // 确保每个动态都有正确的用户信息
  memoData = memoData.map(memo => {
    if (!memo || !memo.creatorId || !memo.link) return memo;
    
    const memoKey = `${memo.link}-${memo.creatorId}`;
    const correctUser = userMap[memoKey];
    
    if (correctUser) {
      return {
        ...correctUser,
        id: memo.id,
        content: memo.content,
        createdTs: memo.createdTs,
        creatorId: memo.creatorId,
        creatorName: memo.creatorName,
        link: memo.link,
        resourceList: memo.resourceList,
        visibility: memo.visibility
      };
    }
    
    return memo;
  });
  
  // ... 剩余代码
}
评论