Profile page redesign #510

Merged
franknoirot merged 11 commits from profile-page into main 2021-09-18 07:16:33 +02:00
12 changed files with 382 additions and 310 deletions
Showing only changes of commit 74a5f9bf2c - Show all commits

View File

@@ -95,16 +95,17 @@ const IdeHeader = ({
<div /> <div />
)} )}
<div className="text-gray-200 grid grid-flow-col-dense gap-4 mr-4 items-center"> <div className="text-gray-200 grid grid-flow-col-dense gap-4 mr-4 items-center">
{ (!children) {!children ? (
? <DefaultTopButtons <DefaultTopButtons
project={project} project={project}
projectTitle={projectTitle} projectTitle={projectTitle}
_projectOwner={_projectOwner} _projectOwner={_projectOwner}
handleRender={handleRender} handleRender={handleRender}
canEdit={canEdit} canEdit={canEdit}
/> />
: children ) : (
} children
)}
{/* <TopButton>Fork</TopButton> */} {/* <TopButton>Fork</TopButton> */}
<div className="h-8 w-8"> <div className="h-8 w-8">
<NavPlusButton /> <NavPlusButton />
@@ -117,88 +118,95 @@ const IdeHeader = ({
export default IdeHeader export default IdeHeader
function DefaultTopButtons({
function DefaultTopButtons({ project, projectTitle, _projectOwner, handleRender, canEdit }) { project,
return (<> projectTitle,
{canEdit && !projectTitle && ( _projectOwner,
<CaptureButton handleRender,
canEdit={canEdit} canEdit,
projectTitle={project?.title} }) {
userName={project?.user?.userName} return (
shouldUpdateImage={!project?.mainImage} <>
TheButton={({ onClick }) => ( {canEdit && !projectTitle && (
<TopButton <CaptureButton
onClick={onClick} canEdit={canEdit}
name="Save Project Image" projectTitle={project?.title}
className=" bg-ch-blue-650 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300" userName={project?.user?.userName}
> shouldUpdateImage={!project?.mainImage}
<Svg name="camera" className="w-6 h-6 text-ch-blue-400" /> TheButton={({ onClick }) => (
</TopButton> <TopButton
)} onClick={onClick}
/> name="Save Project Image"
)} className=" bg-ch-blue-650 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
{!projectTitle && ( >
<TopButton <Svg name="camera" className="w-6 h-6 text-ch-blue-400" />
className="bg-ch-pink-800 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300" </TopButton>
onClick={handleRender} )}
name={canEdit ? 'Save' : 'Preview'}
>
<Svg
name={canEdit ? 'floppy-disk' : 'photograph'}
className="w-6 h-6 text-ch-pink-500"
/> />
franknoirot commented 2021-09-12 20:12:23 +02:00 (Migrated from github.com)
Review

Not sure this is the right move but I made it so a component could override the default buttons by bringing their own as children to it. It let me remove them by providing a dummy element without effecting any other pages.

Not sure this is the right move but I made it so a component could override the default buttons by bringing their own as children to it. It let me remove them by providing a dummy element without effecting any other pages.
Irev-Dev commented 2021-09-14 13:04:13 +02:00 (Migrated from github.com)
Review

Hmm I was trying to figure out what this even needed to be modified for the profile page but because this essentially the top nav bar.

I think this approach is good.

Perhaps later we'll look to remove MainLayout.js and replace it with the component everywhere. i.e. it can be the have the search bar for the projects explorer etc. If we do that than displaying children ONLY is probably better, i.e. it will need DefaultTopButtons to be passed in, in this case.

This is good though.

Hmm I was trying to figure out what this even needed to be modified for the profile page but because this essentially the top nav bar. I think this approach is good. Perhaps later we'll look to remove `MainLayout.js` and replace it with the component everywhere. i.e. it can be the have the search bar for the projects explorer etc. If we do that than displaying children ONLY is probably better, i.e. it will need `DefaultTopButtons` to be passed in, in this case. This is good though.
franknoirot commented 2021-09-15 03:12:15 +02:00 (Migrated from github.com)
Review

Yes I like that idea about MainLayout, it's feeling about ready to be taken out after a little refactor of this header to cover more use cases.

Yes I like that idea about `MainLayout`, it's feeling about ready to be taken out after a little refactor of this header to cover more use cases.
</TopButton> )}
)} {!projectTitle && (
{projectTitle && ( <TopButton
<TopButton className="bg-ch-pink-800 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
className="bg-ch-pink-800 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300" onClick={handleRender}
onClick={() => name={canEdit ? 'Save' : 'Preview'}
navigate(routes.ide({ userName: _projectOwner, projectTitle })) >
} <Svg
name="Editor" name={canEdit ? 'floppy-disk' : 'photograph'}
> className="w-6 h-6 text-ch-pink-500"
<Svg name="terminal" className="w-6 h-6 text-ch-pink-500" /> />
</TopButton> </TopButton>
)} )}
<Popover className="relative outline-none w-full h-full"> {projectTitle && (
{({ open }) => { <TopButton
return ( className="bg-ch-pink-800 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
<> onClick={() =>
<Popover.Button className="h-full w-full outline-none"> navigate(routes.ide({ userName: _projectOwner, projectTitle }))
<TopButton }
Tag="div" name="Editor"
name="Share" >
className=" bg-ch-purple-400 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300" <Svg name="terminal" className="w-6 h-6 text-ch-pink-500" />
> </TopButton>
<Svg )}
name="share" <Popover className="relative outline-none w-full h-full">
className="w-6 h-6 text-ch-purple-500 mt-1" {({ open }) => {
/> return (
</TopButton> <>
</Popover.Button> <Popover.Button className="h-full w-full outline-none">
{open && ( <TopButton
<Popover.Panel className="absolute z-10 mt-4 right-0"> Tag="div"
<Tabs name="Share"
className="bg-ch-purple-gray-200 rounded-md shadow-md overflow-hidden text-gray-700" className=" bg-ch-purple-400 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
selectedTabClassName="bg-ch-gray-700 text-white"
> >
<TabPanel> <Svg
<FullScriptEncoding /> name="share"
</TabPanel> className="w-6 h-6 text-ch-purple-500 mt-1"
<TabPanel> />
<ExternalScript /> </TopButton>
</TabPanel> </Popover.Button>
{open && (
<Popover.Panel className="absolute z-10 mt-4 right-0">
<Tabs
className="bg-ch-purple-gray-200 rounded-md shadow-md overflow-hidden text-gray-700"
selectedTabClassName="bg-ch-gray-700 text-white"
>
<TabPanel>
<FullScriptEncoding />
</TabPanel>
<TabPanel>
<ExternalScript />
</TabPanel>
<TabList className="flex whitespace-nowrap text-gray-700 border-t border-gray-700"> <TabList className="flex whitespace-nowrap text-gray-700 border-t border-gray-700">
<Tab className="p-3 px-5">encoded script</Tab> <Tab className="p-3 px-5">encoded script</Tab>
<Tab className="p-3 px-5">external script</Tab> <Tab className="p-3 px-5">external script</Tab>
</TabList> </TabList>
</Tabs> </Tabs>
</Popover.Panel> </Popover.Panel>
)} )}
</> </>
) )
}} }}
</Popover> </Popover>
</>) </>
)
} }

View File

@@ -11,17 +11,24 @@ import Svg from 'src/components/Svg'
const CLOUDINARY_UPLOAD_PRESET = 'CadHub_project_images' const CLOUDINARY_UPLOAD_PRESET = 'CadHub_project_images'
const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/irevdev/upload' const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/irevdev/upload'
export function ImageFallback({ width = 100, imageId = 'CadHub/eia1kwru54g2kf02s2xx', className = '' }) { export function ImageFallback({
franknoirot commented 2021-09-12 20:15:15 +02:00 (Migrated from github.com)
Review

Having a component that got that fallback user image without the image uploading functionality made me able to migrate the signed-in popover to HeadlessUI from MaterialUI, because I couldn't have a button within a button. I figured it's okay because the use case for the top nav (and ProjectCard) don't ever need the upload functionality.

Having a component that got that fallback user image without the image uploading functionality made me able to migrate the signed-in popover to HeadlessUI from MaterialUI, because I couldn't have a button within a button. I figured it's okay because the use case for the top nav (and `ProjectCard`) don't ever need the upload functionality.
Irev-Dev commented 2021-09-14 12:46:08 +02:00 (Migrated from github.com)
Review

That button within a button error in the console has been annoying me for ages, but obviously I hadn't done anything about, so thanks!

That button within a button error in the console has been annoying me for ages, but obviously I hadn't done anything about, so thanks!
width = 100,
imageId = 'CadHub/eia1kwru54g2kf02s2xx',
Irev-Dev commented 2021-09-14 13:06:34 +02:00 (Migrated from github.com)
Review

not relevant to this PR, but I do think there must be a better way of doing a fallback/empty-state than this image.

not relevant to this PR, but I do think there must be a better way of doing a fallback/empty-state than this image.
franknoirot commented 2021-09-14 14:12:47 +02:00 (Migrated from github.com)
Review

I hear that. I'll add to my list, maybe Cloudinary has a documented best.

I hear that. I'll add to my list, maybe Cloudinary has a documented best.
className = '',
}) {
return ( return (
<div className="relative overflow-hidden w-full h-full"> <div className="relative overflow-hidden w-full h-full">
<CloudinaryImage <CloudinaryImage
className={"object-cover w-full h-full shadow overflow-hidden " + className } className={
cloudName="irevdev" 'object-cover w-full h-full shadow overflow-hidden ' + className
publicId={imageId} }
width={width} cloudName="irevdev"
crop="scale" publicId={imageId}
/> width={width}
</div>) crop="scale"
/>
</div>
)
} }
export default function ImageUploader({ export default function ImageUploader({
@@ -82,10 +89,7 @@ export default function ImageUploader({
{cloudinaryId && isEditable && ( {cloudinaryId && isEditable && (
<button className="w-full py-1 absolute z-10 bg-ch-blue-650 bg-opacity-50 hover:bg-opacity-80 bottom-0 right-0 left-0 flex items-center justify-center text-ch-gray-300"> <button className="w-full py-1 absolute z-10 bg-ch-blue-650 bg-opacity-50 hover:bg-opacity-80 bottom-0 right-0 left-0 flex items-center justify-center text-ch-gray-300">
<span className="font-fira-code text-sm leading-4">Update</span> <span className="font-fira-code text-sm leading-4">Update</span>
<Svg <Svg name="pencil-solid" className=" h-4 w-4 ml-4 mb-2" />
name="pencil-solid"
className=" h-4 w-4 ml-4 mb-2"
/>
</button> </button>
)} )}
{isEditable && <input {...getInputProps()} />} {isEditable && <input {...getInputProps()} />}

View File

@@ -12,43 +12,46 @@ interface KeyValueType {
} }
const KeyValue = ({ const KeyValue = ({
keyName, keyName,
children, children,
hide = false, hide = false,
canEdit = false, canEdit = false,
onEdit, onEdit,
isEditable = false, isEditable = false,
bottom = false, bottom = false,
className = "", className = '',
} : KeyValueType) => { }: KeyValueType) => {
if (!children || hide) return null if (!children || hide) return null
return ( return (
<div className={"flex flex-col " + className}> <div className={'flex flex-col ' + className}>
<div className={"text-ch-blue-400 font-fira-code flex items-center leading-4 text-sm whitespace-nowrap " + (bottom ? "order-2" : "")}> <div
<span className={isEditable ? "text-ch-blue-300" : ""}>{keyName}</span> className={
{canEdit && 'text-ch-blue-400 font-fira-code flex items-center leading-4 text-sm whitespace-nowrap ' +
(isEditable ? ( (bottom ? 'order-2' : '')
<button }
className="font-fira-sans text-ch-gray-300 items-center ml-4 grid grid-flow-col-dense p-px px-2 gap-2 bg-ch-blue-500 bg-opacity-50 hover:bg-opacity-70 rounded-sm" >
franknoirot commented 2021-09-12 20:16:28 +02:00 (Migrated from github.com)
Review

Allows the key to appear below (only visually) using CSS order property,

Allows the key to appear below (only visually) using CSS `order` property,
franknoirot commented 2021-09-15 16:19:33 +02:00 (Migrated from github.com)
Review

These comments aren't showing up as out of date which is worrying me @Irev-Dev , so weird. This component was overhauled, see this commit

These comments aren't showing up as out of date which is worrying me @Irev-Dev , so weird. This component was overhauled, see [this commit](https://github.com/Irev-Dev/cadhub/commit/7b2be01430454fb599ec9448de427c19db55e7c5#diff-bbd6cdd539b9e9bc9272aa05cb7894082d87d6772f64ed849ba0385ba960e5a4R29-R35)
id="rename-button" <span className={isEditable ? 'text-ch-blue-300' : ''}>{keyName}</span>
onClick={onEdit} {canEdit &&
> (isEditable ? (
<Svg <button
name="check" className="font-fira-sans text-ch-gray-300 items-center ml-4 grid grid-flow-col-dense p-px px-2 gap-2 bg-ch-blue-500 bg-opacity-50 hover:bg-opacity-70 rounded-sm"
franknoirot commented 2021-09-12 20:17:12 +02:00 (Migrated from github.com)
Review

Broke out into its own component to be used across both PartProfile and UserProfile

Broke out into its own component to be used across both `PartProfile` and `UserProfile`
Irev-Dev commented 2021-09-14 13:27:37 +02:00 (Migrated from github.com)
Review

I know you like this component but I kinda hate it, well hate is a sting word but the amount of input params is off putting for such a simple component. If we pulling it out to be reused I might be good to make a few changes.

  • hide should be removed entirely, why did I added it? like should we add a hid param to every component that's conditionally rendered 😩 . Cases where it was being used should just be replaced with `{!shouldHide && <KeyValue ... />}
  • isEditable is a terrible name, The variable is for when the component is in a edit state, but i reads like whether the component is allowed to be edited or something. perhaps 'inEditMode`
  • I liked your variable name hasPermissionToEdit could we rename canEdit to that as well? much clearer.

How does that sound, would you mind?

I know you like this component but I kinda hate it, well hate is a sting word but the amount of input params is off putting for such a simple component. If we pulling it out to be reused I might be good to make a few changes. - hide should be removed entirely, why did I added it? like should we add a hid param to every component that's conditionally rendered 😩 . Cases where it was being used should just be replaced with `{!shouldHide && <KeyValue ... />} - `isEditable` is a terrible name, The variable is for when the component is in a edit state, but i reads like whether the component is allowed to be edited or something. perhaps 'inEditMode` - I liked your variable name `hasPermissionToEdit` could we rename `canEdit` to that as well? much clearer. How does that sound, would you mind?
franknoirot commented 2021-09-15 03:10:09 +02:00 (Migrated from github.com)
Review

Yeah totally, love these ideas. I hear you it about the input params too.

Yeah totally, love these ideas. I hear you it about the input params too.
franknoirot commented 2021-09-15 07:57:54 +02:00 (Migrated from github.com)
Review

I think I found something better with it! Take a look at it.

I think I found something better with it! Take a look at it.
className="w-6 h-6" id="rename-button"
strokeWidth={3} onClick={onEdit}
/> >
<span>Update</span> <Svg name="check" className="w-6 h-6" strokeWidth={3} />
</button> <span>Update</span>
) : ( </button>
<button onClick={onEdit}> ) : (
<Svg name="pencil-solid" className="h-4 w-4 ml-4 mb-2" /> <button onClick={onEdit}>
</button> <Svg name="pencil-solid" className="h-4 w-4 ml-4 mb-2" />
))} </button>
</div> ))}
<div className={"text-ch-gray-300 " + (bottom ? "mb-1" : "mt-1")}>{children}</div>
</div> </div>
) <div className={'text-ch-gray-300 ' + (bottom ? 'mb-1' : 'mt-1')}>
Irev-Dev commented 2021-09-14 13:16:39 +02:00 (Migrated from github.com)
Review

I'm not requesting a change this is just an observation. I noticed in the headlessui docs they follow a pattern like this for condition classes

className={`${active && 'bg-blue-500'}`}

At first I didn't like it because the result will often be className="false" so even className="false false" if you have more of them, but I guess there's no harm in having classes that don't do anything in there

I'm not requesting a change this is just an observation. I noticed in the [headlessui docs](https://headlessui.dev/react/menu#basic-example) they follow a pattern like this for condition classes ``` className={`${active && 'bg-blue-500'}`} ``` At first I didn't like it because the result will often be `className="false"` so even `className="false false"` if you have more of them, but I guess there's no harm in having classes that don't do anything in there
franknoirot commented 2021-09-14 14:18:07 +02:00 (Migrated from github.com)
Review

Oh interesting, yeah that's what I was avoiding here, but it would be shorter code their way. I guess the falses would have a negligible impact on things really.

Oh interesting, yeah that's what I was avoiding here, but it would be shorter code their way. I guess the `false`s would have a negligible impact on things really.
} {children}
</div>
</div>
)
}
export default KeyValue export default KeyValue

View File

@@ -52,7 +52,11 @@ const NavPlusButton: React.FC = () => {
' px-4 py-1 my-4 bg-opacity-30 hover:bg-opacity-70 grid grid-flow-col-dense items-center gap-2' ' px-4 py-1 my-4 bg-opacity-30 hover:bg-opacity-70 grid grid-flow-col-dense items-center gap-2'
} }
> >
<div className={dotClasses + " justify-self-center w-5 h-5 rounded-full"}></div> <div
className={
dotClasses + ' justify-self-center w-5 h-5 rounded-full'
}
></div>
<Link to={routes.draftProject({ cadPackage: ideType })}> <Link to={routes.draftProject({ cadPackage: ideType })}>
<div>{name}</div> <div>{name}</div>
<div className="text-xs text-ch-gray-400 font-light">{sub}</div> <div className="text-xs text-ch-gray-400 font-light">{sub}</div>

View File

@@ -46,7 +46,8 @@ const ProfileSlashLogin = () => {
<Popover className="relative outline-none h-8 w-8"> <Popover className="relative outline-none h-8 w-8">
<Popover.Button <Popover.Button
disabled={!isAuthenticated || !currentUser} disabled={!isAuthenticated || !currentUser}
className="h-full w-full outline-none border-ch-gray-400 border-2 rounded-full"> className="h-full w-full outline-none border-ch-gray-400 border-2 rounded-full"
>
{!loading && ( {!loading && (
<ImageFallback <ImageFallback
width={80} width={80}
@@ -55,8 +56,8 @@ const ProfileSlashLogin = () => {
/> />
)} )}
</Popover.Button> </Popover.Button>
{ currentUser && ( {currentUser && (
<Popover.Panel className="w-48 absolute z-10 right-0 bg-ch-gray-700 mt-4 px-3 py-2 rounded shadow-md overflow-hidden text-ch-gray-300"> <Popover.Panel className="w-48 absolute z-10 right-0 bg-ch-gray-700 mt-4 px-3 py-2 rounded shadow-md overflow-hidden text-ch-gray-300">
<Link to={routes.user({ userName: user?.userName })}> <Link to={routes.user({ userName: user?.userName })}>
<h3 className="text-lg hover:text-ch-pink-300"> <h3 className="text-lg hover:text-ch-pink-300">
Hello {user?.name} Hello {user?.name}
@@ -65,14 +66,18 @@ const ProfileSlashLogin = () => {
<hr className="my-2" /> <hr className="my-2" />
<Link <Link
className="my-2 mt-4 block hover:text-ch-pink-300" className="my-2 mt-4 block hover:text-ch-pink-300"
to={routes.user({ userName: user?.userName })}> to={routes.user({ userName: user?.userName })}
>
<div>View Your Profile</div> <div>View Your Profile</div>
</Link> </Link>
<a href="#" onClick={logOut} <a
className="text-ch-gray-400 hover:text-ch-pink-300"> href="#"
onClick={logOut}
className="text-ch-gray-400 hover:text-ch-pink-300"
>
Logout Logout
</a> </a>
</Popover.Panel> </Popover.Panel>
)} )}
</Popover> </Popover>
) : ( ) : (

View File

@@ -34,7 +34,7 @@ const ProjectCard = ({ title, mainImage, user, Reaction, cadPackage }) => (
<div className="w-8 h-8 overflow-hidden rounded-full border border-ch-gray-300 shadow"> <div className="w-8 h-8 overflow-hidden rounded-full border border-ch-gray-300 shadow">
<ImageFallback <ImageFallback
imageId={user?.image} // http://res.cloudinary.com/irevdev/image/upload/c_scale,w_50/v1/CadHub/bc7smqwo9qqmrloyf9xr imageId={user?.image} // http://res.cloudinary.com/irevdev/image/upload/c_scale,w_50/v1/CadHub/bc7smqwo9qqmrloyf9xr
width={80} // http://res.cloudinary.com/irevdev/image/upload/c_scale,w_300/v1/CadHub/bc7smqwo9qqmrloyf9xr width={80} // http://res.cloudinary.com/irevdev/image/upload/c_scale,w_300/v1/CadHub/bc7smqwo9qqmrloyf9xr
/> />
</div> </div>
<div className="ml-3 text-lg text-ch-gray-300 font-fira-sans"> <div className="ml-3 text-lg text-ch-gray-300 font-fira-sans">

View File

@@ -257,4 +257,3 @@ const ProjectProfile = ({
} }
export default ProjectProfile export default ProjectProfile

View File

@@ -4,18 +4,24 @@ import { Link, navigate, routes } from '@redwoodjs/router'
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
import ProjectsOfUser from 'src/components/ProjectsOfUserCell' import ProjectsOfUser from 'src/components/ProjectsOfUserCell'
import IdeHeader from 'src/components/IdeHeader/IdeHeader' import IdeHeader from 'src/components/IdeHeader/IdeHeader'
import Svg from 'src/components/Svg/Svg' import Svg from 'src/components/Svg/Svg'
import { fieldsConfig, fieldReducer, UserProfileType, FieldConfigType } from './userProfileConfig' import {
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
fieldsConfig,
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
fieldReducer,
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
UserProfileType,
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
FieldConfigType,
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
} from './userProfileConfig'
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
function buildFieldsConfig(fieldsConfig, user) { function buildFieldsConfig(fieldsConfig, user) {
Object.entries(fieldsConfig).forEach(([key, field] : [string, FieldConfigType]) => { Object.entries(fieldsConfig).forEach(
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
field.currentValue = field.newValue = user[key] ([key, field]: [string, FieldConfigType]) => {
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
field.name = key field.currentValue = field.newValue = user[key]
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
}) field.name = key
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
}
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
)
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
return fieldsConfig return fieldsConfig
} }
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
const UserProfile = ({ const UserProfile = ({
user, user,
isEditable, isEditable,
@@ -23,11 +29,13 @@ const UserProfile = ({
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
onSave, onSave,
error, error,
projects, projects,
} : UserProfileType) => { }: UserProfileType) => {
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
const { currentUser } = useAuth() const { currentUser } = useAuth()
const hasEditPermission = currentUser?.sub === user.id const hasEditPermission = currentUser?.sub === user.id
useEffect(() => { useEffect(() => {
isEditable && !hasEditPermission && navigate(routes.user({ userName: user.userName })) isEditable &&
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
!hasEditPermission &&
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
navigate(routes.user({ userName: user.userName }))
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
}, [currentUser]) }, [currentUser])
const initializedFields = buildFieldsConfig(fieldsConfig, user) const initializedFields = buildFieldsConfig(fieldsConfig, user)
@@ -49,7 +57,6 @@ const UserProfile = ({
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
projectOwnerImage={user?.image} projectOwnerImage={user?.image}
projectOwnerId={user?.id} projectOwnerId={user?.id}
> >
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
<span></span> <span></span>
</IdeHeader> </IdeHeader>
</div> </div>
@@ -95,14 +102,14 @@ const UserProfile = ({
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
/> />
</div> </div>
<div className="my-5"> <div className="my-5">
<fields.createdAt.component <fields.createdAt.component field={fields.createdAt} />
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
field={fields.createdAt}
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
/>
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
</div> </div>
</section> </section>
{/* Viewer */} {/* Viewer */}
<div className="py-10 px-8 w-full h-full relative bg-ch-gray-800 md:overflow-y-auto ch-scrollbar"> <div className="py-10 px-8 w-full h-full relative bg-ch-gray-800 md:overflow-y-auto ch-scrollbar">
<h3 className="text-2xl text-ch-gray-500 mb-4 md:hidden">Projects</h3> <h3 className="text-2xl text-ch-gray-500 mb-4 md:hidden">
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
Projects
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
</h3>
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
<ProjectsOfUser userName={user?.userName} /> <ProjectsOfUser userName={user?.userName} />
</div> </div>
</div> </div>
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?
franknoirot commented 2021-09-12 20:18:51 +02:00 (Migrated from github.com)
Review

Initializes values from user state into the config object. Is this a code smell?

Initializes values from `user` state into the config object. Is this a code smell?

View File

@@ -5,149 +5,187 @@ import Editor from 'rich-markdown-editor'
import ImageUploader from 'src/components/ImageUploader' import ImageUploader from 'src/components/ImageUploader'
import { User } from 'types/graphql' import { User } from 'types/graphql'
export interface UserProfileType { export interface UserProfileType {
user: User, user: User
isEditable: boolean, isEditable: boolean
loading: boolean, loading: boolean
error: boolean, error: boolean
onSave: Function, onSave: Function
projects: {}[], projects: {}[]
} }
export interface FieldConfigType { export interface FieldConfigType {
name?: string, // introspection ugh name?: string // introspection ugh
editable: boolean, editable: boolean
component?: ReactNode, component?: ReactNode
needsRef?: boolean, needsRef?: boolean
isEditing?: boolean | undefined, isEditing?: boolean | undefined
franknoirot commented 2021-09-12 20:20:29 +02:00 (Migrated from github.com)
Review

Is there a ProjectType defined anywhere? I couldn't find one.

Is there a `ProjectType` defined anywhere? I couldn't find one.
onSave?: Function, onSave?: Function
currentValue?: any, currentValue?: any
newValue?: any, newValue?: any
} }
const ProfileKeyValue = ({ field, dispatch, user, save, hasEditPermission, children, bottom = false }) => { const ProfileKeyValue = ({
field,
dispatch,
user,
save,
hasEditPermission,
children,
bottom = false,
}) => {
return (
<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 })
}}
isEditable={hasEditPermission && field.isEditing}
bottom={bottom}
className="mb-4"
>
{children}
</KeyValue>
)
}
const bioField: FieldConfigType = {
editable: true,
needsRef: true,
component: (props) => {
const ref = useRef(null)
const { dispatch, field } = props
return ( return (
<KeyValue <ProfileKeyValue {...props}>
keyName={field.name} <div
hide={!user[field.name] && !hasEditPermission} id="bio-wrap"
canEdit={hasEditPermission} name="bio"
onEdit={() => { className={
if (field.isEditing) { 'markdown-overrides rounded-sm pb-2 mt-2' +
save(user.userName, { [field.name]: field.newValue }) (field.isEditable ? ' min-h-md' : '')
} }
dispatch({ type: "SET_CURRENT_VALUE", payload: { field: field.name, value: field.newValue }}) onClick={(e) =>
dispatch({ type: "TOGGLE_EDITING", payload: field.name }) e?.target?.id === 'bio-wrap' && ref?.current?.focusAtEnd()
}} }
isEditable={hasEditPermission && field.isEditing} >
bottom={bottom} <Editor
className="mb-4" ref={ref}
> defaultValue={field?.currentValue || ''}
{ children } readOnly={!field.isEditing}
onChange={(bio) =>
dispatch({
type: 'SET_NEW_VALUE',
payload: { field: field.bio, value: bio() },
})
}
/>
</div>
</ProfileKeyValue>
)
},
}
const createdAtField: FieldConfigType = {
editable: false,
component: (props) => {
const { field } = props
return (
<KeyValue keyName="Member Since">
<p className="text-ch-gray-300">
{new Date(field.currentValue).toLocaleDateString()}
</p>
</KeyValue> </KeyValue>
) )
} },
}
const bioField : FieldConfigType = { const imageField: FieldConfigType = {
editable: true, editable: true,
needsRef: true, component: (props) => {
component: (props) => { const { field, user, save, hasEditPermission } = props
const ref = useRef(null) 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 { dispatch, field } = props const nameField: FieldConfigType = {
editable: true,
component: (props) => {
const { user, dispatch, field } = props
return <ProfileKeyValue {...props}> return (
<div <ProfileKeyValue {...props} bottom={true}>
id="bio-wrap" {!field.isEditing ? (
name="bio" <h1 className="text-4xl">{user?.name}</h1>
className={ ) : (
'markdown-overrides rounded-sm pb-2 mt-2' + <InputText
(field.isEditable ? ' min-h-md' : '') className="text-xl"
value={field.newValue}
onChange={({ target: { value } }) =>
dispatch({
type: 'SET_NEW_VALUE',
payload: { field: field.name, value },
})
} }
onClick={(e) => isEditable={!field.isEditable}
e?.target?.id === 'bio-wrap' && />
ref?.current?.focusAtEnd() )}
</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}
<Editor />
ref={ref} )}
defaultValue={field?.currentValue || ''} </ProfileKeyValue>
readOnly={!field.isEditing} )
onChange={(bio) => dispatch({ type: "SET_NEW_VALUE", payload: { field: field.bio, value: bio() }})} },
/>
</div>
</ProfileKeyValue>
},
}
const createdAtField : FieldConfigType = {
editable: false,
component: (props) => {
const { 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 = { export const fieldsConfig = {
@@ -166,21 +204,24 @@ export const fieldsConfig = {
export function fieldReducer(state, action) { export function fieldReducer(state, action) {
switch (action.type) { switch (action.type) {
case "TOGGLE_EDITING": case 'TOGGLE_EDITING':
return { return {
...state, ...state,
[action.payload]: { [action.payload]: {
...state[action.payload], ...state[action.payload],
isEditing: (state[action.payload].editable && !state[action.payload].isEditing) ? true : false, isEditing:
} state[action.payload].editable && !state[action.payload].isEditing
? true
: false,
},
} }
case "SET_NEW_VALUE": case 'SET_NEW_VALUE':
const newState = { const newState = {
...state, ...state,
[action.payload.field]: { [action.payload.field]: {
...state[action.payload.field], ...state[action.payload.field],
newValue: action.payload.value, newValue: action.payload.value,
} },
} }
return newState return newState
default: default:

View File

@@ -135,7 +135,8 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
<Popover className="relative outline-none w-full h-full"> <Popover className="relative outline-none w-full h-full">
<Popover.Button <Popover.Button
disabled={!isAuthenticated || !currentUser} disabled={!isAuthenticated || !currentUser}
className="h-full w-full outline-none border-ch-gray-400 border-2 rounded-full"> className="h-full w-full outline-none border-ch-gray-400 border-2 rounded-full"
>
{!loading && ( {!loading && (
<ImageFallback <ImageFallback
width={80} width={80}
@@ -144,8 +145,8 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
/> />
)} )}
</Popover.Button> </Popover.Button>
{ currentUser && ( {currentUser && (
<Popover.Panel className="w-48 absolute z-10 right-0 bg-ch-gray-700 mt-4 px-3 py-2 rounded shadow-md overflow-hidden text-ch-gray-300"> <Popover.Panel className="w-48 absolute z-10 right-0 bg-ch-gray-700 mt-4 px-3 py-2 rounded shadow-md overflow-hidden text-ch-gray-300">
<Link to={routes.user({ userName: user?.userName })}> <Link to={routes.user({ userName: user?.userName })}>
<h3 className="text-lg hover:text-ch-pink-300"> <h3 className="text-lg hover:text-ch-pink-300">
Hello {user?.name} Hello {user?.name}
@@ -154,14 +155,18 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
<hr className="my-2" /> <hr className="my-2" />
<Link <Link
className="my-2 mt-4 block hover:text-ch-pink-300" className="my-2 mt-4 block hover:text-ch-pink-300"
to={routes.user({ userName: user?.userName })}> to={routes.user({ userName: user?.userName })}
>
<div>View Your Profile</div> <div>View Your Profile</div>
</Link> </Link>
<a href="#" onClick={logOut} <a
className="text-ch-gray-400 hover:text-ch-pink-300"> href="#"
onClick={logOut}
className="text-ch-gray-400 hover:text-ch-pink-300"
>
Logout Logout
</a> </a>
</Popover.Panel> </Popover.Panel>
)} )}
</Popover> </Popover>
</li> </li>

View File

@@ -37,9 +37,7 @@ const AccountRecoveryPage = () => {
className="grid items-center gap-2" className="grid items-center gap-2"
style={{ gridTemplateColumns: 'auto 1fr' }} style={{ gridTemplateColumns: 'auto 1fr' }}
> >
<span className="capitalize text-ch-gray-300 text-sm"> <span className="capitalize text-ch-gray-300 text-sm">email</span>
email
</span>
<InputTextForm <InputTextForm
className="text-xl" className="text-xl"
name="email" name="email"

View File

@@ -49,9 +49,7 @@ const UpdatePasswordPage = () => {
required: true, required: true,
}} }}
/> />
<span className="capitalize text-ch-gray-300 text-sm"> <span className="capitalize text-ch-gray-300 text-sm">confirm</span>
confirm
</span>
<InputTextForm <InputTextForm
className="text-xl" className="text-xl"
name="confirm" name="confirm"