如何为每个用户分组聊天消息?

时间:2018-03-04 11:43:11

标签: javascript json vue.js chat

我使用Vue.js构建了一个群聊消息。我目前正在获取返回如下数组的消息:

"data":[
    {
        "id":1,
        "message":"<p>yo<\/p>",
        "removed":"false",
        "user":{
            "uid":2,
            "metadata":{
                "username":"Testing"
            }
        },
        "post_date":"2018-02-24 14:30"
    },
    {
        "id":2,
        "message":"<p>test<\/p>",
        "removed":"false",
        "user":{
            "uid":1,
            "metadata":{
                "username":"Admin"
            }
        },
        "post_date":"2018-02-24 22:31"
    },
    {
        "id":3,
        "message":"<p>Wassup?<\/p>",
        "removed":"false",
        "user":{
            "uid":1,
            "metadata":{
                "username":"Admin"
            }
        },
        "post_date":"2018-02-24 22:40"
    },
    {
        "id":12,
        "message":"again for testing post date",
        "removed":"false",
        "user":{
            "uid":1,
            "metadata":{
                "username":"Admin"
            }
        },
        "post_date":"2018-03-04 00:59"
    },
    {
        "id":13,
        "message":"Hello!",
        "removed":"false",
        "user":{
            "uid":2,
            "metadata":{
                "username":"Testing"
            }
        },
        "post_date":"2018-03-04 11:13"
    },
    {
        "id":13,
        "message":"<p>Hi!</p>",
        "removed":"false",
        "user":{
            "uid":2,
            "metadata":{
                "username":"Testing"
            }
        },
        "post_date":"2018-03-04 11:13"
    },
],

目前,我只是循环访问数据并将每封邮件输出到单独的div。我希望在同一个用户连续多次发布消息时对消息进行分组。

我该怎么做?它应该是一个选项服务器端(可能是一个可选的group参数吗?)或以某种方式完成客户端?

修改 这是聊天当前的样子: How the chat currently looks

这是理想的外观: Desired chat look

如果我按UID /用户名对它们进行分组,问题是需要按顺序输出消息。因此,如果用户1发送三个消息,则用户2发送两个消息,然后用户1发送另一个消息,所有用户1消息将被分组在一起。相反,用户1的三个消息应该被分组,然后是用户2,然后它应该显示用户1的最后消息。

4 个答案:

答案 0 :(得分:3)

我最近才为自己解决了这个问题。 这是full example

在上面的示例中,用于分组消息的核心业务逻辑可以在src/store.js函数的addMessage下找到。

除非您的服务器还存储所有消息,并且您有某种机制可以确保所有客户端之间的因果排序,否则我建议您在每个客户端上运行逻辑。这样,您可以确保客户在任何情况下都不会看到邮件跳来跳去。最糟糕的是,邮件将显示为未分组。

确定此消息的算法在收到新消息时运行,因此它在每个客户端上运行,但是您也可以对其进行调整以使其适合您的用例!如下所示(我可能使用了错误的流程图元素..歉意)。

addMessage({ state }, { msg, selfHash }) {
  let addAsNewMsg = true;
  const lastMessage = state.messages.slice(-1)[0];
  if (lastMessage && lastMessage.userHash === msg.userHash) {
    // The last message was also sent by the same user
    // Check if it arrived within the grouping threshold duration (60s)
    const lastMessageTime = moment(lastMessage.time);
    const msgTime = moment(msg.time);
    const diffSeconds = msgTime.diff(lastMessageTime, "seconds");
    if (diffSeconds <= 60) {
      addAsNewMsg = false; // We're going to be appending.. so
      lastMessage.message.push(msg.message);
      // We're going to now update the timestamp and (any) other fields.
      delete msg.message; // Since, we've already added this above
      Object.assign(lastMessage, msg); // Update with any remaining properties
    }
  }
  if (addAsNewMsg) {
    state.messages.push(msg);
  }
}

答案 1 :(得分:1)

我的方法是保留对div的引用,该div包含最后一个用户的语音,以及一个表示最后一个要讲话的用户的uid的变量。当出现与最后一个uid匹配的消息时,您只需将其添加到要跟踪的div中;如果新的uid 不匹配,则只需创建一个新的div,将其添加到DOM中,然后更新uid变量即可。

// initialize variables
let lastUid,curDiv;

getData().forEach(msg => {
  if (msg.user.uid !== lastUid) {
    // new user is speaking!
    curDiv = document.createElement("div");
    document.body.appendChild(curDiv);
    lastUid = msg.user.uid;
  }
  // add current message to the current div
  curDiv.innerHTML += msg.message;
});

function getData() {
  // really only put this in a function so that I could hoist it so that my logic can go above.
  return [
      {
          "id":1,
          "message":"<p>yo<\/p>",
          "removed":"false",
          "user":{
              "uid":2,
              "metadata":{
                  "username":"Testing"
              }
          },
          "post_date":"2018-02-24 14:30"
      },
      {
          "id":2,
          "message":"<p>test<\/p>",
          "removed":"false",
          "user":{
              "uid":1,
              "metadata":{
                  "username":"Admin"
              }
          },
          "post_date":"2018-02-24 22:31"
      },
      {
          "id":3,
          "message":"<p>Wassup?<\/p>",
          "removed":"false",
          "user":{
              "uid":1,
              "metadata":{
                  "username":"Admin"
              }
          },
          "post_date":"2018-02-24 22:40"
      },
      {
          "id":12,
          "message":"again for testing post date",
          "removed":"false",
          "user":{
              "uid":1,
              "metadata":{
                  "username":"Admin"
              }
          },
          "post_date":"2018-03-04 00:59"
      },
      {
          "id":13,
          "message":"Hello!",
          "removed":"false",
          "user":{
              "uid":2,
              "metadata":{
                  "username":"Testing"
              }
          },
          "post_date":"2018-03-04 11:13"
      },
      {
          "id":13,
          "message":"<p>Hi!</p>",
          "removed":"false",
          "user":{
              "uid":2,
              "metadata":{
                  "username":"Testing"
              }
          },
          "post_date":"2018-03-04 11:13"
      },
  ]
}
div {
  border: 1px solid black;
  margin-bottom: 5px;
}

答案 2 :(得分:1)

似乎是计算属性的理想用例。首先按post_date

对数据进行排序
computed: {
    sortedPosts() {
        return this.posts.sort((a, b) => {
            if (a.post_date < b.post_date) return -1;
            else if (a.post_date > b.post_date) return +1;
            return 0;
        });
    },

然后按user.uid分组

    groupedPosts() {
        const posts = this.sortedPosts.reduce((post, grouped) => {
            if (grouped.length === 0) 
                grouped.unshift([post]);
            else if (grouped[0][0].user.uid === post.user.uid)
                grouped[0].push(post);
            else
                grouped.unshift([post]);
            return grouped;
        }, []);
        return posts.reverse();
    }

然后在模板中使用了计算所得的属性

<div v-for="group in groupedPosts">
    <div v-for="post in group">

(我尚未测试上面的代码,因此请注意输入错误等)

答案 3 :(得分:0)

您可以在服务器端按用户对邮件进行分组。

迭代数据,将每个对象推入数组对象,其中键是uid,值是相应用户的对象数组。

let dataByUser = {};

//Iterate over your data
for (let i = 0; i < data.length; i++) {
    //If the object doesn't have a key equal to the current objects username, 
    //add it as a new key and set the values to a new array containing this datum.
    if(!dataByUser.hasOwnProperty(data[i].user.uid) {
        dataByUser[data[i].user.uid] = new Array(data[i]);
    } else {
        //If it does exist, push to the array for the existing key.
        dataByUser[data[i].user.uid].push(data[i]);
    }
}

应该以数据数组中每个用户的K:V对为对象,其中的值是包含所有相应消息的对象数组。

然后,您必须编写一些客户端代码,迭代这个新的数据结构并根据需要对其进行格式化。