about summary refs log tree commit diff
path: root/app/javascript
diff options
context:
space:
mode:
authorThibaut Girka <thib@sitedethib.com>2020-01-04 22:54:06 +0100
committerThibaut Girka <thib@sitedethib.com>2020-01-04 23:04:42 +0100
commit01eaeab56df4da4c697b1096f40a400cc9e2b8e8 (patch)
tree6288ee106b4615cacd98362f9fd268317a9cdeff /app/javascript
parent22daf24600d8e99e4569740ee5836d25c70c1e8b (diff)
parent2ecc7802caf4d272191a7fd582fc97996f750827 (diff)
Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
- `app/controllers/application_controller.rb`:
  Conflict due to theming system.
- `app/controllers/oauth/authorizations_controller.rb`:
  Conflict due to theming system.
Diffstat (limited to 'app/javascript')
-rw-r--r--app/javascript/mastodon/actions/statuses.js13
-rw-r--r--app/javascript/mastodon/components/scrollable_list.js7
-rw-r--r--app/javascript/mastodon/components/status.js48
-rw-r--r--app/javascript/mastodon/components/status_content.js25
-rw-r--r--app/javascript/mastodon/containers/status_container.js5
-rw-r--r--app/javascript/mastodon/reducers/statuses.js3
-rw-r--r--app/javascript/mastodon/utils/numbers.js8
-rw-r--r--app/javascript/styles/mastodon/components.scss4
8 files changed, 53 insertions, 60 deletions
diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js
index 06a19afc3..5640201c6 100644
--- a/app/javascript/mastodon/actions/statuses.js
+++ b/app/javascript/mastodon/actions/statuses.js
@@ -26,8 +26,9 @@ export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
 export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
 export const STATUS_UNMUTE_FAIL    = 'STATUS_UNMUTE_FAIL';
 
-export const STATUS_REVEAL = 'STATUS_REVEAL';
-export const STATUS_HIDE   = 'STATUS_HIDE';
+export const STATUS_REVEAL   = 'STATUS_REVEAL';
+export const STATUS_HIDE     = 'STATUS_HIDE';
+export const STATUS_COLLAPSE = 'STATUS_COLLAPSE';
 
 export const REDRAFT = 'REDRAFT';
 
@@ -320,3 +321,11 @@ export function revealStatus(ids) {
     ids,
   };
 };
+
+export function toggleStatusCollapse(id, isCollapsed) {
+  return {
+    type: STATUS_COLLAPSE,
+    id,
+    isCollapsed,
+  };
+}
diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index 421756803..47a87b149 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -208,10 +208,13 @@ export default class ScrollableList extends PureComponent {
   }
 
   attachIntersectionObserver () {
-    this.intersectionObserverWrapper.connect({
+    let nodeOptions = {
       root: this.node,
       rootMargin: '300% 0px',
-    });
+    };
+
+    this.intersectionObserverWrapper
+      .connect(this.props.bindToDocument ? {} : nodeOptions);
   }
 
   detachIntersectionObserver () {
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index e120278a0..0dc00cb98 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -76,6 +76,7 @@ class Status extends ImmutablePureComponent {
     onEmbed: PropTypes.func,
     onHeightChange: PropTypes.func,
     onToggleHidden: PropTypes.func,
+    onToggleCollapsed: PropTypes.func,
     muted: PropTypes.bool,
     hidden: PropTypes.bool,
     unread: PropTypes.bool,
@@ -102,19 +103,6 @@ class Status extends ImmutablePureComponent {
     statusId: undefined,
   };
 
-  // Track height changes we know about to compensate scrolling
-  componentDidMount () {
-    this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
-  }
-
-  getSnapshotBeforeUpdate () {
-    if (this.props.getScrollPosition) {
-      return this.props.getScrollPosition();
-    } else {
-      return null;
-    }
-  }
-
   static getDerivedStateFromProps(nextProps, prevState) {
     if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
       return {
@@ -126,32 +114,6 @@ class Status extends ImmutablePureComponent {
     }
   }
 
-  // Compensate height changes
-  componentDidUpdate (prevProps, prevState, snapshot) {
-    const doShowCard  = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
-
-    if (doShowCard && !this.didShowCard) {
-      this.didShowCard = true;
-
-      if (snapshot !== null && this.props.updateScrollBottom) {
-        if (this.node && this.node.offsetTop < snapshot.top) {
-          this.props.updateScrollBottom(snapshot.height - snapshot.top);
-        }
-      }
-    }
-  }
-
-  componentWillUnmount() {
-    if (this.node && this.props.getScrollPosition) {
-      const position = this.props.getScrollPosition();
-      if (position !== null && this.node.offsetTop < position.top) {
-        requestAnimationFrame(() => {
-          this.props.updateScrollBottom(position.height - position.top);
-        });
-      }
-    }
-  }
-
   handleToggleMediaVisibility = () => {
     this.setState({ showMedia: !this.state.showMedia });
   }
@@ -196,7 +158,11 @@ class Status extends ImmutablePureComponent {
 
   handleExpandedToggle = () => {
     this.props.onToggleHidden(this._properStatus());
-  };
+  }
+
+  handleCollapsedToggle = isCollapsed => {
+    this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
+  }
 
   renderLoadingMediaGallery () {
     return <div className='media-gallery' style={{ height: '110px' }} />;
@@ -466,7 +432,7 @@ class Status extends ImmutablePureComponent {
               </a>
             </div>
 
-            <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable />
+            <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable onCollapsedToggle={this.handleCollapsedToggle} />
 
             {media}
 
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js
index d13091325..5d921fd41 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.js
@@ -23,11 +23,11 @@ export default class StatusContent extends React.PureComponent {
     onExpandedToggle: PropTypes.func,
     onClick: PropTypes.func,
     collapsable: PropTypes.bool,
+    onCollapsedToggle: PropTypes.func,
   };
 
   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 () {
@@ -62,14 +62,16 @@ export default class StatusContent extends React.PureComponent {
       link.setAttribute('rel', 'noopener noreferrer');
     }
 
-    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 });
+    if (this.props.status.get('collapsed', null) === null) {
+      let collapsed =
+          this.props.collapsable
+          && this.props.onClick
+          && node.clientHeight > MAX_HEIGHT
+          && this.props.status.get('spoiler_text').length === 0;
+
+      if(this.props.onCollapsedToggle) this.props.onCollapsedToggle(collapsed);
+
+      this.props.status.set('collapsed', collapsed);
     }
   }
 
@@ -178,6 +180,7 @@ export default class StatusContent extends React.PureComponent {
     }
 
     const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
+    const renderReadMore = this.props.onClick && status.get('collapsed');
 
     const content = { __html: status.get('contentHtml') };
     const spoilerContent = { __html: status.get('spoilerHtml') };
@@ -185,7 +188,7 @@ 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--collapsed': renderReadMore,
     });
 
     if (isRtl(status.get('search_index'))) {
@@ -237,7 +240,7 @@ export default class StatusContent extends React.PureComponent {
         </div>,
       ];
 
-      if (this.state.collapsed) {
+      if (renderReadMore) {
         output.push(readMoreButton);
       }
 
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js
index 35c16a20c..2ba3a3123 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -23,6 +23,7 @@ import {
   deleteStatus,
   hideStatus,
   revealStatus,
+  toggleStatusCollapse,
 } from '../actions/statuses';
 import {
   unmuteAccount,
@@ -190,6 +191,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     }
   },
 
+  onToggleCollapsed (status, isCollapsed) {
+    dispatch(toggleStatusCollapse(status.get('id'), isCollapsed));
+  },
+
   onBlockDomain (domain) {
     dispatch(openModal('CONFIRM', {
       message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js
index 772f98bcb..398a48cff 100644
--- a/app/javascript/mastodon/reducers/statuses.js
+++ b/app/javascript/mastodon/reducers/statuses.js
@@ -12,6 +12,7 @@ import {
   STATUS_UNMUTE_SUCCESS,
   STATUS_REVEAL,
   STATUS_HIDE,
+  STATUS_COLLAPSE,
 } from '../actions/statuses';
 import { TIMELINE_DELETE } from '../actions/timelines';
 import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
@@ -73,6 +74,8 @@ export default function statuses(state = initialState, action) {
         }
       });
     });
+  case STATUS_COLLAPSE:
+    return state.setIn([action.id, 'collapsed'], action.isCollapsed);
   case TIMELINE_DELETE:
     return deleteStatus(state, action.id, action.references);
   default:
diff --git a/app/javascript/mastodon/utils/numbers.js b/app/javascript/mastodon/utils/numbers.js
index f7e4ceb93..af18dcfdd 100644
--- a/app/javascript/mastodon/utils/numbers.js
+++ b/app/javascript/mastodon/utils/numbers.js
@@ -4,9 +4,13 @@ import { FormattedNumber } from 'react-intl';
 export const shortNumberFormat = number => {
   if (number < 1000) {
     return <FormattedNumber value={number} />;
-  } else if (number < 1000000) {
+  } else if (number < 10000) {
     return <Fragment><FormattedNumber value={number / 1000} maximumFractionDigits={1} />K</Fragment>;
-  } else {
+  } else if (number < 1000000) {
+    return <Fragment><FormattedNumber value={number / 1000} maximumFractionDigits={0} />K</Fragment>;
+  } else if (number < 10000000) {
     return <Fragment><FormattedNumber value={number / 1000000} maximumFractionDigits={1} />M</Fragment>;
+  } else {
+    return <Fragment><FormattedNumber value={number / 1000000} maximumFractionDigits={0} />M</Fragment>;
   }
 };
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 01a633c5f..1a5d9e3e3 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -6399,13 +6399,13 @@ noscript {
     &__links {
       font-size: 14px;
       color: $darker-text-color;
+      padding: 10px 0;
 
       a {
         display: inline-block;
         color: $darker-text-color;
         text-decoration: none;
-        padding: 10px;
-        padding-top: 20px;
+        padding: 5px 10px;
         font-weight: 500;
 
         strong {