Added branding_preview. Edited index

merge-requests/119/head
crockwave 2020-07-21 18:38:10 -05:00
rodzic 2af5149ed3
commit eef32c96e3
4 zmienionych plików z 83 dodań i 85 usunięć

Wyświetl plik

@ -0,0 +1,32 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { acctFull } from 'soapbox/utils/accounts';
import StillImage from 'soapbox/components/still_image';
const BrandingPreview = ({ account }) => (
<div className='card h-card'>
<a target='_blank' rel='noopener' href={account.get('url')}>
<div className='card__img'>
<StillImage alt='' src={account.get('header')} />
</div>
<div className='card__bar'>
<div className='avatar'>
<StillImage alt='' className='u-photo' src={account.get('avatar')} width='48' height='48' />
</div>
<div className='display-name'>
<span style={{ display: 'none' }}>{account.get('username')}</span>
<bdi>
<strong className='emojify p-name'>{account.get('display_name')}</strong>
</bdi>
<span>{acctFull(account)}</span>
</div>
</div>
</a>
</div>
);
BrandingPreview.propTypes = {
account: ImmutablePropTypes.map,
};
export default BrandingPreview;

Wyświetl plik

@ -12,6 +12,7 @@ import {
Checkbox, Checkbox,
FileChooser, FileChooser,
} from 'soapbox/features/forms'; } from 'soapbox/features/forms';
import BrandingPreview from './components/branding_preview';
import { import {
Map as ImmutableMap, Map as ImmutableMap,
List as ImmutableList, List as ImmutableList,
@ -19,82 +20,37 @@ import {
import { patchMe } from 'soapbox/actions/me'; import { patchMe } from 'soapbox/actions/me';
import { unescape } from 'lodash'; import { unescape } from 'lodash';
const MAX_FIELDS = 6; // Max promoPanel fields
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.edit_profile', defaultMessage: 'Edit profile' }, heading: { id: 'column.soapbox_settings', defaultMessage: 'Soapbox settings' },
metaFieldLabel: { id: 'edit_profile.fields.meta_fields.label_placeholder', defaultMessage: 'Label' }, promoPanelIcon: { id: 'soapbox_settings.promo_panel.meta_fields.icon_placeholder', defaultMessage: 'Icon' },
metaFieldContent: { id: 'edit_profile.fields.meta_fields.content_placeholder', defaultMessage: 'Content' }, 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 mapStateToProps = state => {
const soapbox = state.get('soapbox'); const soapbox = state.get('soapbox');
return { return {
themeCss: generateThemeCss(state.getIn(['soapbox', 'brandColor'])), themeCss: generateThemeCss(state.getIn(['soapbox', 'brandColor'])),
customCss: state.getIn(['soapbox', 'customCss']),
logo: state.getIn(['soapbox', 'logo']),
logo: state.getIn(['soapbox', 'logo']), logo: state.getIn(['soapbox', 'logo']),
promoPanel: state.getIn(['soapbox', 'promoPanel', 'items']), promoPanel: state.getIn(['soapbox', 'promoPanel', 'items']),
patronEnabled: state.getIn(['soapbox', 'extensions', 'patron', 'enabled']), patronEnabled: state.getIn(['soapbox', 'extensions', 'patron', 'enabled']),
displayMode: state.getIn(['soapbox', 'defaultSettings', 'mode']), autoPlayGif: state.getIn(['soapbox', 'defaultSettings', 'autoPlayGif']),
copyright: state.getIn(['soapbox', 'copyright']), copyright: state.getIn(['soapbox', 'copyright']),
homeFooter: state.getIn(['soapbox', 'navLinks', 'homeFooter']), 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'
// }
// ]
// }
// }
// }
}; };
}; };
// 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 <br> // HTML unescape for special chars, eg <br>
const unescapeParams = (map, params) => ( const unescapeParams = (map, params) => (
params.reduce((map, param) => ( params.reduce((map, param) => (
@ -109,26 +65,31 @@ class ConfigSoapbox extends ImmutablePureComponent {
static propTypes = { static propTypes = {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
account: ImmutablePropTypes.map, themeCss: PropTypes.text,
logo: PropTypes.object,
promoPanel: ImmutablePropTypes.map,
patronEnabled: PropTypes.Boolean,
autoPlayGif: PropTypes.Boolean,
copyright: PropTypes.text,
homeFooter: ImmutablePropTypes.map,
}; };
state = { state = {
isLoading: false, isLoading: false,
fields: normalizeFields(Array.from({ length: MAX_FIELDS })), promoPanel: normalizeFields(Array.from({ length: MAX_FIELDS })),
} }
constructor(props) { constructor(props) {
super(props); super(props);
const initialState = props.account.withMutations(map => { const initialState = props.withMutations(map => {
map.merge(map.get('source')); map.merge(map.get('source'));
map.delete('source'); map.delete('source');
map.set('fields', normalizeFields(map.get('fields'))); map.set('promoPanel', normalizeFields(map.get('promoPanel')));
unescapeParams(map, ['display_name', 'note']);
}); });
this.state = initialState.toObject(); this.state = initialState.toObject();
} }
makePreviewAccount = () => { makePreviewLogo = () => {
const { account } = this.props; const { account } = this.props;
return account.merge(ImmutableMap({ return account.merge(ImmutableMap({
header: this.state.header, header: this.state.header,
@ -137,12 +98,12 @@ class ConfigSoapbox extends ImmutablePureComponent {
})); }));
} }
getFieldParams = () => { getPromoPanelParams = () => {
let params = ImmutableMap(); let params = ImmutableMap();
this.state.fields.forEach((f, i) => this.state.promoPanel.forEach((f, i) =>
params = params params = params
.set(`fields_attributes[${i}][name]`, f.get('name')) .set(`promo_panel_attributes[${i}][name]`, f.get('name'))
.set(`fields_attributes[${i}][value]`, f.get('value')) .set(`promo_panel_attributes[${i}][value]`, f.get('value'))
); );
return params; return params;
} }
@ -150,21 +111,20 @@ class ConfigSoapbox extends ImmutablePureComponent {
getParams = () => { getParams = () => {
const { state } = this; const { state } = this;
return Object.assign({ return Object.assign({
discoverable: state.discoverable, themeCss: state.themeCss,
bot: state.bot, logoFile: state.logoFile,
display_name: state.display_name, patronEnabled: state.patronEnabled,
note: state.note, displayMode: state.displayMode,
avatar: state.avatar_file, copyright: state.copyright,
header: state.header_file, homeFooter: state.homeFooter,
locked: state.locked, }, this.getPromoPanelParams().toJS());
}, this.getFieldParams().toJS());
} }
getFormdata = () => { getFormdata = () => {
const data = this.getParams(); const data = this.getParams();
let formData = new FormData(); let formData = new FormData();
for (let key in data) { for (let key in data) {
const shouldAppend = Boolean(data[key] || key.startsWith('fields_attributes')); const shouldAppend = Boolean(data[key] || key.startsWith('promo_panel_attributes'));
if (shouldAppend) formData.append(key, data[key] || ''); if (shouldAppend) formData.append(key, data[key] || '');
} }
return formData; return formData;
@ -189,7 +149,7 @@ class ConfigSoapbox extends ImmutablePureComponent {
this.setState({ [e.target.name]: e.target.value }); this.setState({ [e.target.name]: e.target.value });
} }
handleFieldChange = (i, key) => { handlePromoPanelChange = (i, key) => {
return (e) => { return (e) => {
this.setState({ this.setState({
fields: this.state.fields.setIn([i, key], e.target.value), fields: this.state.fields.setIn([i, key], e.target.value),
@ -212,7 +172,7 @@ class ConfigSoapbox extends ImmutablePureComponent {
const { intl } = this.props; const { intl } = this.props;
return ( return (
<Column icon='user' heading={intl.formatMessage(messages.heading)} backBtnSlim> <Column icon='gears' heading={intl.formatMessage(messages.heading)} backBtnSlim>
<SimpleForm onSubmit={this.handleSubmit}> <SimpleForm onSubmit={this.handleSubmit}>
<fieldset disabled={this.state.isLoading}> <fieldset disabled={this.state.isLoading}>
<FieldsGroup> <FieldsGroup>
@ -230,7 +190,7 @@ class ConfigSoapbox extends ImmutablePureComponent {
/> />
<div className='fields-row'> <div className='fields-row'>
<div className='fields-row__column fields-row__column-6'> <div className='fields-row__column fields-row__column-6'>
<ProfilePreview account={this.makePreviewAccount()} /> <ProfilePreview account={this.makePreviewLogo()} />
</div> </div>
<div className='fields-row__column fields-group fields-row__column-6'> <div className='fields-row__column fields-group fields-row__column-6'>
<FileChooser <FileChooser
@ -275,12 +235,12 @@ class ConfigSoapbox extends ImmutablePureComponent {
<TextInput <TextInput
placeholder={intl.formatMessage(messages.metaFieldLabel)} placeholder={intl.formatMessage(messages.metaFieldLabel)}
value={field.get('name')} value={field.get('name')}
onChange={this.handleFieldChange(i, 'name')} onChange={this.handlePromoPanelChange(i, 'name')}
/> />
<TextInput <TextInput
placeholder={intl.formatMessage(messages.metaFieldContent)} placeholder={intl.formatMessage(messages.metaFieldContent)}
value={field.get('value')} value={field.get('value')}
onChange={this.handleFieldChange(i, 'value')} onChange={this.handlePromoPanelChange(i, 'value')}
/> />
</div> </div>
)) ))

Wyświetl plik

@ -71,6 +71,7 @@ import {
LoginPage, LoginPage,
Preferences, Preferences,
EditProfile, EditProfile,
ConfigSoapbox,
PasswordReset, PasswordReset,
SecurityForm, SecurityForm,
} from './util/async-components'; } from './util/async-components';
@ -253,6 +254,7 @@ class SwitchingColumnsArea extends React.PureComponent {
<Redirect exact from='/settings' to='/settings/preferences' /> <Redirect exact from='/settings' to='/settings/preferences' />
<WrappedRoute path='/settings/preferences' layout={LAYOUT.DEFAULT} component={Preferences} content={children} /> <WrappedRoute path='/settings/preferences' layout={LAYOUT.DEFAULT} component={Preferences} content={children} />
<WrappedRoute path='/settings/profile' layout={LAYOUT.DEFAULT} component={EditProfile} content={children} /> <WrappedRoute path='/settings/profile' layout={LAYOUT.DEFAULT} component={EditProfile} content={children} />
<WrappedRoute path='/admin' layout={LAYOUT.DEFAULT} component={ConfigSoapbox} content={children} />
<WrappedRoute layout={LAYOUT.EMPTY} component={GenericNotFound} content={children} /> <WrappedRoute layout={LAYOUT.EMPTY} component={GenericNotFound} content={children} />
</Switch> </Switch>

Wyświetl plik

@ -178,6 +178,10 @@ export function EditProfile() {
return import(/* webpackChunkName: "features/edit_profile" */'../../edit_profile'); return import(/* webpackChunkName: "features/edit_profile" */'../../edit_profile');
} }
export function ConfigSoapbox() {
return import(/* webpackChunkName: "features/configuration" */'../../configuration');
}
export function PasswordReset() { export function PasswordReset() {
return import(/* webpackChunkName: "features/auth_login" */'../../auth_login/components/password_reset'); return import(/* webpackChunkName: "features/auth_login" */'../../auth_login/components/password_reset');
} }