mirror of https://github.com/Siphonay/mastodon
Add a keyboard shortcut to hide/show media (#10647)
* Move control of media visibility to parent component * Add keyboard shortcut to toggle media visibility
This commit is contained in:
parent
c90f3b9865
commit
a472190729
|
@ -244,6 +244,8 @@ class MediaGallery extends React.PureComponent {
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
defaultWidth: PropTypes.number,
|
defaultWidth: PropTypes.number,
|
||||||
cacheWidth: PropTypes.func,
|
cacheWidth: PropTypes.func,
|
||||||
|
visible: PropTypes.bool,
|
||||||
|
onToggleVisibility: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -251,19 +253,25 @@ class MediaGallery extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
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,
|
width: this.props.defaultWidth,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
if (!is(nextProps.media, this.props.media)) {
|
if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) {
|
||||||
this.setState({ visible: !nextProps.sensitive });
|
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 = () => {
|
handleOpen = () => {
|
||||||
|
if (this.props.onToggleVisibility) {
|
||||||
|
this.props.onToggleVisibility();
|
||||||
|
} else {
|
||||||
this.setState({ visible: !this.state.visible });
|
this.setState({ visible: !this.state.visible });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleClick = (index) => {
|
handleClick = (index) => {
|
||||||
this.props.onOpenMedia(this.props.media, index);
|
this.props.onOpenMedia(this.props.media, index);
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { HotKeys } from 'react-hotkeys';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
import PollContainer from 'mastodon/containers/poll_container';
|
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
|
// We use the component (and not the container) since we do not want
|
||||||
// to use the progress bar to show download progress
|
// to use the progress bar to show download progress
|
||||||
|
@ -85,6 +86,10 @@ class Status extends ImmutablePureComponent {
|
||||||
'hidden',
|
'hidden',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
state = {
|
||||||
|
showMedia: displayMedia !== 'hide_all' && !this.props.status.get('sensitive') || displayMedia === 'show_all',
|
||||||
|
};
|
||||||
|
|
||||||
// Track height changes we know about to compensate scrolling
|
// Track height changes we know about to compensate scrolling
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
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 = () => {
|
handleClick = () => {
|
||||||
if (this.props.onClick) {
|
if (this.props.onClick) {
|
||||||
this.props.onClick();
|
this.props.onClick();
|
||||||
|
@ -198,6 +207,10 @@ class Status extends ImmutablePureComponent {
|
||||||
this.props.onToggleHidden(this._properStatus());
|
this.props.onToggleHidden(this._properStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleHotkeyToggleSensitive = () => {
|
||||||
|
this.handleToggleMediaVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
_properStatus () {
|
_properStatus () {
|
||||||
const { status } = this.props;
|
const { status } = this.props;
|
||||||
|
|
||||||
|
@ -298,6 +311,8 @@ class Status extends ImmutablePureComponent {
|
||||||
sensitive={status.get('sensitive')}
|
sensitive={status.get('sensitive')}
|
||||||
onOpenVideo={this.handleOpenVideo}
|
onOpenVideo={this.handleOpenVideo}
|
||||||
cacheWidth={this.props.cacheMediaWidth}
|
cacheWidth={this.props.cacheMediaWidth}
|
||||||
|
visible={this.state.showMedia}
|
||||||
|
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Bundle>
|
</Bundle>
|
||||||
|
@ -313,6 +328,8 @@ class Status extends ImmutablePureComponent {
|
||||||
onOpenMedia={this.props.onOpenMedia}
|
onOpenMedia={this.props.onOpenMedia}
|
||||||
cacheWidth={this.props.cacheMediaWidth}
|
cacheWidth={this.props.cacheMediaWidth}
|
||||||
defaultWidth={this.props.cachedMediaWidth}
|
defaultWidth={this.props.cachedMediaWidth}
|
||||||
|
visible={this.state.showMedia}
|
||||||
|
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Bundle>
|
</Bundle>
|
||||||
|
@ -348,6 +365,7 @@ class Status extends ImmutablePureComponent {
|
||||||
moveUp: this.handleHotkeyMoveUp,
|
moveUp: this.handleHotkeyMoveUp,
|
||||||
moveDown: this.handleHotkeyMoveDown,
|
moveDown: this.handleHotkeyMoveDown,
|
||||||
toggleHidden: this.handleHotkeyToggleHidden,
|
toggleHidden: this.handleHotkeyToggleHidden,
|
||||||
|
toggleSensitive: this.handleHotkeyToggleSensitive,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -60,6 +60,10 @@ class KeyboardShortcuts extends ImmutablePureComponent {
|
||||||
<td><kbd>x</kbd></td>
|
<td><kbd>x</kbd></td>
|
||||||
<td><FormattedMessage id='keyboard_shortcuts.toggle_hidden' defaultMessage='to show/hide text behind CW' /></td>
|
<td><FormattedMessage id='keyboard_shortcuts.toggle_hidden' defaultMessage='to show/hide text behind CW' /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><kbd>h</kbd></td>
|
||||||
|
<td><FormattedMessage id='keyboard_shortcuts.toggle_sensitivity' defaultMessage='to show/hide media' /></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><kbd>up</kbd>, <kbd>k</kbd></td>
|
<td><kbd>up</kbd>, <kbd>k</kbd></td>
|
||||||
<td><FormattedMessage id='keyboard_shortcuts.up' defaultMessage='to move up in the list' /></td>
|
<td><FormattedMessage id='keyboard_shortcuts.up' defaultMessage='to move up in the list' /></td>
|
||||||
|
|
|
@ -30,6 +30,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
onHeightChange: PropTypes.func,
|
onHeightChange: PropTypes.func,
|
||||||
domain: PropTypes.string.isRequired,
|
domain: PropTypes.string.isRequired,
|
||||||
compact: PropTypes.bool,
|
compact: PropTypes.bool,
|
||||||
|
showMedia: PropTypes.bool,
|
||||||
|
onToggleMediaVisibility: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -122,6 +124,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
inline
|
inline
|
||||||
onOpenVideo={this.handleOpenVideo}
|
onOpenVideo={this.handleOpenVideo}
|
||||||
sensitive={status.get('sensitive')}
|
sensitive={status.get('sensitive')}
|
||||||
|
visible={this.props.showMedia}
|
||||||
|
onToggleVisibility={this.props.onToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -132,6 +136,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
media={status.get('media_attachments')}
|
media={status.get('media_attachments')}
|
||||||
height={300}
|
height={300}
|
||||||
onOpenMedia={this.props.onOpenMedia}
|
onOpenMedia={this.props.onOpenMedia}
|
||||||
|
visible={this.props.showMedia}
|
||||||
|
onToggleVisibility={this.props.onToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ import { openModal } from '../../actions/modal';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { HotKeys } from 'react-hotkeys';
|
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 { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
|
||||||
import { textForScreenReader } from '../../components/status';
|
import { textForScreenReader } from '../../components/status';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
|
@ -131,6 +131,7 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
|
showMedia: !this.props.status ? undefined : (displayMedia !== 'hide_all' && !this.props.status.get('sensitive') || displayMedia === 'show_all'),
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
|
@ -146,6 +147,13 @@ class Status extends ImmutablePureComponent {
|
||||||
this._scrolledIntoView = false;
|
this._scrolledIntoView = false;
|
||||||
this.props.dispatch(fetchStatus(nextProps.params.statusId));
|
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) => {
|
handleFavouriteClick = (status) => {
|
||||||
|
@ -312,6 +320,10 @@ class Status extends ImmutablePureComponent {
|
||||||
this.handleToggleHidden(this.props.status);
|
this.handleToggleHidden(this.props.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleHotkeyToggleSensitive = () => {
|
||||||
|
this.handleToggleMediaVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
handleMoveUp = id => {
|
handleMoveUp = id => {
|
||||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||||
|
|
||||||
|
@ -432,6 +444,7 @@ class Status extends ImmutablePureComponent {
|
||||||
mention: this.handleHotkeyMention,
|
mention: this.handleHotkeyMention,
|
||||||
openProfile: this.handleHotkeyOpenProfile,
|
openProfile: this.handleHotkeyOpenProfile,
|
||||||
toggleHidden: this.handleHotkeyToggleHidden,
|
toggleHidden: this.handleHotkeyToggleHidden,
|
||||||
|
toggleSensitive: this.handleHotkeyToggleSensitive,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -455,6 +468,8 @@ class Status extends ImmutablePureComponent {
|
||||||
onOpenMedia={this.handleOpenMedia}
|
onOpenMedia={this.handleOpenMedia}
|
||||||
onToggleHidden={this.handleToggleHidden}
|
onToggleHidden={this.handleToggleHidden}
|
||||||
domain={domain}
|
domain={domain}
|
||||||
|
showMedia={this.state.showMedia}
|
||||||
|
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ActionBar
|
<ActionBar
|
||||||
|
|
|
@ -93,6 +93,7 @@ const keyMap = {
|
||||||
goToMuted: 'g m',
|
goToMuted: 'g m',
|
||||||
goToRequests: 'g r',
|
goToRequests: 'g r',
|
||||||
toggleHidden: 'x',
|
toggleHidden: 'x',
|
||||||
|
toggleSensitive: 'h',
|
||||||
};
|
};
|
||||||
|
|
||||||
class SwitchingColumnsArea extends React.PureComponent {
|
class SwitchingColumnsArea extends React.PureComponent {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { fromJS } from 'immutable';
|
import { fromJS, is } from 'immutable';
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen';
|
import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen';
|
||||||
|
@ -102,6 +102,8 @@ class Video extends React.PureComponent {
|
||||||
detailed: PropTypes.bool,
|
detailed: PropTypes.bool,
|
||||||
inline: PropTypes.bool,
|
inline: PropTypes.bool,
|
||||||
cacheWidth: PropTypes.func,
|
cacheWidth: PropTypes.func,
|
||||||
|
visible: PropTypes.bool,
|
||||||
|
onToggleVisibility: PropTypes.func,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
blurhash: PropTypes.string,
|
blurhash: PropTypes.string,
|
||||||
link: PropTypes.node,
|
link: PropTypes.node,
|
||||||
|
@ -117,7 +119,7 @@ class Video extends React.PureComponent {
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
hovered: false,
|
hovered: false,
|
||||||
muted: 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
|
// hard coded in components.scss
|
||||||
|
@ -280,7 +282,16 @@ class Video extends React.PureComponent {
|
||||||
document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
|
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) {
|
if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) {
|
||||||
this._decode();
|
this._decode();
|
||||||
}
|
}
|
||||||
|
@ -316,12 +327,12 @@ class Video extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleReveal = () => {
|
toggleReveal = () => {
|
||||||
if (this.state.revealed) {
|
if (this.props.onToggleVisibility) {
|
||||||
this.video.pause();
|
this.props.onToggleVisibility();
|
||||||
}
|
} else {
|
||||||
|
|
||||||
this.setState({ revealed: !this.state.revealed });
|
this.setState({ revealed: !this.state.revealed });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleLoadedData = () => {
|
handleLoadedData = () => {
|
||||||
if (this.props.startTime) {
|
if (this.props.startTime) {
|
||||||
|
|
Loading…
Reference in New Issue