f303a954e6
aria-label contained body of status with content warning, which should be hidden by default. Remove the label for the case and other cases due to consistency.
184 lines
5.5 KiB
JavaScript
184 lines
5.5 KiB
JavaScript
import React from 'react';
|
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
import PropTypes from 'prop-types';
|
|
import { isRtl } from '../rtl';
|
|
import { FormattedMessage } from 'react-intl';
|
|
import Permalink from './permalink';
|
|
import classnames from 'classnames';
|
|
|
|
export default class StatusContent extends React.PureComponent {
|
|
|
|
static contextTypes = {
|
|
router: PropTypes.object,
|
|
};
|
|
|
|
static propTypes = {
|
|
status: ImmutablePropTypes.map.isRequired,
|
|
expanded: PropTypes.bool,
|
|
onExpandedToggle: PropTypes.func,
|
|
onClick: PropTypes.func,
|
|
};
|
|
|
|
state = {
|
|
hidden: true,
|
|
};
|
|
|
|
_updateStatusLinks () {
|
|
const node = this.node;
|
|
const links = node.querySelectorAll('a');
|
|
|
|
for (var i = 0; i < links.length; ++i) {
|
|
let link = links[i];
|
|
if (link.classList.contains('status-link')) {
|
|
continue;
|
|
}
|
|
link.classList.add('status-link');
|
|
|
|
let mention = this.props.status.get('mentions').find(item => link.href === item.get('url'));
|
|
|
|
if (mention) {
|
|
link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
|
|
link.setAttribute('title', mention.get('acct'));
|
|
} else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
|
|
link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
|
|
} else {
|
|
link.setAttribute('title', link.href);
|
|
}
|
|
|
|
link.setAttribute('target', '_blank');
|
|
link.setAttribute('rel', 'noopener');
|
|
}
|
|
}
|
|
|
|
componentDidMount () {
|
|
this._updateStatusLinks();
|
|
}
|
|
|
|
componentDidUpdate () {
|
|
this._updateStatusLinks();
|
|
}
|
|
|
|
onMentionClick = (mention, e) => {
|
|
if (this.context.router && e.button === 0) {
|
|
e.preventDefault();
|
|
this.context.router.history.push(`/accounts/${mention.get('id')}`);
|
|
}
|
|
}
|
|
|
|
onHashtagClick = (hashtag, e) => {
|
|
hashtag = hashtag.replace(/^#/, '').toLowerCase();
|
|
|
|
if (this.context.router && e.button === 0) {
|
|
e.preventDefault();
|
|
this.context.router.history.push(`/timelines/tag/${hashtag}`);
|
|
}
|
|
}
|
|
|
|
handleMouseDown = (e) => {
|
|
this.startXY = [e.clientX, e.clientY];
|
|
}
|
|
|
|
handleMouseUp = (e) => {
|
|
if (!this.startXY) {
|
|
return;
|
|
}
|
|
|
|
const [ startX, startY ] = this.startXY;
|
|
const [ deltaX, deltaY ] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)];
|
|
|
|
if (e.target.localName === 'button' || e.target.localName === 'a' || (e.target.parentNode && (e.target.parentNode.localName === 'button' || e.target.parentNode.localName === 'a'))) {
|
|
return;
|
|
}
|
|
|
|
if (deltaX + deltaY < 5 && e.button === 0 && this.props.onClick) {
|
|
this.props.onClick();
|
|
}
|
|
|
|
this.startXY = null;
|
|
}
|
|
|
|
handleSpoilerClick = (e) => {
|
|
e.preventDefault();
|
|
|
|
if (this.props.onExpandedToggle) {
|
|
// The parent manages the state
|
|
this.props.onExpandedToggle();
|
|
} else {
|
|
this.setState({ hidden: !this.state.hidden });
|
|
}
|
|
}
|
|
|
|
setRef = (c) => {
|
|
this.node = c;
|
|
}
|
|
|
|
render () {
|
|
const { status } = this.props;
|
|
|
|
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
|
|
|
const content = { __html: status.get('contentHtml') };
|
|
const spoilerContent = { __html: status.get('spoilerHtml') };
|
|
const directionStyle = { direction: 'ltr' };
|
|
const classNames = classnames('status__content', {
|
|
'status__content--with-action': this.props.onClick && this.context.router,
|
|
});
|
|
|
|
if (isRtl(status.get('search_index'))) {
|
|
directionStyle.direction = 'rtl';
|
|
}
|
|
|
|
if (status.get('spoiler_text').length > 0) {
|
|
let mentionsPlaceholder = '';
|
|
|
|
const mentionLinks = status.get('mentions').map(item => (
|
|
<Permalink to={`/accounts/${item.get('id')}`} href={item.get('url')} key={item.get('id')} className='mention'>
|
|
@<span>{item.get('username')}</span>
|
|
</Permalink>
|
|
)).reduce((aggregate, item) => [...aggregate, item, ' '], []);
|
|
|
|
const toggleText = hidden ? <FormattedMessage id='status.show_more' defaultMessage='Show more' /> : <FormattedMessage id='status.show_less' defaultMessage='Show less' />;
|
|
|
|
if (hidden) {
|
|
mentionsPlaceholder = <div>{mentionLinks}</div>;
|
|
}
|
|
|
|
return (
|
|
<div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
|
|
<p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
|
|
<span dangerouslySetInnerHTML={spoilerContent} />
|
|
{' '}
|
|
<button tabIndex='0' className='status__content__spoiler-link' onClick={this.handleSpoilerClick}>{toggleText}</button>
|
|
</p>
|
|
|
|
{mentionsPlaceholder}
|
|
|
|
<div tabIndex={!hidden && 0} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} />
|
|
</div>
|
|
);
|
|
} else if (this.props.onClick) {
|
|
return (
|
|
<div
|
|
ref={this.setRef}
|
|
tabIndex='0'
|
|
className={classNames}
|
|
style={directionStyle}
|
|
onMouseDown={this.handleMouseDown}
|
|
onMouseUp={this.handleMouseUp}
|
|
dangerouslySetInnerHTML={content}
|
|
/>
|
|
);
|
|
} else {
|
|
return (
|
|
<div
|
|
tabIndex='0'
|
|
ref={this.setRef}
|
|
className='status__content'
|
|
style={directionStyle}
|
|
dangerouslySetInnerHTML={content}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
}
|