Page MenuHomePhorge

D8234.1768438604.diff
No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None

D8234.1768438604.diff

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

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)

Event Timeline