refactor: update read index with debounce
This commit is contained in:
@@ -1,40 +1,38 @@
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useInViewRef, useDebounce } from "rooks";
|
||||
import { useInViewRef } from "rooks";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import Reaction from "./Reaction";
|
||||
import Reply from "./Reply";
|
||||
import Profile from "../Profile";
|
||||
import Avatar from "../Avatar";
|
||||
import { useReadMessageMutation } from "../../../app/services/message";
|
||||
|
||||
import StyledWrapper from "./styled";
|
||||
import Commands from "./Commands";
|
||||
|
||||
import EditMessage from "./EditMessage";
|
||||
import renderContent from "./renderContent";
|
||||
function Message({ contextId = 0, mid = "", context = "user" }) {
|
||||
const [updateReadIndex] = useReadMessageMutation();
|
||||
const updateReadDebounced = useDebounce(updateReadIndex, 300);
|
||||
function Message({
|
||||
contextId = 0,
|
||||
mid = "",
|
||||
context = "user",
|
||||
updateReadIndex,
|
||||
read = true,
|
||||
}) {
|
||||
const [myRef, inView] = useInViewRef();
|
||||
const [edit, setEdit] = useState(false);
|
||||
|
||||
const avatarRef = useRef(null);
|
||||
const {
|
||||
footprint,
|
||||
message = {},
|
||||
reactionMessageData,
|
||||
contactsData,
|
||||
loginUid,
|
||||
} = useSelector((store) => {
|
||||
return {
|
||||
footprint: store.footprint,
|
||||
loginUid: store.authData.uid,
|
||||
reactionMessageData: store.reactionMessage,
|
||||
message: store.message[mid] || {},
|
||||
contactsData: store.contacts.byId,
|
||||
};
|
||||
});
|
||||
const { message = {}, reactionMessageData, contactsData } = useSelector(
|
||||
(store) => {
|
||||
return {
|
||||
reactionMessageData: store.reactionMessage,
|
||||
message: store.message[mid] || {},
|
||||
contactsData: store.contacts.byId,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const toggleEditMessage = () => {
|
||||
setEdit((prev) => !prev);
|
||||
@@ -51,20 +49,16 @@ function Message({ contextId = 0, mid = "", context = "user" }) {
|
||||
edited,
|
||||
properties,
|
||||
} = message;
|
||||
const readIndex =
|
||||
context == "user"
|
||||
? footprint.readUsers[contextId]
|
||||
: footprint.readChannels[contextId];
|
||||
|
||||
useEffect(() => {
|
||||
const read = fromUid == loginUid || mid <= readIndex;
|
||||
if (inView && !read) {
|
||||
if (!read) {
|
||||
const data =
|
||||
context == "user"
|
||||
? { users: [{ uid: +contextId, mid }] }
|
||||
: { groups: [{ gid: +contextId, mid }] };
|
||||
updateReadDebounced(data);
|
||||
updateReadIndex(data);
|
||||
}
|
||||
}, [mid, readIndex, inView, fromUid, loginUid]);
|
||||
}, [mid, read]);
|
||||
const reactions = reactionMessageData[mid];
|
||||
const currUser = contactsData[fromUid] || {};
|
||||
// if (!message) return null;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { useDebounce } from "rooks";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useReadMessageMutation } from "../../../app/services/message";
|
||||
import useChatScroll from "../../../common/hook/useChatScroll";
|
||||
import ChannelIcon from "../../../common/component/ChannelIcon";
|
||||
import Send from "../../../common/component/Send";
|
||||
@@ -21,11 +22,22 @@ import AddMemberModal from "./AddMemberModal";
|
||||
|
||||
export default function ChannelChat({ cid = "", dropFiles = [] }) {
|
||||
// const containerRef = useRef(null);
|
||||
const [updateReadIndex] = useReadMessageMutation();
|
||||
const updateReadDebounced = useDebounce(updateReadIndex, 300);
|
||||
const [membersVisible, setMembersVisible] = useState(true);
|
||||
const [addMemberModalVisible, setAddMemberModalVisible] = useState(false);
|
||||
// const dispatch = useDispatch();
|
||||
const { msgIds, userIds, data, messageData } = useSelector((store) => {
|
||||
const {
|
||||
msgIds,
|
||||
userIds,
|
||||
data,
|
||||
messageData,
|
||||
loginUid,
|
||||
footprint,
|
||||
} = useSelector((store) => {
|
||||
return {
|
||||
footprint: store.footprint,
|
||||
loginUid: store.authData.uid,
|
||||
msgIds: store.channelMessage[cid] || [],
|
||||
userIds: store.contacts.ids,
|
||||
data: store.channels.byId[cid] || {},
|
||||
@@ -45,6 +57,7 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
|
||||
const { name, description, is_public, members = [] } = data;
|
||||
const memberIds = members.length == 0 ? userIds : members;
|
||||
console.log("channel message list", msgIds);
|
||||
const readIndex = footprint.readChannels[cid];
|
||||
return (
|
||||
<>
|
||||
{addMemberModalVisible && (
|
||||
@@ -113,9 +126,13 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
|
||||
return Number(a) - Number(b);
|
||||
})
|
||||
.map((mid, idx) => {
|
||||
const prev = idx == 0 ? null : messageData[msgIds[idx - 1]];
|
||||
const curr = messageData[mid];
|
||||
if (!curr) return null;
|
||||
const prev = idx == 0 ? null : messageData[msgIds[idx - 1]];
|
||||
const read = curr?.from_uid == loginUid || mid <= readIndex;
|
||||
return renderMessageFragment({
|
||||
updateReadIndex: updateReadDebounced,
|
||||
read,
|
||||
prev,
|
||||
curr,
|
||||
contextId: cid,
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useDebounce } from "rooks";
|
||||
|
||||
import addIcon from "../../../assets/icons/add.person.svg?url";
|
||||
import callIcon from "../../../assets/icons/call.svg?url";
|
||||
import videoIcon from "../../../assets/icons/video.svg?url";
|
||||
import useChatScroll from "../../../common/hook/useChatScroll";
|
||||
import Send from "../../../common/component/Send";
|
||||
import { useReadMessageMutation } from "../../../app/services/message";
|
||||
import Contact from "../../../common/component/Contact";
|
||||
import Layout from "../Layout";
|
||||
import { StyledHeader, StyledDMChat } from "./styled";
|
||||
import { renderMessageFragment } from "../utils";
|
||||
export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
const [updateReadIndex] = useReadMessageMutation();
|
||||
const updateReadDebounced = useDebounce(updateReadIndex, 300);
|
||||
console.log("dm files", dropFiles);
|
||||
// const [mids, setMids] = useState([]);
|
||||
const { msgIds, currUser, messageData } = useSelector((store) => {
|
||||
return {
|
||||
currUser: store.contacts.byId[uid],
|
||||
msgIds: store.userMessage.byId[uid] || [],
|
||||
messageData: store.message,
|
||||
};
|
||||
});
|
||||
const { msgIds, currUser, messageData, footprint, loginUid } = useSelector(
|
||||
(store) => {
|
||||
return {
|
||||
loginUid: store.authData.uid,
|
||||
footprint: store.footprint,
|
||||
currUser: store.contacts.byId[uid],
|
||||
msgIds: store.userMessage.byId[uid] || [],
|
||||
messageData: store.message,
|
||||
};
|
||||
}
|
||||
);
|
||||
const ref = useChatScroll(msgIds);
|
||||
|
||||
if (!currUser) return null;
|
||||
// console.log("user msgs", msgs);
|
||||
const readIndex = footprint.readUsers[uid];
|
||||
return (
|
||||
<Layout
|
||||
to={uid}
|
||||
@@ -54,7 +64,10 @@ export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
.map((mid, idx) => {
|
||||
const curr = messageData[mid];
|
||||
const prev = idx == 0 ? null : messageData[msgIds[idx - 1]];
|
||||
const read = curr?.from_uid == loginUid || mid <= readIndex;
|
||||
return renderMessageFragment({
|
||||
updateReadIndex: updateReadDebounced,
|
||||
read,
|
||||
prev,
|
||||
curr,
|
||||
contextId: uid,
|
||||
|
||||
+24
-15
@@ -3,18 +3,18 @@ import dayjs from "dayjs";
|
||||
import { ContentTypes } from "../../app/config";
|
||||
import Divider from "../../common/component/Divider";
|
||||
import Message from "../../common/component/Message";
|
||||
function debounce(callback, wait = 2000, immediate = false) {
|
||||
let timeout = null;
|
||||
return function () {
|
||||
const callNow = immediate && !timeout;
|
||||
const next = () => callback.apply(this, arguments);
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(next, wait);
|
||||
if (callNow) {
|
||||
next();
|
||||
}
|
||||
};
|
||||
}
|
||||
// function debounce(callback, wait = 2000, immediate = false) {
|
||||
// let timeout = null;
|
||||
// return function () {
|
||||
// const callNow = immediate && !timeout;
|
||||
// const next = () => callback.apply(this, arguments);
|
||||
// clearTimeout(timeout);
|
||||
// timeout = setTimeout(next, wait);
|
||||
// if (callNow) {
|
||||
// next();
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
export function getUnreadCount({
|
||||
mids = [],
|
||||
messageData = {},
|
||||
@@ -22,10 +22,10 @@ export function getUnreadCount({
|
||||
readIndex = 0,
|
||||
}) {
|
||||
console.log({ mids, loginUid, readIndex });
|
||||
// 先过滤掉from自己的
|
||||
// 先过滤掉空信息和from自己的
|
||||
const others = mids.filter((mid) => {
|
||||
const { from_uid = 0 } = messageData[mid] || {};
|
||||
return from_uid != loginUid;
|
||||
return messageData[mid] && from_uid != loginUid;
|
||||
});
|
||||
if (others.length == 0) return 0;
|
||||
if (readIndex == 0) return others.length;
|
||||
@@ -71,6 +71,8 @@ export const renderPreviewMessage = (message = null) => {
|
||||
return res;
|
||||
};
|
||||
export const renderMessageFragment = ({
|
||||
read = true,
|
||||
updateReadIndex,
|
||||
prev = null,
|
||||
curr = null,
|
||||
contextId = 0,
|
||||
@@ -92,7 +94,14 @@ export const renderMessageFragment = ({
|
||||
return (
|
||||
<React.Fragment key={mid}>
|
||||
{divider && <Divider content={divider}></Divider>}
|
||||
<Message context={context} mid={mid} key={mid} contextId={contextId} />
|
||||
<Message
|
||||
updateReadIndex={updateReadIndex}
|
||||
read={read}
|
||||
context={context}
|
||||
mid={mid}
|
||||
key={mid}
|
||||
contextId={contextId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user