From 12e4c6c083282526abe850b6ac54271b9a42af38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 12 Dec 2021 20:45:59 +0100 Subject: [PATCH 1/3] wip chat pagination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/chats.js | 33 ++++++++-- app/soapbox/components/helmet.js | 2 +- app/soapbox/components/primary_navigation.js | 2 +- app/soapbox/components/scrollable_list.js | 26 +++++--- app/soapbox/components/thumb_navigation.js | 2 +- app/soapbox/features/chats/chat_room.js | 2 +- app/soapbox/features/chats/components/chat.js | 2 +- .../features/chats/components/chat_box.js | 2 +- .../features/chats/components/chat_list.js | 60 +++++++++++++------ .../features/chats/components/chat_panes.js | 5 +- .../features/chats/components/chat_window.js | 2 +- app/soapbox/features/chats/index.js | 13 ++-- .../components/placeholder_chat.js | 30 ++++++++++ .../features/ui/components/tabs_bar.js | 2 +- app/soapbox/reducers/accounts.js | 3 +- app/soapbox/reducers/chat_message_lists.js | 2 + app/soapbox/reducers/chat_messages.js | 2 + app/soapbox/reducers/chats.js | 25 ++++++-- app/soapbox/selectors/index.js | 4 +- app/styles/chats.scss | 9 ++- app/styles/placeholder.scss | 3 +- 21 files changed, 170 insertions(+), 61 deletions(-) create mode 100644 app/soapbox/features/placeholder/components/placeholder_chat.js diff --git a/app/soapbox/actions/chats.js b/app/soapbox/actions/chats.js index 5fa6811ee..5407fa769 100644 --- a/app/soapbox/actions/chats.js +++ b/app/soapbox/actions/chats.js @@ -1,4 +1,4 @@ -import api from '../api'; +import api, { getLinks } from '../api'; import { getSettings, changeSetting } from 'soapbox/actions/settings'; import { v4 as uuidv4 } from 'uuid'; import { Map as ImmutableMap } from 'immutable'; @@ -7,6 +7,10 @@ export const CHATS_FETCH_REQUEST = 'CHATS_FETCH_REQUEST'; export const CHATS_FETCH_SUCCESS = 'CHATS_FETCH_SUCCESS'; export const CHATS_FETCH_FAIL = 'CHATS_FETCH_FAIL'; +export const CHATS_EXPAND_REQUEST = 'CHATS_EXPAND_REQUEST'; +export const CHATS_EXPAND_SUCCESS = 'CHATS_EXPAND_SUCCESS'; +export const CHATS_EXPAND_FAIL = 'CHATS_EXPAND_FAIL'; + export const CHAT_MESSAGES_FETCH_REQUEST = 'CHAT_MESSAGES_FETCH_REQUEST'; export const CHAT_MESSAGES_FETCH_SUCCESS = 'CHAT_MESSAGES_FETCH_SUCCESS'; export const CHAT_MESSAGES_FETCH_FAIL = 'CHAT_MESSAGES_FETCH_FAIL'; @@ -30,14 +34,35 @@ export const CHAT_MESSAGE_DELETE_FAIL = 'CHAT_MESSAGE_DELETE_FAIL'; export function fetchChats() { return (dispatch, getState) => { dispatch({ type: CHATS_FETCH_REQUEST }); - return api(getState).get('/api/v1/pleroma/chats').then(({ data }) => { - dispatch({ type: CHATS_FETCH_SUCCESS, chats: data }); + api(getState).get('/api/v2/pleroma/chats').then((response) => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch({ type: CHATS_FETCH_SUCCESS, chats: response.data, next: next ? next.uri : null }); }).catch(error => { dispatch({ type: CHATS_FETCH_FAIL, error }); }); }; } +export function expandChats() { + return (dispatch, getState) => { + const url = getState().getIn(['chats', 'next']); + + if (url === null) { + return; + } + + dispatch({ type: CHATS_EXPAND_REQUEST }); + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + + dispatch({ type: CHATS_EXPAND_SUCCESS, chats: response.data, next: next ? next.uri : null }); + }).catch(error => { + dispatch({ type: CHATS_EXPAND_FAIL, error }); + }); + }; +} + export function fetchChatMessages(chatId, maxId = null) { return (dispatch, getState) => { dispatch({ type: CHAT_MESSAGES_FETCH_REQUEST, chatId, maxId }); @@ -140,7 +165,7 @@ export function startChat(accountId) { export function markChatRead(chatId, lastReadId) { return (dispatch, getState) => { - const chat = getState().getIn(['chats', chatId]); + const chat = getState().getIn(['chats', 'items', chatId]); if (!lastReadId) lastReadId = chat.get('last_message'); if (chat.get('unread') < 1) return; diff --git a/app/soapbox/components/helmet.js b/app/soapbox/components/helmet.js index 4b1454ffa..3b96b8c10 100644 --- a/app/soapbox/components/helmet.js +++ b/app/soapbox/components/helmet.js @@ -11,7 +11,7 @@ FaviconService.initFaviconService(); const getNotifTotals = state => { const notifications = state.getIn(['notifications', 'unread'], 0); - const chats = state.get('chats').reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0); + const chats = state.getIn(['chats', 'items']).reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0); const reports = state.getIn(['admin', 'openReports']).count(); const approvals = state.getIn(['admin', 'awaitingApproval']).count(); return notifications + chats + reports + approvals; diff --git a/app/soapbox/components/primary_navigation.js b/app/soapbox/components/primary_navigation.js index 9a11e5969..5bac11d33 100644 --- a/app/soapbox/components/primary_navigation.js +++ b/app/soapbox/components/primary_navigation.js @@ -25,7 +25,7 @@ const mapStateToProps = state => { account, logo: getSoapboxConfig(state).get('logo'), notificationCount: state.getIn(['notifications', 'unread']), - chatsCount: state.get('chats').reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0), + chatsCount: state.getIn(['chats', 'items']).reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0), dashboardCount: reportsCount + approvalCount, baseURL: getBaseURL(account), settings: getSettings(state), diff --git a/app/soapbox/components/scrollable_list.js b/app/soapbox/components/scrollable_list.js index ceb554c53..9617d1c68 100644 --- a/app/soapbox/components/scrollable_list.js +++ b/app/soapbox/components/scrollable_list.js @@ -1,6 +1,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; +import classNames from 'classnames'; import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container'; import LoadMore from './load_more'; import MoreFollows from './more_follows'; @@ -45,6 +46,7 @@ class ScrollableList extends PureComponent { placeholderCount: PropTypes.number, autoload: PropTypes.bool, onRefresh: PropTypes.func, + className: PropTypes.string, }; state = { @@ -240,16 +242,22 @@ class ScrollableList extends PureComponent { } renderLoading = () => { - const { prepend, placeholderComponent: Placeholder, placeholderCount } = this.props; + const { className, prepend, placeholderComponent: Placeholder, placeholderCount } = this.props; if (Placeholder && placeholderCount > 0) { - return Array(placeholderCount).fill().map((_, i) => ( - - )); + return ( +
+
+ {Array(placeholderCount).fill().map((_, i) => ( + + ))} +
+
+ ); } return ( -
+
{prepend}
@@ -262,10 +270,10 @@ class ScrollableList extends PureComponent { } renderEmptyMessage = () => { - const { prepend, alwaysPrepend, emptyMessage } = this.props; + const { className, prepend, alwaysPrepend, emptyMessage } = this.props; return ( -
+
{alwaysPrepend && prepend}
@@ -276,13 +284,13 @@ class ScrollableList extends PureComponent { } renderFeed = () => { - const { children, scrollKey, isLoading, hasMore, prepend, onLoadMore, onRefresh, placeholderComponent: Placeholder } = this.props; + const { className, children, scrollKey, isLoading, hasMore, prepend, onLoadMore, onRefresh, placeholderComponent: Placeholder } = this.props; const childrenCount = React.Children.count(children); const trackScroll = true; //placeholder const loadMore = (hasMore && onLoadMore) ? : null; const feed = ( -
+
{prepend} diff --git a/app/soapbox/components/thumb_navigation.js b/app/soapbox/components/thumb_navigation.js index 06b27161d..ba5173097 100644 --- a/app/soapbox/components/thumb_navigation.js +++ b/app/soapbox/components/thumb_navigation.js @@ -21,7 +21,7 @@ const mapStateToProps = state => { account: state.getIn(['accounts', me]), logo: getSoapboxConfig(state).get('logo'), notificationCount: state.getIn(['notifications', 'unread']), - chatsCount: state.get('chats').reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0), + chatsCount: state.getIn(['chats', 'items']).reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0), dashboardCount: reportsCount + approvalCount, features: getFeatures(instance), }; diff --git a/app/soapbox/features/chats/chat_room.js b/app/soapbox/features/chats/chat_room.js index ecd19a495..73d9a32a1 100644 --- a/app/soapbox/features/chats/chat_room.js +++ b/app/soapbox/features/chats/chat_room.js @@ -17,7 +17,7 @@ import { displayFqn } from 'soapbox/utils/state'; const mapStateToProps = (state, { params }) => { const getChat = makeGetChat(); - const chat = state.getIn(['chats', params.chatId], ImmutableMap()).toJS(); + const chat = state.getIn(['chats', 'items', params.chatId], ImmutableMap()).toJS(); return { me: state.get('me'), diff --git a/app/soapbox/features/chats/components/chat.js b/app/soapbox/features/chats/components/chat.js index 233f861f6..462bc7fe4 100644 --- a/app/soapbox/features/chats/components/chat.js +++ b/app/soapbox/features/chats/components/chat.js @@ -15,7 +15,7 @@ const makeMapStateToProps = () => { const getChat = makeGetChat(); const mapStateToProps = (state, { chatId }) => { - const chat = state.getIn(['chats', chatId]); + const chat = state.getIn(['chats', 'items', chatId]); return { chat: chat ? getChat(state, chat.toJS()) : undefined, diff --git a/app/soapbox/features/chats/components/chat_box.js b/app/soapbox/features/chats/components/chat_box.js index e1403bccb..3abf4d673 100644 --- a/app/soapbox/features/chats/components/chat_box.js +++ b/app/soapbox/features/chats/components/chat_box.js @@ -23,7 +23,7 @@ const messages = defineMessages({ const mapStateToProps = (state, { chatId }) => ({ me: state.get('me'), - chat: state.getIn(['chats', chatId]), + chat: state.getIn(['chats', 'items', chatId]), chatMessageIds: state.getIn(['chat_message_lists', chatId], ImmutableOrderedSet()), }); diff --git a/app/soapbox/features/chats/components/chat_list.js b/app/soapbox/features/chats/components/chat_list.js index f89115363..35394174e 100644 --- a/app/soapbox/features/chats/components/chat_list.js +++ b/app/soapbox/features/chats/components/chat_list.js @@ -2,11 +2,19 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { injectIntl } from 'react-intl'; +import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import { debounce } from 'lodash'; +import { expandChats } from 'soapbox/actions/chats'; +import ScrollableList from 'soapbox/components/scrollable_list'; +import PlaceholderChat from 'soapbox/features/placeholder/components/placeholder_chat'; import Chat from './chat'; import { createSelector } from 'reselect'; +const messages = defineMessages({ + emptyMessage: { id: 'chat_panels.main_window.empty', defaultMessage: 'No chats found. To start a chat, visit a user\'s profile' }, +}); + const getSortedChatIds = chats => ( chats .toList() @@ -32,7 +40,9 @@ const makeMapStateToProps = () => { ); const mapStateToProps = state => ({ - chatIds: sortedChatIdsSelector(state.get('chats')), + chatIds: sortedChatIdsSelector(state.getIn(['chats', 'items'])), + hasMore: !!state.getIn(['chats', 'next']), + isLoading: state.getIn(['chats', 'loading']), }); return mapStateToProps; @@ -47,28 +57,40 @@ class ChatList extends ImmutablePureComponent { intl: PropTypes.object.isRequired, chatIds: ImmutablePropTypes.list, onClickChat: PropTypes.func, - emptyMessage: PropTypes.node, + onRefresh: PropTypes.func, + hasMore: PropTypes.func, + isLoading: PropTypes.bool, }; + handleLoadMore = debounce(() => { + this.props.dispatch(expandChats()); + }, 300, { leading: true }); + render() { - const { chatIds, emptyMessage } = this.props; + const { intl, chatIds, hasMore, isLoading } = this.props; return ( -
-
- {chatIds.count() === 0 && -
{emptyMessage}
- } - {chatIds.map(chatId => ( -
- -
- ))} -
-
+ + {chatIds.map(chatId => ( +
+ +
+ ))} +
); } diff --git a/app/soapbox/features/chats/components/chat_panes.js b/app/soapbox/features/chats/components/chat_panes.js index e6362e5b2..f8c9151cf 100644 --- a/app/soapbox/features/chats/components/chat_panes.js +++ b/app/soapbox/features/chats/components/chat_panes.js @@ -20,7 +20,7 @@ const messages = defineMessages({ }); const getChatsUnreadCount = state => { - const chats = state.get('chats'); + const chats = state.getIn(['chats', 'items']); return chats.reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0); }; @@ -30,7 +30,7 @@ const normalizePanes = (chats, panes = ImmutableList()) => ( ); const makeNormalizeChatPanes = () => createSelector([ - state => state.get('chats'), + state => state.getIn(['chats', 'items']), state => getSettings(state).getIn(['chats', 'panes']), ], normalizePanes); @@ -93,7 +93,6 @@ class ChatPanes extends ImmutablePureComponent { <> } /> { const getChat = makeGetChat(); const mapStateToProps = (state, { chatId }) => { - const chat = state.getIn(['chats', chatId]); + const chat = state.getIn(['chats', 'items', chatId]); return { me: state.get('me'), diff --git a/app/soapbox/features/chats/index.js b/app/soapbox/features/chats/index.js index a9f5d89af..930f55a22 100644 --- a/app/soapbox/features/chats/index.js +++ b/app/soapbox/features/chats/index.js @@ -4,11 +4,10 @@ import { connect } from 'react-redux'; import Column from '../../components/column'; import ColumnHeader from '../../components/column_header'; import { fetchChats, launchChat } from 'soapbox/actions/chats'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl } from 'react-intl'; import ChatList from './components/chat_list'; import AudioToggle from 'soapbox/features/chats/components/audio_toggle'; import AccountSearch from 'soapbox/components/account_search'; -import PullToRefresh from 'soapbox/components/pull_to_refresh'; const messages = defineMessages({ title: { id: 'column.chats', defaultMessage: 'Chats' }, @@ -60,12 +59,10 @@ class ChatIndex extends React.PureComponent { onSelected={this.handleSuggestion} /> - - } - /> - + ); } diff --git a/app/soapbox/features/placeholder/components/placeholder_chat.js b/app/soapbox/features/placeholder/components/placeholder_chat.js new file mode 100644 index 000000000..062cfee26 --- /dev/null +++ b/app/soapbox/features/placeholder/components/placeholder_chat.js @@ -0,0 +1,30 @@ +import React from 'react'; +import PlaceholderAvatar from './placeholder_avatar'; +import PlaceholderDisplayName from './placeholder_display_name'; +import { randomIntFromInterval, generateText } from '../utils'; + +export default class PlaceholderAccount extends React.Component { + + render() { + const messageLength = randomIntFromInterval(5, 75); + + return ( +
+
+
+
+
+ +
+ + + {generateText(messageLength)} + +
+
+
+
+ ); + } + +} diff --git a/app/soapbox/features/ui/components/tabs_bar.js b/app/soapbox/features/ui/components/tabs_bar.js index 959ce245c..0c268bda6 100644 --- a/app/soapbox/features/ui/components/tabs_bar.js +++ b/app/soapbox/features/ui/components/tabs_bar.js @@ -171,7 +171,7 @@ const mapStateToProps = state => { logo: getSoapboxConfig(state).get('logo'), features: getFeatures(instance), notificationCount: state.getIn(['notifications', 'unread']), - chatsCount: state.get('chats').reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0), + chatsCount: state.getIn(['chats', 'items']).reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0), dashboardCount: reportsCount + approvalCount, }; }; diff --git a/app/soapbox/reducers/accounts.js b/app/soapbox/reducers/accounts.js index 70b9bb98f..5b7cdd804 100644 --- a/app/soapbox/reducers/accounts.js +++ b/app/soapbox/reducers/accounts.js @@ -3,7 +3,7 @@ import { ACCOUNTS_IMPORT, ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP, } from '../actions/importer'; -import { CHATS_FETCH_SUCCESS, CHAT_FETCH_SUCCESS } from 'soapbox/actions/chats'; +import { CHATS_FETCH_SUCCESS, CHATS_EXPAND_SUCCESS, CHAT_FETCH_SUCCESS } from 'soapbox/actions/chats'; import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming'; import { normalizeAccount as normalizeAccount2 } from 'soapbox/actions/importer/normalizer'; import { @@ -208,6 +208,7 @@ export default function accounts(state = initialState, action) { username: action.username, })); case CHATS_FETCH_SUCCESS: + case CHATS_EXPAND_SUCCESS: return importAccountsFromChats(state, action.chats); case CHAT_FETCH_SUCCESS: case STREAMING_CHAT_UPDATE: diff --git a/app/soapbox/reducers/chat_message_lists.js b/app/soapbox/reducers/chat_message_lists.js index 777eddb19..43f40270d 100644 --- a/app/soapbox/reducers/chat_message_lists.js +++ b/app/soapbox/reducers/chat_message_lists.js @@ -1,5 +1,6 @@ import { CHATS_FETCH_SUCCESS, + CHATS_EXPAND_SUCCESS, CHAT_MESSAGES_FETCH_SUCCESS, CHAT_MESSAGE_SEND_REQUEST, CHAT_MESSAGE_SEND_SUCCESS, @@ -47,6 +48,7 @@ export default function chatMessageLists(state = initialState, action) { case CHAT_MESSAGE_SEND_REQUEST: return updateList(state, action.chatId, [action.uuid]); case CHATS_FETCH_SUCCESS: + case CHATS_EXPAND_SUCCESS: return importLastMessages(state, action.chats); case STREAMING_CHAT_UPDATE: if (action.chat.last_message && diff --git a/app/soapbox/reducers/chat_messages.js b/app/soapbox/reducers/chat_messages.js index 568725da3..e887f9ed4 100644 --- a/app/soapbox/reducers/chat_messages.js +++ b/app/soapbox/reducers/chat_messages.js @@ -1,5 +1,6 @@ import { CHATS_FETCH_SUCCESS, + CHATS_EXPAND_SUCCESS, CHAT_MESSAGES_FETCH_SUCCESS, CHAT_MESSAGE_SEND_REQUEST, CHAT_MESSAGE_SEND_SUCCESS, @@ -38,6 +39,7 @@ export default function chatMessages(state = initialState, action) { pending: true, })); case CHATS_FETCH_SUCCESS: + case CHATS_EXPAND_SUCCESS: return importLastMessages(state, fromJS(action.chats)); case CHAT_MESSAGES_FETCH_SUCCESS: return importMessages(state, fromJS(action.chatMessages)); diff --git a/app/soapbox/reducers/chats.js b/app/soapbox/reducers/chats.js index 56c2c4678..e67c21d12 100644 --- a/app/soapbox/reducers/chats.js +++ b/app/soapbox/reducers/chats.js @@ -1,5 +1,8 @@ import { CHATS_FETCH_SUCCESS, + CHATS_FETCH_REQUEST, + CHATS_EXPAND_SUCCESS, + CHATS_EXPAND_REQUEST, CHAT_FETCH_SUCCESS, CHAT_READ_SUCCESS, CHAT_READ_REQUEST, @@ -8,17 +11,29 @@ import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming'; import { normalizeChat } from 'soapbox/actions/importer/normalizer'; import { Map as ImmutableMap, fromJS } from 'immutable'; -const importChat = (state, chat) => state.set(chat.id, fromJS(normalizeChat(chat))); +const importChat = (state, chat) => state.setIn(['items', chat.id], fromJS(normalizeChat(chat))); -const importChats = (state, chats) => - state.withMutations(mutable => chats.forEach(chat => importChat(mutable, chat))); +const importChats = (state, chats, next) => + state.withMutations(mutable => { + if (next !== undefined) mutable.set('next', next); + chats.forEach(chat => importChat(mutable, chat)); + mutable.set('loading', false); + }); -const initialState = ImmutableMap(); +const initialState = ImmutableMap({ + next: null, + isLoading: false, + items: ImmutableMap({}), +}); export default function chats(state = initialState, action) { switch(action.type) { + case CHATS_FETCH_REQUEST: + case CHATS_EXPAND_REQUEST: + return state.set('loading', true); case CHATS_FETCH_SUCCESS: - return importChats(state, action.chats); + case CHATS_EXPAND_SUCCESS: + return importChats(state, action.chats, action.next); case STREAMING_CHAT_UPDATE: return importChats(state, [action.chat]); case CHAT_FETCH_SUCCESS: diff --git a/app/soapbox/selectors/index.js b/app/soapbox/selectors/index.js index 9594a5750..3f182c222 100644 --- a/app/soapbox/selectors/index.js +++ b/app/soapbox/selectors/index.js @@ -209,8 +209,8 @@ export const getAccountGallery = createSelector([ export const makeGetChat = () => { return createSelector( [ - (state, { id }) => state.getIn(['chats', id]), - (state, { id }) => state.getIn(['accounts', state.getIn(['chats', id, 'account'])]), + (state, { id }) => state.getIn(['chats', 'items', id]), + (state, { id }) => state.getIn(['accounts', state.getIn(['chats', 'items', id, 'account'])]), (state, { last_message }) => state.getIn(['chat_messages', last_message]), ], diff --git a/app/styles/chats.scss b/app/styles/chats.scss index 653344e5f..86b4da8ec 100644 --- a/app/styles/chats.scss +++ b/app/styles/chats.scss @@ -116,6 +116,10 @@ flex-direction: column; overflow: hidden; } + + .chat-list { + overflow-y: auto; + } } .audio-toggle .react-toggle-thumb { @@ -219,7 +223,6 @@ } .chat-list { - overflow-y: auto; flex: 1; &__content { @@ -233,6 +236,10 @@ align-items: start; } + .account { + border-bottom: none; + } + .account__display-name { position: relative; diff --git a/app/styles/placeholder.scss b/app/styles/placeholder.scss index 8366261ac..ded87f579 100644 --- a/app/styles/placeholder.scss +++ b/app/styles/placeholder.scss @@ -46,7 +46,8 @@ } .status__content--placeholder, -.display-name--placeholder { +.display-name--placeholder, +.chat-list-item--placeholder .chat__last-message { letter-spacing: -1px; color: var(--brand-color) !important; opacity: 0.1; From 53497c2132eb28a5272d8b9a90067e47335a3888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 14 Dec 2021 13:25:08 +0100 Subject: [PATCH 2/3] Chats: Create next link if not provided MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/chats.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/soapbox/actions/chats.js b/app/soapbox/actions/chats.js index 5407fa769..f89b97fdb 100644 --- a/app/soapbox/actions/chats.js +++ b/app/soapbox/actions/chats.js @@ -35,7 +35,11 @@ export function fetchChats() { return (dispatch, getState) => { dispatch({ type: CHATS_FETCH_REQUEST }); api(getState).get('/api/v2/pleroma/chats').then((response) => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); + let next = getLinks(response).refs.find(link => link.rel === 'next'); + + if (!next && response.data.length) { + next = { uri: `/api/v2/pleroma/chats?max_id=${response.data[response.data.length - 1].id}&offset=0` }; + } dispatch({ type: CHATS_FETCH_SUCCESS, chats: response.data, next: next ? next.uri : null }); }).catch(error => { From 6a48948af7366a2ef6cd1b7e90f18394051f3c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 14 Dec 2021 13:38:59 +0100 Subject: [PATCH 3/3] Use old chats list API conditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/chats.js | 29 ++++++++++++++++++++++++++--- app/soapbox/utils/features.js | 1 + 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/app/soapbox/actions/chats.js b/app/soapbox/actions/chats.js index f89b97fdb..2ca996cfe 100644 --- a/app/soapbox/actions/chats.js +++ b/app/soapbox/actions/chats.js @@ -1,5 +1,6 @@ import api, { getLinks } from '../api'; import { getSettings, changeSetting } from 'soapbox/actions/settings'; +import { getFeatures } from 'soapbox/utils/features'; import { v4 as uuidv4 } from 'uuid'; import { Map as ImmutableMap } from 'immutable'; @@ -31,9 +32,17 @@ export const CHAT_MESSAGE_DELETE_REQUEST = 'CHAT_MESSAGE_DELETE_REQUEST'; export const CHAT_MESSAGE_DELETE_SUCCESS = 'CHAT_MESSAGE_DELETE_SUCCESS'; export const CHAT_MESSAGE_DELETE_FAIL = 'CHAT_MESSAGE_DELETE_FAIL'; -export function fetchChats() { - return (dispatch, getState) => { - dispatch({ type: CHATS_FETCH_REQUEST }); +export function fetchChatsV1() { + return (dispatch, getState) => + api(getState).get('/api/v1/pleroma/chats').then((response) => { + dispatch({ type: CHATS_FETCH_SUCCESS, chats: response.data }); + }).catch(error => { + dispatch({ type: CHATS_FETCH_FAIL, error }); + }); +} + +export function fetchChatsV2() { + return (dispatch, getState) => api(getState).get('/api/v2/pleroma/chats').then((response) => { let next = getLinks(response).refs.find(link => link.rel === 'next'); @@ -45,6 +54,20 @@ export function fetchChats() { }).catch(error => { dispatch({ type: CHATS_FETCH_FAIL, error }); }); +} + +export function fetchChats() { + return (dispatch, getState) => { + const state = getState(); + const instance = state.get('instance'); + const features = getFeatures(instance); + + dispatch({ type: CHATS_FETCH_REQUEST }); + if (features.chatsV2) { + dispatch(fetchChatsV2()); + } else { + dispatch(fetchChatsV1()); + } }; } diff --git a/app/soapbox/utils/features.js b/app/soapbox/utils/features.js index bcab0098b..c544e7db5 100644 --- a/app/soapbox/utils/features.js +++ b/app/soapbox/utils/features.js @@ -54,6 +54,7 @@ export const getFeatures = createSelector([ importMutes: v.software === PLEROMA && gte(v.version, '2.2.0'), emailList: features.includes('email_list'), chats: v.software === PLEROMA && gte(v.version, '2.1.0'), + chatsV2: v.software === PLEROMA && gte(v.version, '2.3.0'), scopes: v.software === PLEROMA ? 'read write follow push admin' : 'read write follow push', federating: federation.get('enabled', true), // Assume true unless explicitly false richText: v.software === PLEROMA,