diff --git a/app/soapbox/features/configuration/index.js b/app/soapbox/features/configuration/index.js index beaab5c25..c574830f5 100644 --- a/app/soapbox/features/configuration/index.js +++ b/app/soapbox/features/configuration/index.js @@ -4,193 +4,296 @@ 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 { changeSetting } from 'soapbox/actions/settings'; import Column from '../ui/components/column'; import { SimpleForm, FieldsGroup, - RadioGroup, - RadioItem, - SelectDropdown, + TextInput, + Checkbox, + FileChooser, } from 'soapbox/features/forms'; -import SettingsCheckbox from 'soapbox/components/settings_checkbox'; - -const languages = { - en: 'English', - ar: 'العربية', - ast: 'Asturianu', - bg: 'Български', - bn: 'বাংলা', - ca: 'Català', - co: 'Corsu', - cs: 'Čeština', - cy: 'Cymraeg', - da: 'Dansk', - de: 'Deutsch', - el: 'Ελληνικά', - eo: 'Esperanto', - es: 'Español', - eu: 'Euskara', - fa: 'فارسی', - fi: 'Suomi', - fr: 'Français', - ga: 'Gaeilge', - gl: 'Galego', - he: 'עברית', - hi: 'हिन्दी', - hr: 'Hrvatski', - hu: 'Magyar', - hy: 'Հայերեն', - id: 'Bahasa Indonesia', - io: 'Ido', - it: 'Italiano', - ja: '日本語', - ka: 'ქართული', - kk: 'Қазақша', - ko: '한국어', - lt: 'Lietuvių', - lv: 'Latviešu', - ml: 'മലയാളം', - ms: 'Bahasa Melayu', - nl: 'Nederlands', - no: 'Norsk', - oc: 'Occitan', - pl: 'Polski', - pt: 'Português', - 'pt-BR': 'Português do Brasil', - ro: 'Română', - ru: 'Русский', - sk: 'Slovenčina', - sl: 'Slovenščina', - sq: 'Shqip', - sr: 'Српски', - 'sr-Latn': 'Srpski (latinica)', - sv: 'Svenska', - ta: 'தமிழ்', - te: 'తెలుగు', - th: 'ไทย', - tr: 'Türkçe', - uk: 'Українська', - zh: '中文', - 'zh-CN': '简体中文', - 'zh-HK': '繁體中文(香港)', - 'zh-TW': '繁體中文(臺灣)', -}; +import { + Map as ImmutableMap, + List as ImmutableList, +} from 'immutable'; +import { patchMe } from 'soapbox/actions/me'; +import { unescape } from 'lodash'; const messages = defineMessages({ - heading: { id: 'column.preferences', defaultMessage: 'Preferences' }, + heading: { id: 'column.edit_profile', defaultMessage: 'Edit profile' }, + metaFieldLabel: { id: 'edit_profile.fields.meta_fields.label_placeholder', defaultMessage: 'Label' }, + metaFieldContent: { id: 'edit_profile.fields.meta_fields.content_placeholder', defaultMessage: 'Content' }, }); -const mapStateToProps = state => ({ - settings: state.get('settings'), -}); +const mapStateToProps = state => { + const soapbox = state.get('soapbox'); + return { + themeCss: generateThemeCss(state.getIn(['soapbox', 'brandColor'])), + customCss: state.getIn(['soapbox', 'customCss']), + logo: state.getIn(['soapbox', 'logo']), + logo: state.getIn(['soapbox', 'logo']), + promoPanel: state.getIn(['soapbox', 'promoPanel', 'items']), + patronEnabled: state.getIn(['soapbox', 'extensions', 'patron', 'enabled']), + displayMode: state.getIn(['soapbox', 'defaultSettings', 'mode']), + copyright: state.getIn(['soapbox', 'copyright']), + homeFooter: state.getIn(['soapbox', 'navLinks', 'homeFooter']), + + // { + // soapbox: { + // brandColor: '#1ca82b', + // logo: '/favicon.png', + // promoPanel: { + // items: [ + // { + // icon: 'area-chart', + // text: 'Gleasonator stats', + // url: 'https://fediverse.network/gleasonator.com' + // }, + // { + // icon: 'comment-o', + // text: 'Gleasonator theme song', + // url: 'https://media.gleasonator.com/custom/261905_gleasonator_song.mp3' + // } + // ] + // }, + // extensions: { + // patron: { + // enabled: true + // } + // }, + // defaultSettings: { + // mode: 'light' + // }, + // copyright: '♡2020. Copying is an act of love. Please copy and share.', + // navlinks: { + // homeFooter: [ + // { + // title: 'About', + // url: '/about' + // }, + // { + // title: 'Terms of Service', + // url: '/about/tos' + // }, + // { + // title: 'Privacy Policy', + // url: '/about/privacy' + // }, + // { + // title: 'DMCA', + // url: '/about/dmca' + // }, + // { + // title: 'Source Code', + // url: '/about#opensource' + // } + // ] + // } + // } + // } + + }; +}; + +// 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 Preferences extends ImmutablePureComponent { +class ConfigSoapbox extends ImmutablePureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, - settings: ImmutablePropTypes.map, + account: ImmutablePropTypes.map, }; - onSelectChange = path => { - return e => { - this.props.dispatch(changeSetting(path, e.target.value)); - }; - }; + state = { + isLoading: false, + fields: normalizeFields(Array.from({ length: MAX_FIELDS })), + } - onDefaultPrivacyChange = e => { + constructor(props) { + super(props); + const initialState = props.account.withMutations(map => { + map.merge(map.get('source')); + map.delete('source'); + map.set('fields', normalizeFields(map.get('fields'))); + unescapeParams(map, ['display_name', 'note']); + }); + this.state = initialState.toObject(); + } + + makePreviewAccount = () => { + const { account } = this.props; + return account.merge(ImmutableMap({ + header: this.state.header, + avatar: this.state.avatar, + display_name: this.state.display_name, + })); + } + + getFieldParams = () => { + let params = ImmutableMap(); + this.state.fields.forEach((f, i) => + params = params + .set(`fields_attributes[${i}][name]`, f.get('name')) + .set(`fields_attributes[${i}][value]`, f.get('value')) + ); + return params; + } + + getParams = () => { + const { state } = this; + return Object.assign({ + discoverable: state.discoverable, + bot: state.bot, + display_name: state.display_name, + note: state.note, + avatar: state.avatar_file, + header: state.header_file, + locked: state.locked, + }, this.getFieldParams().toJS()); + } + + getFormdata = () => { + const data = this.getParams(); + let formData = new FormData(); + for (let key in data) { + const shouldAppend = Boolean(data[key] || key.startsWith('fields_attributes')); + if (shouldAppend) formData.append(key, data[key] || ''); + } + return formData; + } + + handleSubmit = (event) => { const { dispatch } = this.props; - dispatch(changeSetting(['defaultPrivacy'], e.target.value)); + 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 }); + } + + handleFieldChange = (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 { settings, intl } = this.props; + const { intl } = this.props; return ( - - - - } - items={languages} - defaultValue={settings.get('locale')} - onChange={this.onSelectChange(['locale'])} - /> - - - - } - onChange={this.onDefaultPrivacyChange} - > - } - hint={} - checked={settings.get('defaultPrivacy') === 'public'} - value='public' + + + + + } + name='display_name' + value={this.state.display_name} + onChange={this.handleTextChange} /> - } - hint={} - checked={settings.get('defaultPrivacy') === 'unlisted'} - value='unlisted' + } + name='note' + value={this.state.note} + onChange={this.handleTextChange} /> - } - hint={} - checked={settings.get('defaultPrivacy') === 'private'} - value='private' + + + + + + } + name='header' + hint={} + onChange={this.handleFileChange} + /> + } + name='avatar' + hint={} + onChange={this.handleFileChange} + /> + + + } + hint={} + name='locked' + checked={this.state.locked} + onChange={this.handleCheckboxChange} /> - - - - - } - path={['unfollowModal']} - /> - } - path={['boostModal']} - /> - } - path={['deleteModal']} - /> - - - - } - path={['autoPlayGif']} - /> - } - path={['expandSpoilers']} - /> - } - path={['reduceMotion']} - /> - } - path={['systemFont']} - /> - - } - path={['dyslexicFont']} + } + hint={} + name='bot' + checked={this.state.bot} + onChange={this.handleCheckboxChange} /> - - } - hint={} - path={['demetricator']} - /> - + + + + + + + + + { + this.state.fields.map((field, i) => ( + + + + + )) + } + + + + + + + + + );