diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index 8a6ca3699df..39381faa8cd 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -21,6 +21,7 @@ export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
export const COMPOSE_REPLY = 'COMPOSE_REPLY';
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
+export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
export const COMPOSE_MENTION = 'COMPOSE_MENTION';
export const COMPOSE_RESET = 'COMPOSE_RESET';
export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
@@ -102,6 +103,19 @@ export function mentionCompose(account, router) {
};
};
+export function directCompose(account, router) {
+ return (dispatch, getState) => {
+ dispatch({
+ type: COMPOSE_DIRECT,
+ account: account,
+ });
+
+ if (!getState().getIn(['compose', 'mounted'])) {
+ router.push('/statuses/new');
+ }
+ };
+};
+
export function submitCompose() {
return function (dispatch, getState) {
let status = getState().getIn(['compose', 'text'], '');
diff --git a/app/javascript/flavours/glitch/features/account/components/action_bar.js b/app/javascript/flavours/glitch/features/account/components/action_bar.js
index fb90722f3d0..8b95c08f275 100644
--- a/app/javascript/flavours/glitch/features/account/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/account/components/action_bar.js
@@ -8,6 +8,7 @@ import { me } from 'flavours/glitch/util/initial_state';
const messages = defineMessages({
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
+ direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
@@ -32,6 +33,7 @@ export default class ActionBar extends React.PureComponent {
onFollow: PropTypes.func,
onBlock: PropTypes.func.isRequired,
onMention: PropTypes.func.isRequired,
+ onDirect: PropTypes.func.isRequired,
onReblogToggle: PropTypes.func.isRequired,
onReport: PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired,
@@ -53,6 +55,7 @@ export default class ActionBar extends React.PureComponent {
let extraInfo = '';
menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
+ menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect });
if ('share' in navigator) {
menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
diff --git a/app/javascript/flavours/glitch/features/account_timeline/components/header.js b/app/javascript/flavours/glitch/features/account_timeline/components/header.js
index 39a1850d7a3..a1434b8dd2e 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/components/header.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/components/header.js
@@ -16,6 +16,7 @@ export default class Header extends ImmutablePureComponent {
onFollow: PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired,
onMention: PropTypes.func.isRequired,
+ onDirect: PropTypes.func.isRequired,
onReblogToggle: PropTypes.func.isRequired,
onReport: PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired,
@@ -40,6 +41,10 @@ export default class Header extends ImmutablePureComponent {
this.props.onMention(this.props.account, this.context.router.history);
}
+ handleDirect = () => {
+ this.props.onDirect(this.props.account, this.context.router.history);
+ }
+
handleReport = () => {
this.props.onReport(this.props.account);
}
@@ -89,6 +94,7 @@ export default class Header extends ImmutablePureComponent {
account={account}
onBlock={this.handleBlock}
onMention={this.handleMention}
+ onDirect={this.handleDirect}
onReblogToggle={this.handleReblogToggle}
onReport={this.handleReport}
onMute={this.handleMute}
diff --git a/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js b/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js
index 848119c638c..fb0edfa882f 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js
@@ -9,7 +9,10 @@ import {
unblockAccount,
unmuteAccount,
} from 'flavours/glitch/actions/accounts';
-import { mentionCompose } from 'flavours/glitch/actions/compose';
+import {
+ mentionCompose,
+ directCompose
+} from 'flavours/glitch/actions/compose';
import { initMuteModal } from 'flavours/glitch/actions/mutes';
import { initReport } from 'flavours/glitch/actions/reports';
import { openModal } from 'flavours/glitch/actions/modal';
@@ -67,6 +70,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(mentionCompose(account, router));
},
+ onDirect (account, router) {
+ dispatch(directCompose(account, router));
+ },
+
+ onDirect (account, router) {
+ dispatch(directCompose(account, router));
+ },
+
onReblogToggle (account) {
if (account.getIn(['relationship', 'showing_reblogs'])) {
dispatch(followAccount(account.get('id'), false));
diff --git a/app/javascript/flavours/glitch/features/composer/direct_warning/index.js b/app/javascript/flavours/glitch/features/composer/direct_warning/index.js
new file mode 100644
index 00000000000..947096aed8e
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/composer/direct_warning/index.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import Motion from 'flavours/glitch/util/optional_motion';
+import spring from 'react-motion/lib/spring';
+import { defineMessages, FormattedMessage } from 'react-intl';
+
+// This is the spring used with our motion.
+const motionSpring = spring(1, { damping: 35, stiffness: 400 });
+
+// Messages.
+const messages = defineMessages({
+ disclaimer: {
+ defaultMessage: 'This toot will only be visible to all the mentioned users.',
+ id: 'compose_form.direct_message_warning',
+ },
+});
+
+// The component.
+export default function ComposerDirectWarning () {
+ return (
+
+ {({ opacity, scaleX, scaleY }) => (
+
+
+
+ )}
+
+ );
+}
+
+ComposerDirectWarning.propTypes = {};
diff --git a/app/javascript/flavours/glitch/features/composer/index.js b/app/javascript/flavours/glitch/features/composer/index.js
index 3aa283628ea..de85340a3b1 100644
--- a/app/javascript/flavours/glitch/features/composer/index.js
+++ b/app/javascript/flavours/glitch/features/composer/index.js
@@ -39,6 +39,7 @@ import ComposerTextarea from './textarea';
import ComposerUploadForm from './upload_form';
import ComposerWarning from './warning';
import ComposerHashtagWarning from './hashtag_warning';
+import ComposerDirectWarning from './direct_warning';
// Utils.
import { countableText } from 'flavours/glitch/util/counter';
@@ -326,6 +327,7 @@ class Composer extends React.Component {
onSubmit={handleSubmit}
text={spoilerText}
/>
+ {privacy === 'direct' ? : null}
{privacy === 'private' && amUnlocked ? : null}
{privacy !== 'public' && APPROX_HASHTAG_RE.test(text) ? : null}
{replyContent ? (
diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js
index 8973c77135a..68c7e11f1cb 100644
--- a/app/javascript/flavours/glitch/reducers/compose.js
+++ b/app/javascript/flavours/glitch/reducers/compose.js
@@ -5,6 +5,7 @@ import {
COMPOSE_CYCLE_ELEFRIEND,
COMPOSE_REPLY,
COMPOSE_REPLY_CANCEL,
+ COMPOSE_DIRECT,
COMPOSE_MENTION,
COMPOSE_SUBMIT_REQUEST,
COMPOSE_SUBMIT_SUCCESS,
@@ -325,6 +326,12 @@ export default function compose(state = initialState, action) {
.update('text', text => `${text}@${action.account.get('acct')} `)
.set('focusDate', new Date())
.set('idempotencyKey', uuid());
+ case COMPOSE_DIRECT:
+ return state
+ .update('text', text => `${text}@${action.account.get('acct')} `)
+ .set('privacy', 'direct')
+ .set('focusDate', new Date())
+ .set('idempotencyKey', uuid());
case COMPOSE_SUGGESTIONS_CLEAR:
return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null);
case COMPOSE_SUGGESTIONS_READY: