Merge branch 'email-list' into 'develop'

Email list integration

See merge request soapbox-pub/soapbox-fe!527
merge-requests/528/head
Alex Gleason 3 years ago
commit 845ac6ab08

@ -0,0 +1,19 @@
import api from '../api';
export function getSubscribersCsv() {
return (dispatch, getState) => {
return api(getState).get('/api/v1/pleroma/admin/email_list/subscribers.csv');
};
}
export function getUnsubscribersCsv() {
return (dispatch, getState) => {
return api(getState).get('/api/v1/pleroma/admin/email_list/unsubscribers.csv');
};
}
export function getCombinedCsv() {
return (dispatch, getState) => {
return api(getState).get('/api/v1/pleroma/admin/email_list/combined.csv');
};
}

@ -8,6 +8,19 @@ import Column from '../ui/components/column';
import RegistrationModePicker from './components/registration_mode_picker';
import { parseVersion } from 'soapbox/utils/features';
import sourceCode from 'soapbox/utils/code';
import { getSubscribersCsv, getUnsubscribersCsv, getCombinedCsv } from 'soapbox/actions/email_list';
import { getFeatures } from 'soapbox/utils/features';
// https://stackoverflow.com/a/53230807
const download = (response, filename) => {
const url = URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
link.remove();
};
const messages = defineMessages({
heading: { id: 'column.admin.dashboard', defaultMessage: 'Dashboard' },
@ -15,6 +28,7 @@ const messages = defineMessages({
const mapStateToProps = (state, props) => ({
instance: state.get('instance'),
supportsEmailList: getFeatures(state.get('instance')).emailList,
});
export default @connect(mapStateToProps)
@ -24,10 +38,32 @@ class Dashboard extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
instance: ImmutablePropTypes.map.isRequired,
supportsEmailList: PropTypes.bool,
};
handleSubscribersClick = e => {
this.props.dispatch(getSubscribersCsv()).then((response) => {
download(response, 'subscribers.csv');
}).catch(() => {});
e.preventDefault();
}
handleUnsubscribersClick = e => {
this.props.dispatch(getUnsubscribersCsv()).then((response) => {
download(response, 'unsubscribers.csv');
}).catch(() => {});
e.preventDefault();
}
handleCombinedClick = e => {
this.props.dispatch(getCombinedCsv()).then((response) => {
download(response, 'combined.csv');
}).catch(() => {});
e.preventDefault();
}
render() {
const { intl, instance } = this.props;
const { intl, instance, supportsEmailList } = this.props;
const v = parseVersion(instance.get('version'));
const userCount = instance.getIn(['stats', 'user_count']);
const mau = instance.getIn(['pleroma', 'stats', 'mau']);
@ -96,6 +132,14 @@ class Dashboard extends ImmutablePureComponent {
<li>{v.software} <span className='pull-right'>{v.version}</span></li>
</ul>
</div>
{supportsEmailList && <div className='dashwidget'>
<h4><FormattedMessage id='admin.dashwidgets.email_list_header' defaultMessage='Email list' /></h4>
<ul>
<li><a href='#' onClick={this.handleSubscribersClick} target='_blank'>subscribers.csv</a></li>
<li><a href='#' onClick={this.handleUnsubscribersClick} target='_blank'>unsubscribers.csv</a></li>
<li><a href='#' onClick={this.handleCombinedClick} target='_blank'>combined.csv</a></li>
</ul>
</div>}
</div>
</Column>
);

@ -24,6 +24,7 @@ import { updateNotificationSettings } from 'soapbox/actions/accounts';
import { unescape } from 'lodash';
import { isVerified } from 'soapbox/utils/accounts';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
import { getFeatures } from 'soapbox/utils/features';
const messages = defineMessages({
heading: { id: 'column.edit_profile', defaultMessage: 'Edit profile' },
@ -41,6 +42,7 @@ const mapStateToProps = state => {
account,
maxFields: state.getIn(['instance', 'pleroma', 'metadata', 'fields_limits', 'max_fields'], 4),
verifiedCanEditName: soapbox.get('verifiedCanEditName'),
supportsEmailList: getFeatures(state.get('instance')).emailList,
};
};
@ -78,11 +80,13 @@ class EditProfile extends ImmutablePureComponent {
super(props);
const { account } = this.props;
const strangerNotifications = account.getIn(['pleroma', 'notification_settings', 'block_from_strangers']);
const acceptsEmailList = account.getIn(['pleroma', 'accepts_email_list']);
const initialState = account.withMutations(map => {
map.merge(map.get('source'));
map.delete('source');
map.set('fields', normalizeFields(map.get('fields'), props.maxFields));
map.set('stranger_notifications', strangerNotifications);
map.set('accepts_email_list', acceptsEmailList);
unescapeParams(map, ['display_name', 'bio']);
});
this.state = initialState.toObject();
@ -117,6 +121,7 @@ class EditProfile extends ImmutablePureComponent {
avatar: state.avatar_file,
header: state.header_file,
locked: state.locked,
accepts_email_list: state.accepts_email_list,
}, this.getFieldParams().toJS());
}
@ -179,7 +184,7 @@ class EditProfile extends ImmutablePureComponent {
}
render() {
const { intl, maxFields, account, verifiedCanEditName } = this.props;
const { intl, maxFields, account, verifiedCanEditName, supportsEmailList } = this.props;
const verified = isVerified(account);
const canEditName = verifiedCanEditName || !verified;
@ -246,6 +251,13 @@ class EditProfile extends ImmutablePureComponent {
checked={this.state.stranger_notifications}
onChange={this.handleCheckboxChange}
/>
{supportsEmailList && <Checkbox
label={<FormattedMessage id='edit_profile.fields.accepts_email_list_label' defaultMessage='Subscribe to newsletter' />}
hint={<FormattedMessage id='edit_profile.hints.accepts_email_list' defaultMessage='Opt-in to news and marketing updates.' />}
name='accepts_email_list'
checked={this.state.accepts_email_list}
onChange={this.handleCheckboxChange}
/>}
</FieldsGroup>
<FieldsGroup>
<div className='fields-row__column fields-group'>

@ -18,6 +18,7 @@ import { Map as ImmutableMap } from 'immutable';
import { v4 as uuidv4 } from 'uuid';
import { getSettings } from 'soapbox/actions/settings';
import { openModal } from 'soapbox/actions/modal';
import { getFeatures } from 'soapbox/utils/features';
const messages = defineMessages({
username: { id: 'registration.fields.username_placeholder', defaultMessage: 'Username' },
@ -28,6 +29,7 @@ const messages = defineMessages({
agreement: { id: 'registration.agreement', defaultMessage: 'I agree to the {tos}.' },
tos: { id: 'registration.tos', defaultMessage: 'Terms of Service' },
close: { id: 'registration.confirmation_modal.close', defaultMessage: 'Close' },
newsletter: { id: 'registration.newsletter', defaultMessage: 'Subscribe to newsletter.' },
});
const mapStateToProps = (state, props) => ({
@ -35,6 +37,7 @@ const mapStateToProps = (state, props) => ({
locale: getSettings(state).get('locale'),
needsConfirmation: state.getIn(['instance', 'pleroma', 'metadata', 'account_activation_required']),
needsApproval: state.getIn(['instance', 'approval_required']),
supportsEmailList: getFeatures(state.get('instance')).emailList,
});
export default @connect(mapStateToProps)
@ -135,7 +138,7 @@ class RegistrationForm extends ImmutablePureComponent {
}
render() {
const { instance, intl } = this.props;
const { instance, intl, supportsEmailList } = this.props;
const { params } = this.state;
const isOpen = instance.get('registrations');
const isLoading = this.state.captchaLoading || this.state.submissionLoading;
@ -232,6 +235,11 @@ class RegistrationForm extends ImmutablePureComponent {
onChange={this.onCheckboxChange}
required
/>
{supportsEmailList && <Checkbox
label={intl.formatMessage(messages.newsletter)}
name='accepts_email_list'
onChange={this.onCheckboxChange}
/>}
</div>
<div className='actions'>
<button name='button' type='submit' className='btn button button-primary'>

@ -1,8 +1,10 @@
// Detect backend features to conditionally render elements
import gte from 'semver/functions/gte';
import { List as ImmutableList } from 'immutable';
export const getFeatures = instance => {
const v = parseVersion(instance.get('version'));
const f = instance.getIn(['pleroma', 'metadata', 'features'], ImmutableList());
return {
suggestions: v.software === 'Mastodon' && gte(v.compatVersion, '2.4.3'),
trends: v.software === 'Mastodon' && gte(v.compatVersion, '3.0.0'),
@ -11,6 +13,7 @@ export const getFeatures = instance => {
attachmentLimit: v.software === 'Pleroma' ? Infinity : 4,
focalPoint: v.software === 'Mastodon' && gte(v.compatVersion, '2.3.0'),
importMutes: v.software === 'Pleroma' && gte(v.version, '2.2.0'),
emailList: f.includes('email_list'),
};
};

@ -68,6 +68,10 @@
margin-bottom: 8px;
border-bottom: 1px solid var(--accent-color--med);
}
a {
color: var(--brand-color);
}
}
.unapproved-account {

Loading…
Cancel
Save