Skip to content

Commit

Permalink
Add UI for posting to circles
Browse files Browse the repository at this point in the history
  • Loading branch information
noellabo committed Aug 31, 2020
1 parent 7ef4800 commit 36f6a68
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 4 deletions.
11 changes: 10 additions & 1 deletion app/javascript/mastodon/actions/compose.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,22 @@ export function submitCompose(routerHistory) {

dispatch(submitComposeRequest());

let visibility = getState().getIn(['compose', 'privacy']);
let circleId = null;

if (!(['public', 'unlisted', 'private', 'direct'].includes(visibility))) {
circleId = visibility;
visibility = 'limited';
}

api(getState).post('/api/v1/statuses', {
status,
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
media_ids: media.map(item => item.get('id')),
sensitive: getState().getIn(['compose', 'sensitive']),
spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
visibility: getState().getIn(['compose', 'privacy']),
visibility: visibility,
circle_id: circleId,
poll: getState().getIn(['compose', 'poll'], null),
}, {
headers: {
Expand Down
2 changes: 2 additions & 0 deletions app/javascript/mastodon/containers/mastodon.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ScrollContext } from 'react-router-scroll-4';
import UI from '../features/ui';
import Introduction from '../features/introduction';
import { fetchCustomEmojis } from '../actions/custom_emojis';
import { fetchCircles } from '../actions/circles';
import { hydrateStore } from '../actions/store';
import { connectUserStream } from '../actions/streaming';
import { IntlProvider, addLocaleData } from 'react-intl';
Expand All @@ -25,6 +26,7 @@ const hydrateAction = hydrateStore(initialState);

store.dispatch(hydrateAction);
store.dispatch(fetchCustomEmojis());
store.dispatch(fetchCircles());

const mapStateToProps = state => ({
showIntroduction: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { injectIntl, defineMessages } from 'react-intl';
import IconButton from '../../../components/icon_button';
import Overlay from 'react-overlays/lib/Overlay';
Expand All @@ -8,6 +10,7 @@ import spring from 'react-motion/lib/spring';
import detectPassiveEvents from 'detect-passive-events';
import classNames from 'classnames';
import Icon from 'mastodon/components/icon';
import { createSelector } from 'reselect';

const messages = defineMessages({
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
Expand All @@ -18,6 +21,7 @@ const messages = defineMessages({
private_long: { id: 'privacy.private.long', defaultMessage: 'Visible for followers only' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
direct_long: { id: 'privacy.direct.long', defaultMessage: 'Visible for mentioned users only' },
limited_long: { id: 'privacy.limited.long', defaultMessage: 'Visible for circle users only' },
change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
});

Expand Down Expand Up @@ -148,14 +152,30 @@ class PrivacyDropdownMenu extends React.PureComponent {

}

export default @injectIntl
const getOrderedCircles = createSelector([state => state.get('circles')], circles => {
if (!circles) {
return circles;
}

return circles.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title')));
});

const mapStateToProps = (state) => {
return {
circles: getOrderedCircles(state),
};
};

export default @connect(mapStateToProps)
@injectIntl
class PrivacyDropdown extends React.PureComponent {

static propTypes = {
isUserTouching: PropTypes.func,
isModalOpen: PropTypes.bool.isRequired,
onModalOpen: PropTypes.func,
onModalClose: PropTypes.func,
circles: ImmutablePropTypes.list,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
Expand Down Expand Up @@ -230,14 +250,24 @@ class PrivacyDropdown extends React.PureComponent {
}

componentWillMount () {
const { intl: { formatMessage } } = this.props;
this.setOptions();
}

componentWillUpdate () {
this.setOptions();
}

setOptions () {
const { intl: { formatMessage }, circles } = this.props;

this.options = [
{ icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
{ icon: 'unlock', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
{ icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
{ icon: 'envelope', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
];

circles.forEach(circle => this.options.push({ icon: 'circle-o', value: circle.get('id'), text: circle.get('title'), meta: formatMessage(messages.limited_long) }));
}

render () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ const mapStateToProps = state => ({
needsLockWarning: state.getIn(['compose', 'privacy']) === 'private' && !state.getIn(['accounts', me, 'locked']),
hashtagWarning: state.getIn(['compose', 'privacy']) !== 'public' && APPROX_HASHTAG_RE.test(state.getIn(['compose', 'text'])),
directMessageWarning: state.getIn(['compose', 'privacy']) === 'direct',
limitedMessageWarning: !(['public', 'unlisted', 'private', 'direct'].includes(state.getIn(['compose', 'privacy']))),
limitedTitle: state.getIn(['circles', state.getIn(['compose', 'privacy']), 'title']),
});

const WarningWrapper = ({ needsLockWarning, hashtagWarning, directMessageWarning }) => {
const WarningWrapper = ({ needsLockWarning, hashtagWarning, directMessageWarning, limitedMessageWarning, limitedTitle }) => {
if (needsLockWarning) {
return <Warning message={<FormattedMessage id='compose_form.lock_disclaimer' defaultMessage='Your account is not {locked}. Anyone can follow you to view your follower-only posts.' values={{ locked: <a href='/settings/profile'><FormattedMessage id='compose_form.lock_disclaimer.lock' defaultMessage='locked' /></a> }} />} />;
}
Expand All @@ -55,13 +57,19 @@ const WarningWrapper = ({ needsLockWarning, hashtagWarning, directMessageWarning
return <Warning message={message} />;
}

if (limitedMessageWarning) {
return <Warning message={<FormattedMessage id='compose_form.limited_message_warning' defaultMessage='This toot will only be sent to users in the circle "{title}".' values={{ title: limitedTitle }} />} />;
}

return null;
};

WarningWrapper.propTypes = {
needsLockWarning: PropTypes.bool,
hashtagWarning: PropTypes.bool,
directMessageWarning: PropTypes.bool,
limitedMessageWarning: PropTypes.bool,
limitedTitle: PropTypes.string,
};

export default connect(mapStateToProps)(WarningWrapper);

0 comments on commit 36f6a68

Please sign in to comment.