diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 43647acc0..e3ff242a1 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -285,7 +285,7 @@ export default class Status extends ImmutablePureComponent {
-
+
{media}
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js
index 81013747e..6aa973df0 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.js
@@ -17,11 +17,14 @@ 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 () {
const node = this.node;
@@ -53,6 +56,13 @@ export default class StatusContent extends React.PureComponent {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener');
}
+
+ if (
+ this.props.collapsable
+ && this.state.collapsed === null
+ && node.clientHeight > 200
+ && this.props.status.get('spoiler_text').length === 0
+ ) this.setState({ collapsed: true });
}
componentDidMount () {
@@ -113,6 +123,11 @@ export default class StatusContent extends React.PureComponent {
}
}
+ handleCollapsedClick = (e) => {
+ e.preventDefault();
+ this.setState({ collapsed: !this.state.collapsed });
+ }
+
setRef = (c) => {
this.node = c;
}
@@ -132,6 +147,8 @@ 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,
+ 'status__content--expanded': this.state.collapsed === false,
});
if (isRtl(status.get('search_index'))) {
@@ -175,8 +192,17 @@ export default class StatusContent extends React.PureComponent {
style={directionStyle}
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
- dangerouslySetInnerHTML={content}
- />
+ >
+
+ {this.state.collapsed !== null ?
+
+ : null}
+
);
} else {
return (
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 85d41369d..f64a9d53b 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,55 @@
}
}
+.status__content.status__content--collapsed {
+ padding-bottom: 25px;
+ max-height: 200px;
+
+ i {
+ transform: rotateX(0);
+ }
+}
+
+.status__content.status__content--expanded, {
+ padding-bottom: 25px;
+ height: auto;
+
+ i {
+ transform: rotateX(180deg);
+ }
+}
+
+.status__content__collapse-button {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ width: 100%;
+ height: 25px;
+ font-size: 18px;
+ line-height: 25px;
+ color: $inverted-text-color;
+ text-align: center;
+ background: $action-button-color;
+ transition: background .2s ease-in-out, color .2s ease-in-out;
+ border: 0;
+ border-radius: 2px;
+
+ &:hover {
+ background: lighten($action-button-color, 7%);
+ }
+
+ i {
+ transition: transform .2s ease-in-out;
+
+ &,
+ &:hover {
+ color: $inverted-text-color !important;
+ }
+ }
+}
+
.status__content__spoiler-link {
display: inline-block;
border-radius: 2px;