Fix up KeyValue component, fix save issue of Bio, simplify UserProfile
This commit is contained in:
@@ -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,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user