import StatusPrepend from './status_prepend';
import StatusHeader from './status_header';
import StatusIcons from './status_icons';
-import StatusContent from './status_content';
+import StatusContentContainer from 'flavours/glitch/containers/status_content_container';
import StatusActionBar from './status_action_bar';
import AttachmentList from './attachment_list';
import Card from '../features/status/components/card';
settings={settings.get('status_icons')}
/>
</header>
- <StatusContent
+ <StatusContentContainer
status={status}
media={contentMedia}
extraMedia={extraMedia}
mediaIcons={contentMediaIcons}
expanded={isExpanded}
onExpandedToggle={this.handleExpandedToggle}
- onToggleSpoilerText={onToggleSpoilerText}
parseClick={parseClick}
disabled={!router}
tagLinks={settings.get('tag_misleading_links')}
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
+import ImmutablePureComponent from 'react-immutable-pure-component';
import PropTypes from 'prop-types';
-import { injectIntl, FormattedMessage } from 'react-intl';
+import { FormattedMessage } from 'react-intl';
import Permalink from './permalink';
import classnames from 'classnames';
import Icon from 'flavours/glitch/components/icon';
return !(textMatchesTarget(text, origin, host) || textMatchesTarget(text.toLowerCase(), origin, host));
};
-@injectIntl
-export default class StatusContent extends React.PureComponent {
+const listening = new WeakSet();
+
+export default class StatusContent extends ImmutablePureComponent {
static propTypes = {
status: ImmutablePropTypes.map.isRequired,
hidden: true,
};
+ /** Define listeners for inline spoiler text elements. */
+ _defineSpoilerTextListeners () {
+ const node = this.contentsNode;
+ const { onToggleSpoilerText } = this.props;
+ if (node && onToggleSpoilerText) {
+ [...node.querySelectorAll('.spoilertext')].forEach((elt) => {
+ if (listening.has(elt)) return;
+ elt.querySelector('button').addEventListener(
+ 'click',
+ (e) => {
+ // Ordinarily, calling the `onSpoilerTextClict(elt)` method
+ // will trigger a rerender, but it may not in the case of
+ // modals. This means we may need to do a manual redefining
+ // of spoiler text listeners.
+ this.onSpoilerTextClick(elt, e);
+ this._defineSpoilerTextListeners();
+ },
+ );
+ listening.add(elt);
+ });
+ }
+ }
+
_updateStatusLinks () {
const node = this.contentsNode;
const { tagLinks, rewriteMentions } = this.props;
return;
}
- [...node.querySelectorAll('.spoilertext')].forEach((elt) => {
- elt.querySelector('button').addEventListener(
- 'click',
- this.onSpoilerTextClick.bind(this, elt),
- );
- });
-
const links = node.querySelectorAll('a');
for (var i = 0; i < links.length; ++i) {
componentDidMount () {
const node = this.contentsNode;
- if (node) {
+ const { onToggleSpoilerText } = this.props;
+ if (node && onToggleSpoilerText) {
// Replace spoiler texts with their internationalized versions.
[...node.querySelectorAll('.spoilertext')].forEach((elt) => {
- this.props.onToggleSpoilerText(
+ if (node.querySelector('.spoilertext--button').title) return;
+ onToggleSpoilerText(
this.props.status,
node,
elt,
// The `.onToggleSpoilerText()` method actually replaces the
// `.spoilertext` elements, so we need to call this *after*.
this._updateStatusLinks();
+ this._defineSpoilerTextListeners();
}
componentDidUpdate () {
this._updateStatusLinks();
+ this._defineSpoilerTextListeners();
if (this.props.onUpdate) this.props.onUpdate();
}
hideStatus,
revealStatus,
editStatus,
- modifyStatusBody,
} from 'flavours/glitch/actions/statuses';
import {
initAddFilter,
import AccountContainer from 'flavours/glitch/containers/account_container';
import Spoilers from '../components/spoilers';
import Icon from 'flavours/glitch/components/icon';
-import spoilertextify from 'flavours/glitch/utils/spoilertextify';
const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
}
},
- onToggleSpoilerText (status, oldBody, spoilerElement, intl, open) {
- spoilerElement.replaceWith(spoilertextify(
- spoilerElement.getAttribute('data-spoilertext-content'),
- {
- emojos: status.get('emojis').reduce((obj, emoji) => {
- obj[`:${emoji.get('shortcode')}:`] = emoji.toJS();
- return obj;
- }, {}),
- intl,
- open: open == null
- ? !spoilerElement.classList.contains('open')
- : !!open,
- },
- ));
- dispatch(modifyStatusBody(
- status.get('id'),
- oldBody.innerHTML,
- ));
- },
-
deployPictureInPicture (status, type, mediaProps) {
dispatch((_, getState) => {
if (getState().getIn(['local_settings', 'media', 'pop_in_player'])) {
--- /dev/null
+import { connect } from 'react-redux';
+import { injectIntl } from 'react-intl';
+import StatusContent from 'flavours/glitch/components/status_content';
+import { modifyStatusBody } from 'flavours/glitch/actions/statuses';
+import spoilertextify from 'flavours/glitch/utils/spoilertextify';
+
+const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
+ /**
+ * Modifies the spoiler to be open or closed and then rewrites the
+ * HTML of the status to reflect that state.
+ *
+ * This will also save any other changes to the HTML, for example
+ * link rewriting.
+ */
+ onToggleSpoilerText (status, oldBody, spoilerElement, intl, open) {
+ spoilerElement.replaceWith(spoilertextify(
+ spoilerElement.getAttribute('data-spoilertext-content'),
+ {
+ emojos: status.get('emojis').reduce((obj, emoji) => {
+ obj[`:${emoji.get('shortcode')}:`] = emoji.toJS();
+ return obj;
+ }, {}),
+ intl,
+ open: open == null
+ ? !spoilerElement.classList.contains('open')
+ : !!open,
+ },
+ ));
+ dispatch(modifyStatusBody(
+ status.get('id'),
+ oldBody.innerHTML,
+ ));
+ },
+});
+
+export default injectIntl(connect(null, mapDispatchToProps)(StatusContent));
\ No newline at end of file
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
-import StatusContent from 'flavours/glitch/components/status_content';
+import StatusContentContainer from 'flavours/glitch/containers/status_content_container';
import AttachmentList from 'flavours/glitch/components/attachment_list';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
</div>
</div>
- <StatusContent
+ <StatusContentContainer
status={lastStatus}
parseClick={this.parseClick}
expanded={isExpanded}
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
-import StatusContent from 'flavours/glitch/components/status_content';
+import StatusContentContainer from 'flavours/glitch/containers/status_content_container';
import Avatar from 'flavours/glitch/components/avatar';
import DisplayName from 'flavours/glitch/components/display_name';
import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp';
<div><DisplayName account={status.get('account')} /> · <RelativeTimestamp timestamp={status.get('created_at')} /></div>
</div>
- <StatusContent status={status} media={<MediaAttachments status={status} revealed={false} />} />
+ <StatusContentContainer status={status} media={<MediaAttachments status={status} revealed={false} />} />
</div>
);
import ImmutablePropTypes from 'react-immutable-proptypes';
import Avatar from 'flavours/glitch/components/avatar';
import DisplayName from 'flavours/glitch/components/display_name';
-import StatusContent from 'flavours/glitch/components/status_content';
+import StatusContentContainer from 'flavours/glitch/containers/status_content_container';
import MediaGallery from 'flavours/glitch/components/media_gallery';
import AttachmentList from 'flavours/glitch/components/attachment_list';
import { Link } from 'react-router-dom';
onOpenMedia: PropTypes.func.isRequired,
onOpenVideo: PropTypes.func.isRequired,
onToggleHidden: PropTypes.func,
- onToggleSpoilerText: PropTypes.func,
expanded: PropTypes.bool,
measureHeight: PropTypes.bool,
onHeightChange: PropTypes.func,
render () {
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
- const { expanded, onToggleHidden, onToggleSpoilerText, settings, usingPiP, intl } = this.props;
+ const { expanded, onToggleHidden, settings, usingPiP, intl } = this.props;
const outerStyle = { boxSizing: 'border-box' };
const { compact } = this.props;
<DisplayName account={status.get('account')} localDomain={this.props.domain} />
</a>
- <StatusContent
+ <StatusContentContainer
status={status}
media={contentMedia}
extraMedia={extraMedia}
expanded={expanded}
collapsed={false}
onExpandedToggle={onToggleHidden}
- onToggleSpoilerText={onToggleSpoilerText}
parseClick={this.parseClick}
onUpdate={this.handleChildUpdate}
tagLinks={settings.get('tag_misleading_links')}
editStatus,
hideStatus,
revealStatus,
- modifyStatusBody,
} from 'flavours/glitch/actions/statuses';
import { initMuteModal } from 'flavours/glitch/actions/mutes';
import { initBlockModal } from 'flavours/glitch/actions/blocks';
import { autoUnfoldCW } from 'flavours/glitch/util/content_warning';
import { textForScreenReader, defaultMediaVisibility } from 'flavours/glitch/components/status';
import Icon from 'flavours/glitch/components/icon';
-import spoilertextify from 'flavours/glitch/utils/spoilertextify';
const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
}
}
- handleToggleSpoilerText = (status, oldBody, spoilerElement, intl, open) => {
- spoilerElement.replaceWith(spoilertextify(
- spoilerElement.getAttribute('data-spoilertext-content'),
- {
- emojos: status.get('emojis').reduce((obj, emoji) => {
- obj[`:${emoji.get('shortcode')}:`] = emoji.toJS();
- return obj;
- }, {}),
- intl,
- open: open == null
- ? !spoilerElement.classList.contains('open')
- : !!open,
- },
- ));
- this.props.dispatch(modifyStatusBody(
- status.get('id'),
- oldBody.innerHTML,
- ));
- }
-
handleToggleMediaVisibility = () => {
this.setState({ showMedia: !this.state.showMedia });
}
settings={settings}
onOpenVideo={this.handleOpenVideo}
onOpenMedia={this.handleOpenMedia}
- onToggleSpoilerText={this.handleToggleSpoilerText}
expanded={isExpanded}
onToggleHidden={this.handleToggleHidden}
domain={domain}
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
-import StatusContent from 'flavours/glitch/components/status_content';
+import StatusContentContainer from 'flavours/glitch/containers/status_content_container';
import Avatar from 'flavours/glitch/components/avatar';
import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp';
import DisplayName from 'flavours/glitch/components/display_name';
</a>
</div>
- <StatusContent status={this.props.status} />
+ <StatusContentContainer status={this.props.status} />
</div>
);
import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Button from 'flavours/glitch/components/button';
-import StatusContent from 'flavours/glitch/components/status_content';
+import StatusContentContainer from 'flavours/glitch/containers/status_content_container';
import Avatar from 'flavours/glitch/components/avatar';
import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp';
import DisplayName from 'flavours/glitch/components/display_name';
</a>
</div>
- <StatusContent status={status} />
+ <StatusContentContainer status={status} />
{status.get('media_attachments').size > 0 && (
<AttachmentList
import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Button from 'flavours/glitch/components/button';
-import StatusContent from 'flavours/glitch/components/status_content';
+import StatusContentContainer from 'flavours/glitch/containers/status_content_container';
import Avatar from 'flavours/glitch/components/avatar';
import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp';
import DisplayName from 'flavours/glitch/components/display_name';
</a>
</div>
- <StatusContent status={status} />
+ <StatusContentContainer status={status} />
{status.get('media_attachments').size > 0 && (
<AttachmentList