![Patrick Figel](/assets/img/avatar_default.png)
This introduces a new per-user preference called "Auto-play animated GIFs", which is enabled by default. When a user disables this setting, gifs in toots become click-to-play. Previews of animated gifs were changed to display the video play button so that users can distinguish them from regular images. This setting also affects account avatars in the detailed account view, which was changed to use the same hover-to-play mechanism that is used for animated avatars in timelines. Fixes #1652
172 lines
5 KiB
JavaScript
172 lines
5 KiB
JavaScript
import { connect } from 'react-redux';
|
|
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
import { fetchStatus } from '../../actions/statuses';
|
|
import Immutable from 'immutable';
|
|
import EmbeddedStatus from '../../components/status';
|
|
import MissingIndicator from '../../components/missing_indicator';
|
|
import DetailedStatus from './components/detailed_status';
|
|
import ActionBar from './components/action_bar';
|
|
import Column from '../ui/components/column';
|
|
import {
|
|
favourite,
|
|
unfavourite,
|
|
reblog,
|
|
unreblog
|
|
} from '../../actions/interactions';
|
|
import {
|
|
replyCompose,
|
|
mentionCompose
|
|
} from '../../actions/compose';
|
|
import { deleteStatus } from '../../actions/statuses';
|
|
import { initReport } from '../../actions/reports';
|
|
import {
|
|
makeGetStatus,
|
|
getStatusAncestors,
|
|
getStatusDescendants
|
|
} from '../../selectors';
|
|
import { ScrollContainer } from 'react-router-scroll';
|
|
import ColumnBackButton from '../../components/column_back_button';
|
|
import StatusContainer from '../../containers/status_container';
|
|
import { openModal } from '../../actions/modal';
|
|
import { isMobile } from '../../is_mobile'
|
|
|
|
const makeMapStateToProps = () => {
|
|
const getStatus = makeGetStatus();
|
|
|
|
const mapStateToProps = (state, props) => ({
|
|
status: getStatus(state, Number(props.params.statusId)),
|
|
ancestorsIds: state.getIn(['timelines', 'ancestors', Number(props.params.statusId)]),
|
|
descendantsIds: state.getIn(['timelines', 'descendants', Number(props.params.statusId)]),
|
|
me: state.getIn(['meta', 'me']),
|
|
boostModal: state.getIn(['meta', 'boost_modal']),
|
|
autoPlayGif: state.getIn(['meta', 'auto_play_gif'])
|
|
});
|
|
|
|
return mapStateToProps;
|
|
};
|
|
|
|
const Status = React.createClass({
|
|
contextTypes: {
|
|
router: React.PropTypes.object
|
|
},
|
|
|
|
propTypes: {
|
|
params: React.PropTypes.object.isRequired,
|
|
dispatch: React.PropTypes.func.isRequired,
|
|
status: ImmutablePropTypes.map,
|
|
ancestorsIds: ImmutablePropTypes.list,
|
|
descendantsIds: ImmutablePropTypes.list,
|
|
me: React.PropTypes.number,
|
|
boostModal: React.PropTypes.bool,
|
|
autoPlayGif: React.PropTypes.bool
|
|
},
|
|
|
|
mixins: [PureRenderMixin],
|
|
|
|
componentWillMount () {
|
|
this.props.dispatch(fetchStatus(Number(this.props.params.statusId)));
|
|
},
|
|
|
|
componentWillReceiveProps (nextProps) {
|
|
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
|
this.props.dispatch(fetchStatus(Number(nextProps.params.statusId)));
|
|
}
|
|
},
|
|
|
|
handleFavouriteClick (status) {
|
|
if (status.get('favourited')) {
|
|
this.props.dispatch(unfavourite(status));
|
|
} else {
|
|
this.props.dispatch(favourite(status));
|
|
}
|
|
},
|
|
|
|
handleReplyClick (status) {
|
|
this.props.dispatch(replyCompose(status, this.context.router));
|
|
},
|
|
|
|
handleModalReblog (status) {
|
|
this.props.dispatch(reblog(status));
|
|
},
|
|
|
|
handleReblogClick (status, e) {
|
|
if (status.get('reblogged')) {
|
|
this.props.dispatch(unreblog(status));
|
|
} else {
|
|
if (e.shiftKey || !this.props.boostModal) {
|
|
this.handleModalReblog(status);
|
|
} else {
|
|
this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
|
|
}
|
|
}
|
|
},
|
|
|
|
handleDeleteClick (status) {
|
|
this.props.dispatch(deleteStatus(status.get('id')));
|
|
},
|
|
|
|
handleMentionClick (account, router) {
|
|
this.props.dispatch(mentionCompose(account, router));
|
|
},
|
|
|
|
handleOpenMedia (media, index) {
|
|
this.props.dispatch(openModal('MEDIA', { media, index }));
|
|
},
|
|
|
|
handleOpenVideo (media, time) {
|
|
this.props.dispatch(openModal('VIDEO', { media, time }));
|
|
},
|
|
|
|
handleReport (status) {
|
|
this.props.dispatch(initReport(status.get('account'), status));
|
|
},
|
|
|
|
renderChildren (list) {
|
|
return list.map(id => <StatusContainer key={id} id={id} />);
|
|
},
|
|
|
|
render () {
|
|
let ancestors, descendants;
|
|
const { status, ancestorsIds, descendantsIds, me, autoPlayGif } = this.props;
|
|
|
|
if (status === null) {
|
|
return (
|
|
<Column>
|
|
<ColumnBackButton />
|
|
<MissingIndicator />
|
|
</Column>
|
|
);
|
|
}
|
|
|
|
const account = status.get('account');
|
|
|
|
if (ancestorsIds && ancestorsIds.size > 0) {
|
|
ancestors = <div>{this.renderChildren(ancestorsIds)}</div>;
|
|
}
|
|
|
|
if (descendantsIds && descendantsIds.size > 0) {
|
|
descendants = <div>{this.renderChildren(descendantsIds)}</div>;
|
|
}
|
|
|
|
return (
|
|
<Column>
|
|
<ColumnBackButton />
|
|
|
|
<ScrollContainer scrollKey='thread'>
|
|
<div className='scrollable'>
|
|
{ancestors}
|
|
|
|
<DetailedStatus status={status} autoPlayGif={autoPlayGif} me={me} onOpenVideo={this.handleOpenVideo} onOpenMedia={this.handleOpenMedia} />
|
|
<ActionBar status={status} me={me} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} onMention={this.handleMentionClick} onReport={this.handleReport} />
|
|
|
|
{descendants}
|
|
</div>
|
|
</ScrollContainer>
|
|
</Column>
|
|
);
|
|
}
|
|
|
|
});
|
|
|
|
export default connect(makeMapStateToProps)(Status);
|