Fix up KeyValue component, fix save issue of Bio, simplify UserProfile

This commit is contained in:
Frank Johnson
2021-09-15 01:53:44 -04:00
parent fc6cded59e
commit 7b2be01430
6 changed files with 245 additions and 240 deletions

View File

@@ -5,24 +5,42 @@ import Editor from 'rich-markdown-editor'
import ImageUploader from 'src/components/ImageUploader'
import { User } from 'types/graphql'
export const fieldComponents = {
name: NameField,
userName: UserNameField,
image: ImageField,
bio: BioField,
createdAt: MemberSinceField,
}
export interface UserProfileType {
user: User
isEditable: boolean
isEditing: boolean
loading: boolean
error: boolean
onSave: Function
projects: {}[]
}
export interface FieldConfigType {
name?: string // introspection ugh
editable: boolean
component?: ReactNode
needsRef?: boolean
isEditing?: boolean | undefined
onSave?: Function
currentValue?: any
newValue?: any
export interface FieldType {
name: string
currentValue: any
newValue: any
isEditing: boolean
hasPermissionToEdit: boolean
}
export interface FieldComponentPropsType {
field: FieldType
dispatch?: React.Dispatch<Object>
user?: User
save?: Function
hasPermissionToEdit?: boolean
}
interface ProfileKeyValueType extends FieldComponentPropsType {
children: ReactNode
bottom: boolean
}
const ProfileKeyValue = ({
@@ -30,26 +48,27 @@ const ProfileKeyValue = ({
dispatch,
user,
save,
hasEditPermission,
hasPermissionToEdit,
children,
bottom = false,
}) => {
} : ProfileKeyValueType) => {
return (
<KeyValue
(user[field.name] && hasPermissionToEdit) && <KeyValue
keyName={field.name}
hide={!user[field.name] && !hasEditPermission}
canEdit={hasEditPermission}
onEdit={() => {
if (field.isEditing) {
save(user.userName, { [field.name]: field.newValue })
}
dispatch({
type: 'SET_CURRENT_VALUE',
payload: { field: field.name, value: field.newValue },
})
dispatch({ type: 'TOGGLE_EDITING', payload: field.name })
edit={{
hasPermissionToEdit,
isEditing: field.isEditing,
onEdit: () => {
if (field.isEditing && field.currentValue !== field.newValue) {
save(user.userName, { [field.name]: field.newValue })
}
dispatch({
type: 'SET_CURRENT_VALUE',
payload: { field: field.name, value: field.newValue },
})
dispatch({ type: 'TOGGLE_EDITING', payload: field.name })
},
}}
isEditable={hasEditPermission && field.isEditing}
bottom={bottom}
className="mb-4"
>
@@ -58,148 +77,115 @@ const ProfileKeyValue = ({
)
}
const bioField: FieldConfigType = {
editable: true,
needsRef: true,
component: (props) => {
const ref = useRef(null)
function BioField(props) {
const ref = useRef(null)
const { field, dispatch } = props
const { dispatch, field } = props
return (
<ProfileKeyValue {...props}>
<div
id="bio-wrap"
name="bio"
className={
'markdown-overrides rounded-sm pb-2 mt-2' +
(field.isEditable ? ' min-h-md' : '')
return (
<ProfileKeyValue {...props}>
<div
id="bio-wrap"
name="bio"
className={
'markdown-overrides rounded-sm pb-2 mt-2' +
(field.isEditing ? ' min-h-md' : '')
}
onClick={(e) =>
e?.target?.id === 'bio-wrap' && ref?.current?.focusAtEnd()
}
>
<Editor
ref={ref}
defaultValue={field?.currentValue || ''}
readOnly={!field.isEditing}
onChange={(bio) =>
dispatch({
type: 'SET_NEW_VALUE',
payload: { field: 'bio', value: bio() },
})
}
onClick={(e) =>
e?.target?.id === 'bio-wrap' && ref?.current?.focusAtEnd()
/>
</div>
</ProfileKeyValue>
)
}
function MemberSinceField(props : FieldComponentPropsType) {
return (
<KeyValue keyName="Member Since">
<p className="text-ch-gray-300">
{new Date(props.field.currentValue).toLocaleDateString()}
</p>
</KeyValue>
)
}
function ImageField(props : FieldComponentPropsType) {
const { field, user, save, hasPermissionToEdit } = props
return (
<ImageUploader
className="rounded-3xl rounded-tr-none shadow-md border-2 border-ch-gray-300"
onImageUpload={({ cloudinaryPublicId: image }) => {
save(user.userName, {
image,
})
}}
aspectRatio={1}
isEditable={hasPermissionToEdit}
imageUrl={user.image}
width={300}
/>
)
}
function NameField(props : FieldComponentPropsType) {
const { user, dispatch, field } = props
return (
<ProfileKeyValue {...props} bottom={true}>
{!field.isEditing ? (
<h1 className="text-4xl">{user?.name}</h1>
) : (
<InputText
className="text-xl"
value={field.newValue}
onChange={({ target: { value } }) =>
dispatch({
type: 'SET_NEW_VALUE',
payload: { field: 'name', value },
})
}
>
<Editor
ref={ref}
defaultValue={field?.currentValue || ''}
readOnly={!field.isEditing}
onChange={(bio) =>
dispatch({
type: 'SET_NEW_VALUE',
payload: { field: field.bio, value: bio() },
})
}
/>
</div>
</ProfileKeyValue>
)
},
isEditable={field.isEditing}
/>
)}
</ProfileKeyValue>
)
}
const createdAtField: FieldConfigType = {
editable: false,
component: (props) => {
const { field } = props
function UserNameField(props : FieldComponentPropsType) {
const { dispatch, field } = props
return (
<KeyValue keyName="Member Since">
<p className="text-ch-gray-300">
{new Date(field.currentValue).toLocaleDateString()}
</p>
</KeyValue>
)
},
}
const imageField: FieldConfigType = {
editable: true,
component: (props) => {
const { field, user, save, hasEditPermission } = props
return (
<ImageUploader
className="rounded-3xl rounded-tr-none shadow-md border-2 border-ch-gray-300"
onImageUpload={({ cloudinaryPublicId: image }) => {
save(user.userName, {
image,
})
}}
aspectRatio={1}
isEditable={hasEditPermission && !field.isEditing}
imageUrl={user.image}
width={300}
/>
)
},
}
const nameField: FieldConfigType = {
editable: true,
component: (props) => {
const { user, dispatch, field } = props
return (
<ProfileKeyValue {...props} bottom={true}>
{!field.isEditing ? (
<h1 className="text-4xl">{user?.name}</h1>
) : (
<InputText
className="text-xl"
value={field.newValue}
onChange={({ target: { value } }) =>
dispatch({
type: 'SET_NEW_VALUE',
payload: { field: field.name, value },
})
}
isEditable={!field.isEditable}
/>
)}
</ProfileKeyValue>
)
},
}
const userNameField: FieldConfigType = {
editable: true,
component: (props) => {
const { dispatch, field } = props
return (
<ProfileKeyValue {...props} bottom={true}>
{!field.isEditing ? (
<h2 className="text-ch-gray-400">
@{field?.currentValue?.replace(/([^a-zA-Z\d_:])/g, '-')}
</h2>
) : (
<InputText
className="text-xl"
value={field.newValue}
onChange={({ target: { value } }) =>
dispatch({
type: 'SET_NEW_VALUE',
payload: { field: field.name, value },
})
}
isEditable={!field.isEditable}
/>
)}
</ProfileKeyValue>
)
},
}
export const fieldsConfig = {
bio: bioField,
createdAt: createdAtField,
id: {
editable: false,
},
image: imageField,
name: nameField,
updatedAt: {
editable: false,
},
userName: userNameField,
return (
<ProfileKeyValue {...props} bottom={true}>
{!field.isEditing ? (
<h2 className="text-ch-gray-400">
@{field?.currentValue?.replace(/([^a-zA-Z\d_:])/g, '-')}
</h2>
) : (
<InputText
className="text-xl"
value={field.newValue}
onChange={({ target: { value } }) =>
dispatch({
type: 'SET_NEW_VALUE',
payload: { field: 'userName', value },
})
}
isEditable={field.isEditing}
/>
)}
</ProfileKeyValue>
)
}
export function fieldReducer(state, action) {
@@ -210,7 +196,7 @@ export function fieldReducer(state, action) {
[action.payload]: {
...state[action.payload],
isEditing:
state[action.payload].editable && !state[action.payload].isEditing
state[action.payload].hasPermissionToEdit && !state[action.payload].isEditing
? true
: false,
},