From b4d4138cf9753b0c25a17f01702b61a74de6ec2c Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Sun, 26 May 2019 18:58:14 +0200 Subject: [PATCH] [Glitch] Add keyboard shortcut to hide/show media Port a472190729782f31731674c626c07af483fe9c7f and 988342a56cb58da9ac660eec3e55c3bcbbd6269b to glitch-soc --- .../glitch/components/media_gallery.js | 17 +++++-- .../flavours/glitch/components/status.js | 49 ++++++++++++++++++- .../features/keyboard_shortcuts/index.js | 4 ++ .../status/components/detailed_status.js | 8 ++- .../flavours/glitch/features/status/index.js | 46 +++++++++++++---- .../flavours/glitch/features/ui/index.js | 1 + .../flavours/glitch/features/video/index.js | 21 +++++--- 7 files changed, 120 insertions(+), 26 deletions(-) diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js index 194800d52..6ef101f11 100644 --- a/app/javascript/flavours/glitch/components/media_gallery.js +++ b/app/javascript/flavours/glitch/components/media_gallery.js @@ -257,7 +257,6 @@ export default class MediaGallery extends React.PureComponent { static propTypes = { sensitive: PropTypes.bool, - revealed: PropTypes.bool, standalone: PropTypes.bool, letterbox: PropTypes.bool, fullwidth: PropTypes.bool, @@ -268,6 +267,8 @@ export default class MediaGallery extends React.PureComponent { intl: PropTypes.object.isRequired, defaultWidth: PropTypes.number, cacheWidth: PropTypes.func, + visible: PropTypes.bool, + onToggleVisibility: PropTypes.func, }; static defaultProps = { @@ -275,13 +276,15 @@ export default class MediaGallery extends React.PureComponent { }; state = { - visible: this.props.revealed === undefined ? (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all') : this.props.revealed, + 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) || nextProps.revealed === true) { - this.setState({ visible: nextProps.revealed === undefined ? (displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all') : nextProps.revealed }); + 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 }); } } @@ -294,7 +297,11 @@ export default class MediaGallery extends React.PureComponent { } 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/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 5f10e0c52..7014cab17 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -16,6 +16,7 @@ import NotificationOverlayContainer from 'flavours/glitch/features/notifications import classNames from 'classnames'; import { autoUnfoldCW } from 'flavours/glitch/util/content_warning'; import PollContainer from 'flavours/glitch/containers/poll_container'; +import { displayMedia } from 'flavours/glitch/util/initial_state'; // We use the component (and not the container) since we do not want // to use the progress bar to show download progress @@ -38,6 +39,22 @@ export const textForScreenReader = (intl, status, rebloggedByText = false, expan return values.join(', '); }; +export const defaultMediaVisibility = (status, settings) => { + if (!status) { + return undefined; + } + + if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') { + status = status.get('reblog'); + } + + if (settings.getIn(['media', 'reveal_behind_cw']) && !!status.get('spoiler_text')) { + return true; + } + + return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all'); +} + @injectIntl export default class Status extends ImmutablePureComponent { @@ -82,6 +99,9 @@ export default class Status extends ImmutablePureComponent { isCollapsed: false, autoCollapsed: false, isExpanded: undefined, + showMedia: undefined, + statusId: undefined, + revealBehindCW: undefined, } // Avoid checking props that are functions (and whose equality will always @@ -103,6 +123,7 @@ export default class Status extends ImmutablePureComponent { updateOnStates = [ 'isExpanded', 'isCollapsed', + 'showMedia', ] // If our settings have changed to disable collapsed statuses, then we @@ -160,6 +181,20 @@ export default class Status extends ImmutablePureComponent { } } + if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) { + update.showMedia = defaultMediaVisibility(nextProps.status, nextProps.settings); + update.statusId = nextProps.status.get('id'); + updated = true; + } + + if (nextProps.settings.getIn(['media', 'reveal_behind_cw']) !== prevState.revealBehindCW) { + update.revealBehindCW = nextProps.settings.getIn(['media', 'reveal_behind_cw']); + if (update.revealBehindCW) { + update.showMedia = defaultMediaVisibility(nextProps.status, nextProps.settings); + } + updated = true; + } + return updated ? update : null; } @@ -305,6 +340,10 @@ export default class Status extends ImmutablePureComponent { } } + handleToggleMediaVisibility = () => { + this.setState({ showMedia: !this.state.showMedia }); + } + handleAccountClick = (e) => { if (this.context.router && e.button === 0) { const id = e.currentTarget.getAttribute('data-id'); @@ -374,6 +413,9 @@ export default class Status extends ImmutablePureComponent { this.setCollapsed(!this.state.isCollapsed); } + handleHotkeyToggleSensitive = () => { + this.handleToggleMediaVisibility(); + } handleRef = c => { this.node = c; @@ -490,7 +532,8 @@ export default class Status extends ImmutablePureComponent { onOpenVideo={this.handleOpenVideo} width={this.props.cachedMediaWidth} cacheWidth={this.props.cacheMediaWidth} - revealed={settings.getIn(['media', 'reveal_behind_cw']) && !!status.get('spoiler_text') ? true : undefined} + visible={this.state.showMedia} + onToggleVisibility={this.handleToggleMediaVisibility} />)} ); @@ -508,7 +551,8 @@ export default class Status extends ImmutablePureComponent { onOpenMedia={this.props.onOpenMedia} cacheWidth={this.props.cacheMediaWidth} defaultWidth={this.props.cachedMediaWidth} - revealed={settings.getIn(['media', 'reveal_behind_cw']) && !!status.get('spoiler_text') ? true : undefined} + visible={this.state.showMedia} + onToggleVisibility={this.handleToggleMediaVisibility} /> )} @@ -566,6 +610,7 @@ export default class Status extends ImmutablePureComponent { toggleSpoiler: this.handleExpandedToggle, bookmark: this.handleHotkeyBookmark, toggleCollapse: this.handleHotkeyCollapse, + toggleSensitive: this.handleHotkeyToggleSensitive, }; const computedClass = classNames('status', `status-${status.get('visibility')}`, { diff --git a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js index 2935a6021..f7b475f8d 100644 --- a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js +++ b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js @@ -71,6 +71,10 @@ export default class KeyboardShortcuts extends ImmutablePureComponent { x + + h + + {collapseEnabled && ( shift+x diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index 03d98fde8..ddedac4d4 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -33,6 +33,8 @@ export default class DetailedStatus extends ImmutablePureComponent { onHeightChange: PropTypes.func, domain: PropTypes.string.isRequired, compact: PropTypes.bool, + showMedia: PropTypes.bool, + onToggleMediaVisibility: PropTypes.func, }; state = { @@ -144,7 +146,8 @@ export default class DetailedStatus extends ImmutablePureComponent { preventPlayback={!expanded} onOpenVideo={this.handleOpenVideo} autoplay - revealed={settings.getIn(['media', 'reveal_behind_cw']) && !!status.get('spoiler_text') ? true : undefined} + visible={this.props.showMedia} + onToggleVisibility={this.props.onToggleMediaVisibility} /> ); mediaIcon = 'video-camera'; @@ -158,7 +161,8 @@ export default class DetailedStatus extends ImmutablePureComponent { fullwidth={settings.getIn(['media', 'fullwidth'])} hidden={!expanded} onOpenMedia={this.props.onOpenMedia} - revealed={settings.getIn(['media', 'reveal_behind_cw']) && !!status.get('spoiler_text') ? true : undefined} + visible={this.props.showMedia} + onToggleVisibility={this.props.onToggleMediaVisibility} /> ); mediaIcon = 'picture-o'; diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index 57d70db1a..145a33fff 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -41,7 +41,7 @@ import { HotKeys } from 'react-hotkeys'; import { boostModal, favouriteModal, deleteModal } from 'flavours/glitch/util/initial_state'; import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'flavours/glitch/util/fullscreen'; import { autoUnfoldCW } from 'flavours/glitch/util/content_warning'; -import { textForScreenReader } from 'flavours/glitch/components/status'; +import { textForScreenReader, defaultMediaVisibility } from 'flavours/glitch/components/status'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -134,6 +134,9 @@ export default class Status extends ImmutablePureComponent { isExpanded: undefined, threadExpanded: undefined, statusId: undefined, + loadedStatusId: undefined, + showMedia: undefined, + revealBehindCW: undefined, }; componentDidMount () { @@ -152,17 +155,31 @@ export default class Status extends ImmutablePureComponent { } static getDerivedStateFromProps(props, state) { - if (state.statusId === props.params.statusId || !props.params.statusId) { - return null; + let update = {}; + let updated = false; + + if (props.params.statusId && state.statusId !== props.params.statusId) { + props.dispatch(fetchStatus(props.params.statusId)); + update.threadExpanded = undefined; + update.statusId = props.params.statusId; + updated = true; } - props.dispatch(fetchStatus(props.params.statusId)); + const revealBehindCW = props.settings.getIn(['media', 'reveal_behind_cw']); + if (revealBehindCW !== state.revealBehindCW) { + update.revealBehindCW = revealBehindCW; + if (revealBehindCW) update.showMedia = defaultMediaVisibility(props.status, props.settings); + updated = true; + } - return { - threadExpanded: undefined, - isExpanded: autoUnfoldCW(props.settings, props.status), - statusId: props.params.statusId, - }; + if (props.status && state.loadedStatusId !== props.status.get('id')) { + update.showMedia = defaultMediaVisibility(props.status, props.settings); + update.loadedStatusId = props.status.get('id'); + update.isExpanded = autoUnfoldCW(props.settings, props.status); + updated = true; + } + + return updated ? update : null; } handleExpandedToggle = () => { @@ -171,6 +188,10 @@ export default class Status extends ImmutablePureComponent { } }; + handleToggleMediaVisibility = () => { + this.setState({ showMedia: !this.state.showMedia }); + } + handleModalFavourite = (status) => { this.props.dispatch(favourite(status)); } @@ -304,6 +325,10 @@ export default class Status extends ImmutablePureComponent { this.props.dispatch(openModal('EMBED', { url: status.get('url') })); } + handleHotkeyToggleSensitive = () => { + this.handleToggleMediaVisibility(); + } + handleHotkeyMoveUp = () => { this.handleMoveUp(this.props.status.get('id')); } @@ -477,6 +502,7 @@ export default class Status extends ImmutablePureComponent { mention: this.handleHotkeyMention, openProfile: this.handleHotkeyOpenProfile, toggleSpoiler: this.handleExpandedToggle, + toggleSensitive: this.handleHotkeyToggleSensitive, }; return ( @@ -505,6 +531,8 @@ export default class Status extends ImmutablePureComponent { expanded={isExpanded} onToggleHidden={this.handleExpandedToggle} domain={domain} + showMedia={this.state.showMedia} + onToggleMediaVisibility={this.handleToggleMediaVisibility} /> {