diff --git a/app/assets/javascripts/components/actions/compose.jsx b/app/assets/javascripts/components/actions/compose.jsx
index 05674ba89..948ccf872 100644
--- a/app/assets/javascripts/components/actions/compose.jsx
+++ b/app/assets/javascripts/components/actions/compose.jsx
@@ -23,6 +23,8 @@ export const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE';
+export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
+export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
@@ -68,6 +70,8 @@ export function submitCompose() {
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')),
sensitive: getState().getIn(['compose', 'sensitive']),
+ spoiler: getState().getIn(['compose', 'spoiler']),
+ spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
visibility: getState().getIn(['compose', 'private']) ? 'private' : (getState().getIn(['compose', 'unlisted']) ? 'unlisted' : 'public')
}).then(function (response) {
dispatch(submitComposeSuccess({ ...response.data }));
@@ -218,6 +222,20 @@ export function changeComposeSensitivity(checked) {
};
};
+export function changeComposeSpoilerness(checked) {
+ return {
+ type: COMPOSE_SPOILERNESS_CHANGE,
+ checked
+ };
+};
+
+export function changeComposeSpoilerText(text) {
+ return {
+ type: COMPOSE_SPOILER_TEXT_CHANGE,
+ text
+ };
+};
+
export function changeComposeVisibility(checked) {
return {
type: COMPOSE_VISIBILITY_CHANGE,
diff --git a/app/assets/javascripts/components/components/status_content.jsx b/app/assets/javascripts/components/components/status_content.jsx
index f2c88cee0..7287aa836 100644
--- a/app/assets/javascripts/components/components/status_content.jsx
+++ b/app/assets/javascripts/components/components/status_content.jsx
@@ -18,6 +18,12 @@ const StatusContent = React.createClass({
componentDidMount () {
const node = ReactDOM.findDOMNode(this);
const links = node.querySelectorAll('a');
+ const spoilers = node.querySelectorAll('.spoiler');
+
+ for (var i = 0; i < spoilers.length; ++i) {
+ let spoiler = spoilers[i];
+ spoiler.addEventListener('click', this.onSpoilerClick.bind(this, spoiler), true);
+ }
for (var i = 0; i < links.length; ++i) {
let link = links[i];
@@ -52,6 +58,18 @@ const StatusContent = React.createClass({
}
},
+ onSpoilerClick (spoiler, e) {
+ if (e.button === 0) {
+ //only toggle if we're not clicking a visible link
+ var hasClass = $(spoiler).hasClass('spoiler-on');
+ if (hasClass || e.target === spoiler) {
+ e.stopPropagation();
+ e.preventDefault();
+ $(spoiler).siblings(".spoiler").andSelf().toggleClass('spoiler-on', !hasClass);
+ }
+ }
+ },
+
onNormalClick (e) {
e.stopPropagation();
},
diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx
index 80cb38e16..84d273299 100644
--- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx
+++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx
@@ -14,6 +14,7 @@ import { Motion, spring } from 'react-motion';
const messages = defineMessages({
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
+ spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Content warning' },
publish: { id: 'compose_form.publish', defaultMessage: 'Publish' }
});
@@ -25,6 +26,8 @@ const ComposeForm = React.createClass({
suggestion_token: React.PropTypes.string,
suggestions: ImmutablePropTypes.list,
sensitive: React.PropTypes.bool,
+ spoiler: React.PropTypes.bool,
+ spoiler_text: React.PropTypes.string,
unlisted: React.PropTypes.bool,
private: React.PropTypes.bool,
fileDropDate: React.PropTypes.instanceOf(Date),
@@ -40,6 +43,8 @@ const ComposeForm = React.createClass({
onFetchSuggestions: React.PropTypes.func.isRequired,
onSuggestionSelected: React.PropTypes.func.isRequired,
onChangeSensitivity: React.PropTypes.func.isRequired,
+ onChangeSpoilerness: React.PropTypes.func.isRequired,
+ onChangeSpoilerText: React.PropTypes.func.isRequired,
onChangeVisibility: React.PropTypes.func.isRequired,
onChangeListability: React.PropTypes.func.isRequired,
},
@@ -77,6 +82,15 @@ const ComposeForm = React.createClass({
this.props.onChangeSensitivity(e.target.checked);
},
+ handleChangeSpoilerness (e) {
+ this.props.onChangeSpoilerness(e.target.checked);
+ this.props.onChangeSpoilerText('');
+ },
+
+ handleChangeSpoilerText (e) {
+ this.props.onChangeSpoilerText(e.target.value);
+ },
+
handleChangeVisibility (e) {
this.props.onChangeVisibility(e.target.checked);
},
@@ -115,6 +129,14 @@ const ComposeForm = React.createClass({
return (
+
+ {({ opacity, height }) =>
+
+
+
+ }
+
+
{replyArea}
-
+
@@ -142,6 +164,11 @@ const ComposeForm = React.createClass({
+
+
{({ opacity, height }) =>