[Glitch] Feature: Direct message from menu

Port d1f34151ae to glitch-soc
This commit is contained in:
Thibaut Girka 2018-03-29 21:13:47 +02:00
parent 939ea456d2
commit a5fac975f3
7 changed files with 93 additions and 1 deletions

View file

@ -21,6 +21,7 @@ export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL'; export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
export const COMPOSE_REPLY = 'COMPOSE_REPLY'; export const COMPOSE_REPLY = 'COMPOSE_REPLY';
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL'; export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
export const COMPOSE_MENTION = 'COMPOSE_MENTION'; export const COMPOSE_MENTION = 'COMPOSE_MENTION';
export const COMPOSE_RESET = 'COMPOSE_RESET'; export const COMPOSE_RESET = 'COMPOSE_RESET';
export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST'; 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() { export function submitCompose() {
return function (dispatch, getState) { return function (dispatch, getState) {
let status = getState().getIn(['compose', 'text'], ''); let status = getState().getIn(['compose', 'text'], '');

View file

@ -8,6 +8,7 @@ import { me } from 'flavours/glitch/util/initial_state';
const messages = defineMessages({ const messages = defineMessages({
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' }, mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
@ -32,6 +33,7 @@ export default class ActionBar extends React.PureComponent {
onFollow: PropTypes.func, onFollow: PropTypes.func,
onBlock: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired,
onMention: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired,
onDirect: PropTypes.func.isRequired,
onReblogToggle: PropTypes.func.isRequired, onReblogToggle: PropTypes.func.isRequired,
onReport: PropTypes.func.isRequired, onReport: PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired, onMute: PropTypes.func.isRequired,
@ -53,6 +55,7 @@ export default class ActionBar extends React.PureComponent {
let extraInfo = ''; let extraInfo = '';
menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention }); 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) { if ('share' in navigator) {
menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare }); menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });

View file

@ -16,6 +16,7 @@ export default class Header extends ImmutablePureComponent {
onFollow: PropTypes.func.isRequired, onFollow: PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired,
onMention: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired,
onDirect: PropTypes.func.isRequired,
onReblogToggle: PropTypes.func.isRequired, onReblogToggle: PropTypes.func.isRequired,
onReport: PropTypes.func.isRequired, onReport: PropTypes.func.isRequired,
onMute: 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); this.props.onMention(this.props.account, this.context.router.history);
} }
handleDirect = () => {
this.props.onDirect(this.props.account, this.context.router.history);
}
handleReport = () => { handleReport = () => {
this.props.onReport(this.props.account); this.props.onReport(this.props.account);
} }
@ -89,6 +94,7 @@ export default class Header extends ImmutablePureComponent {
account={account} account={account}
onBlock={this.handleBlock} onBlock={this.handleBlock}
onMention={this.handleMention} onMention={this.handleMention}
onDirect={this.handleDirect}
onReblogToggle={this.handleReblogToggle} onReblogToggle={this.handleReblogToggle}
onReport={this.handleReport} onReport={this.handleReport}
onMute={this.handleMute} onMute={this.handleMute}

View file

@ -9,7 +9,10 @@ import {
unblockAccount, unblockAccount,
unmuteAccount, unmuteAccount,
} from 'flavours/glitch/actions/accounts'; } 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 { initMuteModal } from 'flavours/glitch/actions/mutes';
import { initReport } from 'flavours/glitch/actions/reports'; import { initReport } from 'flavours/glitch/actions/reports';
import { openModal } from 'flavours/glitch/actions/modal'; import { openModal } from 'flavours/glitch/actions/modal';
@ -67,6 +70,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(mentionCompose(account, router)); dispatch(mentionCompose(account, router));
}, },
onDirect (account, router) {
dispatch(directCompose(account, router));
},
onDirect (account, router) {
dispatch(directCompose(account, router));
},
onReblogToggle (account) { onReblogToggle (account) {
if (account.getIn(['relationship', 'showing_reblogs'])) { if (account.getIn(['relationship', 'showing_reblogs'])) {
dispatch(followAccount(account.get('id'), false)); dispatch(followAccount(account.get('id'), false));

View file

@ -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 (
<Motion
defaultStyle={{
opacity: 0,
scaleX: 0.85,
scaleY: 0.75,
}}
style={{
opacity: motionSpring,
scaleX: motionSpring,
scaleY: motionSpring,
}}
>
{({ opacity, scaleX, scaleY }) => (
<div
className='composer--warning'
style={{
opacity: opacity,
transform: `scale(${scaleX}, ${scaleY})`,
}}
>
<FormattedMessage
{...messages.disclaimer}
/>
</div>
)}
</Motion>
);
}
ComposerDirectWarning.propTypes = {};

View file

@ -39,6 +39,7 @@ import ComposerTextarea from './textarea';
import ComposerUploadForm from './upload_form'; import ComposerUploadForm from './upload_form';
import ComposerWarning from './warning'; import ComposerWarning from './warning';
import ComposerHashtagWarning from './hashtag_warning'; import ComposerHashtagWarning from './hashtag_warning';
import ComposerDirectWarning from './direct_warning';
// Utils. // Utils.
import { countableText } from 'flavours/glitch/util/counter'; import { countableText } from 'flavours/glitch/util/counter';
@ -326,6 +327,7 @@ class Composer extends React.Component {
onSubmit={handleSubmit} onSubmit={handleSubmit}
text={spoilerText} text={spoilerText}
/> />
{privacy === 'direct' ? <ComposerDirectWarning /> : null}
{privacy === 'private' && amUnlocked ? <ComposerWarning /> : null} {privacy === 'private' && amUnlocked ? <ComposerWarning /> : null}
{privacy !== 'public' && APPROX_HASHTAG_RE.test(text) ? <ComposerHashtagWarning /> : null} {privacy !== 'public' && APPROX_HASHTAG_RE.test(text) ? <ComposerHashtagWarning /> : null}
{replyContent ? ( {replyContent ? (

View file

@ -5,6 +5,7 @@ import {
COMPOSE_CYCLE_ELEFRIEND, COMPOSE_CYCLE_ELEFRIEND,
COMPOSE_REPLY, COMPOSE_REPLY,
COMPOSE_REPLY_CANCEL, COMPOSE_REPLY_CANCEL,
COMPOSE_DIRECT,
COMPOSE_MENTION, COMPOSE_MENTION,
COMPOSE_SUBMIT_REQUEST, COMPOSE_SUBMIT_REQUEST,
COMPOSE_SUBMIT_SUCCESS, COMPOSE_SUBMIT_SUCCESS,
@ -325,6 +326,12 @@ export default function compose(state = initialState, action) {
.update('text', text => `${text}@${action.account.get('acct')} `) .update('text', text => `${text}@${action.account.get('acct')} `)
.set('focusDate', new Date()) .set('focusDate', new Date())
.set('idempotencyKey', uuid()); .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: case COMPOSE_SUGGESTIONS_CLEAR:
return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null); return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null);
case COMPOSE_SUGGESTIONS_READY: case COMPOSE_SUGGESTIONS_READY: