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}
 | |
|         />
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| 
 | |
| }
 |