static propTypes = {
sensitive: PropTypes.bool,
- revealed: PropTypes.bool,
standalone: PropTypes.bool,
letterbox: PropTypes.bool,
fullwidth: PropTypes.bool,
intl: PropTypes.object.isRequired,
defaultWidth: PropTypes.number,
cacheWidth: PropTypes.func,
+ visible: PropTypes.bool,
+ onToggleVisibility: PropTypes.func,
};
static defaultProps = {
};
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 });
}
}
}
handleOpen = () => {
- this.setState({ visible: !this.state.visible });
+ if (this.props.onToggleVisibility) {
+ this.props.onToggleVisibility();
+ } else {
+ this.setState({ visible: !this.state.visible });
+ }
}
handleClick = (index) => {
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
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 {
isCollapsed: false,
autoCollapsed: false,
isExpanded: undefined,
+ showMedia: undefined,
+ statusId: undefined,
+ revealBehindCW: undefined,
}
// Avoid checking props that are functions (and whose equality will always
updateOnStates = [
'isExpanded',
'isCollapsed',
+ 'showMedia',
]
// If our settings have changed to disable collapsed statuses, then we
}
}
+ 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;
}
}
}
+ handleToggleMediaVisibility = () => {
+ this.setState({ showMedia: !this.state.showMedia });
+ }
+
handleAccountClick = (e) => {
if (this.context.router && e.button === 0) {
const id = e.currentTarget.getAttribute('data-id');
this.setCollapsed(!this.state.isCollapsed);
}
+ handleHotkeyToggleSensitive = () => {
+ this.handleToggleMediaVisibility();
+ }
handleRef = c => {
this.node = c;
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>
);
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>
toggleSpoiler: this.handleExpandedToggle,
bookmark: this.handleHotkeyBookmark,
toggleCollapse: this.handleHotkeyCollapse,
+ toggleSensitive: this.handleHotkeyToggleSensitive,
};
const computedClass = classNames('status', `status-${status.get('visibility')}`, {
<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>
onHeightChange: PropTypes.func,
domain: PropTypes.string.isRequired,
compact: PropTypes.bool,
+ showMedia: PropTypes.bool,
+ onToggleMediaVisibility: PropTypes.func,
};
state = {
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';
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';
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' },
isExpanded: undefined,
threadExpanded: undefined,
statusId: undefined,
+ loadedStatusId: undefined,
+ showMedia: undefined,
+ revealBehindCW: undefined,
};
componentDidMount () {
}
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 = () => {
}
};
+ handleToggleMediaVisibility = () => {
+ this.setState({ showMedia: !this.state.showMedia });
+ }
+
handleModalFavourite = (status) => {
this.props.dispatch(favourite(status));
}
this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
}
+ handleHotkeyToggleSensitive = () => {
+ this.handleToggleMediaVisibility();
+ }
+
handleHotkeyMoveUp = () => {
this.handleMoveUp(this.props.status.get('id'));
}
mention: this.handleHotkeyMention,
openProfile: this.handleHotkeyOpenProfile,
toggleSpoiler: this.handleExpandedToggle,
+ toggleSensitive: this.handleHotkeyToggleSensitive,
};
return (
expanded={isExpanded}
onToggleHidden={this.handleExpandedToggle}
domain={domain}
+ showMedia={this.state.showMedia}
+ onToggleMediaVisibility={this.handleToggleMediaVisibility}
/>
<ActionBar
toggleSpoiler: 'x',
bookmark: 'd',
toggleCollapse: 'shift+x',
+ toggleSensitive: 'h',
};
@connect(mapStateToProps)
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';
width: PropTypes.number,
height: PropTypes.number,
sensitive: PropTypes.bool,
- revealed: PropTypes.bool,
startTime: PropTypes.number,
onOpenVideo: PropTypes.func,
onCloseVideo: PropTypes.func,
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,
};
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 });
}
}
this.video.pause();
}
- this.setState({ revealed: !this.state.revealed });
+ if (this.props.onToggleVisibility) {
+ this.props.onToggleVisibility();
+ } else {
+ this.setState({ revealed: !this.state.revealed });
+ }
}
handleLoadedData = () => {