reply mentions modalto tsx

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
next-virtuoso-proof
marcin mikołajczak 2 years ago
parent 9d996b355d
commit fb3b88b80b

@ -32,7 +32,7 @@ const makeMapStateToProps = () => {
const me = state.get('me');
const account = state.getIn(['accounts', me]);
const parentTo = statusToMentionsAccountIdsArray(state, status, account);
const parentTo = statusToMentionsAccountIdsArray(status, account);
return {
to,

@ -1,130 +0,0 @@
import { List as ImmutableList } from 'immutable';
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
import { connect } from 'react-redux';
import { fetchFavourites, fetchReactions } from 'soapbox/actions/interactions';
import FilterBar from 'soapbox/components/filter_bar';
import ScrollableList from 'soapbox/components/scrollable_list';
import { Modal, Spinner } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
all: { id: 'reactions.all', defaultMessage: 'All' },
});
const mapStateToProps = (state, props) => {
const favourites = state.getIn(['user_lists', 'favourited_by', props.statusId]);
const reactions = state.getIn(['user_lists', 'reactions', props.statusId]);
const allReactions = favourites && reactions && ImmutableList(favourites ? [{ accounts: favourites, count: favourites.size, name: '👍' }] : []).concat(reactions || []);
return {
reactions: allReactions,
};
};
export default @connect(mapStateToProps)
@injectIntl
class ReactionsModal extends React.PureComponent {
static propTypes = {
onClose: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
statusId: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
reaction: PropTypes.string,
dispatch: PropTypes.func.isRequired,
reactions: ImmutablePropTypes.list,
};
state = {
reaction: this.props.reaction,
}
fetchData = () => {
const { dispatch, statusId } = this.props;
dispatch(fetchFavourites(statusId));
dispatch(fetchReactions(statusId));
}
componentDidMount() {
this.fetchData();
}
onClickClose = () => {
this.props.onClose('REACTIONS');
};
handleFilterChange = (reaction) => () => {
this.setState({ reaction });
};
renderFilterBar() {
const { intl, reactions } = this.props;
const { reaction } = this.state;
const items = [
{
text: intl.formatMessage(messages.all),
action: this.handleFilterChange(''),
name: 'all',
},
];
reactions.forEach(reaction => items.push(
{
text: `${reaction.name} ${reaction.count}`,
action: this.handleFilterChange(reaction.name),
name: reaction.name,
},
));
return <FilterBar className='reaction__filter-bar' items={items} active={reaction || 'all'} />;
}
render() {
const { reactions } = this.props;
const { reaction } = this.state;
const accounts = reactions && (reaction
? reactions.find(reaction => reaction.name === this.state.reaction)?.accounts.map(account => ({ id: account, reaction: this.state.reaction }))
: reactions.map(reaction => reaction?.accounts.map(account => ({ id: account, reaction: reaction.name }))).flatten());
let body;
if (!accounts) {
body = <Spinner />;
} else {
const emptyMessage = <FormattedMessage id='status.reactions.empty' defaultMessage='No one has reacted to this post yet. When someone does, they will show up here.' />;
body = (<>
{reactions.size > 0 && this.renderFilterBar()}
<ScrollableList
scrollKey='reactions'
emptyMessage={emptyMessage}
className='space-y-3'
>
{accounts.map((account) =>
<AccountContainer key={`${account.id}-${account.reaction}`} id={account.id} withNote={false} reaction={account.reaction} />,
)}
</ScrollableList>
</>);
}
return (
<Modal
title={<FormattedMessage id='column.reactions' defaultMessage='Reactions' />}
onClose={this.onClickClose}
>
{body}
</Modal>
);
}
}

@ -0,0 +1,110 @@
import { List as ImmutableList } from 'immutable';
import React from 'react';
import { useEffect } from 'react';
import { useState } from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fetchFavourites, fetchReactions } from 'soapbox/actions/interactions';
import FilterBar from 'soapbox/components/filter_bar';
import ScrollableList from 'soapbox/components/scrollable_list';
import { Modal, Spinner } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container';
import { useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
all: { id: 'reactions.all', defaultMessage: 'All' },
});
interface IReactionsModal {
onClose: (string: string) => void,
statusId: string,
username: string,
reaction?: string,
}
const ReactionsModal: React.FC<IReactionsModal> = ({ onClose, statusId, ...props }) => {
const dispatch = useDispatch();
const intl = useIntl();
const [reaction, setReaction] = useState(props.reaction);
const reactions = useAppSelector<Array<{
accounts: Array<string>,
count: number,
name: string,
}>>((state) => {
const favourites = state.user_lists.getIn(['favourited_by', statusId]);
const reactions = state.user_lists.getIn(['reactions', statusId]);
return favourites && reactions && ImmutableList(favourites ? [{ accounts: favourites, count: favourites.size, name: '👍' }] : []).concat(reactions || []);
});
const fetchData = () => {
dispatch(fetchFavourites(statusId));
dispatch(fetchReactions(statusId));
};
const onClickClose = () => {
onClose('REACTIONS');
};
const renderFilterBar = () => {
const items = [
{
text: intl.formatMessage(messages.all),
action: () => setReaction(''),
name: 'all',
},
];
reactions.forEach(reaction => items.push(
{
text: `${reaction.name} ${reaction.count}`,
action: () => setReaction(reaction.name),
name: reaction.name,
},
));
return <FilterBar className='reaction__filter-bar' items={items} active={reaction || 'all'} />;
};
useEffect(() => {
fetchData();
}, []);
const accounts = reactions && (reaction
? reactions.find(({ name }) => name === reaction)?.accounts.map(account => ({ id: account, reaction: reaction }))
: reactions.map(({ accounts, name }) => accounts.map(account => ({ id: account, reaction: name }))).flat());
let body;
if (!accounts) {
body = <Spinner />;
} else {
const emptyMessage = <FormattedMessage id='status.reactions.empty' defaultMessage='No one has reacted to this post yet. When someone does, they will show up here.' />;
body = (<>
{reactions.length > 0 && renderFilterBar()}
<ScrollableList
scrollKey='reactions'
emptyMessage={emptyMessage}
className='space-y-3'
>
{accounts.map((account) =>
<AccountContainer key={`${account.id}-${account.reaction}`} id={account.id} /* reaction={account.reaction} */ />,
)}
</ScrollableList>
</>);
}
return (
<Modal
title={<FormattedMessage id='column.reactions' defaultMessage='Reactions' />}
onClose={onClickClose}
>
{body}
</Modal>
);
};
export default ReactionsModal;

@ -1,85 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import IconButton from 'soapbox/components/icon_button';
import { statusToMentionsAccountIdsArray } from 'soapbox/reducers/compose';
import { makeGetStatus } from 'soapbox/selectors';
import Account from '../../reply_mentions/account';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
confirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
});
const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
return state => {
const status = getStatus(state, { id: state.getIn(['compose', 'in_reply_to']) });
if (!status) {
return {
isReply: false,
};
}
const me = state.get('me');
const account = state.getIn(['accounts', me]);
const mentions = statusToMentionsAccountIdsArray(state, status, account);
return {
mentions,
author: status.getIn(['account', 'id']),
isReply: true,
};
};
};
class ReplyMentionsModal extends ImmutablePureComponent {
static propTypes = {
mentions: ImmutablePropTypes.OrderedSet,
author: PropTypes.string,
intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
};
onClickClose = () => {
const { onClose } = this.props;
onClose('REPLY_MENTIONS');
};
render() {
const { intl, mentions, author } = this.props;
return (
<div className='modal-root__modal reply-mentions-modal'>
<div className='reply-mentions-modal__header'>
<IconButton
className='reply-mentions-modal__back'
src={require('@tabler/icons/icons/arrow-left.svg')}
onClick={this.onClickClose}
aria-label={intl.formatMessage(messages.close)}
title={intl.formatMessage(messages.close)}
/>
<h3 className='reply-mentions-modal__header__title'>
<FormattedMessage id='navigation_bar.in_reply_to' defaultMessage='In reply to' />
</h3>
</div>
<div className='reply-mentions-modal__accounts'>
{mentions.map(accountId => <Account key={accountId} accountId={accountId} added author={author === accountId} />)}
</div>
</div>
);
}
}
export default injectIntl(connect(makeMapStateToProps)(ReplyMentionsModal));

@ -0,0 +1,40 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { Modal } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks';
import { statusToMentionsAccountIdsArray } from 'soapbox/reducers/compose';
import { makeGetStatus } from 'soapbox/selectors';
import Account from '../../reply_mentions/account';
import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities';
interface IReplyMentionsModal {
onClose: (string: string) => void,
}
const ReplyMentionsModal: React.FC<IReplyMentionsModal> = ({ onClose }) => {
const status = useAppSelector<StatusEntity | null>(state => makeGetStatus()(state, { id: state.compose.get('in_reply_to') }));
const account = useAppSelector((state) => state.accounts.get(state.me));
const mentions = statusToMentionsAccountIdsArray(status, account);
const author = (status?.account as AccountEntity).id;
const onClickClose = () => {
onClose('REPLY_MENTIONS');
};
return (
<Modal
title={<FormattedMessage id='navigation_bar.in_reply_to' defaultMessage='In reply to' />}
onClose={onClickClose}
>
<div className='reply-mentions-modal__accounts'>
{mentions.map(accountId => <Account key={accountId} accountId={accountId} added author={author === accountId} />)}
</div>
</Modal>
);
};
export default ReplyMentionsModal;

@ -113,7 +113,7 @@ export const statusToMentionsArray = (status, account) => {
.delete(account.get('acct'));
};
export const statusToMentionsAccountIdsArray = (state, status, account) => {
export const statusToMentionsAccountIdsArray = (status, account) => {
const author = status.getIn(['account', 'id']);
const mentions = status.get('mentions', []).map(m => m.get('id'));

Loading…
Cancel
Save