我怎样才能准确地做一个类似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 来能够将消息从屏幕底部发送到顶部。但现在,因为它是一个倒置的平面列表,所以项目从下到上渲染,它给了我这样的结果:
注意:一开始,消息也被颠倒了,但我通过从数据库发送消息也颠倒了来解决这个问题。
所以,我不知道如何才能实现我的目标,就像第一张图片那样。
谢谢!
我使用辅助函数(
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} />;
}
这就是我的反应方式,
new Set()
来唯一存储日期const dates = new Set();
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>
);
});
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>;
};
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]