Add emoji reaction detail status

This commit is contained in:
KMY 2023-02-26 23:44:52 +09:00
parent de951a0ef9
commit a1485f242d
22 changed files with 393 additions and 16 deletions

View file

@ -0,0 +1,107 @@
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import ColumnHeader from 'mastodon/components/column_header';
import Icon from 'mastodon/components/icon';
import { fetchEmojiReactions, fetchFavourites } from 'mastodon/actions/interactions';
import LoadingIndicator from 'mastodon/components/loading_indicator';
import ScrollableList from 'mastodon/components/scrollable_list';
import AccountContainer from 'mastodon/containers/account_container';
import Column from 'mastodon/features/ui/components/column';
import { Helmet } from 'react-helmet';
import EmojiView from '../../components/emoji_view';
const messages = defineMessages({
refresh: { id: 'refresh', defaultMessage: 'Refresh' },
});
const mapStateToProps = (state, props) => { return {
accountIds: state.getIn(['user_lists', 'emoji_reactioned_by', props.params.statusId]),
} };
export default @connect(mapStateToProps)
@injectIntl
class EmojiReactions extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
accountIds: ImmutablePropTypes.list,
multiColumn: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
componentWillMount () {
if (!this.props.accountIds) {
this.props.dispatch(fetchEmojiReactions(this.props.params.statusId));
}
}
componentWillReceiveProps (nextProps) {
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
this.props.dispatch(fetchEmojiReactions(nextProps.params.statusId));
}
}
handleRefresh = () => {
this.props.dispatch(fetchEmojiReactions(this.props.params.statusId));
};
render () {
const { intl, accountIds, multiColumn } = this.props;
console.dir(accountIds);
if (!accountIds) {
return (
<Column>
<LoadingIndicator />
</Column>
);
}
let groups = {};
for (const emoji_reaction of accountIds) {
const key = emoji_reaction.account.id;
const value = emoji_reaction;
if (!groups[key]) groups[key] = [value];
else groups[key].push(value);
}
console.dir(groups)
const emptyMessage = <FormattedMessage id='empty_column.emoji_reactions' defaultMessage='No one has reacted with emoji this post yet. When someone does, they will show up here.' />;
return (
<Column bindToDocument={!multiColumn}>
<ColumnHeader
showBackButton
multiColumn={multiColumn}
extraButton={(
<button type='button' className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button>
)}
/>
<ScrollableList
scrollKey='emoji_reactions'
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
>
{Object.keys(groups).map((key, index) =>(
<AccountContainer key={index} id={key} withNote={false}>
<div style={ { 'max-width': '100px' } }>
{groups[key].map((value) => <EmojiView name={value.name} url={value.url} staticUrl={value.static_url} />)}
</div>
</AccountContainer>
))}
</ScrollableList>
<Helmet>
<meta name='robots' content='noindex' />
</Helmet>
</Column>
);
}
}

View file

@ -49,6 +49,8 @@ class DetailedStatus extends ImmutablePureComponent {
available: PropTypes.bool,
}),
onToggleMediaVisibility: PropTypes.func,
onEmojiReact: PropTypes.func,
onUnEmojiReact: PropTypes.func,
};
state = {
@ -191,7 +193,7 @@ class DetailedStatus extends ImmutablePureComponent {
let emojiReactionsBar = null;
if (status.get('emoji_reactions')) {
const emojiReactions = status.get('emoji_reactions');
emojiReactionsBar = <StatusEmojiReactionsBar emojiReactions={emojiReactions} status={status} onEmojiReaction={this.props.onEmojiReaction} OnUnEmojiReaction={this.props.OnUnEmojiReaction} />;
emojiReactionsBar = <StatusEmojiReactionsBar emojiReactions={emojiReactions} status={status} onEmojiReact={this.props.onEmojiReact} onUnEmojiReact={this.props.onUnEmojiReact} />;
}
if (status.get('application')) {

View file

@ -14,6 +14,7 @@ import {
pin,
unpin,
emojiReact,
unEmojiReact,
} from '../../../actions/interactions';
import {
muteStatus,
@ -98,6 +99,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(emojiReact(status, emoji));
},
onUnEmojiReact (status, emoji) {
dispatch(unEmojiReact(status, emoji));
},
onPin (status) {
if (status.get('pinned')) {
dispatch(unpin(status));

View file

@ -24,6 +24,8 @@ import Column from '../ui/components/column';
import {
favourite,
unfavourite,
emojiReact,
unEmojiReact,
bookmark,
unbookmark,
reblog,
@ -254,6 +256,16 @@ class Status extends ImmutablePureComponent {
}
};
handleEmojiReact = (status, emoji) => {
const { dispatch } = this.props;
dispatch(emojiReact(status, emoji));
};
handleUnEmojiReact = (status, emoji) => {
const { dispatch } = this.props;
dispatch(unEmojiReact(status, emoji));
};
handlePin = (status) => {
if (status.get('pinned')) {
this.props.dispatch(unpin(status));
@ -644,6 +656,8 @@ class Status extends ImmutablePureComponent {
showMedia={this.state.showMedia}
onToggleMediaVisibility={this.handleToggleMediaVisibility}
pictureInPicture={pictureInPicture}
onEmojiReact={this.handleEmojiReact}
onUnEmojiReact={this.handleUnEmojiReact}
/>
<ActionBar
@ -651,6 +665,7 @@ class Status extends ImmutablePureComponent {
status={status}
onReply={this.handleReplyClick}
onFavourite={this.handleFavouriteClick}
onEmojiReact={this.handleEmojiReact}
onReblog={this.handleReblogClick}
onBookmark={this.handleBookmarkClick}
onDelete={this.handleDeleteClick}

View file

@ -36,6 +36,7 @@ import {
Following,
Reblogs,
Favourites,
EmojiReactions,
DirectTimeline,
HashtagTimeline,
Notifications,
@ -206,6 +207,7 @@ class SwitchingColumnsArea extends React.PureComponent {
<WrappedRoute path='/@:acct/:statusId' exact component={Status} content={children} />
<WrappedRoute path='/@:acct/:statusId/reblogs' component={Reblogs} content={children} />
<WrappedRoute path='/@:acct/:statusId/favourites' component={Favourites} content={children} />
<WrappedRoute path='/@:acct/:statusId/emoji_reactions' component={EmojiReactions} content={children} />
{/* Legacy routes, cannot be easily factored with other routes because they share a param name */}
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
@ -213,6 +215,7 @@ class SwitchingColumnsArea extends React.PureComponent {
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} />
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
<WrappedRoute path='/statuses/:statusId/emoji_reactions' component={EmojiReactions} content={children} />
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
<WrappedRoute path='/blocks' component={Blocks} content={children} />

View file

@ -78,6 +78,10 @@ export function Favourites () {
return import(/* webpackChunkName: "features/favourites" */'../../favourites');
}
export function EmojiReactions () {
return import(/* webpackChunkName: "features/favourites" */'../../emoji_reactions');
}
export function FollowRequests () {
return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests');
}