[Glitch] Add keyboard shortcut to hide/show media
Porta472190729and988342a56cto glitch-soc
This commit is contained in:
		@@ -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) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -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}
 | 
			
		||||
            />)}
 | 
			
		||||
          </Bundle>
 | 
			
		||||
        );
 | 
			
		||||
@@ -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}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
          </Bundle>
 | 
			
		||||
@@ -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')}`, {
 | 
			
		||||
 
 | 
			
		||||
@@ -71,6 +71,10 @@ export default class KeyboardShortcuts extends ImmutablePureComponent {
 | 
			
		||||
                <td><kbd>x</kbd></td>
 | 
			
		||||
                <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>
 | 
			
		||||
              {collapseEnabled && (
 | 
			
		||||
                <tr>
 | 
			
		||||
                  <td><kbd>shift</kbd>+<kbd>x</kbd></td>
 | 
			
		||||
 
 | 
			
		||||
@@ -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';
 | 
			
		||||
 
 | 
			
		||||
@@ -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}
 | 
			
		||||
                />
 | 
			
		||||
 | 
			
		||||
                <ActionBar
 | 
			
		||||
 
 | 
			
		||||
@@ -101,6 +101,7 @@ const keyMap = {
 | 
			
		||||
  toggleSpoiler: 'x',
 | 
			
		||||
  bookmark: 'd',
 | 
			
		||||
  toggleCollapse: 'shift+x',
 | 
			
		||||
  toggleSensitive: 'h',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@connect(mapStateToProps)
 | 
			
		||||
 
 | 
			
		||||
@@ -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 'flavours/glitch/util/fullscreen';
 | 
			
		||||
@@ -94,7 +94,6 @@ export default class Video extends React.PureComponent {
 | 
			
		||||
    width: PropTypes.number,
 | 
			
		||||
    height: PropTypes.number,
 | 
			
		||||
    sensitive: PropTypes.bool,
 | 
			
		||||
    revealed: PropTypes.bool,
 | 
			
		||||
    startTime: PropTypes.number,
 | 
			
		||||
    onOpenVideo: PropTypes.func,
 | 
			
		||||
    onCloseVideo: PropTypes.func,
 | 
			
		||||
@@ -102,9 +101,11 @@ export default class Video extends React.PureComponent {
 | 
			
		||||
    fullwidth: PropTypes.bool,
 | 
			
		||||
    detailed: PropTypes.bool,
 | 
			
		||||
    inline: PropTypes.bool,
 | 
			
		||||
    preventPlayback: PropTypes.bool,
 | 
			
		||||
    intl: PropTypes.object.isRequired,
 | 
			
		||||
    cacheWidth: PropTypes.func,
 | 
			
		||||
    intl: PropTypes.object.isRequired,
 | 
			
		||||
    visible: PropTypes.bool,
 | 
			
		||||
    onToggleVisibility: PropTypes.func,
 | 
			
		||||
    preventPlayback: PropTypes.bool,
 | 
			
		||||
    blurhash: PropTypes.string,
 | 
			
		||||
    link: PropTypes.node,
 | 
			
		||||
  };
 | 
			
		||||
@@ -119,12 +120,12 @@ export default class Video extends React.PureComponent {
 | 
			
		||||
    fullscreen: false,
 | 
			
		||||
    hovered: false,
 | 
			
		||||
    muted: false,
 | 
			
		||||
    revealed: this.props.revealed === undefined ? (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all') : this.props.revealed,
 | 
			
		||||
    revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  componentWillReceiveProps (nextProps) {
 | 
			
		||||
    if (nextProps.revealed === true) {
 | 
			
		||||
      this.setState({ revealed: true });
 | 
			
		||||
    if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
 | 
			
		||||
      this.setState({ revealed: nextProps.visible });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -349,7 +350,11 @@ export default class Video extends React.PureComponent {
 | 
			
		||||
      this.video.pause();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.setState({ revealed: !this.state.revealed });
 | 
			
		||||
    if (this.props.onToggleVisibility) {
 | 
			
		||||
      this.props.onToggleVisibility();
 | 
			
		||||
    } else {
 | 
			
		||||
      this.setState({ revealed: !this.state.revealed });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleLoadedData = () => {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user