refactor: stash long list updates

This commit is contained in:
zerosoul
2022-06-01 10:59:41 +08:00
parent aa7c91416c
commit 5c6aed7165
12 changed files with 274 additions and 221 deletions
+13 -24
View File
@@ -8,6 +8,7 @@ import { removeMessage } from "../slices/message";
import { removeChannelSession } from "../slices/message.channel"; import { removeChannelSession } from "../slices/message.channel";
import { removeReactionMessage } from "../slices/message.reaction"; import { removeReactionMessage } from "../slices/message.reaction";
import { onMessageSendStarted } from "./handlers"; import { onMessageSendStarted } from "./handlers";
import handleChatMessage from "../../common/hook/useStreaming/chat.handler";
export const channelApi = createApi({ export const channelApi = createApi({
reducerPath: "channelApi", reducerPath: "channelApi",
baseQuery, baseQuery,
@@ -58,31 +59,19 @@ export const channelApi = createApi({
}, },
}), }),
getHistoryMessages: builder.query({ getHistoryMessages: builder.query({
query: ({ gid, mid = 0, limit = 50 }) => ({ query: ({ gid, mid = null, limit = 100 }) => ({
url: `/group/${gid}/history?before=${mid}&limit=${limit}`, url: mid
? `/group/${gid}/history?before=${mid}&limit=${limit}`
: `/group/${gid}/history?limit=${limit}`,
}), }),
// async onQueryStarted(id, { dispatch, getState, queryFulfilled }) { async onQueryStarted(id, { dispatch, getState, queryFulfilled }) {
// const { const { data: messages } = await queryFulfilled;
// ui: { channelSetting }, if (messages?.length) {
// channelMessage, messages.forEach((msg) => {
// } = getState(); handleChatMessage(msg, dispatch, getState());
// try { });
// await queryFulfilled; }
// dispatch(removeChannel(id)); },
// if (id == channelSetting) {
// dispatch(toggleChannelSetting());
// }
// // 删掉该channel下的所有消息&reaction
// const mids = channelMessage[id];
// if (mids) {
// dispatch(removeChannelSession(id));
// dispatch(removeMessage(mids));
// dispatch(removeReactionMessage(mids));
// }
// } catch {
// console.log("remove channel error");
// }
// },
}), }),
createInviteLink: builder.query({ createInviteLink: builder.query({
query: (gid) => ({ query: (gid) => ({
+7 -3
View File
@@ -1,7 +1,7 @@
// import { useRef } from "react"; // import { useRef } from "react";
// import styled from "styled-components"; // import styled from "styled-components";
import StyledMenu from "./styled/Menu"; import StyledMenu from "./styled/Menu";
export default function ContextMenu({ items = [], hideMenu }) { export default function ContextMenu({ items = [], hideMenu = null }) {
return ( return (
<StyledMenu> <StyledMenu>
{items.map((item) => { {items.map((item) => {
@@ -11,7 +11,9 @@ export default function ContextMenu({ items = [], hideMenu }) {
icon = null, icon = null,
handler = (evt) => { handler = (evt) => {
evt.preventDefault(); evt.preventDefault();
hideMenu(); if (hideMenu) {
hideMenu();
}
}, },
underline = false, underline = false,
danger = false, danger = false,
@@ -24,7 +26,9 @@ export default function ContextMenu({ items = [], hideMenu }) {
key={title} key={title}
onClick={(evt) => { onClick={(evt) => {
handler(evt); handler(evt);
hideMenu(); if (hideMenu) {
hideMenu();
}
}} }}
> >
{icon} {icon}
+4
View File
@@ -87,8 +87,10 @@ function Message({
const pinInfo = getPinInfo(mid); const pinInfo = getPinInfo(mid);
// return null; // return null;
const _key = properties?.local_id || mid;
return ( return (
<StyledWrapper <StyledWrapper
key={_key}
onContextMenu={handleContextMenuEvent} onContextMenu={handleContextMenuEvent}
data-msg-mid={mid} data-msg-mid={mid}
ref={inviewRef} ref={inviewRef}
@@ -97,6 +99,8 @@ function Message({
} ${contextMenuVisible ? "contextVisible" : ""} `} } ${contextMenuVisible ? "contextVisible" : ""} `}
> >
<Tippy <Tippy
key={_key}
popperOptions={{ strategy: "fixed" }}
disabled={readOnly} disabled={readOnly}
interactive interactive
placement="left" placement="left"
+1
View File
@@ -1,6 +1,7 @@
import styled from "styled-components"; import styled from "styled-components";
const StyledWrapper = styled.div` const StyledWrapper = styled.div`
z-index: 9999;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
+95 -56
View File
@@ -1,127 +1,166 @@
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";
const getFeedWithPagination = (config) => { const getFeedWithPagination = (config) => {
const { pageNumber = 1, pageSize = 10, mids = [], isLast = false } = const { pageNumber = 1, pageSize = 20, mids = [] } = config || {};
config || {};
const shadowMids = mids.slice(0); const shadowMids = mids.slice(0);
if (shadowMids.length == 0) if (shadowMids.length == 0)
return { return {
isFirst: true,
isLast: true,
pageCount: 0, pageCount: 0,
pageSize, pageSize,
pageNumber: 1, pageNumber: 1,
ids: [], ids: [],
}; };
shadowMids.sort((a, b) => { shadowMids.sort((a, b) => {
return Number(a) - Number(b); return Number(b) - Number(a);
}); });
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(
(computedPageNumber - 1) * pageSize, (pageNumber - 1) * pageSize,
computedPageNumber * pageSize pageNumber * pageSize
); );
return { const info = {
isFirst: pageNumber == 1,
isLast: pageNumber == pageCount,
pageCount, pageCount,
pageSize, pageSize,
pageNumber: computedPageNumber, pageNumber,
ids, ids,
}; };
console.log("get page Info", info);
return info;
}; };
export default function useMessageFeed({ context = "channel", id = null }) { export default function useMessageFeed({ context = "channel", id = null }) {
const [loadMoreFromServer] = useLazyGetHistoryMessagesQuery();
const listRef = useRef([]); const listRef = useRef([]);
const pageRef = useRef(null); const pageRef = useRef(null);
const [hasMore, setHasMore] = useState(true); const [hasMore, setHasMore] = useState(true);
const [appends, setAppends] = useState([]); const [prepends, setPrepends] = useState([]);
// const [appends, setAppends] = useState([]);
const [items, setItems] = useState([]); const [items, setItems] = useState([]);
const { mids, messageData } = useSelector((store) => { const { mids } = useSelector((store) => {
return { return {
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(() => {
if (appends.length) { const container = document.querySelector(`#RUSTCHAT_FEED_${context}_${id}`);
const feedsWrapperEle = document.querySelector( const handler = (e) => {
`#RUSTCHAT_FEED_${context}_${id}` e.preventDefault();
); var n = 0;
if (feedsWrapperEle) { if ("deltaY" in e) n = 1 === e.deltaMode ? 20 * -e.deltaY : -e.deltaY;
feedsWrapperEle.scrollTop = feedsWrapperEle.scrollHeight; else if ("wheelDeltaY" in e) n = (e.wheelDeltaY / 120) * 20;
else if ("wheelDelta" in e) n = (e.wheelDelta / 120) * 20;
else {
n = (-e.detail / 3) * 20;
} }
container.scrollTop += n;
};
if (container) {
container.addEventListener("wheel", handler);
} }
}, [appends, context, id]); return () => {
if (container) {
container.removeEventListener("wheel", handler);
}
};
}, [context, id]);
useEffect(() => { useEffect(() => {
if (listRef.current.length == 0) { const fetchMore = () => {
// 初次 if (listRef.current.length == 0 && mids.length) {
const pageInfo = getFeedWithPagination({ mids, isLast: true }); // 初次
console.log("pull up 2", pageInfo); const pageInfo = getFeedWithPagination({ mids });
pageRef.current = pageInfo; console.log("pull down 2", pageInfo);
listRef.current = pageInfo.ids; pageRef.current = pageInfo;
setItems(listRef.current); listRef.current = pageInfo.ids;
console.log("message pageInfo", mids, pageInfo); setItems(listRef.current);
} else { console.log("message pageInfo", mids, pageInfo);
// 追加 } else {
const lastMid = listRef.current.slice(-1); // 追加
const sorteds = mids.slice(0).sort((a, b) => { const lastMid = listRef.current[0];
return Number(a) - Number(b); const sorteds = mids.slice(0).sort((a, b) => {
}); return Number(b) - Number(a);
const appends = sorteds.filter((s) => s > lastMid); });
if (appends.length) { const prepends = sorteds.filter((s) => s > lastMid);
setAppends(appends); if (prepends.length) {
setPrepends(prepends);
}
console.log("prepends", prepends, items);
} }
console.log("appends", appends); };
} fetchMore();
}, [mids]); }, [mids]);
const pullUp = () => {
const pullDown = async () => {
// 向下加载
const currPageInfo = pageRef.current; const currPageInfo = pageRef.current;
console.log("pull up", currPageInfo); console.log("pull down", currPageInfo);
// 一页 // 最后一页
if (currPageInfo && currPageInfo.pageNumber == 1) { if (currPageInfo && currPageInfo.isLast) {
setHasMore(false); const [mid] = currPageInfo.ids.slice(-1);
return; const { data: newList } = await loadMoreFromServer({ mid, gid: id });
if (newList.length == 0) {
setHasMore(false);
return;
}
} }
let pageInfo = null; let pageInfo = null;
if (!currPageInfo) { if (!currPageInfo) {
// 初始化 // 初始化
pageInfo = getFeedWithPagination({ pageInfo = getFeedWithPagination({
mids, mids,
isLast: true,
}); });
} else { } else {
const prevPageNumber = currPageInfo.pageNumber - 1; const nextPageNumber = currPageInfo.pageNumber + 1;
pageInfo = getFeedWithPagination({ pageInfo = getFeedWithPagination({
mids, mids,
pageNumber: prevPageNumber, pageNumber: nextPageNumber,
}); });
} }
pageRef.current = pageInfo; pageRef.current = pageInfo;
listRef.current = [...pageInfo.ids, ...listRef.current]; listRef.current = [...listRef.current, ...pageInfo.ids];
setTimeout(() => { setTimeout(() => {
setHasMore(!pageInfo.isLast);
setItems(listRef.current); setItems(listRef.current);
console.log("pull up", currPageInfo, listRef.current); console.log("pull down update", currPageInfo, listRef.current);
setHasMore(pageInfo.pageNumber !== 1); }, 1000);
}, 800);
};
const pullDown = () => {
// 向下加载
}; };
return { return {
mids, mids,
appends, prepends,
// appends,
hasMore, hasMore,
pullUp, // pullUp,
pullDown, pullDown,
list: items, list: items,
}; };
+30 -29
View File
@@ -1,8 +1,9 @@
// import { useRef, useEffect } from "react"; import { useRef, useEffect } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { Waveform } from "@uiball/loaders"; import { Waveform } from "@uiball/loaders";
const Styled = styled.div` const Styled = styled.div`
margin-top: 80px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@@ -10,35 +11,35 @@ const Styled = styled.div`
padding: 30px 0; padding: 30px 0;
/* background-color: #eee; */ /* background-color: #eee; */
`; `;
export default function LoadMore() { export default function LoadMore({ pullDown = null }) {
// const ref = useRef(undefined); const ref = useRef(undefined);
// useEffect(() => { useEffect(() => {
// const observer = new IntersectionObserver( const observer = new IntersectionObserver(
// (entries) => { (entries) => {
// entries.forEach((entry) => { entries.forEach((entry) => {
// const intersecting = entry.isIntersecting; const intersecting = entry.isIntersecting;
// // const currEle = entry.target; // const currEle = entry.target;
// if (intersecting && loadMore) { if (intersecting && pullDown) {
// // load more // load more
// console.log("inview"); console.log("inview");
// loadMore(); pullDown();
// } }
// }); });
// }, },
// { threshold: 0 } { threshold: 0 }
// ); );
// const currEle = ref?.current; const currEle = ref?.current;
// if (currEle) { if (currEle) {
// observer.observe(ref.current); observer.observe(ref.current);
// } }
// return () => { return () => {
// if (currEle) { if (currEle) {
// observer.unobserve(currEle); observer.unobserve(currEle);
// } }
// }; };
// }, [ref]); }, [ref, pullDown]);
return ( return (
<Styled> <Styled ref={ref}>
<Waveform <Waveform
className="loading" className="loading"
size={24} size={24}
+49 -39
View File
@@ -3,11 +3,14 @@ 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 useChatScroll from "../../../common/hook/useChatScroll";
import useMessageFeed from "../../../common/hook/useMessageFeed";
import ChannelIcon from "../../../common/component/ChannelIcon"; import ChannelIcon from "../../../common/component/ChannelIcon";
import Tooltip from "../../../common/component/Tooltip"; import Tooltip from "../../../common/component/Tooltip";
import Contact from "../../../common/component/Contact"; import Contact from "../../../common/component/Contact";
@@ -30,8 +33,13 @@ import {
StyledHeader, StyledHeader,
} from "./styled"; } from "./styled";
import InviteModal from "../../../common/component/InviteModal"; import InviteModal from "../../../common/component/InviteModal";
import LoadMore from "./LoadMore";
export default function ChannelChat({ cid = "", dropFiles = [] }) { export default function ChannelChat({ cid = "", dropFiles = [] }) {
const { list: msgIds, prepends, hasMore, pullDown } = useMessageFeed({
context: "channel",
id: cid,
});
const [toolVisible, setToolVisible] = useState(""); const [toolVisible, setToolVisible] = useState("");
const { pathname } = useLocation(); const { pathname } = useLocation();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -41,7 +49,7 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
const [addMemberModalVisible, setAddMemberModalVisible] = useState(false); const [addMemberModalVisible, setAddMemberModalVisible] = useState(false);
const { const {
selects, selects,
msgIds, // msgIds,
userIds, userIds,
data, data,
messageData, messageData,
@@ -54,13 +62,13 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
footprint: store.footprint, footprint: store.footprint,
loginUser: store.contacts.byId[store.authData.uid], loginUser: store.contacts.byId[store.authData.uid],
loginUid: store.authData.uid, loginUid: store.authData.uid,
msgIds: store.channelMessage[cid] || [], // msgIds: store.channelMessage[cid] || [],
userIds: store.contacts.ids, userIds: store.contacts.ids,
data: store.channels.byId[cid] || {}, data: store.channels.byId[cid] || {},
messageData: store.message || {}, messageData: store.message || {},
}; };
}); });
const ref = useChatScroll(msgIds); // const ref = useChatScroll(msgIds);
// const handleClearUnreads = () => { // const handleClearUnreads = () => {
// dispatch(readMessage(msgIds)); // dispatch(readMessage(msgIds));
// }; // };
@@ -86,6 +94,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];
return ( return (
<> <>
{addMemberModalVisible && ( {addMemberModalVisible && (
@@ -223,41 +232,42 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
) : null ) : null
} }
> >
<StyledChannelChat ref={ref}> <StyledChannelChat id={`RUSTCHAT_FEED_channel_${cid}`}>
<div className="info"> {/* <div className="feed"> */}
<h2 className="title">Welcome to #{name} !</h2> {feeds.map((mid, idx) => {
<p className="desc">This is the start of the #{name} channel. </p> const curr = messageData[mid];
<NavLink if (!curr) return null;
to={`/setting/channel/${cid}?f=${pathname}`} const isFirst = idx == 0;
className="edit" const prev =
> idx == feeds.length - 1 ? null : messageData[feeds[idx + 1]];
<EditIcon className="icon" /> const read = curr?.from_uid == loginUid || mid <= readIndex;
Edit Channel return renderMessageFragment({
</NavLink> selectMode: !!selects,
</div> updateReadIndex: updateReadDebounced,
<div className="feed"> read,
{[...msgIds] isFirst,
.sort((a, b) => { prev,
return Number(a) - Number(b); curr,
}) contextId: cid,
.map((mid, idx) => { context: "channel",
const curr = messageData[mid]; });
if (!curr) return null; })}
const isFirst = idx == 0; {hasMore ? (
const prev = idx == 0 ? null : messageData[msgIds[idx - 1]]; <LoadMore pullDown={pullDown} />
const read = curr?.from_uid == loginUid || mid <= readIndex; ) : (
return renderMessageFragment({ <div className="info">
selectMode: !!selects, <h2 className="title">Welcome to #{name} !</h2>
updateReadIndex: updateReadDebounced, <p className="desc">This is the start of the #{name} channel. </p>
read, <NavLink
isFirst, to={`/setting/channel/${cid}?f=${pathname}`}
prev, className="edit"
curr, >
contextId: cid, <EditIcon className="icon" />
context: "channel", Edit Channel
}); </NavLink>
})} </div>
</div> )}
{/* </div> */}
</StyledChannelChat> </StyledChannelChat>
{/* {unreads != 0 && ( {/* {unreads != 0 && (
<StyledNotification> <StyledNotification>
@@ -2,15 +2,12 @@ import { useState, useEffect } from "react";
import { useDebounce } from "rooks"; 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 InfiniteScroll from "react-infinite-scroller";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import PinList from "./PinList"; import PinList from "./PinList";
import LoadMore from "./LoadMore";
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 useChatScroll from "../../../common/hook/useChatScroll";
import useMessageFeed from "../../../common/hook/useMessageFeed";
import ChannelIcon from "../../../common/component/ChannelIcon"; import ChannelIcon from "../../../common/component/ChannelIcon";
import Tooltip from "../../../common/component/Tooltip"; import Tooltip from "../../../common/component/Tooltip";
import Contact from "../../../common/component/Contact"; import Contact from "../../../common/component/Contact";
@@ -26,14 +23,15 @@ import IconHeadphone from "../../../assets/icons/headphone.svg";
import boardosIcon from "../../../assets/icons/app.boardos.svg?url"; import boardosIcon from "../../../assets/icons/app.boardos.svg?url";
import webrowseIcon from "../../../assets/icons/app.webrowse.svg?url"; import webrowseIcon from "../../../assets/icons/app.webrowse.svg?url";
import addIcon from "../../../assets/icons/add.svg?url"; import addIcon from "../../../assets/icons/add.svg?url";
import { StyledContacts, StyledChannelChat, StyledHeader } from "./styled"; import {
// StyledNotification,
StyledContacts,
StyledChannelChat,
StyledHeader,
} from "./styled";
import InviteModal from "../../../common/component/InviteModal"; import InviteModal from "../../../common/component/InviteModal";
export default function ChannelChat({ cid = "", dropFiles = [] }) { export default function ChannelChat({ cid = "", dropFiles = [] }) {
const { list: msgIdList, appends, hasMore, pullUp } = useMessageFeed({
context: "channel",
id: cid,
});
const [toolVisible, setToolVisible] = useState(""); const [toolVisible, setToolVisible] = useState("");
const { pathname } = useLocation(); const { pathname } = useLocation();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -43,6 +41,7 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
const [addMemberModalVisible, setAddMemberModalVisible] = useState(false); const [addMemberModalVisible, setAddMemberModalVisible] = useState(false);
const { const {
selects, selects,
msgIds,
userIds, userIds,
data, data,
messageData, messageData,
@@ -55,16 +54,16 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
footprint: store.footprint, footprint: store.footprint,
loginUser: store.contacts.byId[store.authData.uid], loginUser: store.contacts.byId[store.authData.uid],
loginUid: store.authData.uid, loginUid: store.authData.uid,
msgIds: store.channelMessage[cid] || [],
userIds: store.contacts.ids, userIds: store.contacts.ids,
data: store.channels.byId[cid] || {}, data: store.channels.byId[cid] || {},
messageData: store.message || {}, messageData: store.message || {},
}; };
}); });
// const ref = useChatScroll(mids); const ref = useChatScroll(msgIds);
// const handleClearUnreads = () => { // const handleClearUnreads = () => {
// dispatch(readMessage(msgIds)); // dispatch(readMessage(msgIds));
// }; // };
// remember the route while switching out
useEffect(() => { useEffect(() => {
dispatch(updateRemeberedNavs()); dispatch(updateRemeberedNavs());
return () => { return () => {
@@ -84,10 +83,9 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
? userIds ? userIds
: members.slice(0).sort((n) => (n == owner ? -1 : 0)); : members.slice(0).sort((n) => (n == owner ? -1 : 0));
const addVisible = loginUser?.is_admin || owner == loginUid; const addVisible = loginUser?.is_admin || owner == loginUid;
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 finalHasMore = selects ? false : hasMore;
const msgs = [...msgIdList, ...appends];
return ( return (
<> <>
{addMemberModalVisible && ( {addMemberModalVisible && (
@@ -211,10 +209,10 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
return ( return (
<Contact <Contact
enableContextMenu={true} enableContextMenu={true}
cid={cid}
owner={owner == uid} owner={owner == uid}
key={uid} key={uid}
uid={uid} uid={uid}
cid={cid}
dm dm
popover popover
/> />
@@ -225,48 +223,41 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
) : null ) : null
} }
> >
<StyledChannelChat id={`RUSTCHAT_FEED_channel_${cid}`}> <StyledChannelChat ref={ref}>
{!hasMore && ( <div className="info">
<div className="info"> <h2 className="title">Welcome to #{name} !</h2>
<h2 className="title">Welcome to #{name} !</h2> <p className="desc">This is the start of the #{name} channel. </p>
<p className="desc">This is the start of the #{name} channel. </p> <NavLink
<NavLink to={`/setting/channel/${cid}?f=${pathname}`}
to={`/setting/channel/${cid}?f=${pathname}`} className="edit"
className="edit" >
> <EditIcon className="icon" />
<EditIcon className="icon" /> Edit Channel
Edit Channel </NavLink>
</NavLink> </div>
</div> <div className="feed">
)} {[...msgIds]
{/* <div className="feed"> */} .sort((a, b) => {
<InfiniteScroll return Number(a) - Number(b);
threshold={100} })
hasMore={hasMore} .map((mid, idx) => {
useWindow={false} const curr = messageData[mid];
loader={<LoadMore />} if (!curr) return null;
isReverse={true} const isFirst = idx == 0;
loadMore={pullUp} const prev = idx == 0 ? null : messageData[msgIds[idx - 1]];
> const read = curr?.from_uid == loginUid || mid <= readIndex;
{msgs.map((mid, idx) => { return renderMessageFragment({
const curr = messageData[mid]; selectMode: !!selects,
if (!curr) return null; updateReadIndex: updateReadDebounced,
const isFirst = idx == 0; read,
const prev = idx == 0 ? null : messageData[msgs[idx - 1]]; isFirst,
const read = curr?.from_uid == loginUid || mid <= readIndex; prev,
return renderMessageFragment({ curr,
selectMode: !!selects, contextId: cid,
updateReadIndex: updateReadDebounced, context: "channel",
read, });
isFirst, })}
prev, </div>
curr,
contextId: cid,
context: "channel",
});
})}
</InfiniteScroll>
{/* </div> */}
</StyledChannelChat> </StyledChannelChat>
{/* {unreads != 0 && ( {/* {unreads != 0 && (
<StyledNotification> <StyledNotification>
@@ -36,7 +36,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, appends, hasMore, pullDown } = useMessageFeed({ const { list: msgIds, prepends, hasMore, pullDown } = useMessageFeed({
context: "channel", context: "channel",
id: cid, id: cid,
}); });
@@ -94,6 +94,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];
return ( return (
<> <>
{addMemberModalVisible && ( {addMemberModalVisible && (
@@ -231,14 +232,14 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
) : null ) : null
} }
> >
<StyledChannelChat id="ScrollFeedWrapper"> <StyledChannelChat id={`RUSTCHAT_FEED_channel_${cid}`}>
{/* <div className="feed"> */} {/* <div className="feed"> */}
{[...msgIds].map((mid, idx) => { {feeds.map((mid, idx) => {
const curr = messageData[mid]; const curr = messageData[mid];
if (!curr) return null; if (!curr) return null;
const isFirst = idx == 0; const isFirst = idx == 0;
const prev = const prev =
idx == msgIds.length - 1 ? null : messageData[msgIds[idx + 1]]; idx == feeds.length - 1 ? null : messageData[feeds[idx + 1]];
const read = curr?.from_uid == loginUid || mid <= readIndex; const read = curr?.from_uid == loginUid || mid <= readIndex;
return renderMessageFragment({ return renderMessageFragment({
selectMode: !!selects, selectMode: !!selects,
+9 -4
View File
@@ -88,6 +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;
/* pagination start */
transform: rotate(180deg);
direction: rtl;
> div,
> hr {
direction: ltr;
transform: rotate(180deg);
}
/* pagination end */
> .info { > .info {
padding-top: 62px; padding-top: 62px;
display: flex; display: flex;
@@ -129,9 +138,5 @@ export const StyledChannelChat = styled.article`
} }
} }
/* > .feed { /* > .feed {
overflow-anchor: none;
> div {
overflow-anchor: auto;
}
} */ } */
`; `;
+4 -4
View File
@@ -128,10 +128,10 @@ const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => {
title: muted ? "Unmute" : "Mute", title: muted ? "Unmute" : "Mute",
handler: handleMute, handler: handleMute,
}, },
{ // {
title: "Notification Settings", // title: "Notification Settings",
underline: true, // underline: true,
}, // },
{ {
title: "Invite People", title: "Invite People",
handler: toggleInviteModalVisible, handler: toggleInviteModalVisible,
+10 -2
View File
@@ -179,10 +179,9 @@ export const renderMessageFragment = ({
} }
} }
const _key = local_id || mid; const _key = local_id || mid;
// 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}
@@ -202,8 +201,17 @@ export const renderMessageFragment = ({
contextId={contextId} contextId={contextId}
/> />
</MessageWrapper> </MessageWrapper>
{divider && <Divider content={divider}></Divider>}
</React.Fragment> </React.Fragment>
); );
// React.memo(
// (prevs, nexts) => {
// // curr.properties?.local_id
// const prevObj = prevs.curr || undefined;
// const nextObj = nexts.curr || undefined;
// return prevObj?.properties?.local_id === nextObj?.properties?.local_id;
// }
// );
}; };
export default getUnreadCount; export default getUnreadCount;