Skip to content

Commit f088f1e

Browse files
ClearlyClaireGargron
authored andcommitted
Add a keyboard shortcut to hide/show media (mastodon#10647)
* Move control of media visibility to parent component * Add keyboard shortcut to toggle media visibility
1 parent 51d7142 commit f088f1e

7 files changed

Lines changed: 75 additions & 12 deletions

File tree

app/javascript/mastodon/components/media_gallery.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,25 +244,33 @@ class MediaGallery extends React.PureComponent {
244244
intl: PropTypes.object.isRequired,
245245
defaultWidth: PropTypes.number,
246246
cacheWidth: PropTypes.func,
247+
visible: PropTypes.bool,
248+
onToggleVisibility: PropTypes.func,
247249
};
248250

249251
static defaultProps = {
250252
standalone: false,
251253
};
252254

253255
state = {
254-
visible: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all',
256+
visible: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
255257
width: this.props.defaultWidth,
256258
};
257259

258260
componentWillReceiveProps (nextProps) {
259-
if (!is(nextProps.media, this.props.media)) {
260-
this.setState({ visible: !nextProps.sensitive });
261+
if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) {
262+
this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' });
263+
} else if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
264+
this.setState({ visible: nextProps.visible });
261265
}
262266
}
263267

264268
handleOpen = () => {
265-
this.setState({ visible: !this.state.visible });
269+
if (this.props.onToggleVisibility) {
270+
this.props.onToggleVisibility();
271+
} else {
272+
this.setState({ visible: !this.state.visible });
273+
}
266274
}
267275

268276
handleClick = (index) => {

app/javascript/mastodon/components/status.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { HotKeys } from 'react-hotkeys';
1717
import classNames from 'classnames';
1818
import Icon from 'mastodon/components/icon';
1919
import PollContainer from 'mastodon/containers/poll_container';
20+
import { displayMedia } from '../initial_state';
2021

2122
// We use the component (and not the container) since we do not want
2223
// to use the progress bar to show download progress
@@ -85,6 +86,10 @@ class Status extends ImmutablePureComponent {
8586
'hidden',
8687
];
8788

89+
state = {
90+
showMedia: displayMedia !== 'hide_all' && !this.props.status.get('sensitive') || displayMedia === 'show_all',
91+
};
92+
8893
// Track height changes we know about to compensate scrolling
8994
componentDidMount () {
9095
this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
@@ -122,6 +127,10 @@ class Status extends ImmutablePureComponent {
122127
}
123128
}
124129

130+
handleToggleMediaVisibility = () => {
131+
this.setState({ showMedia: !this.state.showMedia });
132+
}
133+
125134
handleClick = () => {
126135
if (this.props.onClick) {
127136
this.props.onClick();
@@ -198,6 +207,10 @@ class Status extends ImmutablePureComponent {
198207
this.props.onToggleHidden(this._properStatus());
199208
}
200209

210+
handleHotkeyToggleSensitive = () => {
211+
this.handleToggleMediaVisibility();
212+
}
213+
201214
_properStatus () {
202215
const { status } = this.props;
203216

@@ -298,6 +311,8 @@ class Status extends ImmutablePureComponent {
298311
sensitive={status.get('sensitive')}
299312
onOpenVideo={this.handleOpenVideo}
300313
cacheWidth={this.props.cacheMediaWidth}
314+
visible={this.state.showMedia}
315+
onToggleVisibility={this.handleToggleMediaVisibility}
301316
/>
302317
)}
303318
</Bundle>
@@ -313,6 +328,8 @@ class Status extends ImmutablePureComponent {
313328
onOpenMedia={this.props.onOpenMedia}
314329
cacheWidth={this.props.cacheMediaWidth}
315330
defaultWidth={this.props.cachedMediaWidth}
331+
visible={this.state.showMedia}
332+
onToggleVisibility={this.handleToggleMediaVisibility}
316333
/>
317334
)}
318335
</Bundle>
@@ -348,6 +365,7 @@ class Status extends ImmutablePureComponent {
348365
moveUp: this.handleHotkeyMoveUp,
349366
moveDown: this.handleHotkeyMoveDown,
350367
toggleHidden: this.handleHotkeyToggleHidden,
368+
toggleSensitive: this.handleHotkeyToggleSensitive,
351369
};
352370

353371
return (

app/javascript/mastodon/features/keyboard_shortcuts/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ class KeyboardShortcuts extends ImmutablePureComponent {
6060
<td><kbd>x</kbd></td>
6161
<td><FormattedMessage id='keyboard_shortcuts.toggle_hidden' defaultMessage='to show/hide text behind CW' /></td>
6262
</tr>
63+
<tr>
64+
<td><kbd>h</kbd></td>
65+
<td><FormattedMessage id='keyboard_shortcuts.toggle_sensitivity' defaultMessage='to show/hide media' /></td>
66+
</tr>
6367
<tr>
6468
<td><kbd>up</kbd>, <kbd>k</kbd></td>
6569
<td><FormattedMessage id='keyboard_shortcuts.up' defaultMessage='to move up in the list' /></td>

app/javascript/mastodon/features/status/components/detailed_status.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
3030
onHeightChange: PropTypes.func,
3131
domain: PropTypes.string.isRequired,
3232
compact: PropTypes.bool,
33+
showMedia: PropTypes.bool,
34+
onToggleMediaVisibility: PropTypes.func,
3335
};
3436

3537
state = {
@@ -122,6 +124,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
122124
inline
123125
onOpenVideo={this.handleOpenVideo}
124126
sensitive={status.get('sensitive')}
127+
visible={this.props.showMedia}
128+
onToggleVisibility={this.props.onToggleMediaVisibility}
125129
/>
126130
);
127131
} else {
@@ -132,6 +136,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
132136
media={status.get('media_attachments')}
133137
height={300}
134138
onOpenMedia={this.props.onOpenMedia}
139+
visible={this.props.showMedia}
140+
onToggleVisibility={this.props.onToggleMediaVisibility}
135141
/>
136142
);
137143
}

app/javascript/mastodon/features/status/index.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { openModal } from '../../actions/modal';
4141
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
4242
import ImmutablePureComponent from 'react-immutable-pure-component';
4343
import { HotKeys } from 'react-hotkeys';
44-
import { boostModal, deleteModal } from '../../initial_state';
44+
import { boostModal, deleteModal, displayMedia } from '../../initial_state';
4545
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
4646
import { textForScreenReader } from '../../components/status';
4747
import Icon from 'mastodon/components/icon';
@@ -131,6 +131,7 @@ class Status extends ImmutablePureComponent {
131131

132132
state = {
133133
fullscreen: false,
134+
showMedia: !this.props.status ? undefined : (displayMedia !== 'hide_all' && !this.props.status.get('sensitive') || displayMedia === 'show_all'),
134135
};
135136

136137
componentWillMount () {
@@ -146,6 +147,13 @@ class Status extends ImmutablePureComponent {
146147
this._scrolledIntoView = false;
147148
this.props.dispatch(fetchStatus(nextProps.params.statusId));
148149
}
150+
if (!Immutable.is(nextProps.status, this.props.status) && nextProps.status) {
151+
this.setState({ showMedia: displayMedia !== 'hide_all' && !nextProps.status.get('sensitive') || displayMedia === 'show_all' });
152+
}
153+
}
154+
155+
handleToggleMediaVisibility = () => {
156+
this.setState({ showMedia: !this.state.showMedia });
149157
}
150158

151159
handleFavouriteClick = (status) => {
@@ -312,6 +320,10 @@ class Status extends ImmutablePureComponent {
312320
this.handleToggleHidden(this.props.status);
313321
}
314322

323+
handleHotkeyToggleSensitive = () => {
324+
this.handleToggleMediaVisibility();
325+
}
326+
315327
handleMoveUp = id => {
316328
const { status, ancestorsIds, descendantsIds } = this.props;
317329

@@ -432,6 +444,7 @@ class Status extends ImmutablePureComponent {
432444
mention: this.handleHotkeyMention,
433445
openProfile: this.handleHotkeyOpenProfile,
434446
toggleHidden: this.handleHotkeyToggleHidden,
447+
toggleSensitive: this.handleHotkeyToggleSensitive,
435448
};
436449

437450
return (
@@ -455,6 +468,8 @@ class Status extends ImmutablePureComponent {
455468
onOpenMedia={this.handleOpenMedia}
456469
onToggleHidden={this.handleToggleHidden}
457470
domain={domain}
471+
showMedia={this.state.showMedia}
472+
onToggleMediaVisibility={this.handleToggleMediaVisibility}
458473
/>
459474

460475
<ActionBar

app/javascript/mastodon/features/ui/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ const keyMap = {
9393
goToMuted: 'g m',
9494
goToRequests: 'g r',
9595
toggleHidden: 'x',
96+
toggleSensitive: 'h',
9697
};
9798

9899
class SwitchingColumnsArea extends React.PureComponent {

app/javascript/mastodon/features/video/index.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
4-
import { fromJS } from 'immutable';
4+
import { fromJS, is } from 'immutable';
55
import { throttle } from 'lodash';
66
import classNames from 'classnames';
77
import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen';
@@ -102,6 +102,8 @@ class Video extends React.PureComponent {
102102
detailed: PropTypes.bool,
103103
inline: PropTypes.bool,
104104
cacheWidth: PropTypes.func,
105+
visible: PropTypes.bool,
106+
onToggleVisibility: PropTypes.func,
105107
intl: PropTypes.object.isRequired,
106108
blurhash: PropTypes.string,
107109
link: PropTypes.node,
@@ -117,7 +119,7 @@ class Video extends React.PureComponent {
117119
fullscreen: false,
118120
hovered: false,
119121
muted: false,
120-
revealed: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all',
122+
revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
121123
};
122124

123125
// hard coded in components.scss
@@ -280,7 +282,16 @@ class Video extends React.PureComponent {
280282
document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
281283
}
282284

283-
componentDidUpdate (prevProps) {
285+
componentWillReceiveProps (nextProps) {
286+
if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
287+
this.setState({ revealed: nextProps.visible });
288+
}
289+
}
290+
291+
componentDidUpdate (prevProps, prevState) {
292+
if (prevState.revealed && !this.state.revealed && this.video) {
293+
this.video.pause();
294+
}
284295
if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) {
285296
this._decode();
286297
}
@@ -316,11 +327,11 @@ class Video extends React.PureComponent {
316327
}
317328

318329
toggleReveal = () => {
319-
if (this.state.revealed) {
320-
this.video.pause();
330+
if (this.props.onToggleVisibility) {
331+
this.props.onToggleVisibility();
332+
} else {
333+
this.setState({ revealed: !this.state.revealed });
321334
}
322-
323-
this.setState({ revealed: !this.state.revealed });
324335
}
325336

326337
handleLoadedData = () => {

0 commit comments

Comments
 (0)