refactor: message feed
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { ViewportList } from 'react-viewport-list';
|
import { ViewportList } from 'react-viewport-list';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import User from "../../../common/component/User";
|
import User from "../../../common/component/User";
|
||||||
import IconAdd from "../../../assets/icons/add.svg";
|
import IconAdd from "../../../assets/icons/add.svg";
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import InviteModal from "../../../common/component/InviteModal";
|
import InviteModal from "../../../common/component/InviteModal";
|
||||||
|
|
||||||
|
|
||||||
@@ -16,9 +16,7 @@ type Props = {
|
|||||||
|
|
||||||
const Members = ({ uids, addVisible, ownerId, cid, membersVisible }: Props) => {
|
const Members = ({ uids, addVisible, ownerId, cid, membersVisible }: Props) => {
|
||||||
const { t } = useTranslation("chat");
|
const { t } = useTranslation("chat");
|
||||||
const ref = useRef<HTMLDivElement | null>(
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
null,
|
|
||||||
);
|
|
||||||
const [addMemberModalVisible, setAddMemberModalVisible] = useState(false);
|
const [addMemberModalVisible, setAddMemberModalVisible] = useState(false);
|
||||||
const toggleAddVisible = () => {
|
const toggleAddVisible = () => {
|
||||||
setAddMemberModalVisible((prev) => !prev);
|
setAddMemberModalVisible((prev) => !prev);
|
||||||
@@ -34,6 +32,7 @@ const Members = ({ uids, addVisible, ownerId, cid, membersVisible }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ViewportList
|
<ViewportList
|
||||||
|
initialPrerender={15}
|
||||||
viewportRef={ref}
|
viewportRef={ref}
|
||||||
items={uids}
|
items={uids}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,24 +1,17 @@
|
|||||||
import { useState, useEffect, memo } from "react";
|
import { useState, useEffect, memo } from "react";
|
||||||
import { useDebounce } from "rooks";
|
import { useLocation } from "react-router-dom";
|
||||||
import { NavLink, useLocation } from "react-router-dom";
|
|
||||||
import Tippy from "@tippyjs/react";
|
import Tippy from "@tippyjs/react";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import PinList from "./PinList";
|
import PinList from "./PinList";
|
||||||
import FavList from "../FavList";
|
import FavList from "../FavList";
|
||||||
import { useReadMessageMutation } from "../../../app/services/message";
|
|
||||||
import { updateRememberedNavs } from "../../../app/slices/ui";
|
import { updateRememberedNavs } from "../../../app/slices/ui";
|
||||||
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 Layout from "../Layout";
|
import Layout from "../Layout";
|
||||||
import { renderMessageFragment } from "../utils";
|
|
||||||
import EditIcon from "../../../assets/icons/edit.svg";
|
|
||||||
import IconFav from "../../../assets/icons/bookmark.svg";
|
import IconFav from "../../../assets/icons/bookmark.svg";
|
||||||
import IconPeople from "../../../assets/icons/people.svg";
|
import IconPeople from "../../../assets/icons/people.svg";
|
||||||
import IconPin from "../../../assets/icons/pin.svg";
|
import IconPin from "../../../assets/icons/pin.svg";
|
||||||
|
|
||||||
|
|
||||||
import LoadMore from "../LoadMore";
|
|
||||||
import { useAppSelector } from "../../../app/store";
|
import { useAppSelector } from "../../../app/store";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import GoBackNav from "../../../common/component/GoBackNav";
|
import GoBackNav from "../../../common/component/GoBackNav";
|
||||||
@@ -29,37 +22,19 @@ type Props = {
|
|||||||
};
|
};
|
||||||
function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
||||||
const { t } = useTranslation("chat");
|
const { t } = useTranslation("chat");
|
||||||
const {
|
|
||||||
pulling,
|
|
||||||
list: msgIds,
|
|
||||||
appends,
|
|
||||||
hasMore,
|
|
||||||
pullUp
|
|
||||||
} = useMessageFeed({
|
|
||||||
context: "channel",
|
|
||||||
id: cid
|
|
||||||
});
|
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [updateReadIndex] = useReadMessageMutation();
|
|
||||||
const updateReadDebounced = useDebounce(updateReadIndex, 300);
|
|
||||||
const [membersVisible, setMembersVisible] = useState(true);
|
const [membersVisible, setMembersVisible] = useState(true);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selects,
|
|
||||||
userIds,
|
userIds,
|
||||||
data,
|
data,
|
||||||
messageData,
|
|
||||||
loginUser,
|
loginUser,
|
||||||
footprint
|
|
||||||
} = useAppSelector((store) => {
|
} = useAppSelector((store) => {
|
||||||
return {
|
return {
|
||||||
selects: store.ui.selectMessages[`channel_${cid}`],
|
|
||||||
footprint: store.footprint,
|
|
||||||
loginUser: store.authData.user,
|
loginUser: store.authData.user,
|
||||||
userIds: store.users.ids,
|
userIds: store.users.ids,
|
||||||
data: store.channels.byId[cid],
|
data: store.channels.byId[cid],
|
||||||
messageData: store.message || {}
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -77,9 +52,7 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
const { name, description, is_public, members = [], owner } = data;
|
const { name, description, is_public, members = [], owner } = data;
|
||||||
const memberIds = is_public ? userIds : members.slice(0).sort((n) => (n == owner ? -1 : 0));
|
const memberIds = is_public ? userIds : members.slice(0).sort((n) => (n == owner ? -1 : 0));
|
||||||
const addVisible = loginUser?.is_admin || owner == loginUser?.uid;
|
const addVisible = loginUser?.is_admin || owner == loginUser?.uid;
|
||||||
const readIndex = footprint.readChannels[cid];
|
|
||||||
const pinCount = data?.pinned_messages?.length || 0;
|
const pinCount = data?.pinned_messages?.length || 0;
|
||||||
const feeds = [...msgIds, ...appends];
|
|
||||||
const toolClass = `relative cursor-pointer`;
|
const toolClass = `relative cursor-pointer`;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -142,40 +115,7 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
users={
|
users={
|
||||||
<Members uids={memberIds} addVisible={addVisible} cid={cid} ownerId={owner} membersVisible={membersVisible} />
|
<Members uids={memberIds} addVisible={addVisible} cid={cid} ownerId={owner} membersVisible={membersVisible} />
|
||||||
}
|
}
|
||||||
>
|
/>
|
||||||
<>
|
|
||||||
{hasMore ? (
|
|
||||||
<LoadMore pullUp={pullUp} pulling={pulling} />
|
|
||||||
) : (
|
|
||||||
<div className="pt-14 px-1 md:px-0 flex flex-col items-start gap-2">
|
|
||||||
<h2 className="font-bold text-4xl dark:text-white">{t("welcome_channel", { name })}</h2>
|
|
||||||
<p className="text-gray-600 dark:text-gray-300">{t("welcome_desc", { name })} </p>
|
|
||||||
{loginUser?.is_admin && (
|
|
||||||
<NavLink to={`/setting/channel/${cid}?f=${pathname}`} className="flex items-center gap-1 bg-clip-text text-fill-transparent bg-gradient-to-r from-blue-500 to-primary-400 ">
|
|
||||||
<EditIcon className="w-4 h-4 fill-blue-500" />
|
|
||||||
{t("edit_channel")}
|
|
||||||
</NavLink>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{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 == loginUser?.uid || mid <= readIndex;
|
|
||||||
return renderMessageFragment({
|
|
||||||
selectMode: !!selects,
|
|
||||||
updateReadIndex: updateReadDebounced,
|
|
||||||
read,
|
|
||||||
prev,
|
|
||||||
curr,
|
|
||||||
contextId: cid,
|
|
||||||
context: "channel"
|
|
||||||
});
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
</Layout>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { useDebounce } from "rooks";
|
|
||||||
import Tippy from "@tippyjs/react";
|
import Tippy from "@tippyjs/react";
|
||||||
import FavList from "../FavList";
|
import FavList from "../FavList";
|
||||||
import Tooltip from "../../../common/component/Tooltip";
|
import Tooltip from "../../../common/component/Tooltip";
|
||||||
import FavIcon from "../../../assets/icons/bookmark.svg";
|
import FavIcon from "../../../assets/icons/bookmark.svg";
|
||||||
import { useReadMessageMutation } from "../../../app/services/message";
|
|
||||||
import User from "../../../common/component/User";
|
import User from "../../../common/component/User";
|
||||||
import Layout from "../Layout";
|
import Layout from "../Layout";
|
||||||
import LoadMore from "../LoadMore";
|
|
||||||
import { renderMessageFragment } from "../utils";
|
|
||||||
import useMessageFeed from "../../../common/hook/useMessageFeed";
|
|
||||||
import { useAppSelector } from "../../../app/store";
|
import { useAppSelector } from "../../../app/store";
|
||||||
import GoBackNav from "../../../common/component/GoBackNav";
|
import GoBackNav from "../../../common/component/GoBackNav";
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -17,30 +12,12 @@ type Props = {
|
|||||||
dropFiles?: File[];
|
dropFiles?: File[];
|
||||||
};
|
};
|
||||||
const DMChat: FC<Props> = ({ uid = 0, dropFiles }) => {
|
const DMChat: FC<Props> = ({ uid = 0, dropFiles }) => {
|
||||||
const {
|
const { currUser } = useAppSelector((store) => {
|
||||||
pulling,
|
|
||||||
list: msgIds,
|
|
||||||
appends,
|
|
||||||
hasMore,
|
|
||||||
pullUp
|
|
||||||
} = useMessageFeed({
|
|
||||||
context: "user",
|
|
||||||
id: uid
|
|
||||||
});
|
|
||||||
const [updateReadIndex] = useReadMessageMutation();
|
|
||||||
const updateReadDebounced = useDebounce(updateReadIndex, 300);
|
|
||||||
const { currUser, messageData, footprint, loginUid, selects } = useAppSelector((store) => {
|
|
||||||
return {
|
return {
|
||||||
selects: store.ui.selectMessages[`user_${uid}`],
|
|
||||||
loginUid: store.authData.user?.uid,
|
|
||||||
footprint: store.footprint,
|
|
||||||
currUser: store.users.byId[uid],
|
currUser: store.users.byId[uid],
|
||||||
messageData: store.message
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
if (!currUser) return null;
|
if (!currUser) return null;
|
||||||
const readIndex = footprint.readUsers[uid];
|
|
||||||
const feeds = [...msgIds, ...appends];
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
to={uid}
|
to={uid}
|
||||||
@@ -70,25 +47,7 @@ const DMChat: FC<Props> = ({ uid = 0, dropFiles }) => {
|
|||||||
<User interactive={false} uid={currUser.uid} />
|
<User interactive={false} uid={currUser.uid} />
|
||||||
</header>
|
</header>
|
||||||
}
|
}
|
||||||
>
|
/>
|
||||||
<>
|
|
||||||
{hasMore ? <LoadMore pullUp={pullUp} pulling={pulling} /> : null}
|
|
||||||
{[...feeds].map((mid, idx) => {
|
|
||||||
const curr = messageData[mid];
|
|
||||||
const prev = idx == 0 ? null : messageData[feeds[idx - 1]];
|
|
||||||
const read = curr?.from_uid == loginUid || mid <= readIndex;
|
|
||||||
return renderMessageFragment({
|
|
||||||
selectMode: !!selects,
|
|
||||||
updateReadIndex: updateReadDebounced,
|
|
||||||
read,
|
|
||||||
prev,
|
|
||||||
curr,
|
|
||||||
contextId: uid,
|
|
||||||
context: "user"
|
|
||||||
});
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
</Layout>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default DMChat;
|
export default DMChat;
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { NavLink, useLocation } from 'react-router-dom';
|
||||||
|
import { useDebounce } from 'rooks';
|
||||||
|
import { ViewportList } from 'react-viewport-list';
|
||||||
|
|
||||||
|
import { useReadMessageMutation } from '../../../app/services/message';
|
||||||
|
import { useAppSelector } from '../../../app/store';
|
||||||
|
import EditIcon from "../../../assets/icons/edit.svg";
|
||||||
|
import { renderMessageFragment } from '../utils';
|
||||||
|
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
context: "user" | "channel",
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessageFeed = ({ context, id }: Props) => {
|
||||||
|
// const listRef = useRef<ViewportListRef | null>(null);
|
||||||
|
const ref = useRef<HTMLDivElement | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
const { t } = useTranslation("chat");
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const [updateReadIndex] = useReadMessageMutation();
|
||||||
|
const updateReadDebounced = useDebounce(updateReadIndex, 300);
|
||||||
|
const {
|
||||||
|
mids,
|
||||||
|
selects,
|
||||||
|
data,
|
||||||
|
messageData,
|
||||||
|
loginUser,
|
||||||
|
footprint
|
||||||
|
} = useAppSelector((store) => {
|
||||||
|
return {
|
||||||
|
mids: context == "user" ? store.userMessage.byId[id] : store.channelMessage[id],
|
||||||
|
selects: store.ui.selectMessages[`${context}_${id}`],
|
||||||
|
footprint: store.footprint,
|
||||||
|
loginUser: store.authData.user,
|
||||||
|
data: context == "channel" ? store.channels.byId[id] : undefined,
|
||||||
|
messageData: store.message || {}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// useEffect(() => {
|
||||||
|
// // 滚动到记忆位置
|
||||||
|
// if (listRef && listRef.current) {
|
||||||
|
// listRef.current.scrollToIndex({ index: 5 });
|
||||||
|
// }
|
||||||
|
// }, [context, id]);
|
||||||
|
|
||||||
|
const readIndex = context == "channel" ? footprint.readChannels[id] : Number.POSITIVE_INFINITY;
|
||||||
|
const isEmptyList = !mids || mids.length == 0;
|
||||||
|
return (
|
||||||
|
<article ref={ref} id={`VOCECHAT_FEED_${context}_${id}`} className="w-full h-full px-1 md:px-4 py-4.5 overflow-x-hidden overflow-y-scroll will-change-contents">
|
||||||
|
{context == "channel" && <div className="pt-14 px-1 md:px-0 flex flex-col items-start gap-2">
|
||||||
|
<h2 className="font-bold text-4xl dark:text-white">{t("welcome_channel", { name: data?.name })}</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-300">{t("welcome_desc", { name: data?.name })} </p>
|
||||||
|
{loginUser?.is_admin && (
|
||||||
|
<NavLink to={`/setting/channel/${id}/overview?f=${pathname}`} className="flex items-center gap-1 bg-clip-text text-fill-transparent bg-gradient-to-r from-blue-500 to-primary-400 ">
|
||||||
|
<EditIcon className="w-4 h-4 fill-blue-500" />
|
||||||
|
{t("edit_channel")}
|
||||||
|
</NavLink>
|
||||||
|
)}
|
||||||
|
</div>}
|
||||||
|
{isEmptyList ? null : <ViewportList
|
||||||
|
overscan={10}
|
||||||
|
// itemSize={100}
|
||||||
|
initialPrerender={50}
|
||||||
|
scrollThreshold={2000}
|
||||||
|
// ref={listRef}
|
||||||
|
viewportRef={ref}
|
||||||
|
items={mids}
|
||||||
|
>
|
||||||
|
{((mid, idx) => {
|
||||||
|
const curr = messageData[mid];
|
||||||
|
if (!curr) return null;
|
||||||
|
const isFirst = idx == 0;
|
||||||
|
const prev = isFirst ? null : messageData[mids[idx - 1]];
|
||||||
|
const read = curr?.from_uid == loginUser?.uid || mid <= readIndex;
|
||||||
|
return renderMessageFragment({
|
||||||
|
selectMode: !!selects,
|
||||||
|
updateReadIndex: updateReadDebounced,
|
||||||
|
read,
|
||||||
|
prev,
|
||||||
|
curr,
|
||||||
|
contextId: id,
|
||||||
|
context
|
||||||
|
});
|
||||||
|
})}
|
||||||
|
</ViewportList>}
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MessageFeed;
|
||||||
@@ -15,10 +15,10 @@ import LicenseUpgradeTip from "./LicenseOutdatedTip";
|
|||||||
import DnDTip from "./DnDTip";
|
import DnDTip from "./DnDTip";
|
||||||
import IconWarning from '../../../assets/icons/warning.svg';
|
import IconWarning from '../../../assets/icons/warning.svg';
|
||||||
import ImagePreview from "../../../common/component/ImagePreview";
|
import ImagePreview from "../../../common/component/ImagePreview";
|
||||||
|
import MessageFeed from "./MessageFeed";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
children: ReactElement;
|
|
||||||
header: ReactElement;
|
header: ReactElement;
|
||||||
aside?: ReactElement | null;
|
aside?: ReactElement | null;
|
||||||
users?: ReactElement | null;
|
users?: ReactElement | null;
|
||||||
@@ -29,7 +29,6 @@ interface Props {
|
|||||||
|
|
||||||
const Layout: FC<Props> = ({
|
const Layout: FC<Props> = ({
|
||||||
readonly = false,
|
readonly = false,
|
||||||
children,
|
|
||||||
header,
|
header,
|
||||||
aside = null,
|
aside = null,
|
||||||
users = null,
|
users = null,
|
||||||
@@ -97,9 +96,7 @@ const Layout: FC<Props> = ({
|
|||||||
<div className="w-full h-full flex items-start justify-between relative" >
|
<div className="w-full h-full flex items-start justify-between relative" >
|
||||||
<div className="rounded-br-2xl flex flex-col absolute bottom-0 w-full h-full" ref={messagesContainer}>
|
<div className="rounded-br-2xl flex flex-col absolute bottom-0 w-full h-full" ref={messagesContainer}>
|
||||||
{/* 消息流 */}
|
{/* 消息流 */}
|
||||||
<article id={`VOCECHAT_FEED_${context}_${to}`} className="w-full h-full px-1 md:px-4 py-4.5 overflow-x-hidden overflow-y-scroll">
|
<MessageFeed context={context} id={to} />
|
||||||
{children}
|
|
||||||
</article>
|
|
||||||
{/* 发送框 */}
|
{/* 发送框 */}
|
||||||
<div className={`px-2 py-0 md:p-4 ${selects ? "selecting" : ""}`}>
|
<div className={`px-2 py-0 md:p-4 ${selects ? "selecting" : ""}`}>
|
||||||
{readonly ? (
|
{readonly ? (
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import { FC } from "react";
|
import { FC, useRef } from "react";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import { ViewportList } from "react-viewport-list";
|
||||||
import Session from "./Session";
|
import Session from "./Session";
|
||||||
import DeleteChannelConfirmModal from "../../settingChannel/DeleteConfirmModal";
|
import DeleteChannelConfirmModal from "../../settingChannel/DeleteConfirmModal";
|
||||||
import InviteModal from "../../../common/component/InviteModal";
|
import InviteModal from "../../../common/component/InviteModal";
|
||||||
import { useAppSelector } from "../../../app/store";
|
import { useAppSelector } from "../../../app/store";
|
||||||
export interface ChatSession {
|
export interface ChatSession {
|
||||||
key: string;
|
|
||||||
type: "user" | "channel";
|
type: "user" | "channel";
|
||||||
id: number;
|
id: number;
|
||||||
mid?: number;
|
mid: number;
|
||||||
unread?: number;
|
unread: number;
|
||||||
}
|
}
|
||||||
type Props = {
|
type Props = {
|
||||||
tempSession?: ChatSession;
|
tempSession?: ChatSession;
|
||||||
};
|
};
|
||||||
const SessionList: FC<Props> = ({ tempSession }) => {
|
const SessionList: FC<Props> = ({ tempSession }) => {
|
||||||
|
const ref = useRef<HTMLUListElement | null>(null);
|
||||||
const [deleteId, setDeleteId] = useState<number>();
|
const [deleteId, setDeleteId] = useState<number>();
|
||||||
const [inviteChannelId, setInviteChannelId] = useState<number>();
|
const [inviteChannelId, setInviteChannelId] = useState<number>();
|
||||||
const [sessions, setSessions] = useState<ChatSession[]>([]);
|
const [sessions, setSessions] = useState<ChatSession[]>([]);
|
||||||
@@ -35,27 +36,31 @@ const SessionList: FC<Props> = ({ tempSession }) => {
|
|||||||
const cSessions = channelIDs.map((id) => {
|
const cSessions = channelIDs.map((id) => {
|
||||||
const mids = channelMessage[id];
|
const mids = channelMessage[id];
|
||||||
if (!mids || mids.length == 0) {
|
if (!mids || mids.length == 0) {
|
||||||
return { key: `channel_${id}`, unreads: 0, id, type: "channel" };
|
return { unreads: 0, id, type: "channel" };
|
||||||
}
|
}
|
||||||
// 先转换成数字,再排序
|
// 先转换成数字,再排序
|
||||||
const mid = [...mids].sort((a, b) => +a - +b).pop();
|
const mid = [...mids].sort((a, b) => +a - +b).pop();
|
||||||
return { key: `channel_${id}`, id, mid, type: "channel" };
|
return { id, mid, type: "channel" };
|
||||||
});
|
});
|
||||||
const uSessions = DMs.map((id) => {
|
const uSessions = DMs.map((id) => {
|
||||||
|
console.log("adddd", id);
|
||||||
const mids = userMessage[id];
|
const mids = userMessage[id];
|
||||||
if (!mids || mids.length == 0) {
|
if (!mids || mids.length == 0) {
|
||||||
return { key: `user_${id}`, unreads: 0, id, type: "user" };
|
return { unreads: 0, id, type: "user" };
|
||||||
}
|
}
|
||||||
// 先转换成数字,再排序
|
// 先转换成数字,再排序
|
||||||
const mid = [...mids].sort((a, b) => +a - +b).pop();
|
const mid = [...mids].sort((a, b) => +a - +b).pop();
|
||||||
return { key: `user_${id}`, type: "user", id, mid };
|
return { type: "user", id, mid };
|
||||||
});
|
});
|
||||||
const temps = [...(cSessions as ChatSession[]), ...(uSessions as ChatSession[])].sort((a, b) => {
|
const temps = [...(cSessions as ChatSession[]), ...(uSessions as ChatSession[])].sort((a, b) => {
|
||||||
const { mid: aMid = 0 } = a;
|
const { mid: aMid = 0 } = a;
|
||||||
const { mid: bMid = 0 } = b;
|
const { mid: bMid = 0 } = b;
|
||||||
return bMid - aMid;
|
return bMid - aMid;
|
||||||
});
|
});
|
||||||
setSessions(tempSession ? [tempSession, ...temps] : temps);
|
console.log("before qqqq", temps);
|
||||||
|
const newSessions = tempSession ? [tempSession, ...temps] : temps;
|
||||||
|
console.log("qqqq", newSessions);
|
||||||
|
setSessions(newSessions);
|
||||||
}, [
|
}, [
|
||||||
channelIDs,
|
channelIDs,
|
||||||
DMs,
|
DMs,
|
||||||
@@ -66,11 +71,19 @@ const SessionList: FC<Props> = ({ tempSession }) => {
|
|||||||
userMessage,
|
userMessage,
|
||||||
tempSession
|
tempSession
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ul className="flex flex-col gap-0.5 p-2 overflow-auto">
|
<ul ref={ref} className="flex flex-col gap-0.5 p-2 overflow-auto">
|
||||||
{sessions.map((s) => {
|
<ViewportList
|
||||||
const { key, type, id, mid = 0 } = s;
|
initialPrerender={10}
|
||||||
|
viewportRef={ref}
|
||||||
|
items={sessions}
|
||||||
|
>
|
||||||
|
{(s) => {
|
||||||
|
console.log("ssss", sessions);
|
||||||
|
const { type, id, mid = 0 } = s;
|
||||||
|
const key = `${type}_${id}`;
|
||||||
return (
|
return (
|
||||||
<Session
|
<Session
|
||||||
key={key}
|
key={key}
|
||||||
@@ -81,7 +94,8 @@ const SessionList: FC<Props> = ({ tempSession }) => {
|
|||||||
setDeleteChannelId={setDeleteId}
|
setDeleteChannelId={setDeleteId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
}}
|
||||||
|
</ViewportList>
|
||||||
</ul>
|
</ul>
|
||||||
{!!deleteId && (
|
{!!deleteId && (
|
||||||
<DeleteChannelConfirmModal
|
<DeleteChannelConfirmModal
|
||||||
|
|||||||
@@ -36,15 +36,15 @@ function ChatPage() {
|
|||||||
const toggleSessionList = () => {
|
const toggleSessionList = () => {
|
||||||
setSessionListVisible(prev => !prev);
|
setSessionListVisible(prev => !prev);
|
||||||
};
|
};
|
||||||
const tmpSession =
|
const tmpSession = user_id == 0 ? undefined :
|
||||||
sessionUids.findIndex((i) => i == user_id) > -1
|
sessionUids.findIndex((i) => i == +user_id) == -1
|
||||||
? undefined
|
? {
|
||||||
: {
|
mid: 0,
|
||||||
key: `user_${user_id}`,
|
unread: 0,
|
||||||
unreads: 0,
|
|
||||||
id: +user_id,
|
id: +user_id,
|
||||||
type: "user" as "user" | "channel"
|
type: "user" as const
|
||||||
};
|
}
|
||||||
|
: undefined;
|
||||||
// console.log("temp uid", tmpUid);
|
// console.log("temp uid", tmpUid);
|
||||||
const placeholderVisible = channel_id == 0 && user_id == 0;
|
const placeholderVisible = channel_id == 0 && user_id == 0;
|
||||||
const isMainPath = isHomePath || isChatHomePath;
|
const isMainPath = isHomePath || isChatHomePath;
|
||||||
|
|||||||
Reference in New Issue
Block a user