refactor: message feed pagination

This commit is contained in:
zerosoul
2022-06-05 13:02:35 +08:00
parent 13a4c7c302
commit c73d531fa5
9 changed files with 415 additions and 121 deletions
+3 -1
View File
@@ -59,10 +59,12 @@ export const onMessageSendStarted = async (
console.log("message server mid", server_mid); console.log("message server mid", server_mid);
batch(() => { batch(() => {
dispatch(removeContextMessage({ id, mid: ts })); dispatch(removeContextMessage({ id, mid: ts }));
dispatch(removeMessage(ts));
dispatch(addMessage({ mid: server_mid, ...tmpMsg })); dispatch(addMessage({ mid: server_mid, ...tmpMsg }));
dispatch(addContextMessage({ id, mid: server_mid })); dispatch(addContextMessage({ id, mid: server_mid }));
}); });
setTimeout(() => {
dispatch(removeMessage(ts));
}, 300);
// dispatch(removePendingMessage({ id, mid:ts, type: from })); // dispatch(removePendingMessage({ id, mid:ts, type: from }));
} catch { } catch {
console.log("message send failed"); console.log("message send failed");
+2 -1
View File
@@ -27,6 +27,7 @@ export default function MessageContextMenu({
// isMarkdown, // isMarkdown,
canEdit, canEdit,
canPin, canPin,
canDelete,
canCopy, canCopy,
canReply, canReply,
pinned, pinned,
@@ -97,7 +98,7 @@ export default function MessageContextMenu({
icon: <IconSelect className="icon" />, icon: <IconSelect className="icon" />,
handler: handleSelect, handler: handleSelect,
}, },
{ canDelete && {
title: "Delete", title: "Delete",
danger: true, danger: true,
icon: <IconDelete className="icon" />, icon: <IconDelete className="icon" />,
+1 -1
View File
@@ -103,7 +103,7 @@ function Message({
popperOptions={{ strategy: "fixed" }} popperOptions={{ strategy: "fixed" }}
disabled={readOnly} disabled={readOnly}
interactive interactive
placement="left" placement="right"
trigger="click" trigger="click"
content={<Profile uid={fromUid} type="card" cid={contextId} />} content={<Profile uid={fromUid} type="card" cid={contextId} />}
> >
+90 -87
View File
@@ -1,8 +1,10 @@
import { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { useLazyGetHistoryMessagesQuery } from "../../app/services/channel"; import { useLazyGetHistoryMessagesQuery } from "../../app/services/channel";
const getFeedWithPagination = (config) => { const getFeedWithPagination = (config) => {
const { pageNumber = 1, pageSize = 20, mids = [] } = config || {}; const { pageNumber = 1, pageSize = 30, mids = [], isLast = false } =
config || {};
const shadowMids = mids.slice(0); const shadowMids = mids.slice(0);
if (shadowMids.length == 0) if (shadowMids.length == 0)
@@ -15,119 +17,112 @@ const getFeedWithPagination = (config) => {
ids: [], ids: [],
}; };
shadowMids.sort((a, b) => { shadowMids.sort((a, b) => {
return Number(b) - Number(a); return Number(a) - Number(b);
}); });
console.log("message pagination", shadowMids); console.log("message pagination", shadowMids);
const pageCount = Math.ceil(shadowMids.length / pageSize); const pageCount = Math.ceil(shadowMids.length / pageSize);
const computedPageNumber = isLast ? pageCount : pageNumber;
const ids = shadowMids.slice( const ids = shadowMids.slice(
(pageNumber - 1) * pageSize, (computedPageNumber - 1) * pageSize,
pageNumber * pageSize computedPageNumber * pageSize
); );
const info = { return {
isFirst: pageNumber == 1, isFirst: computedPageNumber == 1,
isLast: pageNumber == pageCount, isLast: computedPageNumber == pageCount,
pageCount, pageCount,
pageSize, pageSize,
pageNumber, pageNumber: computedPageNumber,
ids, ids,
}; };
console.log("get page Info", info);
return info;
}; };
let curScrollPos = 0;
let oldScroll = 0;
export default function useMessageFeed({ context = "channel", id = null }) { export default function useMessageFeed({ context = "channel", id = null }) {
const [loadMoreFromServer] = useLazyGetHistoryMessagesQuery(); const [loadMoreFromServer] = useLazyGetHistoryMessagesQuery();
const listRef = useRef([]); const listRef = useRef([]);
const pageRef = useRef(null); const pageRef = useRef(null);
const containerRef = useRef(null);
const [hasMore, setHasMore] = useState(true); const [hasMore, setHasMore] = useState(true);
const [prepends, setPrepends] = useState([]); const [appends, setAppends] = useState([]);
// const [appends, setAppends] = useState([]);
const [items, setItems] = useState([]); const [items, setItems] = useState([]);
const { mids } = useSelector((store) => { const { mids, messageData, loginUid } = useSelector((store) => {
return { return {
loginUid: store.authData.uid,
mids: mids:
context == "channel" context == "channel"
? store.channelMessage[id] || [] ? store.channelMessage[id] || []
: store.userMessage.byId[id] || [], : store.userMessage.byId[id] || [],
// messageData: store.message, messageData: store.message,
}; };
}); });
useEffect(() => { useEffect(() => {
listRef.current = []; listRef.current = [];
pageRef.current = []; pageRef.current = [];
setItems([]); setItems([]);
setPrepends([]);
setHasMore(true); setHasMore(true);
// setAppends([]); setAppends([]);
}, [context, id]); }, [context, id]);
// useEffect(() => {
// if (prepends.length) {
// const feedsWrapperEle = document.querySelector(
// `#RUSTCHAT_FEED_${context}_${id}`
// );
// const [newestId]=prepends;
// if (feedsWrapperEle) {
// feedsWrapperEle.scrollTop = feedsWrapperEle.scrollHeight;
// }
// }
// }, [prepends, context, id,messageData]);
useEffect(() => { useEffect(() => {
const container = document.querySelector(`#RUSTCHAT_FEED_${context}_${id}`); if (items.length) {
const handler = (e) => { containerRef.current = document.querySelector(
e.preventDefault(); `#RUSTCHAT_FEED_${context}_${id}`
var n = 0; );
if ("deltaY" in e) n = 1 === e.deltaMode ? 20 * -e.deltaY : -e.deltaY; if (containerRef.current) {
else if ("wheelDeltaY" in e) n = (e.wheelDeltaY / 120) * 20; const newScroll =
else if ("wheelDelta" in e) n = (e.wheelDelta / 120) * 20; containerRef.current.scrollHeight - containerRef.current.clientHeight;
else { containerRef.current.scrollTop = curScrollPos + (newScroll - oldScroll);
n = (-e.detail / 3) * 20;
} }
container.scrollTop += n;
};
if (container) {
container.addEventListener("wheel", handler);
} }
return () => { }, [items, context, id]);
if (container) {
container.removeEventListener("wheel", handler);
}
};
}, [context, id]);
useEffect(() => { useEffect(() => {
const fetchMore = () => { if (listRef.current.length == 0 && mids.length) {
if (listRef.current.length == 0 && mids.length) { // 初次
// 初次 const pageInfo = getFeedWithPagination({ mids, isLast: true });
const pageInfo = getFeedWithPagination({ mids }); console.log("pull up 2", pageInfo);
console.log("pull down 2", pageInfo); pageRef.current = pageInfo;
pageRef.current = pageInfo; listRef.current = pageInfo.ids;
listRef.current = pageInfo.ids; setItems(listRef.current);
setItems(listRef.current); console.log("message pageInfo", mids, pageInfo);
console.log("message pageInfo", mids, pageInfo); } else {
} else { // 追加
// 追加 const lastMid = listRef.current.slice(-1);
const lastMid = listRef.current[0]; const sorteds = mids.slice(0).sort((a, b) => {
const sorteds = mids.slice(0).sort((a, b) => { return Number(a) - Number(b);
return Number(b) - Number(a); });
}); const appends = sorteds.filter((s) => s > lastMid);
const prepends = sorteds.filter((s) => s > lastMid); if (appends.length) {
if (prepends.length) { const [newestMsgId] = appends.slice(-1);
setPrepends(prepends); // 自己发的消息
const container = containerRef.current;
if (container) {
const msgFromSelf = loginUid == messageData[newestMsgId]?.from_uid;
const scrollDistance =
container.scrollHeight -
(container.offsetHeight + container.scrollTop);
console.log("scrollDistance", msgFromSelf, scrollDistance);
if (msgFromSelf) {
container.scrollTop = container.scrollHeight;
} else if (scrollDistance <= 100) {
setTimeout(() => {
container.scrollTop = container.scrollHeight;
}, 100);
}
} }
console.log("prepends", prepends, items); setAppends(appends);
} }
}; console.log("appends", appends, listRef.current);
fetchMore(); }
}, [mids]); }, [mids, messageData, loginUid]);
const pullUp = async () => {
const pullDown = async () => {
// 向下加载
const currPageInfo = pageRef.current; const currPageInfo = pageRef.current;
console.log("pull down", currPageInfo); console.log("pull up", currPageInfo);
// 最后一页 // 一页
if (currPageInfo && currPageInfo.isLast) { if (currPageInfo && currPageInfo.isFirst) {
const [mid] = currPageInfo.ids.slice(-1); const [firstMid] = currPageInfo.ids;
const { data: newList } = await loadMoreFromServer({ mid, gid: id }); const { data: newList } = await loadMoreFromServer({
mid: firstMid,
gid: id,
});
if (newList.length == 0) { if (newList.length == 0) {
setHasMore(false); setHasMore(false);
return; return;
@@ -138,29 +133,37 @@ export default function useMessageFeed({ context = "channel", id = null }) {
// 初始化 // 初始化
pageInfo = getFeedWithPagination({ pageInfo = getFeedWithPagination({
mids, mids,
isLast: true,
}); });
} else { } else {
const nextPageNumber = currPageInfo.pageNumber + 1; const prevPageNumber = currPageInfo.pageNumber - 1;
pageInfo = getFeedWithPagination({ pageInfo = getFeedWithPagination({
mids, mids,
pageNumber: nextPageNumber, pageNumber: prevPageNumber,
}); });
} }
pageRef.current = pageInfo; pageRef.current = pageInfo;
listRef.current = [...listRef.current, ...pageInfo.ids]; listRef.current = [...pageInfo.ids, ...listRef.current];
setTimeout(() => { setTimeout(() => {
setHasMore(!pageInfo.isLast); const container = containerRef.current;
if (container) {
curScrollPos = container.scrollTop;
oldScroll = container.scrollHeight - container.clientHeight;
}
setItems(listRef.current); setItems(listRef.current);
console.log("pull down update", currPageInfo, listRef.current); console.log("pull up", currPageInfo, listRef.current);
}, 1000); setHasMore(pageInfo.pageNumber !== 1);
}, 800);
};
const pullDown = () => {
// 向下加载
}; };
return { return {
mids, mids,
prepends, appends,
// appends,
hasMore, hasMore,
// pullUp, pullUp,
pullDown, pullDown,
list: items, list: items,
}; };
+4 -4
View File
@@ -11,7 +11,7 @@ const Styled = styled.div`
padding: 30px 0; padding: 30px 0;
/* background-color: #eee; */ /* background-color: #eee; */
`; `;
export default function LoadMore({ pullDown = null }) { export default function LoadMore({ pullUp = null }) {
const ref = useRef(undefined); const ref = useRef(undefined);
useEffect(() => { useEffect(() => {
const observer = new IntersectionObserver( const observer = new IntersectionObserver(
@@ -19,10 +19,10 @@ export default function LoadMore({ pullDown = null }) {
entries.forEach((entry) => { entries.forEach((entry) => {
const intersecting = entry.isIntersecting; const intersecting = entry.isIntersecting;
// const currEle = entry.target; // const currEle = entry.target;
if (intersecting && pullDown) { if (intersecting && pullUp) {
// load more // load more
console.log("inview"); console.log("inview");
pullDown(); pullUp();
} }
}); });
}, },
@@ -37,7 +37,7 @@ export default function LoadMore({ pullDown = null }) {
observer.unobserve(currEle); observer.unobserve(currEle);
} }
}; };
}, [ref, pullDown]); }, [ref, pullUp]);
return ( return (
<Styled ref={ref}> <Styled ref={ref}>
<Waveform <Waveform
+23 -24
View File
@@ -3,12 +3,10 @@ import { useDebounce } from "rooks";
import { NavLink, useLocation } from "react-router-dom"; import { NavLink, useLocation } from "react-router-dom";
import Tippy from "@tippyjs/react"; import Tippy from "@tippyjs/react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
// import InfiniteScroll from "react-infinite-scroll-component";
import PinList from "./PinList"; import PinList from "./PinList";
import FavList from "../FavList"; import FavList from "../FavList";
import { useReadMessageMutation } from "../../../app/services/message"; import { useReadMessageMutation } from "../../../app/services/message";
import { updateRemeberedNavs } from "../../../app/slices/ui"; import { updateRemeberedNavs } from "../../../app/slices/ui";
// import useChatScroll from "../../../common/hook/useChatScroll";
import useMessageFeed from "../../../common/hook/useMessageFeed"; import useMessageFeed from "../../../common/hook/useMessageFeed";
import ChannelIcon from "../../../common/component/ChannelIcon"; import ChannelIcon from "../../../common/component/ChannelIcon";
@@ -36,7 +34,7 @@ import InviteModal from "../../../common/component/InviteModal";
import LoadMore from "./LoadMore"; import LoadMore from "./LoadMore";
export default function ChannelChat({ cid = "", dropFiles = [] }) { export default function ChannelChat({ cid = "", dropFiles = [] }) {
const { list: msgIds, prepends, hasMore, pullDown } = useMessageFeed({ const { list: msgIds, appends, hasMore, pullUp } = useMessageFeed({
context: "channel", context: "channel",
id: cid, id: cid,
}); });
@@ -94,7 +92,7 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
console.log("channel message list", msgIds); console.log("channel message list", msgIds);
const readIndex = footprint.readChannels[cid]; const readIndex = footprint.readChannels[cid];
const pinCount = data?.pinned_messages?.length || 0; const pinCount = data?.pinned_messages?.length || 0;
const feeds = [...prepends, ...msgIds]; const feeds = [...msgIds, ...appends];
return ( return (
<> <>
{addMemberModalVisible && ( {addMemberModalVisible && (
@@ -233,27 +231,9 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
} }
> >
<StyledChannelChat id={`RUSTCHAT_FEED_channel_${cid}`}> <StyledChannelChat id={`RUSTCHAT_FEED_channel_${cid}`}>
{/* <div className="feed"> */} {/* <div className="anchor"></div> */}
{feeds.map((mid, idx) => {
const curr = messageData[mid];
if (!curr) return null;
const isFirst = idx == 0;
const prev =
idx == feeds.length - 1 ? null : messageData[feeds[idx + 1]];
const read = curr?.from_uid == loginUid || mid <= readIndex;
return renderMessageFragment({
selectMode: !!selects,
updateReadIndex: updateReadDebounced,
read,
isFirst,
prev,
curr,
contextId: cid,
context: "channel",
});
})}
{hasMore ? ( {hasMore ? (
<LoadMore pullDown={pullDown} /> <LoadMore pullUp={pullUp} />
) : ( ) : (
<div className="info"> <div className="info">
<h2 className="title">Welcome to #{name} !</h2> <h2 className="title">Welcome to #{name} !</h2>
@@ -267,6 +247,25 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
</NavLink> </NavLink>
</div> </div>
)} )}
{/* <div className="feed"> */}
{feeds.map((mid, idx) => {
const curr = messageData[mid];
if (!curr) return null;
const isFirst = idx == 0;
const prev = isFirst ? null : messageData[feeds[idx - 1]];
const read = curr?.from_uid == loginUid || mid <= readIndex;
return renderMessageFragment({
selectMode: !!selects,
updateReadIndex: updateReadDebounced,
read,
isFirst,
prev,
curr,
contextId: cid,
context: "channel",
});
})}
{/* </div> */} {/* </div> */}
</StyledChannelChat> </StyledChannelChat>
{/* {unreads != 0 && ( {/* {unreads != 0 && (
@@ -0,0 +1,288 @@
import { useState, useEffect } from "react";
import { useDebounce } from "rooks";
import { NavLink, useLocation } from "react-router-dom";
import Tippy from "@tippyjs/react";
import { useDispatch, useSelector } from "react-redux";
// import InfiniteScroll from "react-infinite-scroll-component";
import PinList from "./PinList";
import FavList from "../FavList";
import { useReadMessageMutation } from "../../../app/services/message";
import { updateRemeberedNavs } from "../../../app/slices/ui";
// import useChatScroll from "../../../common/hook/useChatScroll";
import useMessageFeed from "../../../common/hook/useMessageFeed";
import ChannelIcon from "../../../common/component/ChannelIcon";
import Tooltip from "../../../common/component/Tooltip";
import Contact from "../../../common/component/Contact";
import Layout from "../Layout";
import { renderMessageFragment } from "../utils";
import EditIcon from "../../../assets/icons/edit.svg";
// import alertIcon from "../../../assets/icons/alert.svg?url";
import IconFav from "../../../assets/icons/bookmark.svg";
import IconPeople from "../../../assets/icons/people.svg";
import IconPin from "../../../assets/icons/pin.svg";
// import searchIcon from "../../../assets/icons/search.svg?url";
import IconHeadphone from "../../../assets/icons/headphone.svg";
import boardosIcon from "../../../assets/icons/app.boardos.svg?url";
import webrowseIcon from "../../../assets/icons/app.webrowse.svg?url";
import addIcon from "../../../assets/icons/add.svg?url";
import {
// StyledNotification,
StyledContacts,
StyledChannelChat,
StyledHeader,
} from "./styled";
import InviteModal from "../../../common/component/InviteModal";
import LoadMore from "./LoadMore";
export default function ChannelChat({ cid = "", dropFiles = [] }) {
const { list: msgIds, prepends, hasMore, pullDown } = useMessageFeed({
context: "channel",
id: cid,
});
const [toolVisible, setToolVisible] = useState("");
const { pathname } = useLocation();
const dispatch = useDispatch();
const [updateReadIndex] = useReadMessageMutation();
const updateReadDebounced = useDebounce(updateReadIndex, 300);
const [membersVisible, setMembersVisible] = useState(true);
const [addMemberModalVisible, setAddMemberModalVisible] = useState(false);
const {
selects,
// msgIds,
userIds,
data,
messageData,
loginUid,
loginUser,
footprint,
} = useSelector((store) => {
return {
selects: store.ui.selectMessages[`channel_${cid}`],
footprint: store.footprint,
loginUser: store.contacts.byId[store.authData.uid],
loginUid: store.authData.uid,
// msgIds: store.channelMessage[cid] || [],
userIds: store.contacts.ids,
data: store.channels.byId[cid] || {},
messageData: store.message || {},
};
});
// const ref = useChatScroll(msgIds);
// const handleClearUnreads = () => {
// dispatch(readMessage(msgIds));
// };
useEffect(() => {
dispatch(updateRemeberedNavs());
return () => {
dispatch(updateRemeberedNavs({ path: pathname }));
};
}, [pathname]);
const toggleMembersVisible = () => {
setMembersVisible((prev) => !prev);
};
const toggleAddVisible = () => {
setAddMemberModalVisible((prev) => !prev);
};
if (!data) return null;
const { name, description, is_public, members = [], owner } = data;
const memberIds = is_public
? userIds
: members.slice(0).sort((n) => (n == owner ? -1 : 0));
const addVisible = loginUser?.is_admin || owner == loginUid;
console.log("channel message list", msgIds);
const readIndex = footprint.readChannels[cid];
const pinCount = data?.pinned_messages?.length || 0;
const feeds = [...prepends, ...msgIds];
return (
<>
{addMemberModalVisible && (
<InviteModal cid={cid} closeModal={toggleAddVisible} />
)}
<Layout
to={cid}
context="channel"
dropFiles={dropFiles}
// ref={containerRef}
aside={
<>
<ul className="tools">
<li className="tool">
<Tooltip tip="Voice/Video Chat" placement="left">
<IconHeadphone />
</Tooltip>
</li>
<Tooltip
tip="Pin"
placement="left"
disabled={toolVisible == "pin"}
>
<Tippy
onShow={() => {
setToolVisible("pin");
}}
onHide={() => {
setToolVisible("");
}}
placement="left-start"
popperOptions={{ strategy: "fixed" }}
offset={[0, 80]}
interactive
trigger="click"
content={<PinList id={cid} />}
>
<li
className={`tool ${pinCount > 0 ? "badge" : ""} ${
toolVisible == "pin" ? "active" : ""
} `}
data-count={pinCount}
>
<IconPin />
</li>
</Tippy>
</Tooltip>
<Tooltip
tip="Favorite"
placement="left"
disabled={toolVisible == "favorite"}
>
<Tippy
onShow={() => {
setToolVisible("favorite");
}}
onHide={() => {
setToolVisible("");
}}
placement="left-start"
popperOptions={{ strategy: "fixed" }}
offset={[0, 180]}
interactive
trigger="click"
content={<FavList cid={cid} />}
>
<li
className={`tool fav ${
toolVisible == "favorite" ? "active" : ""
} `}
data-count={pinCount}
>
<IconFav />
</li>
</Tippy>
</Tooltip>
<li
className={`tool ${membersVisible ? "active" : ""}`}
onClick={toggleMembersVisible}
>
<Tooltip tip="Channel Members" placement="left">
<IconPeople />
</Tooltip>
</li>
</ul>
<hr className="divider" />
<ul className="apps">
<li className="app">
<Tooltip tip="Webrowse" placement="left">
<img src={webrowseIcon} alt="app icon" />
</Tooltip>
</li>
<li className="app">
<Tooltip tip="BoardOS" placement="left">
<img src={boardosIcon} alt="app icon" />
</Tooltip>
</li>
</ul>
</>
}
header={
<StyledHeader className="head">
<div className="txt">
<ChannelIcon personal={!is_public} />
<span className="title">{name}</span>
<span className="desc">{description}</span>
</div>
</StyledHeader>
}
contacts={
membersVisible ? (
<>
<StyledContacts>
{addVisible && (
<div className="add" onClick={toggleAddVisible}>
<img className="icon" src={addIcon} />
<div className="txt">Add members</div>
</div>
)}
{memberIds.map((uid) => {
return (
<Contact
enableContextMenu={true}
cid={cid}
owner={owner == uid}
key={uid}
uid={uid}
dm
popover
/>
);
})}
</StyledContacts>
</>
) : null
}
>
<StyledChannelChat id={`RUSTCHAT_FEED_channel_${cid}`}>
{/* <div className="feed"> */}
{feeds.map((mid, idx) => {
const curr = messageData[mid];
if (!curr) return null;
const isFirst = idx == 0;
const prev =
idx == feeds.length - 1 ? null : messageData[feeds[idx + 1]];
const read = curr?.from_uid == loginUid || mid <= readIndex;
return renderMessageFragment({
selectMode: !!selects,
updateReadIndex: updateReadDebounced,
read,
isFirst,
prev,
curr,
contextId: cid,
context: "channel",
});
})}
{hasMore ? (
<LoadMore pullDown={pullDown} />
) : (
<div className="info">
<h2 className="title">Welcome to #{name} !</h2>
<p className="desc">This is the start of the #{name} channel. </p>
<NavLink
to={`/setting/channel/${cid}?f=${pathname}`}
className="edit"
>
<EditIcon className="icon" />
Edit Channel
</NavLink>
</div>
)}
{/* </div> */}
</StyledChannelChat>
{/* {unreads != 0 && (
<StyledNotification>
<div className="content">
{unreads} new messages
{msgs.lastAccess
? `since ${dayjs(msgs.lastAccess).format("YYYY-MM-DD h:mm:ss A")}`
: ""}
</div>
<button onClick={handleClearUnreads} className="clear">
Mark As Read
</button>
</StyledNotification>
)} */}
</Layout>
</>
);
}
+3 -2
View File
@@ -88,14 +88,15 @@ export const StyledChannelChat = styled.article`
height: -webkit-fill-available; height: -webkit-fill-available;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
/* overflow-anchor: none; */
/* pagination start */ /* pagination start */
transform: rotate(180deg); /* transform: rotate(180deg);
direction: rtl; direction: rtl;
> div, > div,
> hr { > hr {
direction: ltr; direction: ltr;
transform: rotate(180deg); transform: rotate(180deg);
} } */
/* pagination end */ /* pagination end */
> .info { > .info {
padding-top: 62px; padding-top: 62px;
+1 -1
View File
@@ -182,6 +182,7 @@ export const renderMessageFragment = ({
console.log("_key", _key, local_id, mid); console.log("_key", _key, local_id, mid);
return ( return (
<React.Fragment key={_key}> <React.Fragment key={_key}>
{divider && <Divider content={divider}></Divider>}
<MessageWrapper <MessageWrapper
key={_key} key={_key}
data-key={_key} data-key={_key}
@@ -201,7 +202,6 @@ export const renderMessageFragment = ({
contextId={contextId} contextId={contextId}
/> />
</MessageWrapper> </MessageWrapper>
{divider && <Divider content={divider}></Divider>}
</React.Fragment> </React.Fragment>
); );
// React.memo( // React.memo(