From 46ca470f01802818bf2bdb5c165fffa627e439c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 6 Jan 2022 14:43:58 +0100 Subject: [PATCH] Turn some pages into modals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../components/status_reply_mentions.js | 31 ++++- app/soapbox/features/favourites/index.js | 72 ---------- app/soapbox/features/mentions/index.js | 96 ------------- app/soapbox/features/reactions/index.js | 128 ------------------ app/soapbox/features/reblogs/index.js | 100 -------------- .../components/status_interaction_bar.js | 76 +++++++++-- .../ui/components/favourites_modal.js | 90 ++++++++++++ .../features/ui/components/mentions_modal.js | 94 +++++++++++++ .../features/ui/components/modal_root.js | 8 ++ .../features/ui/components/reactions_modal.js | 119 ++++++++++++++++ .../features/ui/components/reblogs_modal.js | 92 +++++++++++++ app/soapbox/features/ui/index.js | 8 -- .../features/ui/util/async-components.js | 32 ++--- app/styles/components/modal.scss | 15 +- 14 files changed, 529 insertions(+), 432 deletions(-) delete mode 100644 app/soapbox/features/favourites/index.js delete mode 100644 app/soapbox/features/mentions/index.js delete mode 100644 app/soapbox/features/reactions/index.js delete mode 100644 app/soapbox/features/reblogs/index.js create mode 100644 app/soapbox/features/ui/components/favourites_modal.js create mode 100644 app/soapbox/features/ui/components/mentions_modal.js create mode 100644 app/soapbox/features/ui/components/reactions_modal.js create mode 100644 app/soapbox/features/ui/components/reblogs_modal.js diff --git a/app/soapbox/components/status_reply_mentions.js b/app/soapbox/components/status_reply_mentions.js index 580880fee..1bb43562d 100644 --- a/app/soapbox/components/status_reply_mentions.js +++ b/app/soapbox/components/status_reply_mentions.js @@ -1,15 +1,35 @@ import React from 'react'; +import { connect } from 'react-redux'; import { FormattedMessage, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; +import { openModal } from 'soapbox/actions/modal'; -export default @injectIntl +const mapDispatchToProps = (dispatch) => ({ + onOpenMentionsModal(username, statusId) { + dispatch(openModal('MENTIONS', { + username, + statusId, + })); + }, +}); + +export default @connect(null, mapDispatchToProps) +@injectIntl class StatusReplyMentions extends ImmutablePureComponent { static propTypes = { status: ImmutablePropTypes.map.isRequired, + onOpenMentionsModal: PropTypes.func, + } + + handleOpenMentionsModal = () => { + const { status, onOpenMentionsModal } = this.props; + + onOpenMentionsModal(status.getIn(['account', 'acct']), status.get('id')); } render() { @@ -54,6 +74,7 @@ class StatusReplyMentions extends ImmutablePureComponent { } } + // The typical case with a reply-to and a list of mentions. return (
@@ -68,9 +89,13 @@ class StatusReplyMentions extends ImmutablePureComponent { {' '} )), more: to.size > 2 && ( - + + - + ), }} /> diff --git a/app/soapbox/features/favourites/index.js b/app/soapbox/features/favourites/index.js deleted file mode 100644 index 7bf1852d9..000000000 --- a/app/soapbox/features/favourites/index.js +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import PropTypes from 'prop-types'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import LoadingIndicator from '../../components/loading_indicator'; -import { fetchFavourites } from '../../actions/interactions'; -import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; -import AccountContainer from '../../containers/account_container'; -import Column from '../ui/components/column'; -import ScrollableList from '../../components/scrollable_list'; - -const messages = defineMessages({ - heading: { id: 'column.favourites', defaultMessage: 'Likes' }, -}); - -const mapStateToProps = (state, props) => ({ - accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]), -}); - -export default @connect(mapStateToProps) -@injectIntl -class Favourites extends ImmutablePureComponent { - - static propTypes = { - intl: PropTypes.object.isRequired, - params: PropTypes.object.isRequired, - dispatch: PropTypes.func.isRequired, - accountIds: ImmutablePropTypes.orderedSet, - }; - - componentDidMount() { - this.props.dispatch(fetchFavourites(this.props.params.statusId)); - } - - componentDidUpdate(prevProps) { - const { statusId } = this.props.params; - const { prevStatusId } = prevProps.params; - - if (statusId !== prevStatusId && statusId) { - this.props.dispatch(fetchFavourites(statusId)); - } - } - - render() { - const { intl, accountIds } = this.props; - - if (!accountIds) { - return ( - - - - ); - } - - const emptyMessage = ; - - return ( - - - {accountIds.map(id => - , - )} - - - ); - } - -} diff --git a/app/soapbox/features/mentions/index.js b/app/soapbox/features/mentions/index.js deleted file mode 100644 index d77e7b8f2..000000000 --- a/app/soapbox/features/mentions/index.js +++ /dev/null @@ -1,96 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { OrderedSet as ImmutableOrderedSet } from 'immutable'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import PropTypes from 'prop-types'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import LoadingIndicator from '../../components/loading_indicator'; -import MissingIndicator from '../../components/missing_indicator'; -import { fetchStatus } from '../../actions/statuses'; -import { injectIntl, defineMessages } from 'react-intl'; -import AccountContainer from '../../containers/account_container'; -import Column from '../ui/components/column'; -import ScrollableList from '../../components/scrollable_list'; -import { makeGetStatus } from '../../selectors'; - -const messages = defineMessages({ - heading: { id: 'column.mentions', defaultMessage: 'Mentions' }, -}); - -const mapStateToProps = (state, props) => { - const getStatus = makeGetStatus(); - const status = getStatus(state, { - id: props.params.statusId, - username: props.params.username, - }); - - return { - status, - accountIds: status ? ImmutableOrderedSet(status.get('mentions').map(m => m.get('id'))) : null, - }; -}; - -export default @connect(mapStateToProps) -@injectIntl -class Mentions extends ImmutablePureComponent { - - static propTypes = { - intl: PropTypes.object.isRequired, - params: PropTypes.object.isRequired, - dispatch: PropTypes.func.isRequired, - accountIds: ImmutablePropTypes.orderedSet, - status: ImmutablePropTypes.map, - }; - - fetchData = () => { - const { dispatch, params } = this.props; - const { statusId } = params; - - dispatch(fetchStatus(statusId)); - } - - componentDidMount() { - this.fetchData(); - } - - componentDidUpdate(prevProps) { - const { params } = this.props; - - if (params.statusId !== prevProps.params.statusId) { - this.fetchData(); - } - } - - render() { - const { intl, accountIds, status } = this.props; - - if (!accountIds) { - return ( - - - - ); - } - - if (!status) { - return ( - - - - ); - } - - return ( - - - {accountIds.map(id => - , - )} - - - ); - } - -} diff --git a/app/soapbox/features/reactions/index.js b/app/soapbox/features/reactions/index.js deleted file mode 100644 index 5750c3053..000000000 --- a/app/soapbox/features/reactions/index.js +++ /dev/null @@ -1,128 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { OrderedSet as ImmutableOrderedSet } from 'immutable'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import PropTypes from 'prop-types'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; -import LoadingIndicator from '../../components/loading_indicator'; -import MissingIndicator from '../../components/missing_indicator'; -import { fetchFavourites, fetchReactions } from '../../actions/interactions'; -import { fetchStatus } from '../../actions/statuses'; -import AccountContainer from '../../containers/account_container'; -import Column from '../ui/components/column'; -import ScrollableList from '../../components/scrollable_list'; -import { makeGetStatus } from '../../selectors'; - -const messages = defineMessages({ - heading: { id: 'column.reactions', defaultMessage: 'Reactions' }, -}); - -const mapStateToProps = (state, props) => { - const getStatus = makeGetStatus(); - const status = getStatus(state, { - id: props.params.statusId, - username: props.params.username, - }); - - const favourites = state.getIn(['user_lists', 'favourited_by', props.params.statusId]); - const reactions = state.getIn(['user_lists', 'reactions', props.params.statusId]); - const allReactions = favourites && reactions && ImmutableOrderedSet(favourites ? [{ accounts: favourites, count: favourites.size, name: '👍' }] : []).union(reactions || []); - - return { - status, - reactions: allReactions, - accounts: allReactions && (props.params.reaction - ? allReactions.find(reaction => reaction.name === props.params.reaction).accounts.map(account => ({ id: account, reaction: props.params.reaction })) - : allReactions.map(reaction => reaction.accounts.map(account => ({ id: account, reaction: reaction.name }))).flatten()), - }; -}; - -export default @connect(mapStateToProps) -@injectIntl -class Reactions extends ImmutablePureComponent { - - static contextTypes = { - router: PropTypes.object.isRequired, - }; - - static propTypes = { - params: PropTypes.object.isRequired, - dispatch: PropTypes.func.isRequired, - reactions: ImmutablePropTypes.orderedSet, - accounts: ImmutablePropTypes.orderedSet, - status: ImmutablePropTypes.map, - }; - - fetchData = () => { - const { dispatch, params } = this.props; - const { statusId } = params; - - dispatch(fetchFavourites(statusId)); - dispatch(fetchReactions(statusId)); - dispatch(fetchStatus(statusId)); - } - - componentDidMount() { - this.fetchData(); - } - - componentDidUpdate(prevProps) { - const { params } = this.props; - - if (params.statusId !== prevProps.params.statusId) { - this.fetchData(); - } - } - - handleFilterChange = (reaction) => () => { - const { params } = this.props; - const { username, statusId } = params; - - this.context.router.history.replace(`/@${username}/posts/${statusId}/reactions/${reaction}`); - }; - - render() { - const { intl, params, reactions, accounts, status } = this.props; - - if (!accounts) { - return ( - - - - ); - } - - if (!status) { - return ( - - - - ); - } - - const emptyMessage = ; - - return ( - - { - reactions.size > 0 && ( -
- - {reactions?.filter(reaction => reaction.count).map(reaction => )} -
- ) - } - - {accounts.map((account) => - , - )} - -
- ); - } - -} diff --git a/app/soapbox/features/reblogs/index.js b/app/soapbox/features/reblogs/index.js deleted file mode 100644 index ea536940c..000000000 --- a/app/soapbox/features/reblogs/index.js +++ /dev/null @@ -1,100 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import PropTypes from 'prop-types'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import LoadingIndicator from '../../components/loading_indicator'; -import MissingIndicator from '../../components/missing_indicator'; -import { fetchReblogs } from '../../actions/interactions'; -import { fetchStatus } from '../../actions/statuses'; -import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; -import AccountContainer from '../../containers/account_container'; -import Column from '../ui/components/column'; -import ScrollableList from '../../components/scrollable_list'; -import { makeGetStatus } from '../../selectors'; - -const messages = defineMessages({ - heading: { id: 'column.reblogs', defaultMessage: 'Reposts' }, -}); - -const mapStateToProps = (state, props) => { - const getStatus = makeGetStatus(); - const status = getStatus(state, { - id: props.params.statusId, - username: props.params.username, - }); - - return { - status, - accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]), - }; -}; - -export default @connect(mapStateToProps) -@injectIntl -class Reblogs extends ImmutablePureComponent { - - static propTypes = { - intl: PropTypes.object.isRequired, - params: PropTypes.object.isRequired, - dispatch: PropTypes.func.isRequired, - accountIds: ImmutablePropTypes.orderedSet, - status: ImmutablePropTypes.map, - }; - - fetchData = () => { - const { dispatch, params } = this.props; - const { statusId } = params; - - dispatch(fetchReblogs(statusId)); - dispatch(fetchStatus(statusId)); - } - - componentDidMount() { - this.fetchData(); - } - - componentDidUpdate(prevProps) { - const { params } = this.props; - - if (params.statusId !== prevProps.params.statusId) { - this.fetchData(); - } - } - - render() { - const { intl, accountIds, status } = this.props; - - if (!accountIds) { - return ( - - - - ); - } - - if (!status) { - return ( - - - - ); - } - - const emptyMessage = ; - - return ( - - - {accountIds.map(id => - , - )} - - - ); - } - -} diff --git a/app/soapbox/features/status/components/status_interaction_bar.js b/app/soapbox/features/status/components/status_interaction_bar.js index b75769d66..e7f2b0f05 100644 --- a/app/soapbox/features/status/components/status_interaction_bar.js +++ b/app/soapbox/features/status/components/status_interaction_bar.js @@ -8,9 +8,9 @@ import emojify from 'soapbox/features/emoji/emoji'; import { reduceEmoji } from 'soapbox/utils/emoji_reacts'; import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types'; import { getFeatures } from 'soapbox/utils/features'; -import { Link } from 'react-router-dom'; import Icon from 'soapbox/components/icon'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; +import { openModal } from 'soapbox/actions/modal'; const mapStateToProps = state => { const instance = state.get('instance'); @@ -21,7 +21,29 @@ const mapStateToProps = state => { }; }; -export default @connect(mapStateToProps) +const mapDispatchToProps = (dispatch) => ({ + onOpenReblogsModal(username, statusId) { + dispatch(openModal('REBLOGS', { + username, + statusId, + })); + }, + onOpenFavouritesModal(username, statusId) { + dispatch(openModal('FAVOURITES', { + username, + statusId, + })); + }, + onOpenReactionsModal(username, statusId, reaction) { + dispatch(openModal('REACTIONS', { + username, + statusId, + reaction, + })); + }, +}); + +export default @connect(mapStateToProps, mapDispatchToProps) class StatusInteractionBar extends ImmutablePureComponent { static propTypes = { @@ -29,6 +51,8 @@ class StatusInteractionBar extends ImmutablePureComponent { me: SoapboxPropTypes.me, allowedEmoji: ImmutablePropTypes.list, features: PropTypes.object.isRequired, + onOpenReblogsModal: PropTypes.func, + onOpenReactionsModal: PropTypes.func, } getNormalizedReacts = () => { @@ -41,22 +65,39 @@ class StatusInteractionBar extends ImmutablePureComponent { ).reverse(); } + handleOpenReblogsModal = () => { + const { status, onOpenReblogsModal } = this.props; + + onOpenReblogsModal(status.getIn(['account', 'acct']), status.get('id')); + } + getReposts = () => { const { status } = this.props; + if (status.get('reblogs_count')) { return ( - + - + ); } return ''; } + handleOpenFavouritesModal = () => { + const { status, onOpenFavouritesModal } = this.props; + + onOpenFavouritesModal(status.getIn(['account', 'acct']), status.get('id')); + } + getFavourites = () => { const { features, status } = this.props; @@ -72,9 +113,13 @@ class StatusInteractionBar extends ImmutablePureComponent { if (features.exposableReactions) { return ( - + {favourites} - + ); } else { return ( @@ -88,8 +133,14 @@ class StatusInteractionBar extends ImmutablePureComponent { return ''; } + handleOpenReactionsModal = (reaction) => () => { + const { status, onOpenReactionsModal } = this.props; + + onOpenReactionsModal(status.getIn(['account', 'acct']), status.get('id'), reaction.get('name')); + } + getEmojiReacts = () => { - const { status, features } = this.props; + const { features } = this.props; const emojiReacts = this.getNormalizedReacts(); const count = emojiReacts.reduce((acc, cur) => ( @@ -112,7 +163,16 @@ class StatusInteractionBar extends ImmutablePureComponent { ); if (features.exposableReactions) { - return {emojiReact}; + return ( + + {emojiReact} + + ); } return {emojiReact}; diff --git a/app/soapbox/features/ui/components/favourites_modal.js b/app/soapbox/features/ui/components/favourites_modal.js new file mode 100644 index 000000000..f0cf51965 --- /dev/null +++ b/app/soapbox/features/ui/components/favourites_modal.js @@ -0,0 +1,90 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { injectIntl, FormattedMessage, defineMessages } from 'react-intl'; +import IconButton from 'soapbox/components/icon_button'; +import LoadingIndicator from 'soapbox/components/loading_indicator'; +import AccountContainer from 'soapbox/containers/account_container'; +import ScrollableList from 'soapbox/components/scrollable_list'; +import { fetchFavourites } from 'soapbox/actions/interactions'; + +const messages = defineMessages({ + close: { id: 'lightbox.close', defaultMessage: 'Close' }, +}); + +const mapStateToProps = (state, props) => { + return { + accountIds: state.getIn(['user_lists', 'favourited_by', props.statusId]), + }; +}; + +export default @connect(mapStateToProps) +@injectIntl +class FavouritesModal extends React.PureComponent { + + static propTypes = { + onClose: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + statusId: PropTypes.string.isRequired, + username: PropTypes.string.isRequired, + dispatch: PropTypes.func.isRequired, + accountIds: ImmutablePropTypes.orderedSet, + }; + + fetchData = () => { + const { dispatch, statusId } = this.props; + + dispatch(fetchFavourites(statusId)); + } + + componentDidMount() { + this.fetchData(); + } + + onClickClose = () => { + this.props.onClose('FAVOURITES'); + }; + + render() { + const { intl, accountIds } = this.props; + + let body; + + if (!accountIds) { + body = ; + } else { + const emptyMessage = ; + + body = ( + + {accountIds.map(id => + , + )} + + ); + } + + + return ( +
+
+

+ +

+ +
+ {body} +
+ ); + } + +} diff --git a/app/soapbox/features/ui/components/mentions_modal.js b/app/soapbox/features/ui/components/mentions_modal.js new file mode 100644 index 000000000..b1554c2d2 --- /dev/null +++ b/app/soapbox/features/ui/components/mentions_modal.js @@ -0,0 +1,94 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { OrderedSet as ImmutableOrderedSet } from 'immutable'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; +import IconButton from 'soapbox/components/icon_button'; +import LoadingIndicator from 'soapbox/components/loading_indicator'; +import AccountContainer from 'soapbox/containers/account_container'; +import ScrollableList from 'soapbox/components/scrollable_list'; +import { makeGetStatus } from 'soapbox/selectors'; +import { fetchStatus } from 'soapbox/actions/statuses'; + +const messages = defineMessages({ + close: { id: 'lightbox.close', defaultMessage: 'Close' }, +}); + +const mapStateToProps = (state, props) => { + const getStatus = makeGetStatus(); + const status = getStatus(state, { + id: props.statusId, + username: props.username, + }); + + return { + accountIds: status ? ImmutableOrderedSet(status.get('mentions').map(m => m.get('id'))) : null, + }; +}; + +export default @connect(mapStateToProps) +@injectIntl +class MentionsModal extends React.PureComponent { + + static propTypes = { + onClose: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + statusId: PropTypes.string.isRequired, + username: PropTypes.string.isRequired, + dispatch: PropTypes.func.isRequired, + accountIds: ImmutablePropTypes.orderedSet, + }; + + fetchData = () => { + const { dispatch, statusId } = this.props; + + dispatch(fetchStatus(statusId)); + } + + componentDidMount() { + this.fetchData(); + } + + onClickClose = () => { + this.props.onClose('MENTIONS'); + }; + + render() { + const { intl, accountIds } = this.props; + + let body; + + if (!accountIds) { + body = ; + } else { + body = ( + + {accountIds.map(id => + , + )} + + ); + } + + return ( +
+
+

+ +

+ +
+ {body} +
+ ); + } + +} diff --git a/app/soapbox/features/ui/components/modal_root.js b/app/soapbox/features/ui/components/modal_root.js index dd5d4970b..52f94871d 100644 --- a/app/soapbox/features/ui/components/modal_root.js +++ b/app/soapbox/features/ui/components/modal_root.js @@ -25,6 +25,10 @@ import { UnauthorizedModal, EditFederationModal, ComponentModal, + ReactionsModal, + FavouritesModal, + ReblogsModal, + MentionsModal, } from '../../../features/ui/util/async-components'; const MODAL_COMPONENTS = { @@ -47,6 +51,10 @@ const MODAL_COMPONENTS = { 'CRYPTO_DONATE': CryptoDonateModal, 'EDIT_FEDERATION': EditFederationModal, 'COMPONENT': ComponentModal, + 'REBLOGS': ReblogsModal, + 'FAVOURITES': FavouritesModal, + 'REACTIONS': ReactionsModal, + 'MENTIONS': MentionsModal, }; export default class ModalRoot extends React.PureComponent { diff --git a/app/soapbox/features/ui/components/reactions_modal.js b/app/soapbox/features/ui/components/reactions_modal.js new file mode 100644 index 000000000..8b905ac25 --- /dev/null +++ b/app/soapbox/features/ui/components/reactions_modal.js @@ -0,0 +1,119 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { List as ImmutableList } from 'immutable'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { injectIntl, FormattedMessage, defineMessages } from 'react-intl'; +import IconButton from 'soapbox/components/icon_button'; +import LoadingIndicator from 'soapbox/components/loading_indicator'; +import AccountContainer from 'soapbox/containers/account_container'; +import ScrollableList from 'soapbox/components/scrollable_list'; +import { fetchFavourites, fetchReactions } from 'soapbox/actions/interactions'; + +const messages = defineMessages({ + close: { id: 'lightbox.close', defaultMessage: 'Close' }, +}); + +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 }); + }; + + render() { + const { intl, 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 = ; + } else { + const emptyMessage = ; + + body = (<> + { + reactions.size > 0 && ( +
+ + {reactions?.filter(reaction => reaction.count).map(reaction => )} +
+ ) + } + + {accounts.map((account) => + , + )} + + ); + } + + + return ( +
+
+

+ +

+ +
+ {body} +
+ ); + } + +} diff --git a/app/soapbox/features/ui/components/reblogs_modal.js b/app/soapbox/features/ui/components/reblogs_modal.js new file mode 100644 index 000000000..c6d4811f5 --- /dev/null +++ b/app/soapbox/features/ui/components/reblogs_modal.js @@ -0,0 +1,92 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { injectIntl, FormattedMessage, defineMessages } from 'react-intl'; +import IconButton from 'soapbox/components/icon_button'; +import LoadingIndicator from 'soapbox/components/loading_indicator'; +import AccountContainer from 'soapbox/containers/account_container'; +import ScrollableList from 'soapbox/components/scrollable_list'; +import { fetchReblogs } from 'soapbox/actions/interactions'; +import { fetchStatus } from 'soapbox/actions/statuses'; + +const messages = defineMessages({ + close: { id: 'lightbox.close', defaultMessage: 'Close' }, +}); + +const mapStateToProps = (state, props) => { + return { + accountIds: state.getIn(['user_lists', 'reblogged_by', props.statusId]), + }; +}; + +export default @connect(mapStateToProps) +@injectIntl +class ReblogsModal extends React.PureComponent { + + static propTypes = { + onClose: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + statusId: PropTypes.string.isRequired, + username: PropTypes.string.isRequired, + dispatch: PropTypes.func.isRequired, + accountIds: ImmutablePropTypes.orderedSet, + }; + + fetchData = () => { + const { dispatch, statusId } = this.props; + + dispatch(fetchReblogs(statusId)); + dispatch(fetchStatus(statusId)); + } + + componentDidMount() { + this.fetchData(); + } + + onClickClose = () => { + this.props.onClose('REBLOGS'); + }; + + render() { + const { intl, accountIds } = this.props; + + let body; + + if (!accountIds) { + body = ; + } else { + const emptyMessage = ; + + body = ( + + {accountIds.map(id => + , + )} + + ); + } + + + return ( +
+
+

+ +

+ +
+ {body} +
+ ); + } + +} diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index 2de0dcbfb..9f13aedf4 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -56,10 +56,6 @@ import { HomeTimeline, Followers, Following, - Reblogs, - Reactions, - Mentions, - Favourites, DirectTimeline, Conversations, HashtagTimeline, @@ -298,10 +294,6 @@ class SwitchingColumnsArea extends React.PureComponent { - - - - diff --git a/app/soapbox/features/ui/util/async-components.js b/app/soapbox/features/ui/util/async-components.js index 69dd8aff6..c80b53ee9 100644 --- a/app/soapbox/features/ui/util/async-components.js +++ b/app/soapbox/features/ui/util/async-components.js @@ -94,22 +94,6 @@ export function Following() { return import(/* webpackChunkName: "features/following" */'../../following'); } -export function Reblogs() { - return import(/* webpackChunkName: "features/reblogs" */'../../reblogs'); -} - -export function Reactions() { - return import(/* webpackChunkName: "features/reactions" */'../../reactions'); -} - -export function Mentions() { - return import(/* webpackChunkName: "features/mentions" */'../../mentions'); -} - -export function Favourites() { - return import(/* webpackChunkName: "features/favourites" */'../../favourites'); -} - export function FollowRequests() { return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests'); } @@ -214,6 +198,22 @@ export function ComponentModal() { return import(/* webpackChunkName: "features/ui" */'../components/component_modal'); } +export function ReblogsModal() { + return import(/* webpackChunkName: "features/ui" */'../components/reblogs_modal'); +} + +export function FavouritesModal() { + return import(/* webpackChunkName: "features/ui" */'../components/favourites_modal'); +} + +export function ReactionsModal() { + return import(/* webpackChunkName: "features/ui" */'../components/reactions_modal'); +} + +export function MentionsModal() { + return import(/* webpackChunkName: "features/ui" */'../components/mentions_modal'); +} + export function ListEditor() { return import(/* webpackChunkName: "features/list_editor" */'../../list_editor'); } diff --git a/app/styles/components/modal.scss b/app/styles/components/modal.scss index b39da7545..4c1a61598 100644 --- a/app/styles/components/modal.scss +++ b/app/styles/components/modal.scss @@ -339,7 +339,10 @@ .confirmation-modal, .report-modal, .actions-modal, -.mute-modal { +.mute-modal, +.reactions-modal, +.reblogs-modal, +.mentions-modal { position: relative; flex-direction: column; overflow: hidden; @@ -385,6 +388,16 @@ } } +.reactions-modal, +.reblogs-modal, +.mentions-modal { + height: 80vh; + + .slist { + overflow: auto; + } +} + .boost-modal__container { overflow-x: scroll; padding: 10px;