Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F33066155
D8234.1768438604.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D8234.1768438604.diff
View Options
diff --git a/web/chat/chat-thread-list-search.react.js b/web/chat/chat-thread-list-search.react.js
new file mode 100644
--- /dev/null
+++ b/web/chat/chat-thread-list-search.react.js
@@ -0,0 +1,29 @@
+// @flow
+
+import invariant from 'invariant';
+import * as React from 'react';
+
+import { ThreadListContext } from './thread-list-provider.js';
+import Search from '../components/search.react.js';
+
+function ChatThreadListSearch(): React.Node {
+ const threadListContext = React.useContext(ThreadListContext);
+ invariant(
+ threadListContext,
+ 'threadListContext should be set in ChatThreadListSearch',
+ );
+ const { setSearchText, searchText } = threadListContext;
+
+ return React.useMemo(
+ () => (
+ <Search
+ onChangeText={setSearchText}
+ searchText={searchText}
+ placeholder="Search chats"
+ />
+ ),
+ [searchText, setSearchText],
+ );
+}
+
+export default ChatThreadListSearch;
diff --git a/web/chat/chat-thread-list.css b/web/chat/chat-thread-list.css
--- a/web/chat/chat-thread-list.css
+++ b/web/chat/chat-thread-list.css
@@ -15,9 +15,6 @@
top: -7px;
left: 30px;
}
-.thread:first-child {
- padding-top: 6px;
-}
.activeThread,
.threadListSidebar:hover {
@@ -265,6 +262,7 @@
display: flex;
flex-direction: column;
overflow: auto;
+ flex: 1;
}
div.createNewThread {
diff --git a/web/chat/chat-thread-list.react.js b/web/chat/chat-thread-list.react.js
--- a/web/chat/chat-thread-list.react.js
+++ b/web/chat/chat-thread-list.react.js
@@ -1,27 +1,62 @@
// @flow
import invariant from 'invariant';
+import _sum from 'lodash/fp/sum.js';
import * as React from 'react';
+import AutoSizer from 'react-virtualized-auto-sizer';
+import { VariableSizeList } from 'react-window';
+import type { ChatThreadItem } from 'lib/selectors/chat-selectors.js';
import { emptyItemText } from 'lib/shared/thread-utils.js';
import ChatThreadListItem from './chat-thread-list-item.react.js';
+import ChatThreadListSearch from './chat-thread-list-search.react.js';
import css from './chat-thread-list.css';
import { ThreadListContext } from './thread-list-provider.js';
import BackgroundIllustration from '../assets/background-illustration.react.js';
import Button from '../components/button.react.js';
-import Search from '../components/search.react.js';
import { useSelector } from '../redux/redux-utils.js';
import { useOnClickNewThread } from '../selectors/thread-selectors.js';
+type Item = ChatThreadItem | { +type: 'search' } | { +type: 'empty' };
+
+const sizes = {
+ search: 68,
+ empty: 249,
+ thread: 81,
+ sidebars: { sidebar: 32, seeMore: 22, spacer: 6 },
+};
+
+const itemKey = (index: number, data: $ReadOnlyArray<Item>) => {
+ if (data[index].type === 'search') {
+ return 'search';
+ } else if (data[index].type === 'empty') {
+ return 'empty';
+ } else {
+ return data[index].threadInfo.id;
+ }
+};
+
+const renderItem = ({ index, data, style }) => {
+ let item;
+ if (data[index].type === 'search') {
+ item = <ChatThreadListSearch />;
+ } else if (data[index].type === 'empty') {
+ item = <EmptyItem />;
+ } else {
+ item = <ChatThreadListItem item={data[index]} />;
+ }
+
+ return <div style={style}>{item}</div>;
+};
+
function ChatThreadList(): React.Node {
const threadListContext = React.useContext(ThreadListContext);
invariant(
threadListContext,
'threadListContext should be set in ChatThreadList',
);
- const { activeTab, threadList, setSearchText, searchText } =
- threadListContext;
+ const { activeTab, threadList } = threadListContext;
const onClickNewThread = useOnClickNewThread();
@@ -33,31 +68,66 @@
const communityID = useSelector(state => state.communityPickerStore.chat);
- const threadComponents: React.Node[] = React.useMemo(() => {
- const threads = threadList
- .filter(
+ const threadListContainerRef = React.useRef();
+
+ const threads = React.useMemo(
+ () =>
+ threadList.filter(
item =>
!communityID ||
item.threadInfo.community === communityID ||
item.threadInfo.id === communityID,
- )
- .map(item => <ChatThreadListItem item={item} key={item.threadInfo.id} />);
- if (threads.length === 0 && isBackground) {
- threads.push(<EmptyItem key="emptyItem" />);
+ ),
+ [communityID, threadList],
+ );
+
+ React.useEffect(() => {
+ if (threadListContainerRef.current) {
+ threadListContainerRef.current.resetAfterIndex(0, false);
}
- return threads;
- }, [threadList, isBackground, communityID]);
+ }, [threads]);
+
+ const threadListContainer = React.useMemo(() => {
+ const items: Item[] = [{ type: 'search' }, ...threads];
+
+ if (isBackground && threads.length === 0) {
+ items.push({ type: 'empty' });
+ }
+
+ const itemSize = index => {
+ if (items[index].type === 'search') {
+ return sizes.search;
+ } else if (items[index].type === 'empty') {
+ return sizes.empty;
+ }
+
+ const sidebarHeight = _sum(
+ items[index].sidebars.map(s => sizes.sidebars[s.type]),
+ );
+ return sizes.thread + sidebarHeight;
+ };
+
+ return (
+ <AutoSizer disableWidth>
+ {({ height }) => (
+ <VariableSizeList
+ itemData={items}
+ itemCount={items.length}
+ itemSize={itemSize}
+ itemKey={itemKey}
+ height={height}
+ ref={threadListContainerRef}
+ >
+ {renderItem}
+ </VariableSizeList>
+ )}
+ </AutoSizer>
+ );
+ }, [isBackground, threads]);
return (
<>
- <div className={css.threadListContainer}>
- <Search
- onChangeText={setSearchText}
- searchText={searchText}
- placeholder="Search chats"
- />
- <div>{threadComponents}</div>
- </div>
+ <div className={css.threadListContainer}>{threadListContainer}</div>
<div className={css.createNewThread}>
<Button
variant="filled"
diff --git a/web/package.json b/web/package.json
--- a/web/package.json
+++ b/web/package.json
@@ -78,6 +78,8 @@
"react-router-dom": "^5.2.0",
"react-switch": "^7.0.0",
"react-timeago": "^7.1.0",
+ "react-virtualized-auto-sizer": "^1.0.19",
+ "react-window": "^1.8.9",
"redux": "^4.0.4",
"redux-devtools-extension": "^2.13.2",
"redux-persist": "^6.0.0",
diff --git a/yarn.lock b/yarn.lock
--- a/yarn.lock
+++ b/yarn.lock
@@ -16679,7 +16679,7 @@
dependencies:
fs-monkey "^1.0.3"
-memoize-one@^5.0.0:
+"memoize-one@>=3.1.1 <6", memoize-one@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
@@ -20119,6 +20119,19 @@
loose-envify "^1.4.0"
prop-types "^15.6.2"
+react-virtualized-auto-sizer@^1.0.19:
+ version "1.0.19"
+ resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.19.tgz#1117147dbec3f23db94ded1f0ddd9f1fd0692aef"
+ integrity sha512-jAPNLjG1DBnLQUMZz1py/5z7/wC8Gn8G+WAXQplDdVr9zO/xdTWLtDM3aPHm8e2gLT2xQxMql507IMuLA6himw==
+
+react-window@^1.8.9:
+ version "1.8.9"
+ resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.9.tgz#24bc346be73d0468cdf91998aac94e32bc7fa6a8"
+ integrity sha512-+Eqx/fj1Aa5WnhRfj9dJg4VYATGwIUP2ItwItiJ6zboKWA6EX3lYDAXfGF2hyNqplEprhbtjbipiADEcwQ823Q==
+ dependencies:
+ "@babel/runtime" "^7.0.0"
+ memoize-one ">=3.1.1 <6"
+
react@18.1.0:
version "18.1.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890"
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jan 15, 12:56 AM (8 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5931083
Default Alt Text
D8234.1768438604.diff (7 KB)
Attached To
Mode
D8234: [web] Use react-window
Attached
Detach File
Event Timeline
Log In to Comment