refactor: lots updates
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import Modal from "../../../common/component/Modal";
|
||||
import Button from "../../../common/component/styled/Button";
|
||||
import Contact from "../../../common/component/Contact";
|
||||
import StyledCheckbox from "../../../common/component/styled/Checkbox";
|
||||
import { useAddMembersMutation } from "../../../app/services/channel";
|
||||
import closeIcon from "../../../assets/icons/close.svg?url";
|
||||
import toast from "react-hot-toast";
|
||||
// import useFilteredUsers from "../../../common/hook/useFilteredUsers";
|
||||
const Styled = styled.div`
|
||||
padding: 16px;
|
||||
filter: drop-shadow(0px 25px 50px rgba(31, 41, 55, 0.25));
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
min-width: 410px;
|
||||
> .head {
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 28px;
|
||||
color: #374151;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.close {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
> .filter {
|
||||
width: 376px;
|
||||
height: 40px;
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
border-radius: 4px;
|
||||
padding: 6px 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
.selects {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
|
||||
white-space: nowrap;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0; /* Remove scrollbar space */
|
||||
height: 0; /* Remove scrollbar space */
|
||||
background: transparent; /* Optional: just make scrollbar invisible */
|
||||
}
|
||||
.select {
|
||||
padding: 4px 6px;
|
||||
background: #52edff;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
.close {
|
||||
cursor: pointer;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* .input{
|
||||
background: none;
|
||||
padding: ;
|
||||
} */
|
||||
}
|
||||
.users {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* height: 260px; */
|
||||
padding-bottom: 20px;
|
||||
max-height: 364px;
|
||||
overflow: scroll;
|
||||
.user {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
/* margin: 0 4px; */
|
||||
width: -webkit-fill-available;
|
||||
border-radius: 8px;
|
||||
&:hover {
|
||||
background: rgba(116, 127, 141, 0.1);
|
||||
}
|
||||
> div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .btn {
|
||||
width: 100%;
|
||||
margin-top: 16px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function AddMemberModal({ uids = [], cid = null, closeModal }) {
|
||||
const [
|
||||
addMembers,
|
||||
{ isLoading: isAdding, isSuccess },
|
||||
] = useAddMembersMutation();
|
||||
const [selects, setSelects] = useState([]);
|
||||
const { channel, contactIds, contactData } = useSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[cid],
|
||||
contactIds: store.contacts.ids,
|
||||
contactData: store.contacts.byId,
|
||||
};
|
||||
});
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
toast.success("Add members successfully!");
|
||||
closeModal();
|
||||
}
|
||||
}, [isSuccess]);
|
||||
|
||||
const handleAddMembers = () => {
|
||||
addMembers({ id: cid, members: selects });
|
||||
};
|
||||
// const { input, updateInput, contacts } = useFilteredUsers();
|
||||
const toggleCheckMember = ({ currentTarget }) => {
|
||||
const { uid } = currentTarget.dataset;
|
||||
if (selects.includes(+uid)) {
|
||||
setSelects((prevs) => {
|
||||
return prevs.filter((id) => id != uid);
|
||||
});
|
||||
} else {
|
||||
setSelects([...selects, +uid]);
|
||||
}
|
||||
};
|
||||
// const handleFilterInput = (evt) => {
|
||||
// updateInput(evt.target.value);
|
||||
// };
|
||||
if (!channel) return null;
|
||||
console.log("selects", selects);
|
||||
return (
|
||||
<Modal>
|
||||
<Styled>
|
||||
<div className="head">
|
||||
Add friends to #{channel.name}{" "}
|
||||
<img onClick={closeModal} className="close" src={closeIcon} />
|
||||
</div>
|
||||
<div className="filter">
|
||||
<ul className="selects">
|
||||
{selects.map((uid) => {
|
||||
return (
|
||||
<li className="select" key={uid}>
|
||||
{contactData[uid].name}
|
||||
<img
|
||||
data-uid={uid}
|
||||
onClick={toggleCheckMember}
|
||||
className="close"
|
||||
src={closeIcon}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
{/* <input
|
||||
type="text"
|
||||
className="input"
|
||||
value={input}
|
||||
onChange={handleFilterInput}
|
||||
/> */}
|
||||
</div>
|
||||
<ul className="users">
|
||||
{contactIds.map((uid) => {
|
||||
const added = uids.includes(uid);
|
||||
return (
|
||||
<li
|
||||
key={uid}
|
||||
data-uid={uid}
|
||||
className="user"
|
||||
onClick={added ? null : toggleCheckMember}
|
||||
>
|
||||
<StyledCheckbox
|
||||
disabled={added}
|
||||
readOnly
|
||||
checked={added || selects.includes(uid)}
|
||||
name="cb"
|
||||
id="cb"
|
||||
/>
|
||||
<Contact uid={uid} interactive={false} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<Button
|
||||
disabled={selects.length == 0 || isAdding}
|
||||
className="btn main"
|
||||
onClick={handleAddMembers}
|
||||
>
|
||||
{isAdding ? `Adding` : "Add"} to #{channel.name}
|
||||
</Button>
|
||||
</Styled>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +1,48 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
// import dayjs from "dayjs";
|
||||
import { useSelector } from "react-redux";
|
||||
import useChatScroll from "../../../common/hook/useChatScroll";
|
||||
|
||||
import Message from "../../../common/component/Message";
|
||||
import ChannelIcon from "../../../common/component/ChannelIcon";
|
||||
import Send from "../../../common/component/Send";
|
||||
// import { readMessage } from "../../../app/slices/message";
|
||||
import Contact from "../../../common/component/Contact";
|
||||
import Layout from "../Layout";
|
||||
import { renderMessageFragment } from "../utils";
|
||||
import alertIcon from "../../../assets/icons/alert.svg?url";
|
||||
import peopleIcon from "../../../assets/icons/people.svg?url";
|
||||
import pinIcon from "../../../assets/icons/pin.svg?url";
|
||||
import addIcon from "../../../assets/icons/add.svg?url";
|
||||
import {
|
||||
StyledNotification,
|
||||
// StyledNotification,
|
||||
StyledContacts,
|
||||
StyledChannelChat,
|
||||
StyledHeader,
|
||||
} from "./styled";
|
||||
import AddMemberModal from "./AddMemberModal";
|
||||
|
||||
export default function ChannelChat({ cid = "", dropFiles = [] }) {
|
||||
// const containerRef = useRef(null);
|
||||
const [membersVisible, setMembersVisible] = useState(true);
|
||||
const [addMemberModalVisible, setAddMemberModalVisible] = useState(false);
|
||||
const [dragFiles, setDragFiles] = useState([]);
|
||||
// const dispatch = useDispatch();
|
||||
const { msgIds, userIds, data } = useSelector((store) => {
|
||||
const { msgIds, userIds, data, messageData } = useSelector((store) => {
|
||||
return {
|
||||
msgIds: store.channelMessage[cid] || [],
|
||||
userIds: store.contacts.ids,
|
||||
data: store.channels.byId[cid],
|
||||
data: store.channels.byId[cid] || {},
|
||||
messageData: store.message || {},
|
||||
};
|
||||
});
|
||||
const ref = useChatScroll(msgIds);
|
||||
// const handleClearUnreads = () => {
|
||||
// dispatch(readMessage(msgIds));
|
||||
// };
|
||||
const toggleMembersVisible = () => {
|
||||
setMembersVisible((prev) => !prev);
|
||||
};
|
||||
const toggleAddVisible = () => {
|
||||
setAddMemberModalVisible((prev) => !prev);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (dropFiles.length) {
|
||||
setDragFiles(dropFiles);
|
||||
@@ -41,65 +52,84 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
|
||||
const memberIds = members.length == 0 ? userIds : members;
|
||||
console.log("channel message list", msgIds);
|
||||
return (
|
||||
<Layout
|
||||
setDragFiles={setDragFiles}
|
||||
// ref={containerRef}
|
||||
header={
|
||||
<StyledHeader>
|
||||
<div className="txt">
|
||||
<ChannelIcon personal={!is_public} />
|
||||
<span className="title">{name}</span>
|
||||
<span className="desc">{description}</span>
|
||||
<>
|
||||
{addMemberModalVisible && (
|
||||
<AddMemberModal
|
||||
cid={cid}
|
||||
uids={members}
|
||||
closeModal={toggleAddVisible}
|
||||
/>
|
||||
)}
|
||||
<Layout
|
||||
setDragFiles={setDragFiles}
|
||||
// ref={containerRef}
|
||||
header={
|
||||
<StyledHeader>
|
||||
<div className="txt">
|
||||
<ChannelIcon personal={!is_public} />
|
||||
<span className="title">{name}</span>
|
||||
<span className="desc">{description}</span>
|
||||
</div>
|
||||
<ul className="opts">
|
||||
<li className="opt">
|
||||
<img src={alertIcon} alt="opt icon" />
|
||||
</li>
|
||||
<li className="opt">
|
||||
<img src={pinIcon} alt="opt icon" />
|
||||
</li>
|
||||
<li className="opt" onClick={toggleMembersVisible}>
|
||||
<img src={peopleIcon} alt="opt icon" />
|
||||
</li>
|
||||
</ul>
|
||||
</StyledHeader>
|
||||
}
|
||||
contacts={
|
||||
membersVisible ? (
|
||||
<>
|
||||
<StyledContacts>
|
||||
{!is_public && (
|
||||
<div className="add" onClick={toggleAddVisible}>
|
||||
<img className="icon" src={addIcon} />
|
||||
<div className="txt">Add members</div>
|
||||
</div>
|
||||
)}
|
||||
{memberIds.map((uid) => {
|
||||
return <Contact key={uid} uid={uid} popover />;
|
||||
})}
|
||||
</StyledContacts>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
>
|
||||
<StyledChannelChat>
|
||||
<div className="wrapper" ref={ref}>
|
||||
<div className="info">
|
||||
<h2 className="title">Welcome to #{name} !</h2>
|
||||
<p className="desc">This is the start of the #{name} channel. </p>
|
||||
{/* <button className="edit">Edit Channel</button> */}
|
||||
</div>
|
||||
<div className="chat">
|
||||
{[...msgIds]
|
||||
.sort((a, b) => {
|
||||
return Number(a) - Number(b);
|
||||
})
|
||||
.map((mid, idx) => {
|
||||
const prev = idx == 0 ? null : messageData[msgIds[idx - 1]];
|
||||
const curr = messageData[mid];
|
||||
return renderMessageFragment({
|
||||
prev,
|
||||
curr,
|
||||
contextId: cid,
|
||||
context: "channel",
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<ul className="opts">
|
||||
<li className="opt">
|
||||
<img
|
||||
src="https://static.nicegoodthings.com/project/rustchat/icon.alert.svg"
|
||||
alt="opt icon"
|
||||
/>
|
||||
</li>
|
||||
<li className="opt">
|
||||
<img
|
||||
src="https://static.nicegoodthings.com/project/rustchat/icon.pin.svg"
|
||||
alt="opt icon"
|
||||
/>
|
||||
</li>
|
||||
<li className="opt">
|
||||
<img
|
||||
src="https://static.nicegoodthings.com/project/rustchat/icon.people.svg"
|
||||
alt="opt icon"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</StyledHeader>
|
||||
}
|
||||
contacts={
|
||||
<StyledContacts>
|
||||
{memberIds.map((uid) => {
|
||||
return <Contact key={uid} uid={uid} popover />;
|
||||
})}
|
||||
</StyledContacts>
|
||||
}
|
||||
>
|
||||
<StyledChannelChat>
|
||||
<div className="wrapper" ref={ref}>
|
||||
<div className="info">
|
||||
<h2 className="title">Welcome to #{name} !</h2>
|
||||
<p className="desc">This is the start of the #{name} channel. </p>
|
||||
{/* <button className="edit">Edit Channel</button> */}
|
||||
</div>
|
||||
<div className="chat">
|
||||
{msgIds.map((mid, idx) => {
|
||||
// if (!msg) return null;
|
||||
return <Message contextId={cid} mid={mid} key={idx} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Send dragFiles={dragFiles} id={cid} type="channel" name={name} />
|
||||
<div className="placeholder"></div>
|
||||
</StyledChannelChat>
|
||||
{/* {unreads != 0 && (
|
||||
<Send dragFiles={dragFiles} id={cid} type="channel" name={name} />
|
||||
<div className="placeholder"></div>
|
||||
</StyledChannelChat>
|
||||
{/* {unreads != 0 && (
|
||||
<StyledNotification>
|
||||
<div className="content">
|
||||
{unreads} new messages
|
||||
@@ -112,6 +142,7 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
|
||||
</button>
|
||||
</StyledNotification>
|
||||
)} */}
|
||||
</Layout>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,29 @@ export const StyledContacts = styled.div`
|
||||
overflow-y: scroll;
|
||||
background: #f5f6f7;
|
||||
padding: 8px;
|
||||
> .add {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 4px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
user-select: none;
|
||||
&:hover {
|
||||
background: rgba(116, 127, 141, 0.1);
|
||||
}
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.txt {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #52525b;
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const StyledChannelChat = styled.article`
|
||||
position: relative;
|
||||
|
||||
@@ -7,7 +7,7 @@ import useContextMenu from "../../common/hook/useContextMenu";
|
||||
import ContextMenu from "../../common/component/ContextMenu";
|
||||
import { toggleChannelSetting } from "../../app/slices/ui";
|
||||
import ChannelIcon from "../../common/component/ChannelIcon";
|
||||
import getUnreadCount from "./getUnreadCount";
|
||||
import { getUnreadCount } from "./utils";
|
||||
const NavItem = ({ id, setFiles, contextMenuEventHandler }) => {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
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 Message from "../../../common/component/Message";
|
||||
import Send from "../../../common/component/Send";
|
||||
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 = [] }) {
|
||||
console.log("dm files", dropFiles);
|
||||
const [dragFiles, setDragFiles] = useState([]);
|
||||
// const [mids, setMids] = useState([]);
|
||||
const { msgIds, currUser } = useSelector((store) => {
|
||||
const { msgIds, currUser, messageData, readUsers } = useSelector((store) => {
|
||||
return {
|
||||
currUser: store.contacts.byId[uid],
|
||||
msgIds: store.userMessage.byId[uid] || [],
|
||||
messageData: store.message,
|
||||
readUsers: store.footprint.readUser || {},
|
||||
};
|
||||
});
|
||||
const ref = useChatScroll(msgIds);
|
||||
@@ -30,6 +33,7 @@ export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
|
||||
if (!currUser) return null;
|
||||
// console.log("user msgs", msgs);
|
||||
const readIndex = readUsers[uid] || 0;
|
||||
return (
|
||||
<Layout
|
||||
setDragFiles={setDragFiles}
|
||||
@@ -38,28 +42,13 @@ export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
<Contact interactive={false} uid={currUser.uid} />
|
||||
<ul className="opts">
|
||||
<li className="opt">
|
||||
<img
|
||||
src="https://static.nicegoodthings.com/project/rustchat/icon.call.svg"
|
||||
alt="opt icon"
|
||||
/>
|
||||
<img src={callIcon} alt="opt icon" />
|
||||
</li>
|
||||
<li className="opt">
|
||||
<img
|
||||
src="https://static.nicegoodthings.com/project/rustchat/icon.video.svg"
|
||||
alt="opt icon"
|
||||
/>
|
||||
<img src={videoIcon} alt="opt icon" />
|
||||
</li>
|
||||
<li className="opt">
|
||||
<img
|
||||
src="https://static.nicegoodthings.com/project/rustchat/icon.people.add.svg"
|
||||
alt="opt icon"
|
||||
/>
|
||||
</li>
|
||||
<li className="opt">
|
||||
<img
|
||||
src="https://static.nicegoodthings.com/project/rustchat/icon.mark.read.svg"
|
||||
alt="opt icon"
|
||||
/>
|
||||
<img src={addIcon} alt="opt icon" />
|
||||
</li>
|
||||
</ul>
|
||||
</StyledHeader>
|
||||
@@ -67,11 +56,23 @@ export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
>
|
||||
<StyledDMChat>
|
||||
<div className="chat" ref={ref}>
|
||||
{msgIds.map((mid) => {
|
||||
// if (!msg) return null;
|
||||
// console.log("user msg", msg);
|
||||
return <Message mid={mid} key={mid} contextId={uid} />;
|
||||
})}
|
||||
{[...msgIds]
|
||||
.sort((a, b) => {
|
||||
return Number(a) - Number(b);
|
||||
})
|
||||
.map((mid, idx) => {
|
||||
const curr = messageData[mid];
|
||||
const self = curr.from_uid == currUser.uid;
|
||||
const read = self ? true : mid <= readIndex ? true : false;
|
||||
const prev = idx == 0 ? null : messageData[msgIds[idx - 1]];
|
||||
return renderMessageFragment({
|
||||
prev,
|
||||
curr,
|
||||
contextId: uid,
|
||||
read,
|
||||
context: "user",
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
</StyledDMChat>
|
||||
<div className="placeholder"></div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import { useDrop } from "react-dnd";
|
||||
import { NativeTypes } from "react-dnd-html5-backend";
|
||||
import { useSelector } from "react-redux";
|
||||
import { renderPreviewMessage } from "./utils";
|
||||
import Contact from "../../common/component/Contact";
|
||||
dayjs.extend(relativeTime);
|
||||
const NavItem = ({ uid, mid, unreads, setFiles }) => {
|
||||
@@ -48,8 +49,12 @@ const NavItem = ({ uid, mid, unreads, setFiles }) => {
|
||||
</div>
|
||||
|
||||
<div className="down">
|
||||
{currMsg && <div className="msg">{currMsg.content}</div>}
|
||||
{unreads > 0 && <i className="badge">{unreads}</i>}
|
||||
{renderPreviewMessage(currMsg)}
|
||||
{unreads > 0 && (
|
||||
<i className={`badge ${unreads > 99 ? "dot" : ""}`}>
|
||||
{unreads > 99 ? null : unreads}
|
||||
</i>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</NavLink>
|
||||
|
||||
@@ -14,7 +14,7 @@ const StyledWrapper = styled.article`
|
||||
padding: 0 20px;
|
||||
box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.main {
|
||||
> .main {
|
||||
height: calc(100vh - 56px);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// import React from "react";
|
||||
import { VariableSizeList as List } from "react-window";
|
||||
import Message from "../../common/component/Message";
|
||||
|
||||
export default function MessageList({ messages = [] }) {
|
||||
const Row = ({ index, style }) => (
|
||||
<div style={style}>
|
||||
<Message {...messages[index]} />
|
||||
</div>
|
||||
);
|
||||
const getItemSize = (index) =>
|
||||
messages[index].content_type.startsWith("image") ? 150 : 56;
|
||||
return (
|
||||
<List
|
||||
height={window.innerHeight - 56}
|
||||
width={"100%"}
|
||||
itemCount={messages.length}
|
||||
itemSize={getItemSize}
|
||||
// estimatedItemSize={56}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
const getUnreadCount = (mids, messageData) => {
|
||||
if (!mids || !messageData) return 0;
|
||||
let unreads = 0;
|
||||
mids.forEach((id) => {
|
||||
if (!messageData[id].read) {
|
||||
unreads++;
|
||||
}
|
||||
});
|
||||
return unreads;
|
||||
};
|
||||
|
||||
export default getUnreadCount;
|
||||
@@ -16,7 +16,7 @@ import ChannelList from "./ChannelList";
|
||||
import ContactsModal from "../../common/component/ContactsModal";
|
||||
import ChannelModal from "../../common/component/ChannelModal";
|
||||
import DMList from "./DMList";
|
||||
import getUnreadCount from "./getUnreadCount";
|
||||
import { getUnreadCount } from "./utils";
|
||||
|
||||
export default function ChatPage() {
|
||||
const [channelDropFiles, setChannelDropFiles] = useState([]);
|
||||
@@ -37,10 +37,6 @@ export default function ChatPage() {
|
||||
const toggleChannelModalVisible = () => {
|
||||
setChannelModalVisible((prev) => !prev);
|
||||
};
|
||||
// const getUnreadCount = (gid) => {
|
||||
// return Object.values(ChannelMsgData[gid] || {}).filter((m) => m.read)
|
||||
// .length;
|
||||
// };
|
||||
const handleToggleExpand = (evt) => {
|
||||
const { currentTarget } = evt;
|
||||
const listEle = currentTarget.parentElement.parentElement;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import React from "react";
|
||||
import dayjs from "dayjs";
|
||||
import { ContentTypes } from "../../app/config";
|
||||
import Divider from "../../common/component/Divider";
|
||||
import Message from "../../common/component/Message";
|
||||
export const getUnreadCount = (mids, messageData) => {
|
||||
if (!mids || !messageData) return 0;
|
||||
let unreads = 0;
|
||||
mids.forEach((id) => {
|
||||
if (messageData[id] && !messageData[id].read) {
|
||||
unreads++;
|
||||
}
|
||||
});
|
||||
return unreads;
|
||||
};
|
||||
export const renderPreviewMessage = (message = null) => {
|
||||
if (!message) return null;
|
||||
const { content_type, content } = message;
|
||||
let res = null;
|
||||
switch (content_type) {
|
||||
case ContentTypes.text:
|
||||
{
|
||||
res = <div className="msg">{content}</div>;
|
||||
}
|
||||
break;
|
||||
case ContentTypes.imageJPG:
|
||||
case ContentTypes.image:
|
||||
{
|
||||
res = <div className="msg">[image]</div>;
|
||||
}
|
||||
|
||||
break;
|
||||
case ContentTypes.markdown:
|
||||
{
|
||||
res = <div className="msg">[markdown]</div>;
|
||||
}
|
||||
|
||||
break;
|
||||
case ContentTypes.file:
|
||||
{
|
||||
res = <div className="msg">[file]</div>;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
export const renderMessageFragment = ({
|
||||
prev = null,
|
||||
curr = null,
|
||||
contextId = 0,
|
||||
read = true,
|
||||
context = "user",
|
||||
}) => {
|
||||
if (!curr) return null;
|
||||
let { created_at, mid } = curr;
|
||||
let divider = null;
|
||||
let time = dayjs(created_at).format("YYYY/MM/DD");
|
||||
if (!prev) {
|
||||
divider = time;
|
||||
} else {
|
||||
let { created_at: prev_created_at } = prev;
|
||||
if (!dayjs(prev_created_at).isSame(created_at, "day")) {
|
||||
divider = time;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment key={mid}>
|
||||
{divider && <Divider content={divider}></Divider>}
|
||||
<Message
|
||||
read={read}
|
||||
context={context}
|
||||
mid={mid}
|
||||
key={mid}
|
||||
contextId={contextId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default getUnreadCount;
|
||||
@@ -1,25 +1,29 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import initCache, { useRehydrate } from "../../app/cache";
|
||||
import { useLazyGetContactsQuery } from "../../app/services/contact";
|
||||
import { useLazyInitStreamingQuery } from "../../app/services/streaming";
|
||||
// import { useLazyInitStreamingQuery } from "../../app/services/streaming";
|
||||
import { resetAuthData, setUid } from "../../app/slices/auth.data";
|
||||
import { fullfillContacts } from "../../app/slices/contacts";
|
||||
|
||||
// import { useGetChannelsQuery } from "../../app/services/channel";
|
||||
import { useLazyGetServerQuery } from "../../app/services/server";
|
||||
import useStreaming from "../../common/hook/useStreaming";
|
||||
import { KEY_UID } from "../../app/config";
|
||||
// pollingInterval: 0,
|
||||
// const querySetting = {
|
||||
// refetchOnMountOrArgChange: true,
|
||||
// };
|
||||
let request = null;
|
||||
export default function usePreload() {
|
||||
const { rehydrate, rehydrated } = useRehydrate();
|
||||
const [initStreaming, { isLoading: streaming }] = useLazyInitStreamingQuery();
|
||||
// const [initStreaming, { isLoading: streaming }] = useLazyInitStreamingQuery();
|
||||
const [checked, setChecked] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const store = useSelector((store) => store);
|
||||
const { startStreaming, streaming, initializing } = useStreaming();
|
||||
const [
|
||||
getContacts,
|
||||
{
|
||||
@@ -41,6 +45,11 @@ export default function usePreload() {
|
||||
useEffect(() => {
|
||||
initCache();
|
||||
rehydrate();
|
||||
return () => {
|
||||
if (request) {
|
||||
request.abort();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -51,10 +60,10 @@ export default function usePreload() {
|
||||
}
|
||||
}, [rehydrated]);
|
||||
useEffect(() => {
|
||||
if (checked && rehydrated) {
|
||||
initStreaming({}, false);
|
||||
if (checked && rehydrated && !initializing && !streaming) {
|
||||
request = startStreaming(store);
|
||||
}
|
||||
}, [checked, rehydrated]);
|
||||
}, [checked, rehydrated, store, streaming, initializing]);
|
||||
|
||||
useEffect(() => {
|
||||
const local_uid = localStorage.getItem(KEY_UID);
|
||||
@@ -76,10 +85,9 @@ export default function usePreload() {
|
||||
}
|
||||
}
|
||||
}, [contacts]);
|
||||
console.log("loading", contactsLoading, serverLoading, !checked, streaming);
|
||||
// console.log("loading", contactsLoading, serverLoading, !checked);
|
||||
return {
|
||||
loading:
|
||||
contactsLoading || serverLoading || !checked || !rehydrated || streaming,
|
||||
loading: contactsLoading || serverLoading || !checked || !rehydrated,
|
||||
error: contactsError && serverError,
|
||||
success: contactsSuccess && serverSuccess,
|
||||
data: {
|
||||
|
||||
+14
-5
@@ -1,9 +1,6 @@
|
||||
// import { useState } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { Route, Routes, HashRouter } from "react-router-dom";
|
||||
import { Provider } from "react-redux";
|
||||
|
||||
// import { persistStore } from 'redux-persist';
|
||||
// import { PersistGate } from 'redux-persist/integration/react';
|
||||
import { Provider, useSelector } from "react-redux";
|
||||
// import Welcome from './Welcome'
|
||||
import NotFoundPage from "./404";
|
||||
import LoginPage from "./login";
|
||||
@@ -14,8 +11,20 @@ import RequireAuth from "../common/component/RequireAuth";
|
||||
|
||||
import store from "../app/store";
|
||||
import InvitePage from "./invite";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
const PageRoutes = () => {
|
||||
const { online } = useSelector((store) => store.ui);
|
||||
// 掉线检测
|
||||
useEffect(() => {
|
||||
let toastId = 0;
|
||||
if (!online) {
|
||||
toast.error("Network Offline!", { duration: Infinity });
|
||||
} else {
|
||||
toast.dismiss(toastId);
|
||||
}
|
||||
}, [online]);
|
||||
|
||||
return (
|
||||
<HashRouter>
|
||||
<Routes>
|
||||
|
||||
Reference in New Issue
Block a user