From a5fac975f3951e954f8a1685150e716250a16a78 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Thu, 29 Mar 2018 21:13:47 +0200 Subject: [PATCH 1/4] [Glitch] Feature: Direct message from menu Port d1f34151aee564bb1e60ee48107797681c869a81 to glitch-soc --- .../flavours/glitch/actions/compose.js | 14 ++++++ .../features/account/components/action_bar.js | 3 ++ .../account_timeline/components/header.js | 6 +++ .../containers/header_container.js | 13 ++++- .../features/composer/direct_warning/index.js | 49 +++++++++++++++++++ .../glitch/features/composer/index.js | 2 + .../flavours/glitch/reducers/compose.js | 7 +++ 7 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 app/javascript/flavours/glitch/features/composer/direct_warning/index.js diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js index 8a6ca3699d..39381faa8c 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 fb90722f3d..8b95c08f27 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 39a1850d7a..a1434b8dd2 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 848119c638..fb0edfa882 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 0000000000..947096aed8 --- /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 3aa283628e..de85340a3b 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 8973c77135..68c7e11f1c 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: From fb6de5310dd2537af6dd94ada7e521d7ab5dfb09 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Mon, 2 Apr 2018 17:15:29 +0200 Subject: [PATCH 2/4] [Glitch] Fix issues with sending direct messages from user profile Port 4fd71accd419fb79cc75e0ebf30df374d174ebf5 to glitch-soc --- app/javascript/flavours/glitch/reducers/compose.js | 2 +- app/javascript/flavours/glitch/reducers/search.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js index 68c7e11f1c..bf0395a39e 100644 --- a/app/javascript/flavours/glitch/reducers/compose.js +++ b/app/javascript/flavours/glitch/reducers/compose.js @@ -328,7 +328,7 @@ export default function compose(state = initialState, action) { .set('idempotencyKey', uuid()); case COMPOSE_DIRECT: return state - .update('text', text => `${text}@${action.account.get('acct')} `) + .update('text', text => `@${action.account.get('acct')} `) .set('privacy', 'direct') .set('focusDate', new Date()) .set('idempotencyKey', uuid()); diff --git a/app/javascript/flavours/glitch/reducers/search.js b/app/javascript/flavours/glitch/reducers/search.js index f9bf920980..dc6be97e24 100644 --- a/app/javascript/flavours/glitch/reducers/search.js +++ b/app/javascript/flavours/glitch/reducers/search.js @@ -4,7 +4,11 @@ import { SEARCH_FETCH_SUCCESS, SEARCH_SHOW, } from 'flavours/glitch/actions/search'; -import { COMPOSE_MENTION, COMPOSE_REPLY } from 'flavours/glitch/actions/compose'; +import { + COMPOSE_MENTION, + COMPOSE_REPLY, + COMPOSE_DIRECT, +} from 'flavours/glitch/actions/compose'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; const initialState = ImmutableMap({ @@ -29,6 +33,7 @@ export default function search(state = initialState, action) { return state.set('hidden', false); case COMPOSE_REPLY: case COMPOSE_MENTION: + case COMPOSE_DIRECT: return state.set('hidden', true); case SEARCH_FETCH_SUCCESS: return state.set('results', ImmutableMap({ From 97c69de4162bf9dd5670c68ed03a6bb998e7c53d Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Tue, 10 Apr 2018 21:38:02 +0200 Subject: [PATCH 3/4] [Glitch] Feature: Direct message from Statuses Port 904a2479dd2085dfc94f33746ad6f7a755e72609 to glitch-soc --- .../flavours/glitch/components/status.js | 2 ++ .../glitch/components/status_action_bar.js | 7 +++++++ .../glitch/containers/status_container.js | 5 +++++ .../features/status/components/action_bar.js | 8 ++++++++ .../flavours/glitch/features/status/index.js | 6 ++++++ .../flavours/glitch/reducers/compose.js | 20 ++++++++++--------- 6 files changed, 39 insertions(+), 9 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index bff396f04f..f929d17a68 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -32,6 +32,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/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index da6e4e6baf..6ae4bc08d6 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -10,6 +10,7 @@ import RelativeTimestamp from './relative_timestamp'; 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}' }, @@ -44,6 +45,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, @@ -98,6 +100,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')); } @@ -157,6 +163,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/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js index 3fc6a6a795..acec00e124 100644 --- a/app/javascript/flavours/glitch/containers/status_container.js +++ b/app/javascript/flavours/glitch/containers/status_container.js @@ -5,6 +5,7 @@ import { makeGetStatus } from 'flavours/glitch/selectors'; import { replyCompose, mentionCompose, + directCompose, } from 'flavours/glitch/actions/compose'; import { reblog, @@ -131,6 +132,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ } }, + onDirect (account, router) { + dispatch(directCompose(account, router)); + }, + onMention (account, router) { dispatch(mentionCompose(account, router)); }, diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js index 1ea0fa4214..003f134a8e 100644 --- a/app/javascript/flavours/glitch/features/status/components/action_bar.js +++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js @@ -8,6 +8,7 @@ import { me } from 'flavours/glitch/util/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' }, @@ -43,6 +44,7 @@ export default class ActionBar extends React.PureComponent { onMuteConversation: PropTypes.func, onBlock: PropTypes.func, onDelete: PropTypes.func.isRequired, + onDirect: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, onReport: PropTypes.func, onPin: PropTypes.func, @@ -70,6 +72,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); } @@ -115,6 +121,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'])) { @@ -128,6 +135,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/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index 7e1658dbb4..6c9da8e3e6 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -21,6 +21,7 @@ import { import { replyCompose, mentionCompose, + directCompose, } from 'flavours/glitch/actions/compose'; import { blockAccount } from 'flavours/glitch/actions/accounts'; import { muteStatus, unmuteStatus, deleteStatus } from 'flavours/glitch/actions/statuses'; @@ -170,6 +171,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)); } @@ -399,6 +404,7 @@ export default class Status extends ImmutablePureComponent { onReblog={this.handleReblogClick} onBookmark={this.handleBookmarkClick} onDelete={this.handleDeleteClick} + onDirect={this.handleDirectClick} onMention={this.handleMentionClick} onMute={this.handleMuteClick} onMuteConversation={this.handleConversationMuteClick} diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js index bf0395a39e..f79fa63d24 100644 --- a/app/javascript/flavours/glitch/reducers/compose.js +++ b/app/javascript/flavours/glitch/reducers/compose.js @@ -322,16 +322,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: From 784712791da8c7d21afa62a3bf6a8fdac8c27a80 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Wed, 16 May 2018 19:45:02 +0200 Subject: [PATCH 4/4] [Glitch] Reword the direct message warning Port 53c2164e9c8e2538de386a526a97db187ecae470 to glitch-soc --- .../flavours/glitch/features/composer/direct_warning/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/features/composer/direct_warning/index.js b/app/javascript/flavours/glitch/features/composer/direct_warning/index.js index 947096aed8..804c6538bf 100644 --- a/app/javascript/flavours/glitch/features/composer/direct_warning/index.js +++ b/app/javascript/flavours/glitch/features/composer/direct_warning/index.js @@ -9,7 +9,7 @@ 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.', + defaultMessage: 'This toot will only be sent to all the mentioned users. However, the operators of your instance and any receiving instances may see this message.', id: 'compose_form.direct_message_warning', }, });