diff options
-rw-r--r-- | app/javascript/mastodon/components/status.js | 2 | ||||
-rw-r--r-- | app/javascript/mastodon/components/status_content.js | 40 | ||||
-rw-r--r-- | app/javascript/styles/mastodon/components.scss | 22 |
3 files changed, 59 insertions, 5 deletions
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 6c595c712..90c689a75 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -285,7 +285,7 @@ class Status extends ImmutablePureComponent { </a> </div> - <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} /> + <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable /> {media} diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js index 81013747e..f221474f3 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.js @@ -6,6 +6,8 @@ import { FormattedMessage } from 'react-intl'; import Permalink from './permalink'; import classnames from 'classnames'; +const MAX_HEIGHT = 322; // 20px * 16 (+ 2px padding at the top) + export default class StatusContent extends React.PureComponent { static contextTypes = { @@ -17,10 +19,12 @@ export default class StatusContent extends React.PureComponent { expanded: PropTypes.bool, onExpandedToggle: PropTypes.func, onClick: PropTypes.func, + collapsable: PropTypes.bool, }; state = { hidden: true, + collapsed: null, // `collapsed: null` indicates that an element doesn't need collapsing, while `true` or `false` indicates that it does (and is/isn't). }; _updateStatusLinks () { @@ -53,6 +57,16 @@ export default class StatusContent extends React.PureComponent { link.setAttribute('target', '_blank'); link.setAttribute('rel', 'noopener'); } + + if ( + this.props.collapsable + && this.props.onClick + && this.state.collapsed === null + && node.clientHeight > MAX_HEIGHT + && this.props.status.get('spoiler_text').length === 0 + ) { + this.setState({ collapsed: true }); + } } componentDidMount () { @@ -113,6 +127,11 @@ export default class StatusContent extends React.PureComponent { } } + handleCollapsedClick = (e) => { + e.preventDefault(); + this.setState({ collapsed: !this.state.collapsed }); + } + setRef = (c) => { this.node = c; } @@ -132,12 +151,19 @@ export default class StatusContent extends React.PureComponent { const classNames = classnames('status__content', { 'status__content--with-action': this.props.onClick && this.context.router, 'status__content--with-spoiler': status.get('spoiler_text').length > 0, + 'status__content--collapsed': this.state.collapsed === true, }); if (isRtl(status.get('search_index'))) { directionStyle.direction = 'rtl'; } + const readMoreButton = ( + <button className='status__content__read-more-button' onClick={this.props.onClick}> + <FormattedMessage id='status.read_more' defaultMessage='Read more' /><i className='fa fa-fw fa-angle-right' /> + </button> + ); + if (status.get('spoiler_text').length > 0) { let mentionsPlaceholder = ''; @@ -167,17 +193,23 @@ export default class StatusContent extends React.PureComponent { </div> ); } else if (this.props.onClick) { - return ( + const output = [ <div ref={this.setRef} tabIndex='0' className={classNames} style={directionStyle} + dangerouslySetInnerHTML={content} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} - dangerouslySetInnerHTML={content} - /> - ); + />, + ]; + + if (this.state.collapsed) { + output.push(readMoreButton); + } + + return output; } else { return ( <div diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index c73c220d4..c57f99fea 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -631,11 +631,13 @@ .status__content, .reply-indicator__content { + position: relative; font-size: 15px; line-height: 20px; word-wrap: break-word; font-weight: 400; overflow: hidden; + text-overflow: ellipsis; white-space: pre-wrap; padding-top: 2px; color: $primary-text-color; @@ -721,6 +723,26 @@ } } +.status__content.status__content--collapsed { + max-height: 20px * 15; // 15 lines is roughly above 500 characters +} + +.status__content__read-more-button { + display: block; + font-size: 15px; + line-height: 20px; + color: lighten($ui-highlight-color, 8%); + border: 0; + background: transparent; + padding: 0; + padding-top: 8px; + + &:hover, + &:active { + text-decoration: underline; + } +} + .status__content__spoiler-link { display: inline-block; border-radius: 2px; |