Basic custom profile fields

api-accept
Alex Gleason 2 years ago
parent b66b68b37c
commit cd1a404351
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -0,0 +1,57 @@
import React from 'react';
import Button from '../button/button';
import Stack from '../stack/stack';
interface IStreamfield {
values: any[],
onAddItem?: () => void,
onChange: (values: any[]) => void,
component: React.ComponentType<{ onChange: (value: any) => void, value: any }>,
maxItems?: number,
}
/** List of inputs that can be added or removed. */
const Streamfield: React.FC<IStreamfield> = ({
values,
onAddItem,
onChange,
component: Component,
maxItems = Infinity,
}) => {
const handleChange = (i: number) => {
return (value: any) => {
const newData = [...values];
newData[i] = value;
onChange(newData);
};
};
return (
<Stack space={4}>
<Stack>
{values.map((value, i) => {
return (
<Component key={i} onChange={handleChange(i)} value={value} />
);
})}
</Stack>
{onAddItem && (
<Button
icon={require('@tabler/icons/icons/plus.svg')}
onClick={onAddItem}
disabled={values.length >= maxItems}
theme='ghost'
block
>
Add
</Button>
)}
</Stack>
);
};
export default Streamfield;

@ -7,11 +7,12 @@ import snackbar from 'soapbox/actions/snackbar';
import {
Checkbox,
} from 'soapbox/features/forms';
import { useAppDispatch, useOwnAccount, useFeatures } from 'soapbox/hooks';
import { useAppSelector, useAppDispatch, useOwnAccount, useFeatures } from 'soapbox/hooks';
import { normalizeAccount } from 'soapbox/normalizers';
import resizeImage from 'soapbox/utils/resize_image';
import { Button, Column, Form, FormActions, FormGroup, Input, Textarea } from '../../components/ui';
import { Button, Column, Form, FormActions, FormGroup, Input, Textarea, HStack } from '../../components/ui';
import Streamfield from '../../components/ui/streamfield/streamfield';
import ProfilePreview from './components/profile_preview';
@ -40,13 +41,6 @@ const messages = defineMessages({
cancel: { id: 'common.cancel', defaultMessage: 'Cancel' },
});
// /** Forces fields to be maxFields size, filling empty values. */
// const normalizeFields = (fields, maxFields: number) => (
// ImmutableList(fields).setSize(Math.max(fields.size, maxFields)).map(field =>
// field ? field : ImmutableMap({ name: '', value: '' }),
// )
// );
/**
* Profile metadata `name` and `value`.
* (By default, max 4 fields and 255 characters per property/value)
@ -121,7 +115,7 @@ const accountToCredentials = (account: Account): AccountCredentials => {
display_name: account.display_name,
note: account.source.get('note'),
locked: account.locked,
fields_attributes: [...account.source.get<Iterable<AccountCredentialsField>>('fields', [])],
fields_attributes: [...account.source.get<Iterable<AccountCredentialsField>>('fields', []).toJS()],
stranger_notifications: account.getIn(['pleroma', 'notification_settings', 'block_from_strangers']) === true,
accepts_email_list: account.getIn(['pleroma', 'accepts_email_list']) === true,
hide_followers: hideNetwork,
@ -134,6 +128,27 @@ const accountToCredentials = (account: Account): AccountCredentials => {
};
};
interface IProfileField {
value: AccountCredentialsField,
onChange: (field: AccountCredentialsField) => void,
}
const ProfileField: React.FC<IProfileField> = ({ value, onChange }) => {
const handleChange = (key: string): React.ChangeEventHandler<HTMLInputElement> => {
return e => {
onChange({ ...value, [key]: e.currentTarget.value });
};
};
return (
<HStack space={2} grow>
<Input type='text' value={value.name} onChange={handleChange('name')} />
<Input type='text' value={value.value} onChange={handleChange('value')} />
</HStack>
);
};
/** Edit profile page. */
const EditProfile: React.FC = () => {
const intl = useIntl();
@ -141,7 +156,7 @@ const EditProfile: React.FC = () => {
const account = useOwnAccount();
const features = useFeatures();
// const maxFields = useAppSelector(state => state.instance.pleroma.getIn(['metadata', 'fields_limits', 'max_fields'], 4) as number);
const maxFields = useAppSelector(state => state.instance.pleroma.getIn(['metadata', 'fields_limits', 'max_fields'], 4) as number);
const [isLoading, setLoading] = useState(false);
const [data, setData] = useState<AccountCredentials>({});
@ -229,27 +244,15 @@ const EditProfile: React.FC = () => {
};
};
// handleFieldChange = (i, key) => {
// return (e) => {
// this.setState({
// fields: this.state.fields.setIn([i, key], e.target.value),
// });
// };
// };
//
// handleAddField = () => {
// this.setState({
// fields: this.state.fields.push(ImmutableMap({ name: '', value: '' })),
// });
// };
//
// handleDeleteField = i => {
// return () => {
// this.setState({
// fields: normalizeFields(this.state.fields.delete(i), Math.min(this.props.maxFields, 4)),
// });
// };
// };
const handleFieldsChange = (fields: AccountCredentialsField[]) => {
updateData('fields_attributes', fields);
};
const handleAddField = () => {
const oldFields = data.fields_attributes || [];
const fields = [...oldFields, { name: '', value: '' }];
updateData('fields_attributes', fields);
};
/** Memoized avatar preview URL. */
const avatarUrl = useMemo(() => {
@ -412,47 +415,14 @@ const EditProfile: React.FC = () => {
)}
</div>
{/* </FieldsGroup> */}
{/*<FieldsGroup>
<div className='fields-row__column fields-group'>
<div className='input with_block_label'>
<label><FormattedMessage id='edit_profile.fields.meta_fields_label' defaultMessage='Profile metadata' /></label>
<span className='hint'>
<FormattedMessage id='edit_profile.hints.meta_fields' defaultMessage='You can have up to {count, plural, one {# item} other {# items}} displayed as a table on your profile' values={{ count: maxFields }} />
</span>
{
this.state.fields.map((field, i) => (
<div className='row' key={i}>
<TextInput
placeholder={intl.formatMessage(messages.metaFieldLabel)}
value={field.get('name')}
onChange={this.handleFieldChange(i, 'name')}
/>
<TextInput
placeholder={intl.formatMessage(messages.metaFieldContent)}
value={field.get('value')}
onChange={this.handleFieldChange(i, 'value')}
/>
{
this.state.fields.size > 4 && <Icon className='delete-field' src={require('@tabler/icons/icons/circle-x.svg')} onClick={this.handleDeleteField(i)} />
}
</div>
))
}
{
this.state.fields.size < maxFields && (
<div className='actions add-row'>
<div name='button' type='button' role='presentation' className='btn button button-secondary' onClick={this.handleAddField}>
<Icon src={require('@tabler/icons/icons/circle-plus.svg')} />
<FormattedMessage id='edit_profile.meta_fields.add' defaultMessage='Add new item' />
</div>
</div>
)
}
</div>
</div>
</FieldsGroup>*/}
{/* </fieldset> */}
<Streamfield
values={data.fields_attributes || []}
onChange={handleFieldsChange}
onAddItem={handleAddField}
component={ProfileField}
maxItems={maxFields}
/>
<FormActions>
<Button to='/settings' theme='ghost'>
{intl.formatMessage(messages.cancel)}

Loading…
Cancel
Save