From cd1a404351166c7a820ea26204d5cf7c3e37506a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 2 May 2022 21:10:47 -0500 Subject: [PATCH] Basic custom profile fields --- .../components/ui/streamfield/streamfield.tsx | 57 +++++++++ app/soapbox/features/edit_profile/index.tsx | 116 +++++++----------- 2 files changed, 100 insertions(+), 73 deletions(-) create mode 100644 app/soapbox/components/ui/streamfield/streamfield.tsx diff --git a/app/soapbox/components/ui/streamfield/streamfield.tsx b/app/soapbox/components/ui/streamfield/streamfield.tsx new file mode 100644 index 0000000000..50ba79cad6 --- /dev/null +++ b/app/soapbox/components/ui/streamfield/streamfield.tsx @@ -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 = ({ + values, + onAddItem, + onChange, + component: Component, + maxItems = Infinity, +}) => { + + const handleChange = (i: number) => { + return (value: any) => { + const newData = [...values]; + newData[i] = value; + onChange(newData); + }; + }; + + return ( + + + + {values.map((value, i) => { + return ( + + ); + })} + + + {onAddItem && ( + + )} + + ); +}; + +export default Streamfield; diff --git a/app/soapbox/features/edit_profile/index.tsx b/app/soapbox/features/edit_profile/index.tsx index ee6b4f47ff..710f11ec1b 100644 --- a/app/soapbox/features/edit_profile/index.tsx +++ b/app/soapbox/features/edit_profile/index.tsx @@ -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>('fields', [])], + fields_attributes: [...account.source.get>('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 = ({ value, onChange }) => { + + const handleChange = (key: string): React.ChangeEventHandler => { + return e => { + onChange({ ...value, [key]: e.currentTarget.value }); + }; + }; + + return ( + + + + + ); +}; + /** 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({}); @@ -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 = () => { )} - {/* */} - {/* -
-
- - - - - { - this.state.fields.map((field, i) => ( -
- - - { - this.state.fields.size > 4 && - } -
- )) - } - { - this.state.fields.size < maxFields && ( -
-
- - -
-
- ) - } -
-
-
*/} - {/* */} + +