如何像 Whatsapp 聊天屏幕那样按组显示日期?

问题描述 投票:0回答:3

我怎样才能准确地做一个类似Whatsapp聊天屏幕中的日期系统?

正如您所看到的,这些消息按日期分组,我的意思是它们是按日期分隔的。

这是我找到的更好解释的屏幕截图:

我在 FlatList 中执行此操作,同时逐一渲染消息。

这就是我所做的

        let previousDate = "";
    if (index > 0) {
        previousDate = moment(this.state.messages[index - 1].created_at).format(
            "L"
        );
    } else {
        previousDate = moment(this.state.messages.created_at).format("L");
    }

    let currentDate = moment(item.created_at).format("L");

因此,我为 FlatList 的

renderItem
属性创建了一个功能组件,因此
item
index
来自 FlatList 的实际数据。

我在这里想做的是,基本上抓取当前渲染项目的

created_at
并将其与前一个项目的
created_at
进行比较,为此,我使用存储在状态中的原始数据。但不幸的是,当 FlatList 渲染第一个索引号为
0
的项目时,状态中的原始数据中没有可以比较的前一个元素,这就是为什么我检查是否大于 0 并从前一个索引项目中获取日期。在 Else 情况下,这意味着在渲染第一个项目时,不要查找前一个项目,而只获取
created_at

下面我检查

currentDate
previousDate
是否不相同,渲染自定义组件,否则不渲染任何内容。

    {previousDate && !moment(currentDate).isSame(previousDate, "day") ? ( // custom component) : null}

它应该像这样工作,但主要问题是,我使用

inverted
FlatList 来能够将消息从屏幕底部发送到顶部。但现在,因为它是一个倒置的平面列表,所以项目从下到上渲染,它给了我这样的结果:

注意:一开始,消息也被颠倒了,但我通过从数据库发送消息也颠倒了来解决这个问题。

所以,我不知道如何才能实现我的目标,就像第一张图片那样。

谢谢!

react-native chat whatsapp
3个回答
12
投票

我使用辅助函数(

generateItems
)来解决您所描述的问题。下面是我用来按天对消息进行分组的代码,然后在
<Message />
属性中渲染
<Day />
renderItem
。正如您所描述的,这是使用
inverted
FlatList

import moment from 'moment';

function groupedDays(messages) {
  return messages.reduce((acc, el, i) => {
    const messageDay = moment(el.created_at).format('YYYY-MM-DD');
    if (acc[messageDay]) {
      return { ...acc, [messageDay]: acc[messageDay].concat([el]) };
    }
    return { ...acc, [messageDay]: [el] };
  }, {});
}

function generateItems(messages) {
  const days = groupedDays(messages);
  const sortedDays = Object.keys(days).sort(
    (x, y) => moment(y, 'YYYY-MM-DD').unix() - moment(x, 'YYYY-MM-DD').unix()
  );
  const items = sortedDays.reduce((acc, date) => {
    const sortedMessages = days[date].sort(
      (x, y) => new Date(y.created_at) - new Date(x.created_at)
    );
    return acc.concat([...sortedMessages, { type: 'day', date, id: date }]);
  }, []);
  return items;
}

export default generateItems;

这里是我的列表以及

renderItem
函数供参考:

<MessageList
  data={generatedItems}
  extraData={generatedItems}
  inverted
  keyExtractor={item => item.id.toString()}
  renderItem={renderItem}
/>

function renderItem({ item }) {
  if (item.type && item.type === 'day') {
    return <Day {...item} />;
  }
  return <Message {...item} />;
}

1
投票

这就是我的反应方式,

  1. 创建一个
    new Set()
    来唯一存储日期
const dates = new Set();
  1. 循环聊天数组时,在渲染日期之前检查日期是否已存在于唯一的
    Set
chats.map((chat) => {
  
  // For easier uniqueness check,
  // Formated date string example '16082021'
  const dateNum = format(chat.timestamp, 'ddMMyyyy');

  return (
    <React.Fragment key={chat.chat_key}>

      // Do not render date if it already exists in set
      {dates.has(dateNum) ? null : renderDate(chat, dateNum)}

      <ChatroomChatBubble chat={chat} />
    </React.Fragment>
  );
});
  1. 最后,渲染日期后,将日期编号添加到数组中,这样就不会再次渲染
const renderDate = (chat, dateNum) => {
  const timestampDate = format(chat.timestamp, 'EEEE, dd/MM/yyyy');

  // Add to Set so it does not render again
  dates.add(dateNum);

  return <Text>{timestampDate}</Text>;
};

0
投票
Here is the complete solution in react. Just copy it and change the array according to your requirments and it will work. For styling i am using tailwindCss. You can replace styling with your requirements.

组件消息列表

    import moment from 'moment';
    import React from 'react';
    import Message from './message';
    
        const MessageList = React.memo(({ messages }) => {
        
            const msgDates = new Set();
        
            const formatMsgDate = (created_date) => {
        
                const today = moment().startOf('day');
                const msgDate = moment(created_date);
                let dateDay = '';
        
                if(msgDate.isSame(today, 'day')){
                    dateDay = 'Today';
                }
                else if (msgDate.isSame(today.clone().subtract(1, 'days'), 'day')){
                    dateDay = 'Yesterday';
                }
                else{
                    dateDay = msgDate.format('MMMM D, YYYY');
                }
        
                return dateDay
            }
        
            const renderMsgDate = (message, nextMessage) => {
                const dateTimeStamp = moment(message.created, 'YYYY-MM-DD').valueOf();
                let nextMsgdateTimeStamp = '';
                if(nextMessage){
                    nextMsgdateTimeStamp = moment(nextMessage.created, 'YYYY-MM-DD').valueOf();
                }
        
                if(msgDates.has(dateTimeStamp) || (nextMsgdateTimeStamp && nextMsgdateTimeStamp == dateTimeStamp)){
                    return null
                }
                else{
                   
                    msgDates.add(dateTimeStamp);
        
                    return (
                        <div className='py-2 flex justify-center items-center'>
                            <span id="msg_day" className='px-2 py-1 shadow font-medium rounded-lg text-gray-400 bg-white border-gray-300'>
                                {formatMsgDate(message.created)}
                            </span>
                        </div>
                    )
                }
            }
        
            return (
                <>
                    {messages?.map((message, index) => (
                        <>
                            <Message key={index} msg={message} />
                            {renderMsgDate(message, messages[index+1])}
                        </>
                    ))}
                </>
            );
        });
        
        export default MessageList;

消息组件

在此输入代码

import React, { useMemo } from 'react';
import ReactTimeAgo from 'react-time-ago'
import { useGroupChatContext } from '../../context/groupchatContext';

const Message = React.memo(({ msg }) => {

  const groupChatContext = useGroupChatContext();

  const {authData} = useMemo(() => ({
    authData: groupChatContext.authData,
  }));


  return (
    <div className={`flex gap-3 pb-4 ${msg.nurse_id == authData?.id ? 'flex-row-reverse text-start' : 'w-full'}`}>


        <div className={`${msg.nurse_id == authData.id ? 'text-right' : 'text-left'}`}>
          <h2 className=" text-sm font-semibold ml-2"> {msg.nurse_id == authData.id ? null : msg.sender} <span className="text-xs text-gray-500 ml-2">{msg.status == 'sending' ? 'Sending...' : <ReactTimeAgo timeStyle="twitter-first-minute" date={new Date(msg.created)} />}</span></h2>
          <div className="mt-1 flex flex-col gap-2">
            <div className={`rounded-md ${msg.nurse_id == authData.id ? 'bg-bg-primary': 'bg-white' }  p-2 shadow`}>
              <p className={`${msg.nurse_id == authData.id ? 'text-white': 'text-gray-800' } text-start`} dangerouslySetInnerHTML={{ __html: msg.message?.text }}></p>
            </div>
          </div>
        </div>
      </div>
  );
});

export default Message;

The final Result

[enter image description here][1]
[enter image description here][2]
[enter image description here][3]
© www.soinside.com 2019 - 2024. All rights reserved.