diff --git a/app/soapbox/actions/chats.js b/app/soapbox/actions/chats.js index 8003bb8cb..0a8d1d75c 100644 --- a/app/soapbox/actions/chats.js +++ b/app/soapbox/actions/chats.js @@ -61,14 +61,14 @@ export function fetchChatsV2() { export function fetchChats() { return (dispatch, getState) => { const state = getState(); - const instance = state.get('instance'); + const { instance } = state; const features = getFeatures(instance); dispatch({ type: CHATS_FETCH_REQUEST }); if (features.chatsV2) { - dispatch(fetchChatsV2()); + return dispatch(fetchChatsV2()); } else { - dispatch(fetchChatsV1()); + return dispatch(fetchChatsV1()); } }; } diff --git a/app/soapbox/features/chats/components/chat_list.tsx b/app/soapbox/features/chats/components/chat_list.tsx index 874c71041..2608a8f03 100644 --- a/app/soapbox/features/chats/components/chat_list.tsx +++ b/app/soapbox/features/chats/components/chat_list.tsx @@ -1,12 +1,13 @@ import { Map as ImmutableMap } from 'immutable'; -import { debounce } from 'lodash'; -import React from 'react'; +import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useDispatch } from 'react-redux'; +import { Virtuoso } from 'react-virtuoso'; import { createSelector } from 'reselect'; -import { expandChats } from 'soapbox/actions/chats'; -import ScrollableList from 'soapbox/components/scrollable_list'; +import { fetchChats, expandChats } from 'soapbox/actions/chats'; +import PullToRefresh from 'soapbox/components/pull-to-refresh'; +import { Text } from 'soapbox/components/ui'; import PlaceholderChat from 'soapbox/features/placeholder/components/placeholder_chat'; import { useAppSelector } from 'soapbox/hooks'; @@ -16,10 +17,6 @@ const messages = defineMessages({ emptyMessage: { id: 'chat_panels.main_window.empty', defaultMessage: 'No chats found. To start a chat, visit a user\'s profile' }, }); -const handleLoadMore = debounce((dispatch) => { - dispatch(expandChats()); -}, 300, { leading: true }); - const getSortedChatIds = (chats: ImmutableMap) => ( chats .toList() @@ -45,10 +42,10 @@ const sortedChatIdsSelector = createSelector( interface IChatList { onClickChat: (chat: any) => void, - onRefresh: () => void, + useWindowScroll?: boolean, } -const ChatList: React.FC = ({ onClickChat, onRefresh }) => { +const ChatList: React.FC = ({ onClickChat, useWindowScroll = false }) => { const dispatch = useDispatch(); const intl = useIntl(); @@ -56,28 +53,41 @@ const ChatList: React.FC = ({ onClickChat, onRefresh }) => { const hasMore = useAppSelector(state => !!state.chats.get('next')); const isLoading = useAppSelector(state => state.chats.get('isLoading')); + const handleLoadMore = useCallback(() => { + if (hasMore && !isLoading) { + dispatch(expandChats()); + } + }, [dispatch, hasMore, isLoading]); + + const handleRefresh = () => { + return dispatch(fetchChats()) as any; + }; + return ( - handleLoadMore(dispatch)} - onRefresh={onRefresh} - placeholderComponent={PlaceholderChat} - placeholderCount={20} - > - {chatIds.map((chatId: string) => ( -
- -
- ))} -
+ + ( + + )} + components={{ + ScrollSeekPlaceholder: () => , + Footer: () => hasMore ? : null, + EmptyPlaceholder: () => { + if (isLoading) { + return ( + <>{Array(20).fill()} + ); + } else { + return {intl.formatMessage(messages.emptyMessage)}; + } + }, + }} + /> + ); }; diff --git a/app/soapbox/features/chats/index.tsx b/app/soapbox/features/chats/index.tsx index c13335ff3..cd53ca1c9 100644 --- a/app/soapbox/features/chats/index.tsx +++ b/app/soapbox/features/chats/index.tsx @@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; -import { fetchChats, launchChat } from 'soapbox/actions/chats'; +import { launchChat } from 'soapbox/actions/chats'; import AccountSearch from 'soapbox/components/account_search'; import AudioToggle from 'soapbox/features/chats/components/audio_toggle'; @@ -29,10 +29,6 @@ const ChatIndex: React.FC = () => { history.push(`/chats/${chat.id}`); }; - const handleRefresh = () => { - return dispatch(fetchChats()); - }; - return (
@@ -46,7 +42,7 @@ const ChatIndex: React.FC = () => { ); diff --git a/app/styles/chats.scss b/app/styles/chats.scss index 72d845c28..f41aaf4e9 100644 --- a/app/styles/chats.scss +++ b/app/styles/chats.scss @@ -90,12 +90,16 @@ &__content { @apply flex flex-1 flex-col overflow-hidden bg-white dark:bg-slate-800; + > div { + @apply max-h-full; + } + .chat-box { @apply flex flex-1 flex-col overflow-hidden; } .chat-list { - @apply overflow-y-auto; + @apply overflow-y-auto max-h-full; } } diff --git a/package.json b/package.json index 89d94fa92..efdaac696 100644 --- a/package.json +++ b/package.json @@ -169,6 +169,7 @@ "react-swipeable-views": "^0.14.0", "react-textarea-autosize": "^8.3.3", "react-toggle": "^4.0.1", + "react-virtuoso": "^2.9.1", "redux": "^4.1.1", "redux-immutable": "^4.0.0", "redux-thunk": "^2.2.0", diff --git a/yarn.lock b/yarn.lock index ece5c1947..baf32288c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2388,6 +2388,18 @@ "@typescript-eslint/types" "5.15.0" eslint-visitor-keys "^3.0.0" +"@virtuoso.dev/react-urx@^0.2.12": + version "0.2.13" + resolved "https://registry.yarnpkg.com/@virtuoso.dev/react-urx/-/react-urx-0.2.13.tgz#e2cfc42d259d2a002695e7517d34cb97b64ee9c4" + integrity sha512-MY0ugBDjFb5Xt8v2HY7MKcRGqw/3gTpMlLXId2EwQvYJoC8sP7nnXjAxcBtTB50KTZhO0SbzsFimaZ7pSdApwA== + dependencies: + "@virtuoso.dev/urx" "^0.2.13" + +"@virtuoso.dev/urx@^0.2.12", "@virtuoso.dev/urx@^0.2.13": + version "0.2.13" + resolved "https://registry.yarnpkg.com/@virtuoso.dev/urx/-/urx-0.2.13.tgz#a65e7e8d923cb03397ac876bfdd45c7f71c8edf1" + integrity sha512-iirJNv92A1ZWxoOHHDYW/1KPoi83939o83iUBQHIim0i3tMeSKEh+bxhJdTHQ86Mr4uXx9xGUTq69cp52ZP8Xw== + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -8979,6 +8991,14 @@ react-transition-group@^2.2.1: prop-types "^15.6.2" react-lifecycles-compat "^3.0.4" +react-virtuoso@^2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-2.9.1.tgz#9a4279824d07ea3e0ebd76be932f55f7244b10ad" + integrity sha512-TtaCj0qGVCicti2o/ScN5Pqe7ARw6cAGQzd9vDcJcbS2ZpOWNe248Ifn+QP0lk92VP/ROgDwWajH1aEZz056Ow== + dependencies: + "@virtuoso.dev/react-urx" "^0.2.12" + "@virtuoso.dev/urx" "^0.2.12" + react@^16.13.1: version "16.14.0" resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"