about summary refs log tree commit diff
path: root/app/javascript
diff options
context:
space:
mode:
authorEmelia Smith <ThisIsMissEm@users.noreply.github.com>2018-04-09 17:09:11 +0200
committerEugen Rochko <eugen@zeonfederated.com>2018-04-09 17:09:11 +0200
commit904a2479dd2085dfc94f33746ad6f7a755e72609 (patch)
tree374190e579fc965e7e872c91bf5176119bf0a7ea /app/javascript
parente057c0e525692a7dcca188f254f043e3c77e1a8a (diff)
Feature: Direct message from Statuses (#7089)
* Fix: Switching between composing direct message and mention from menus

Previously clicking "direct message" followed by "mention" resulted in the composed status staying as "direct", along with weird spacing of items in the text area. This attempts to fix that.

* Fix: Add missing proptype check for onMention in Status component

* Add the ability to send a direct message to a user from the menu on Statuses

* Add space between "Embed" and "Mention" on expanded statuses menu
Diffstat (limited to 'app/javascript')
-rw-r--r--app/javascript/mastodon/components/status.js2
-rw-r--r--app/javascript/mastodon/components/status_action_bar.js7
-rw-r--r--app/javascript/mastodon/containers/status_container.js5
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.js8
-rw-r--r--app/javascript/mastodon/features/status/index.js6
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json8
-rw-r--r--app/javascript/mastodon/locales/en.json1
-rw-r--r--app/javascript/mastodon/reducers/compose.js20
8 files changed, 48 insertions, 9 deletions
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index a918a94f8..6129b3f1e 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -31,6 +31,8 @@ export default class Status extends ImmutablePureComponent {
     onFavourite: PropTypes.func,
     onReblog: PropTypes.func,
     onDelete: PropTypes.func,
+    onDirect: PropTypes.func,
+    onMention: PropTypes.func,
     onPin: PropTypes.func,
     onOpenMedia: PropTypes.func,
     onOpenVideo: PropTypes.func,
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js
index e036dc1da..10f34b0c7 100644
--- a/app/javascript/mastodon/components/status_action_bar.js
+++ b/app/javascript/mastodon/components/status_action_bar.js
@@ -9,6 +9,7 @@ import { me } from '../initial_state';
 
 const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
+  direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
@@ -41,6 +42,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
     onFavourite: PropTypes.func,
     onReblog: PropTypes.func,
     onDelete: PropTypes.func,
+    onDirect: PropTypes.func,
     onMention: PropTypes.func,
     onMute: PropTypes.func,
     onBlock: PropTypes.func,
@@ -92,6 +94,10 @@ export default class StatusActionBar extends ImmutablePureComponent {
     this.props.onMention(this.props.status.get('account'), this.context.router.history);
   }
 
+  handleDirectClick = () => {
+    this.props.onDirect(this.props.status.get('account'), this.context.router.history);
+  }
+
   handleMuteClick = () => {
     this.props.onMute(this.props.status.get('account'));
   }
@@ -149,6 +155,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
       menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
     } else {
       menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
+      menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
       menu.push(null);
       menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
       menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js
index 4579bd132..f22509edf 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -5,6 +5,7 @@ import { makeGetStatus } from '../selectors';
 import {
   replyCompose,
   mentionCompose,
+  directCompose,
 } from '../actions/compose';
 import {
   reblog,
@@ -102,6 +103,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     }
   },
 
+  onDirect (account, router) {
+    dispatch(directCompose(account, router));
+  },
+
   onMention (account, router) {
     dispatch(mentionCompose(account, router));
   },
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
index 13cc10c9c..4aa6b08f2 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -8,6 +8,7 @@ import { me } from '../../../initial_state';
 
 const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
+  direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   reply: { id: 'status.reply', defaultMessage: 'Reply' },
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
@@ -37,6 +38,7 @@ export default class ActionBar extends React.PureComponent {
     onReblog: PropTypes.func.isRequired,
     onFavourite: PropTypes.func.isRequired,
     onDelete: PropTypes.func.isRequired,
+    onDirect: PropTypes.func.isRequired,
     onMention: PropTypes.func.isRequired,
     onMute: PropTypes.func,
     onMuteConversation: PropTypes.func,
@@ -63,6 +65,10 @@ export default class ActionBar extends React.PureComponent {
     this.props.onDelete(this.props.status);
   }
 
+  handleDirectClick = () => {
+    this.props.onDirect(this.props.status.get('account'), this.context.router.history);
+  }
+
   handleMentionClick = () => {
     this.props.onMention(this.props.status.get('account'), this.context.router.history);
   }
@@ -108,6 +114,7 @@ export default class ActionBar extends React.PureComponent {
 
     if (publicStatus) {
       menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
+      menu.push(null);
     }
 
     if (me === status.getIn(['account', 'id'])) {
@@ -121,6 +128,7 @@ export default class ActionBar extends React.PureComponent {
       menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
     } else {
       menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
+      menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
       menu.push(null);
       menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
       menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 2f482b292..55eff0823 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -19,6 +19,7 @@ import {
 import {
   replyCompose,
   mentionCompose,
+  directCompose,
 } from '../../actions/compose';
 import { blockAccount } from '../../actions/accounts';
 import {
@@ -148,6 +149,10 @@ export default class Status extends ImmutablePureComponent {
     }
   }
 
+  handleDirectClick = (account, router) => {
+    this.props.dispatch(directCompose(account, router));
+  }
+
   handleMentionClick = (account, router) => {
     this.props.dispatch(mentionCompose(account, router));
   }
@@ -379,6 +384,7 @@ export default class Status extends ImmutablePureComponent {
                   onFavourite={this.handleFavouriteClick}
                   onReblog={this.handleReblogClick}
                   onDelete={this.handleDeleteClick}
+                  onDirect={this.handleDirectClick}
                   onMention={this.handleMentionClick}
                   onMute={this.handleMuteClick}
                   onMuteConversation={this.handleConversationMuteClick}
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 52b9db9ac..03be288bb 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -198,6 +198,10 @@
         "id": "status.delete"
       },
       {
+        "defaultMessage": "Direct message @{name}",
+        "id": "status.direct"
+      },
+      {
         "defaultMessage": "Mention @{name}",
         "id": "status.mention"
       },
@@ -1371,6 +1375,10 @@
         "id": "status.delete"
       },
       {
+        "defaultMessage": "Direct message @{name}",
+        "id": "status.direct"
+      },
+      {
         "defaultMessage": "Mention @{name}",
         "id": "status.mention"
       },
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 2286d2e83..a389735c1 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -240,6 +240,7 @@
   "status.block": "Block @{name}",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favourite",
   "status.load_more": "Load more",
diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js
index 1f4177585..87049ea79 100644
--- a/app/javascript/mastodon/reducers/compose.js
+++ b/app/javascript/mastodon/reducers/compose.js
@@ -259,16 +259,18 @@ export default function compose(state = initialState, action) {
   case COMPOSE_UPLOAD_PROGRESS:
     return state.set('progress', Math.round((action.loaded / action.total) * 100));
   case COMPOSE_MENTION:
-    return state
-      .update('text', text => `${text}@${action.account.get('acct')} `)
-      .set('focusDate', new Date())
-      .set('idempotencyKey', uuid());
+    return state.withMutations(map => {
+      map.update('text', text => [text.trim(), `@${action.account.get('acct')} `].filter((str) => str.length !== 0).join(' '));
+      map.set('focusDate', new Date());
+      map.set('idempotencyKey', uuid());
+    });
   case COMPOSE_DIRECT:
-    return state
-      .update('text', text => `@${action.account.get('acct')} `)
-      .set('privacy', 'direct')
-      .set('focusDate', new Date())
-      .set('idempotencyKey', uuid());
+    return state.withMutations(map => {
+      map.update('text', text => [text.trim(), `@${action.account.get('acct')} `].filter((str) => str.length !== 0).join(' '));
+      map.set('privacy', 'direct');
+      map.set('focusDate', new Date());
+      map.set('idempotencyKey', uuid());
+    });
   case COMPOSE_SUGGESTIONS_CLEAR:
     return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null);
   case COMPOSE_SUGGESTIONS_READY: