diff --git a/app/soapbox/actions/import_data.js b/app/soapbox/actions/import_data.js new file mode 100644 index 000000000..251d2972d --- /dev/null +++ b/app/soapbox/actions/import_data.js @@ -0,0 +1,56 @@ +import api from '../api'; +import { showAlert } from 'soapbox/actions/alerts'; + +export const IMPORT_FOLLOWS_REQUEST = 'IMPORT_FOLLOWS_REQUEST'; +export const IMPORT_FOLLOWS_SUCCESS = 'IMPORT_FOLLOWS_SUCCESS'; +export const IMPORT_FOLLOWS_FAIL = 'IMPORT_FOLLOWS_FAIL'; + +export const IMPORT_BLOCKS_REQUEST = 'IMPORT_BLOCKS_REQUEST'; +export const IMPORT_BLOCKS_SUCCESS = 'IMPORT_BLOCKS_SUCCESS'; +export const IMPORT_BLOCKS_FAIL = 'IMPORT_BLOCKS_FAIL'; + +export const IMPORT_MUTES_REQUEST = 'IMPORT_MUTES_REQUEST'; +export const IMPORT_MUTES_SUCCESS = 'IMPORT_MUTES_SUCCESS'; +export const IMPORT_MUTES_FAIL = 'IMPORT_MUTES_FAIL'; + +export function importFollows(params) { + return (dispatch, getState) => { + dispatch({ type: IMPORT_FOLLOWS_REQUEST }); + return api(getState) + .post('/api/pleroma/follow_import', params) + .then(response => { + dispatch(showAlert('', 'Followers imported successfully')); + dispatch({ type: IMPORT_FOLLOWS_SUCCESS, config: response.data }); + }).catch(error => { + dispatch({ type: IMPORT_FOLLOWS_FAIL, error }); + }); + }; +} + +export function importBlocks(params) { + return (dispatch, getState) => { + dispatch({ type: IMPORT_BLOCKS_REQUEST }); + return api(getState) + .post('/api/pleroma/blocks_import', params) + .then(response => { + dispatch(showAlert('', 'Blocks imported successfully')); + dispatch({ type: IMPORT_BLOCKS_SUCCESS, config: response.data }); + }).catch(error => { + dispatch({ type: IMPORT_BLOCKS_FAIL, error }); + }); + }; +} + +export function importMutes(params) { + return (dispatch, getState) => { + dispatch({ type: IMPORT_MUTES_REQUEST }); + return api(getState) + .post('/api/pleroma/mutes_import', params) + .then(response => { + dispatch(showAlert('', 'Mutes imported successfully')); + dispatch({ type: IMPORT_MUTES_SUCCESS, config: response.data }); + }).catch(error => { + dispatch({ type: IMPORT_MUTES_FAIL, error }); + }); + }; +} diff --git a/app/soapbox/components/sidebar_menu.js b/app/soapbox/components/sidebar_menu.js index 6cc615c65..ea9f753d4 100644 --- a/app/soapbox/components/sidebar_menu.js +++ b/app/soapbox/components/sidebar_menu.js @@ -29,6 +29,7 @@ const messages = defineMessages({ filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' }, admin_settings: { id: 'navigation_bar.admin_settings', defaultMessage: 'Admin settings' }, soapbox_config: { id: 'navigation_bar.soapbox_config', defaultMessage: 'Soapbox config' }, + import_data: { id: 'navigation_bar.import_data', defaultMessage: 'Import data' }, security: { id: 'navigation_bar.security', defaultMessage: 'Security' }, logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, lists: { id: 'column.lists', defaultMessage: 'Lists' }, @@ -180,6 +181,10 @@ class SidebarMenu extends ImmutablePureComponent { {intl.formatMessage(messages.preferences)} + + + {intl.formatMessage(messages.import_data)} + {intl.formatMessage(messages.security)} diff --git a/app/soapbox/features/compose/components/action_bar.js b/app/soapbox/features/compose/components/action_bar.js index 8bc6ef4c4..863d49f22 100644 --- a/app/soapbox/features/compose/components/action_bar.js +++ b/app/soapbox/features/compose/components/action_bar.js @@ -20,6 +20,7 @@ const messages = defineMessages({ filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' }, admin_settings: { id: 'navigation_bar.admin_settings', defaultMessage: 'Admin settings' }, soapbox_config: { id: 'navigation_bar.soapbox_config', defaultMessage: 'Soapbox config' }, + import_data: { id: 'navigation_bar.import_data', defaultMessage: 'Import data' }, security: { id: 'navigation_bar.security', defaultMessage: 'Security' }, logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, keyboard_shortcuts: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Hotkeys' }, @@ -84,6 +85,7 @@ class ActionBar extends React.PureComponent { menu.push({ text: intl.formatMessage(messages.soapbox_config), to: '/soapbox/config' }); } menu.push({ text: intl.formatMessage(messages.preferences), to: '/settings/preferences' }); + menu.push({ text: intl.formatMessage(messages.import_data), to: '/settings/import' }); menu.push({ text: intl.formatMessage(messages.security), to: '/auth/edit' }); menu.push({ text: intl.formatMessage(messages.logout), to: '/auth/sign_out', action: onClickLogOut }); diff --git a/app/soapbox/features/import_data/components/csv_importer.js b/app/soapbox/features/import_data/components/csv_importer.js new file mode 100644 index 000000000..7f837b067 --- /dev/null +++ b/app/soapbox/features/import_data/components/csv_importer.js @@ -0,0 +1,79 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { injectIntl } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import PropTypes from 'prop-types'; +import { + SimpleInput, + SimpleForm, + FieldsGroup, +} from 'soapbox/features/forms'; + +export default @connect() +@injectIntl +class CSVImporter extends ImmutablePureComponent { + + static propTypes = { + action: PropTypes.func.isRequired, + messages: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + state = { + file: null, + isLoading: false, + } + + handleSubmit = (event) => { + const { dispatch, action } = this.props; + + let params = new FormData(); + params.append('list', this.state.file); + + this.setState({ isLoading: true }); + dispatch(action(params)).then(() => { + this.setState({ isLoading: false }); + }).catch((error) => { + this.setState({ isLoading: false }); + }); + + event.preventDefault(); + } + + handleFileChange = e => { + const [file] = e.target.files || []; + this.setState({ file }); + } + + render() { + const { intl, messages } = this.props; + + return ( + +
+ +
+
+ +
+
+
+
+
+ +
+
+ ); + } + +} diff --git a/app/soapbox/features/import_data/index.js b/app/soapbox/features/import_data/index.js new file mode 100644 index 000000000..c1d788ff7 --- /dev/null +++ b/app/soapbox/features/import_data/index.js @@ -0,0 +1,56 @@ +import React from 'react'; +import { defineMessages, injectIntl } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import PropTypes from 'prop-types'; +import Column from '../ui/components/column'; +import { + importFollows, + importBlocks, + // importMutes, +} from 'soapbox/actions/import_data'; +import CSVImporter from './components/csv_importer'; + +const messages = defineMessages({ + heading: { id: 'column.import_data', defaultMessage: 'Import data' }, + submit: { id: 'import_data.actions.import', defaultMessage: 'Import' }, +}); + +const followMessages = defineMessages({ + input_label: { id: 'import_data.follows_label', defaultMessage: 'Follows' }, + input_hint: { id: 'import_data.hints.follows', defaultMessage: 'CSV file containing a list of followed accounts' }, + submit: { id: 'import_data.actions.import_follows', defaultMessage: 'Import follows' }, +}); + +const blockMessages = defineMessages({ + input_label: { id: 'import_data.blocks_label', defaultMessage: 'Blocks' }, + input_hint: { id: 'import_data.hints.blocks', defaultMessage: 'CSV file containing a list of blocked accounts' }, + submit: { id: 'import_data.actions.import_blocks', defaultMessage: 'Import blocks' }, +}); + +// Not yet supported by Pleroma stable, in develop branch +// const muteMessages = defineMessages({ +// input_label: { id: 'import_data.mutes_label', defaultMessage: 'Mutes' }, +// input_hint: { id: 'import_data.hints.mutes', defaultMessage: 'CSV file containing a list of muted accounts' }, +// submit: { id: 'import_data.actions.import_mutes', defaultMessage: 'Import mutes' }, +// }); + +export default @injectIntl +class ImportData extends ImmutablePureComponent { + + static propTypes = { + intl: PropTypes.object.isRequired, + }; + + render() { + const { intl } = this.props; + + return ( + + + + {/* */} + + ); + } + +} diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index 3f37ffe40..70b891ce8 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -78,6 +78,7 @@ import { Preferences, EditProfile, SoapboxConfig, + ImportData, PasswordReset, SecurityForm, MfaForm, @@ -263,6 +264,7 @@ class SwitchingColumnsArea extends React.PureComponent { + diff --git a/app/soapbox/features/ui/util/async-components.js b/app/soapbox/features/ui/util/async-components.js index 73a8a5540..83f26c270 100644 --- a/app/soapbox/features/ui/util/async-components.js +++ b/app/soapbox/features/ui/util/async-components.js @@ -186,6 +186,10 @@ export function SoapboxConfig() { return import(/* webpackChunkName: "features/soapbox_config" */'../../soapbox_config'); } +export function ImportData() { + return import(/* webpackChunkName: "features/import_data" */'../../import_data'); +} + export function PasswordReset() { return import(/* webpackChunkName: "features/auth_login" */'../../auth_login/components/password_reset'); }