]> cat aescling's git repositories - mastodon.git/commitdiff
Feature: Direct message from Statuses (#7089)
authorEmelia Smith <ThisIsMissEm@users.noreply.github.com>
Mon, 9 Apr 2018 15:09:11 +0000 (17:09 +0200)
committerEugen Rochko <eugen@zeonfederated.com>
Mon, 9 Apr 2018 15:09:11 +0000 (17:09 +0200)
* 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

app/javascript/mastodon/components/status.js
app/javascript/mastodon/components/status_action_bar.js
app/javascript/mastodon/containers/status_container.js
app/javascript/mastodon/features/status/components/action_bar.js
app/javascript/mastodon/features/status/index.js
app/javascript/mastodon/locales/defaultMessages.json
app/javascript/mastodon/locales/en.json
app/javascript/mastodon/reducers/compose.js

index a918a94f876257231bf91f622eafbb1e328a4a80..6129b3f1ecb718666af4f8652a5fa94978d1e423 100644 (file)
@@ -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,
index e036dc1da4acb52aae6e9597315e9786391eb6aa..10f34b0c755d4a3638cdf013817da3b1774df515 100644 (file)
@@ -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 });
index 4579bd132dd1d7d6f8807181d4a8ac4a0c6373d6..f22509edfbea7102ae332835bc9b67374c4fb69e 100644 (file)
@@ -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));
   },
index 13cc10c9c55044a1ba9f4cc8401d4edabc9c44dd..4aa6b08f2c4d11dd5bdb8fbedc778df56eb8a159 100644 (file)
@@ -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 });
index 2f482b292ba761bdbcf41239f85ae84f006f5d5b..55eff082399108e9a985e471b8b0fffbaec4de45 100644 (file)
@@ -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}
index 52b9db9ac77295156d056d4a2bd1b78411ac348a..03be288bb69a99b68c94b2116fb38d146925b549 100644 (file)
         "defaultMessage": "Delete",
         "id": "status.delete"
       },
+      {
+        "defaultMessage": "Direct message @{name}",
+        "id": "status.direct"
+      },
       {
         "defaultMessage": "Mention @{name}",
         "id": "status.mention"
         "defaultMessage": "Delete",
         "id": "status.delete"
       },
+      {
+        "defaultMessage": "Direct message @{name}",
+        "id": "status.direct"
+      },
       {
         "defaultMessage": "Mention @{name}",
         "id": "status.mention"
index 2286d2e83d52f5b0ef4a562d1e4fd34f60845c3f..a389735c1ac3e7a175e96357c406a3f80782a322 100644 (file)
   "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",
index 1f41775855e2820bd1b4244853d400fb27900ed1..87049ea793f828452e980761d03f2cb1848bf6b0 100644 (file)
@@ -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: