diff options
author | ThibG <thib@sitedethib.com> | 2019-05-25 23:20:51 +0200 |
---|---|---|
committer | multiple creatures <dev@multiple-creature.party> | 2019-11-19 15:32:13 -0600 |
commit | 62f13e9f9a13b32bdf72a4508c99fe0a04828232 (patch) | |
tree | 1968b01d2cc22c7752802d7755ea70187b9360c1 | |
parent | b6b88a8a1842a4cf050bda45618a639b38c30d0c (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
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 = () => { |