Merge branch 'chats-pagination' into 'develop'

use `/api/v2/pleroma/chats`

See merge request soapbox-pub/soapbox-fe!911
This commit is contained in:
Alex Gleason
2021-12-14 15:13:11 +00:00
22 changed files with 201 additions and 64 deletions

View File

@ -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'),

View File

@ -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,

View File

@ -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()),
});

View File

@ -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 (
<div className='chat-list'>
<div className='chat-list__content'>
{chatIds.count() === 0 &&
<div className='empty-column-indicator'>{emptyMessage}</div>
}
{chatIds.map(chatId => (
<div key={chatId} className='chat-list-item'>
<Chat
chatId={chatId}
onClick={this.props.onClickChat}
/>
</div>
))}
</div>
</div>
<ScrollableList
className='chat-list'
scrollKey='awaiting-approval'
emptyMessage={intl.formatMessage(messages.emptyMessage)}
hasMore={hasMore}
isLoading={isLoading}
showLoading={isLoading && chatIds.size === 0}
onLoadMore={this.handleLoadMore}
onRefresh={this.props.onRefresh}
placeholderComponent={PlaceholderChat}
placeholderCount={20}
>
{chatIds.map(chatId => (
<div key={chatId} className='chat-list-item'>
<Chat
chatId={chatId}
onClick={this.props.onClickChat}
/>
</div>
))}
</ScrollableList>
);
}

View File

@ -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 {
<>
<ChatList
onClickChat={this.handleClickChat}
emptyMessage={<FormattedMessage id='chat_panels.main_window.empty' defaultMessage="No chats found. To start a chat, visit a user's profile." />}
/>
<AccountSearch
placeholder={intl.formatMessage(messages.searchPlaceholder)}

View File

@ -22,7 +22,7 @@ const makeMapStateToProps = () => {
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'),

View File

@ -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}
/>
<PullToRefresh onRefresh={this.handleRefresh}>
<ChatList
onClickChat={this.handleClickChat}
emptyMessage={<FormattedMessage id='chat_panels.main_window.empty' defaultMessage="No chats found. To start a chat, visit a user's profile." />}
/>
</PullToRefresh>
<ChatList
onClickChat={this.handleClickChat}
onRefresh={this.handleRefresh}
/>
</Column>
);
}

View File

@ -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 (
<div className='chat-list-item chat-list-item--placeholder'>
<div className='account'>
<div className='account__wrapper'>
<div className='account__display-name'>
<div className='account__avatar-wrapper'>
<PlaceholderAvatar size={36} />
</div>
<PlaceholderDisplayName minLength={3} maxLength={25} />
<span className='chat__last-message'>
{generateText(messageLength)}
</span>
</div>
</div>
</div>
</div>
);
}
}

View File

@ -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,
};
};