about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThibG <thib@sitedethib.com>2019-05-25 23:20:51 +0200
committermultiple creatures <dev@multiple-creature.party>2019-11-19 15:32:13 -0600
commit62f13e9f9a13b32bdf72a4508c99fe0a04828232 (patch)
tree1968b01d2cc22c7752802d7755ea70187b9360c1
parentb6b88a8a1842a4cf050bda45618a639b38c30d0c (diff)
Add a keyboard shortcut to hide/show media (#10647)
* Move control of media visibility to parent component

* Add keyboard shortcut to toggle media visibility
-rw-r--r--app/javascript/mastodon/components/media_gallery.js16
-rw-r--r--app/javascript/mastodon/components/status.js18
-rw-r--r--app/javascript/mastodon/features/keyboard_shortcuts/index.js4
-rw-r--r--app/javascript/mastodon/features/status/components/detailed_status.js6
-rw-r--r--app/javascript/mastodon/features/status/index.js17
-rw-r--r--app/javascript/mastodon/features/ui/index.js1
-rw-r--r--app/javascript/mastodon/features/video/index.js25
7 files changed, 75 insertions, 12 deletions
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index abd17647e..56618462b 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -244,6 +244,8 @@ class MediaGallery extends React.PureComponent {
     intl: PropTypes.object.isRequired,
     defaultWidth: PropTypes.number,
     cacheWidth: PropTypes.func,
+    visible: PropTypes.bool,
+    onToggleVisibility: PropTypes.func,
   };
 
   static defaultProps = {
@@ -251,18 +253,24 @@ class MediaGallery extends React.PureComponent {
   };
 
   state = {
-    visible: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all',
+    visible: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
     width: this.props.defaultWidth,
   };
 
   componentWillReceiveProps (nextProps) {
-    if (!is(nextProps.media, this.props.media)) {
-      this.setState({ visible: !nextProps.sensitive });
+    if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) {
+      this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' });
+    } else if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
+      this.setState({ visible: nextProps.visible });
     }
   }
 
   handleOpen = () => {
-    this.setState({ visible: !this.state.visible });
+    if (this.props.onToggleVisibility) {
+      this.props.onToggleVisibility();
+    } else {
+      this.setState({ visible: !this.state.visible });
+    }
   }
 
   handleClick = (index) => {
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 3b70fc137..b9603468a 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -17,6 +17,7 @@ import { HotKeys } from 'react-hotkeys';
 import classNames from 'classnames';
 import Icon from 'mastodon/components/icon';
 import PollContainer from 'mastodon/containers/poll_container';
+import { displayMedia } from '../initial_state';
 
 // We use the component (and not the container) since we do not want
 // to use the progress bar to show download progress
@@ -85,6 +86,10 @@ class Status extends ImmutablePureComponent {
     'hidden',
   ];
 
+  state = {
+    showMedia: displayMedia !== 'hide_all' && !this.props.status.get('sensitive') || displayMedia === 'show_all',
+  };
+
   // 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');
@@ -122,6 +127,10 @@ class Status extends ImmutablePureComponent {
     }
   }
 
+  handleToggleMediaVisibility = () => {
+    this.setState({ showMedia: !this.state.showMedia });
+  }
+
   handleClick = () => {
     if (this.props.onClick) {
       this.props.onClick();
@@ -198,6 +207,10 @@ class Status extends ImmutablePureComponent {
     this.props.onToggleHidden(this._properStatus());
   }
 
+  handleHotkeyToggleSensitive = () => {
+    this.handleToggleMediaVisibility();
+  }
+
   _properStatus () {
     const { status } = this.props;
 
@@ -297,6 +310,8 @@ class Status extends ImmutablePureComponent {
                 sensitive={status.get('sensitive')}
                 onOpenVideo={this.handleOpenVideo}
                 cacheWidth={this.props.cacheMediaWidth}
+                visible={this.state.showMedia}
+                onToggleVisibility={this.handleToggleMediaVisibility}
               />
             )}
           </Bundle>
@@ -312,6 +327,8 @@ class Status extends ImmutablePureComponent {
                 onOpenMedia={this.props.onOpenMedia}
                 cacheWidth={this.props.cacheMediaWidth}
                 defaultWidth={this.props.cachedMediaWidth}
+                visible={this.state.showMedia}
+                onToggleVisibility={this.handleToggleMediaVisibility}
               />
             )}
           </Bundle>
@@ -347,6 +364,7 @@ class Status extends ImmutablePureComponent {
       moveUp: this.handleHotkeyMoveUp,
       moveDown: this.handleHotkeyMoveDown,
       toggleHidden: this.handleHotkeyToggleHidden,
+      toggleSensitive: this.handleHotkeyToggleSensitive,
     };
 
     return (
diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.js b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
index ab1ac511e..01b45652c 100644
--- a/app/javascript/mastodon/features/keyboard_shortcuts/index.js
+++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
@@ -61,6 +61,10 @@ class KeyboardShortcuts extends ImmutablePureComponent {
                 <td><FormattedMessage id='keyboard_shortcuts.toggle_hidden' defaultMessage='to show/hide text behind CW' /></td>
               </tr>
               <tr>
+                <td><kbd>h</kbd></td>
+                <td><FormattedMessage id='keyboard_shortcuts.toggle_sensitivity' defaultMessage='to show/hide media' /></td>
+              </tr>
+              <tr>
                 <td><kbd>up</kbd>, <kbd>k</kbd></td>
                 <td><FormattedMessage id='keyboard_shortcuts.up' defaultMessage='to move up in the list' /></td>
               </tr>
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.js
index b9f75906e..ac745c1aa 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.js
+++ b/app/javascript/mastodon/features/status/components/detailed_status.js
@@ -30,6 +30,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
     onHeightChange: PropTypes.func,
     domain: PropTypes.string.isRequired,
     compact: PropTypes.bool,
+    showMedia: PropTypes.bool,
+    onToggleMediaVisibility: PropTypes.func,
   };
 
   state = {
@@ -122,6 +124,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
             inline
             onOpenVideo={this.handleOpenVideo}
             sensitive={status.get('sensitive')}
+            visible={this.props.showMedia}
+            onToggleVisibility={this.props.onToggleMediaVisibility}
           />
         );
       } else {
@@ -132,6 +136,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
             media={status.get('media_attachments')}
             height={300}
             onOpenMedia={this.props.onOpenMedia}
+            visible={this.props.showMedia}
+            onToggleVisibility={this.props.onToggleMediaVisibility}
           />
         );
       }
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 6279bb468..499afe6f7 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -41,7 +41,7 @@ import { openModal } from '../../actions/modal';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import { HotKeys } from 'react-hotkeys';
-import { boostModal, deleteModal } from '../../initial_state';
+import { boostModal, deleteModal, displayMedia } from '../../initial_state';
 import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
 import { textForScreenReader } from '../../components/status';
 import Icon from 'mastodon/components/icon';
@@ -131,6 +131,7 @@ class Status extends ImmutablePureComponent {
 
   state = {
     fullscreen: false,
+    showMedia: !this.props.status ? undefined : (displayMedia !== 'hide_all' && !this.props.status.get('sensitive') || displayMedia === 'show_all'),
   };
 
   componentWillMount () {
@@ -146,6 +147,13 @@ class Status extends ImmutablePureComponent {
       this._scrolledIntoView = false;
       this.props.dispatch(fetchStatus(nextProps.params.statusId));
     }
+    if (!Immutable.is(nextProps.status, this.props.status) && nextProps.status) {
+      this.setState({ showMedia: displayMedia !== 'hide_all' && !nextProps.status.get('sensitive') || displayMedia === 'show_all' });
+    }
+  }
+
+  handleToggleMediaVisibility = () => {
+    this.setState({ showMedia: !this.state.showMedia });
   }
 
   handleFavouriteClick = (status) => {
@@ -312,6 +320,10 @@ class Status extends ImmutablePureComponent {
     this.handleToggleHidden(this.props.status);
   }
 
+  handleHotkeyToggleSensitive = () => {
+    this.handleToggleMediaVisibility();
+  }
+
   handleMoveUp = id => {
     const { status, ancestorsIds, descendantsIds } = this.props;
 
@@ -432,6 +444,7 @@ class Status extends ImmutablePureComponent {
       mention: this.handleHotkeyMention,
       openProfile: this.handleHotkeyOpenProfile,
       toggleHidden: this.handleHotkeyToggleHidden,
+      toggleSensitive: this.handleHotkeyToggleSensitive,
     };
 
     return (
@@ -455,6 +468,8 @@ class Status extends ImmutablePureComponent {
                   onOpenMedia={this.handleOpenMedia}
                   onToggleHidden={this.handleToggleHidden}
                   domain={domain}
+                  showMedia={this.state.showMedia}
+                  onToggleMediaVisibility={this.handleToggleMediaVisibility}
                 />
 
                 <ActionBar
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index 61fd77af7..791133afd 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -93,6 +93,7 @@ const keyMap = {
   goToMuted: 'g m',
   goToRequests: 'g r',
   toggleHidden: 'x',
+  toggleSensitive: 'h',
 };
 
 class SwitchingColumnsArea extends React.PureComponent {
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index 00a63a3d9..b0c408527 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { fromJS } from 'immutable';
+import { fromJS, is } from 'immutable';
 import { throttle } from 'lodash';
 import classNames from 'classnames';
 import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen';
@@ -102,6 +102,8 @@ class Video extends React.PureComponent {
     detailed: PropTypes.bool,
     inline: PropTypes.bool,
     cacheWidth: PropTypes.func,
+    visible: PropTypes.bool,
+    onToggleVisibility: PropTypes.func,
     intl: PropTypes.object.isRequired,
     blurhash: PropTypes.string,
     link: PropTypes.node,
@@ -117,7 +119,7 @@ class Video extends React.PureComponent {
     fullscreen: false,
     hovered: false,
     muted: false,
-    revealed: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all',
+    revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
   };
 
   // hard coded in components.scss
@@ -280,7 +282,16 @@ class Video extends React.PureComponent {
     document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
   }
 
-  componentDidUpdate (prevProps) {
+  componentWillReceiveProps (nextProps) {
+    if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
+      this.setState({ revealed: nextProps.visible });
+    }
+  }
+
+  componentDidUpdate (prevProps, prevState) {
+    if (prevState.revealed && !this.state.revealed && this.video) {
+      this.video.pause();
+    }
     if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) {
       this._decode();
     }
@@ -316,11 +327,11 @@ class Video extends React.PureComponent {
   }
 
   toggleReveal = () => {
-    if (this.state.revealed) {
-      this.video.pause();
+    if (this.props.onToggleVisibility) {
+      this.props.onToggleVisibility();
+    } else {
+      this.setState({ revealed: !this.state.revealed });
     }
-
-    this.setState({ revealed: !this.state.revealed });
   }
 
   handleLoadedData = () => {