import React from 'react'; import { connect } from 'react-redux'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Column from '../ui/components/column'; import { SimpleForm, FieldsGroup, TextInput, Checkbox, FileChooser, } from 'soapbox/features/forms'; import BrandingPreview from './components/branding_preview'; import { Map as ImmutableMap, List as ImmutableList, } from 'immutable'; import { patchMe } from 'soapbox/actions/me'; import { unescape } from 'lodash'; const MAX_FIELDS = 6; // Max promoPanel fields const messages = defineMessages({ heading: { id: 'column.soapbox_settings', defaultMessage: 'Soapbox settings' }, promoPanelIcon: { id: 'soapbox_settings.promo_panel.meta_fields.icon_placeholder', defaultMessage: 'Icon' }, promoPanelLabel: { id: 'soapbox_settings.promo_panel.meta_fields.label_placeholder', defaultMessage: 'Label' }, promoPanelURL: { id: 'soapbox_settings.promo_panel.meta_fields.url_placeholder', defaultMessage: 'URL' }, homeFooterLabel: { id: 'soapbox_settings.home_footer.meta_fields.label_placeholder', defaultMessage: 'Label' }, homeFooterURL: { id: 'soapbox_settings.home_footer.meta_fields.url_placeholder', defaultMessage: 'URL' }, }); const mapStateToProps = state => { const soapbox = state.get('soapbox'); return { themeCss: generateThemeCss(state.getIn(['soapbox', 'brandColor'])), logo: state.getIn(['soapbox', 'logo']), promoPanel: state.getIn(['soapbox', 'promoPanel', 'items']), patronEnabled: state.getIn(['soapbox', 'extensions', 'patron', 'enabled']), autoPlayGif: state.getIn(['soapbox', 'defaultSettings', 'autoPlayGif']), copyright: state.getIn(['soapbox', 'copyright']), homeFooter: state.getIn(['soapbox', 'navLinks', 'homeFooter']), }; }; // Forces fields to be MAX_SIZE, filling empty values const normalizeFields = fields => ( ImmutableList(fields).setSize(MAX_FIELDS).map(field => field ? field : ImmutableMap({ name: '', value: '' }) ) ); // HTML unescape for special chars, eg const unescapeParams = (map, params) => ( params.reduce((map, param) => ( map.set(param, unescape(map.get(param))) ), map) ); export default @connect(mapStateToProps) @injectIntl class ConfigSoapbox extends ImmutablePureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, themeCss: PropTypes.text, logo: PropTypes.object, promoPanel: ImmutablePropTypes.map, patronEnabled: PropTypes.Boolean, autoPlayGif: PropTypes.Boolean, copyright: PropTypes.text, homeFooter: ImmutablePropTypes.map, }; state = { isLoading: false, promoPanel: normalizeFields(Array.from({ length: MAX_FIELDS })), } constructor(props) { super(props); const initialState = props.withMutations(map => { map.merge(map.get('source')); map.delete('source'); map.set('promoPanel', normalizeFields(map.get('promoPanel'))); }); this.state = initialState.toObject(); } makePreviewLogo = () => { const { account } = this.props; return account.merge(ImmutableMap({ header: this.state.header, avatar: this.state.avatar, display_name: this.state.display_name, })); } getPromoPanelParams = () => { let params = ImmutableMap(); this.state.promoPanel.forEach((f, i) => params = params .set(`promo_panel_attributes[${i}][name]`, f.get('name')) .set(`promo_panel_attributes[${i}][value]`, f.get('value')) ); return params; } getParams = () => { const { state } = this; return Object.assign({ themeCss: state.themeCss, logoFile: state.logoFile, patronEnabled: state.patronEnabled, displayMode: state.displayMode, copyright: state.copyright, homeFooter: state.homeFooter, }, this.getPromoPanelParams().toJS()); } getFormdata = () => { const data = this.getParams(); let formData = new FormData(); for (let key in data) { const shouldAppend = Boolean(data[key] || key.startsWith('promo_panel_attributes')); if (shouldAppend) formData.append(key, data[key] || ''); } return formData; } handleSubmit = (event) => { const { dispatch } = this.props; dispatch(patchMe(this.getFormdata())).then(() => { this.setState({ isLoading: false }); }).catch((error) => { this.setState({ isLoading: false }); }); this.setState({ isLoading: true }); event.preventDefault(); } handleCheckboxChange = e => { this.setState({ [e.target.name]: e.target.checked }); } handleTextChange = e => { this.setState({ [e.target.name]: e.target.value }); } handlePromoPanelChange = (i, key) => { return (e) => { this.setState({ fields: this.state.fields.setIn([i, key], e.target.value), }); }; } handleFileChange = e => { const { name } = e.target; const [file] = e.target.files || []; const url = file ? URL.createObjectURL(file) : this.state[name]; this.setState({ [name]: url, [`${name}_file`]: file, }); } render() { const { intl } = this.props; return ( } name='display_name' value={this.state.display_name} onChange={this.handleTextChange} /> } name='note' value={this.state.note} onChange={this.handleTextChange} /> } name='header' hint={} onChange={this.handleFileChange} /> } name='avatar' hint={} onChange={this.handleFileChange} /> } hint={} name='locked' checked={this.state.locked} onChange={this.handleCheckboxChange} /> } hint={} name='bot' checked={this.state.bot} onChange={this.handleCheckboxChange} /> { this.state.fields.map((field, i) => ( )) } ); } }