chore: stash updates
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
const getFeedWithPagination = (config) => {
|
const getFeedWithPagination = (config) => {
|
||||||
const { pageNumber = 1, pageSize = 20, mids = [] } = config || {};
|
const { pageNumber = 1, pageSize = 10, mids = [], isLast = false } =
|
||||||
|
config || {};
|
||||||
const shadowMids = mids.slice(0);
|
const shadowMids = mids.slice(0);
|
||||||
|
|
||||||
if (shadowMids.length == 0)
|
if (shadowMids.length == 0)
|
||||||
@@ -12,30 +13,27 @@ 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,
|
|
||||||
isLast: pageNumber == pageCount,
|
|
||||||
pageCount,
|
pageCount,
|
||||||
pageSize,
|
pageSize,
|
||||||
pageNumber,
|
pageNumber: computedPageNumber,
|
||||||
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 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 [appends, setAppends] = useState([]);
|
||||||
const [items, setItems] = useState([]);
|
const [items, setItems] = useState([]);
|
||||||
const { mids, messageData } = useSelector((store) => {
|
const { mids, messageData } = useSelector((store) => {
|
||||||
return {
|
return {
|
||||||
@@ -51,72 +49,46 @@ export default function useMessageFeed({ context = "channel", id = null }) {
|
|||||||
pageRef.current = [];
|
pageRef.current = [];
|
||||||
setItems([]);
|
setItems([]);
|
||||||
setHasMore(true);
|
setHasMore(true);
|
||||||
// setAppends([]);
|
setAppends([]);
|
||||||
}, [context, id]);
|
}, [context, id]);
|
||||||
// useEffect(() => {
|
|
||||||
// if (appends.length) {
|
|
||||||
// const feedsWrapperEle = document.querySelector(
|
|
||||||
// `#RUSTCHAT_FEED_${context}_${id}`
|
|
||||||
// );
|
|
||||||
// if (feedsWrapperEle) {
|
|
||||||
// feedsWrapperEle.scrollTop = feedsWrapperEle.scrollHeight;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }, [appends, context, id]);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const container = document.querySelector("#ScrollFeedWrapper");
|
if (appends.length) {
|
||||||
const handler = (e) => {
|
const feedsWrapperEle = 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 (feedsWrapperEle) {
|
||||||
else if ("wheelDeltaY" in e) n = (e.wheelDeltaY / 120) * 20;
|
feedsWrapperEle.scrollTop = feedsWrapperEle.scrollHeight;
|
||||||
else if ("wheelDelta" in e) n = (e.wheelDelta / 120) * 20;
|
|
||||||
else {
|
|
||||||
// if (!("detail"in e))
|
|
||||||
// return void a.v(e, "invalid wheel event: ");
|
|
||||||
n = (-e.detail / 3) * 20;
|
|
||||||
}
|
}
|
||||||
container.scrollTop += n;
|
|
||||||
};
|
|
||||||
if (container) {
|
|
||||||
container.addEventListener("wheel", handler);
|
|
||||||
}
|
}
|
||||||
return () => {
|
}, [appends, context, id]);
|
||||||
if (container) {
|
|
||||||
container.removeEventListener("wheel", handler);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (listRef.current.length == 0) {
|
if (listRef.current.length == 0) {
|
||||||
// 初次
|
// 初次
|
||||||
const pageInfo = getFeedWithPagination({ mids });
|
const pageInfo = getFeedWithPagination({ mids, isLast: true });
|
||||||
console.log("pull down 2", pageInfo);
|
console.log("pull up 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[0];
|
const lastMid = listRef.current.slice(-1);
|
||||||
const sorteds = mids.slice(0).sort((a, b) => {
|
const sorteds = mids.slice(0).sort((a, b) => {
|
||||||
return Number(b) - Number(a);
|
return Number(a) - Number(b);
|
||||||
});
|
});
|
||||||
const prepends = sorteds.filter((s) => s > lastMid);
|
const appends = sorteds.filter((s) => s > lastMid);
|
||||||
if (prepends.length) {
|
if (appends.length) {
|
||||||
setItems((prevs) => [...prepends, ...prevs]);
|
setAppends(appends);
|
||||||
}
|
}
|
||||||
console.log("prepends", prepends, items);
|
console.log("appends", appends);
|
||||||
}
|
}
|
||||||
}, [mids]);
|
}, [mids]);
|
||||||
const pullDown = () => {
|
const pullUp = () => {
|
||||||
// 向下加载
|
|
||||||
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.pageNumber == 1) {
|
||||||
setHasMore(true);
|
setHasMore(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let pageInfo = null;
|
let pageInfo = null;
|
||||||
@@ -124,28 +96,32 @@ 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(() => {
|
||||||
setItems(listRef.current);
|
setItems(listRef.current);
|
||||||
console.log("pull up", currPageInfo, listRef.current);
|
console.log("pull up", currPageInfo, listRef.current);
|
||||||
setHasMore(!pageInfo.isLast);
|
setHasMore(pageInfo.pageNumber !== 1);
|
||||||
}, 1000);
|
}, 800);
|
||||||
|
};
|
||||||
|
const pullDown = () => {
|
||||||
|
// 向下加载
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mids,
|
mids,
|
||||||
// appends,
|
appends,
|
||||||
hasMore,
|
hasMore,
|
||||||
// pullUp,
|
pullUp,
|
||||||
pullDown,
|
pullDown,
|
||||||
list: items,
|
list: items,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
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;
|
||||||
@@ -11,35 +10,35 @@ 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() {
|
||||||
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 && pullDown) {
|
// if (intersecting && loadMore) {
|
||||||
// load more
|
// // load more
|
||||||
console.log("inview");
|
// console.log("inview");
|
||||||
pullDown();
|
// loadMore();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
},
|
// },
|
||||||
{ 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, pullDown]);
|
// }, [ref]);
|
||||||
return (
|
return (
|
||||||
<Styled ref={ref}>
|
<Styled>
|
||||||
<Waveform
|
<Waveform
|
||||||
className="loading"
|
className="loading"
|
||||||
size={24}
|
size={24}
|
||||||
|
|||||||
@@ -0,0 +1,287 @@
|
|||||||
|
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, appends, 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;
|
||||||
|
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="ScrollFeedWrapper">
|
||||||
|
{/* <div className="feed"> */}
|
||||||
|
{[...msgIds].map((mid, idx) => {
|
||||||
|
const curr = messageData[mid];
|
||||||
|
if (!curr) return null;
|
||||||
|
const isFirst = idx == 0;
|
||||||
|
const prev =
|
||||||
|
idx == msgIds.length - 1 ? null : messageData[msgIds[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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
export const StyledHeader = styled.header`
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.txt {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #1c1c1e;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #616161;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const StyledNotification = styled.div`
|
||||||
|
padding: 3px 8px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 10px;
|
||||||
|
width: 900px;
|
||||||
|
height: 24px;
|
||||||
|
background: linear-gradient(135deg, #3c8ce7 0%, #00eaff 100%);
|
||||||
|
border-radius: 0px 0px 8px 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.clear {
|
||||||
|
cursor: pointer;
|
||||||
|
color: inherit;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const StyledContacts = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
/* todo */
|
||||||
|
width: 226px;
|
||||||
|
height: calc(100vh - 56px - 22px);
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding: 8px;
|
||||||
|
box-shadow: inset 1px 0px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
> .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`
|
||||||
|
padding: 18px 16px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
height: -webkit-fill-available;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
transform: rotate(180deg) translateZ(0);
|
||||||
|
direction: rtl;
|
||||||
|
/* display: flex;
|
||||||
|
flex-direction: column-reverse; */
|
||||||
|
> div,
|
||||||
|
> hr {
|
||||||
|
direction: ltr;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
> .info {
|
||||||
|
padding-top: 62px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
.title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 36px;
|
||||||
|
line-height: 44px;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
color: #78787c;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.edit {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
.icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
path {
|
||||||
|
fill: #3c8ce7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
padding: 0;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
background: linear-gradient(135deg, #3c8ce7 0%, #00eaff 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* > .feed {
|
||||||
|
} */
|
||||||
|
`;
|
||||||
Reference in New Issue
Block a user