From 6a69a1c1bfe62467439ce6ac5e86a03509475d80 Mon Sep 17 00:00:00 2001 From: Frank Johnson Date: Sat, 11 Sep 2021 21:19:34 -0400 Subject: [PATCH 01/12] Updated styles on Pwd Recovery page --- app/web/src/layouts/MainLayout/MainLayout.js | 4 ++-- .../src/pages/AccountRecoveryPage/AccountRecoveryPage.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/web/src/layouts/MainLayout/MainLayout.js b/app/web/src/layouts/MainLayout/MainLayout.js index edf5d30..d0617fd 100644 --- a/app/web/src/layouts/MainLayout/MainLayout.js +++ b/app/web/src/layouts/MainLayout/MainLayout.js @@ -91,7 +91,7 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => { } }, [hash, client]) return ( -
+
) diff --git a/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js b/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js index b585671..20344b3 100644 --- a/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js +++ b/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js @@ -31,14 +31,14 @@ const AccountRecoveryPage = () => { />
-

Send recovery email

+

Send recovery email

- - email: + + email { }} />
- + Send email From 126b60f5ddfc68f699cb246d522190c1c28a8f7e Mon Sep 17 00:00:00 2001 From: Frank Johnson Date: Sat, 11 Sep 2021 22:05:35 -0400 Subject: [PATCH 02/12] Style tweaks to signed-in user modal in top nav --- .../components/ImageUploader/ImageUploader.js | 23 +++--- .../NavPlusButton/NavPlusButton.tsx | 5 +- app/web/src/layouts/MainLayout/MainLayout.js | 81 ++++++++----------- .../AccountRecoveryPage.js | 6 +- .../UpdatePasswordPage/UpdatePasswordPage.js | 16 ++-- 5 files changed, 62 insertions(+), 69 deletions(-) diff --git a/app/web/src/components/ImageUploader/ImageUploader.js b/app/web/src/components/ImageUploader/ImageUploader.js index d61f67a..aa757cd 100644 --- a/app/web/src/components/ImageUploader/ImageUploader.js +++ b/app/web/src/components/ImageUploader/ImageUploader.js @@ -11,6 +11,19 @@ import Svg from 'src/components/Svg' const CLOUDINARY_UPLOAD_PRESET = 'CadHub_project_images' const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/irevdev/upload' +export function ImageFallback({ width = 100, cloudinaryId = 'CadHub/eia1kwru54g2kf02s2xx', className = '' }) { + return ( +
+ +
) +} + export default function ImageUploader({ onImageUpload = () => {}, imageUrl, @@ -78,15 +91,7 @@ export default function ImageUploader({ )} {isEditable && } {(cloudinaryId || !isEditable) && ( -
- -
+ )} {!cloudinaryId && } {!cloudinaryId && isEditable && ( diff --git a/app/web/src/components/NavPlusButton/NavPlusButton.tsx b/app/web/src/components/NavPlusButton/NavPlusButton.tsx index 0aa44e2..cadd9a9 100644 --- a/app/web/src/components/NavPlusButton/NavPlusButton.tsx +++ b/app/web/src/components/NavPlusButton/NavPlusButton.tsx @@ -36,15 +36,16 @@ const NavPlusButton: React.FC = () => { - +

New Project

+
    {menuOptions.map(({ name, sub, ideType, bgClasses, dotClasses }) => (
  • -
    +
    {name}
    {sub}
    diff --git a/app/web/src/layouts/MainLayout/MainLayout.js b/app/web/src/layouts/MainLayout/MainLayout.js index d0617fd..26cd001 100644 --- a/app/web/src/layouts/MainLayout/MainLayout.js +++ b/app/web/src/layouts/MainLayout/MainLayout.js @@ -3,7 +3,7 @@ import { Link, routes, navigate } from '@redwoodjs/router' import { useAuth } from '@redwoodjs/auth' import { Toaster, toast } from '@redwoodjs/web/toast' import Tooltip from '@material-ui/core/Tooltip' -import Popover from '@material-ui/core/Popover' +import { Popover } from '@headlessui/react' import { getActiveClasses } from 'get-active-classes' import Footer from 'src/components/Footer' import { useLocation } from '@redwoodjs/router' @@ -13,7 +13,7 @@ import ReactGA from 'react-ga' import { isBrowser } from '@redwoodjs/prerender/browserUtils' import Svg from 'src/components/Svg' -import ImageUploader from 'src/components/ImageUploader' +import { ImageFallback } from 'src/components/ImageUploader' import useUser from 'src/helpers/hooks/useUser' let previousSubmission = '' @@ -131,20 +131,39 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
  • {isAuthenticated ? ( -
  • - +
  • ) : (
  • @@ -158,38 +177,6 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
  • )}
- {isAuthenticated && currentUser && ( - -
- -

- Hello {user?.name} -

- -
-
- -
Your Profile
- - - Logout - -
-
- )} diff --git a/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js b/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js index 20344b3..eb5a5b3 100644 --- a/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js +++ b/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js @@ -51,10 +51,10 @@ const AccountRecoveryPage = () => { }, }} /> + + Send email +
- - Send email - diff --git a/app/web/src/pages/UpdatePasswordPage/UpdatePasswordPage.js b/app/web/src/pages/UpdatePasswordPage/UpdatePasswordPage.js index 996e53e..3cb6095 100644 --- a/app/web/src/pages/UpdatePasswordPage/UpdatePasswordPage.js +++ b/app/web/src/pages/UpdatePasswordPage/UpdatePasswordPage.js @@ -32,14 +32,14 @@ const UpdatePasswordPage = () => {
-

Reset Password

+

Reset Password

- - password: + + password { required: true, }} /> - - confirm: + + confirm { required: true, }} /> + + Update +
- - Update -
From 3dbb963e4e08cb684a61b2bba775c5524c551bbb Mon Sep 17 00:00:00 2001 From: Frank Johnson Date: Sat, 11 Sep 2021 22:14:18 -0400 Subject: [PATCH 03/12] Updated signed in user menu in IDE --- .../src/components/IdeHeader/IdeHeader.tsx | 2 +- .../ProfileSlashLogin/ProfileSlashLogin.tsx | 78 ++++++++----------- 2 files changed, 35 insertions(+), 45 deletions(-) diff --git a/app/web/src/components/IdeHeader/IdeHeader.tsx b/app/web/src/components/IdeHeader/IdeHeader.tsx index 62822db..43ac8f3 100644 --- a/app/web/src/components/IdeHeader/IdeHeader.tsx +++ b/app/web/src/components/IdeHeader/IdeHeader.tsx @@ -174,7 +174,7 @@ const IdeHeader = ({ }} {/* Fork */} -
+
diff --git a/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx b/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx index 22c0b8e..f403fd2 100644 --- a/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx +++ b/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx @@ -2,7 +2,8 @@ import { useState } from 'react' import { useAuth } from '@redwoodjs/auth' import { Link, routes } from '@redwoodjs/router' import ReactGA from 'react-ga' -import Popover from '@material-ui/core/Popover' +import { Popover } from '@headlessui/react' +import { ImageFallback } from 'src/components/ImageUploader' import useUser from 'src/helpers/hooks/useUser' import LoginModal from 'src/components/LoginModal' @@ -42,17 +43,38 @@ const ProfileSlashLogin = () => { return (
{isAuthenticated ? ( -
- -
+ + + {!loading && ( + + )} + + { currentUser && ( + + +

+ Hello {user?.name} +

+ +
+ +
View Your Profile
+ + + Logout + +
+ )} +
) : ( )} - {isAuthenticated && currentUser && ( - -
- -

- Hello {user?.name} -

- -
-
- -
Your Profile
- - - Logout - -
-
- )} setIsLoginModalOpen(false)} From 55d48057daa149c8cee1343eb46a0fa62013a408 Mon Sep 17 00:00:00 2001 From: Frank Johnson Date: Sun, 12 Sep 2021 05:03:58 -0400 Subject: [PATCH 04/12] Initial profile refactor of layout and config --- app/web/config/tailwind.config.js | 3 + app/web/src/components/InputText/InputText.js | 2 +- .../InputTextForm/InputTextForm.tsx | 2 +- app/web/src/components/KeyValue/KeyValue.tsx | 54 +++++ .../components/ProjectCard/ProjectCard.tsx | 5 +- .../ProjectProfile/ProjectProfile.tsx | 49 +---- app/web/src/components/Projects/Projects.tsx | 2 +- .../src/components/UserProfile/UserProfile.js | 113 ----------- .../components/UserProfile/UserProfile.tsx | 112 +++++++++++ .../UserProfile/userProfileConfig.tsx | 189 ++++++++++++++++++ app/web/src/index.css | 13 ++ .../src/pages/EditUserPage/EditUserPage.js | 5 +- app/web/src/pages/UserPage/UserPage.js | 4 +- 13 files changed, 383 insertions(+), 170 deletions(-) create mode 100644 app/web/src/components/KeyValue/KeyValue.tsx delete mode 100644 app/web/src/components/UserProfile/UserProfile.js create mode 100644 app/web/src/components/UserProfile/UserProfile.tsx create mode 100644 app/web/src/components/UserProfile/userProfileConfig.tsx diff --git a/app/web/config/tailwind.config.js b/app/web/config/tailwind.config.js index ea1ef82..7031fa3 100644 --- a/app/web/config/tailwind.config.js +++ b/app/web/config/tailwind.config.js @@ -66,6 +66,9 @@ module.exports = { gridAutoColumns: { 'preview-layout': 'minmax(30rem, 1fr) minmax(auto, 2fr)', }, + gridTemplateColumns: { + 'profile-layout': 'minmax(32rem, 1fr) 2fr', + }, keyframes: { 'bounce-sm': { '0%, 100%': { diff --git a/app/web/src/components/InputText/InputText.js b/app/web/src/components/InputText/InputText.js index 30e447a..4f4cd3b 100644 --- a/app/web/src/components/InputText/InputText.js +++ b/app/web/src/components/InputText/InputText.js @@ -23,7 +23,7 @@ const InputText = ({ )} /> { <>
void + isEditable?: boolean + bottom?: boolean + className?: string +} + +const KeyValue = ({ + keyName, + children, + hide = false, + canEdit = false, + onEdit, + isEditable = false, + bottom = false, + className = "", + } : KeyValueType) => { + if (!children || hide) return null + return ( +
+
+ {keyName} + {canEdit && + (isEditable ? ( + + ) : ( + + ))} +
+
{children}
+
+ ) + } + + export default KeyValue \ No newline at end of file diff --git a/app/web/src/components/ProjectCard/ProjectCard.tsx b/app/web/src/components/ProjectCard/ProjectCard.tsx index 374c1c7..2016b4b 100644 --- a/app/web/src/components/ProjectCard/ProjectCard.tsx +++ b/app/web/src/components/ProjectCard/ProjectCard.tsx @@ -4,6 +4,7 @@ import CadPackage from 'src/components/CadPackage/CadPackage' import { countEmotes } from 'src/helpers/emote' import ImageUploader from 'src/components/ImageUploader' +import { ImageFallback } from '../ImageUploader/ImageUploader' const ProjectCard = ({ title, mainImage, user, Reaction, cadPackage }) => (
  • (
  • - diff --git a/app/web/src/components/ProjectProfile/ProjectProfile.tsx b/app/web/src/components/ProjectProfile/ProjectProfile.tsx index d04835d..b3d1d26 100644 --- a/app/web/src/components/ProjectProfile/ProjectProfile.tsx +++ b/app/web/src/components/ProjectProfile/ProjectProfile.tsx @@ -17,51 +17,7 @@ import { useIdeInit } from 'src/components/EncodedUrl/helpers' import ProfileViewer from '../ProfileViewer/ProfileViewer' import Svg from 'src/components/Svg/Svg' import OpenscadStaticImageMessage from 'src/components/OpenscadStaticImageMessage/OpenscadStaticImageMessage' - -const KeyValue = ({ - keyName, - children, - hide = false, - canEdit = false, - onEdit, - isEditable = false, -}: { - keyName: string - children: React.ReactNode - hide?: boolean - canEdit?: boolean - onEdit?: () => void - isEditable?: boolean -}) => { - if (!children || hide) return null - return ( -
    -
    - {keyName} - {canEdit && - (isEditable ? ( - - ) : ( - - ))} -
    -
    {children}
    -
    - ) -} +import KeyValue from 'src/components/KeyValue/KeyValue' const ProjectProfile = ({ userProject, @@ -140,7 +96,7 @@ const ProjectProfile = ({
    {/* Side panel */} -
    +

    {project?.title.replace(/-/g, ' ')} @@ -301,3 +257,4 @@ const ProjectProfile = ({ } export default ProjectProfile + diff --git a/app/web/src/components/Projects/Projects.tsx b/app/web/src/components/Projects/Projects.tsx index cbe59a0..b4a31e3 100644 --- a/app/web/src/components/Projects/Projects.tsx +++ b/app/web/src/components/Projects/Projects.tsx @@ -36,7 +36,7 @@ const ProjectsList = ({ return (
      {filteredProjects.map( diff --git a/app/web/src/components/UserProfile/UserProfile.js b/app/web/src/components/UserProfile/UserProfile.js deleted file mode 100644 index 3abc975..0000000 --- a/app/web/src/components/UserProfile/UserProfile.js +++ /dev/null @@ -1,113 +0,0 @@ -import { useState, useEffect } from 'react' -import { useAuth } from '@redwoodjs/auth' -import { navigate, routes } from '@redwoodjs/router' -import Editor from 'rich-markdown-editor' -import ImageUploader from 'src/components/ImageUploader' -import Button from 'src/components/Button' -import ProfileTextInput from 'src/components/ProfileTextInput' -import ProjectsOfUser from 'src/components/ProjectsOfUserCell' - -const UserProfile = ({ - user, - isEditable, - loading, - onSave, - error, - projects, -}) => { - const { currentUser } = useAuth() - const canEdit = currentUser?.sub === user.id - const isImageEditable = !isEditable && canEdit // image is editable when not in profile edit mode in order to separate them as it's too hard too to upload an image to cloudinary temporarily until the use saves (and maybe have to clean up) for the time being - useEffect(() => { - isEditable && !canEdit && navigate(routes.user({ userName: user.userName })) - }, [currentUser]) - const [input, setInput] = useState({ - userName: user.userName, - name: user.name, - bio: user.bio, - image: user.image, - }) - const { userName, name } = input - const editableTextFields = { userName, name } - return ( - <> -
      -
      - {!isEditable && ( -
      - { - onSave(user.userName, { - ...input, - image, - }) - }} - aspectRatio={1} - isEditable={isImageEditable} - imageUrl={user.image} - width={300} - /> -
      - )} -
      - - setInput({ - ...input, - name, - userName: userName.replace(/([^a-zA-Z\d_:])/g, '-'), - }) - } - isEditable={isEditable} - /> - {isEditable ? ( - // TODO replace pencil with a save icon - ) : canEdit ? ( - - ) : null} -
      -
      -
      -

      Bio:

      -
      - - setInput({ - ...input, - bio: bioFn(), - }) - } - /> -
      -
      -
      -

      Projects:

      - -
      -
      - - ) -} - -export default UserProfile diff --git a/app/web/src/components/UserProfile/UserProfile.tsx b/app/web/src/components/UserProfile/UserProfile.tsx new file mode 100644 index 0000000..4368782 --- /dev/null +++ b/app/web/src/components/UserProfile/UserProfile.tsx @@ -0,0 +1,112 @@ +import { useState, useEffect, useRef, useReducer, ReactNode } from 'react' +import { useAuth } from '@redwoodjs/auth' +import { Link, navigate, routes } from '@redwoodjs/router' +import ProjectsOfUser from 'src/components/ProjectsOfUserCell' +import IdeHeader from 'src/components/IdeHeader/IdeHeader' +import Svg from 'src/components/Svg/Svg' +import { fieldsConfig, fieldReducer, UserProfileType, FieldConfigType } from './userProfileConfig' + +function buildFieldsConfig(fieldsConfig, user) { + Object.entries(fieldsConfig).forEach(([key, field] : [string, FieldConfigType]) => { + field.currentValue = field.newValue = user[key] + field.name = key + }) + + return fieldsConfig +} + + +const UserProfile = ({ + user, + isEditable, + loading, + onSave, + error, + projects, +} : UserProfileType) => { + const { currentUser } = useAuth() + const hasEditPermission = currentUser?.sub === user.id + useEffect(() => { + isEditable && !hasEditPermission && navigate(routes.user({ userName: user.userName })) + }, [currentUser]) + + const initializedFields = buildFieldsConfig(fieldsConfig, user) + const [fields, fieldDispatch] = useReducer(fieldReducer, initializedFields) + + return ( + <> +
      +
      + + + + {}} + projectOwner={user?.userName} + projectOwnerImage={user?.image} + projectOwnerId={user?.id} + /> +
      +
      +
      + {/* Side panel */} +
      +
      + {!isEditable && ( +
      + +
      + )} +
      + + +
      +
      +
      + +
      +
      + +
      +
      + {/* Viewer */} +
      +

      Projects

      + +
      +
      +
      +
      + + ) +} + +export default UserProfile diff --git a/app/web/src/components/UserProfile/userProfileConfig.tsx b/app/web/src/components/UserProfile/userProfileConfig.tsx new file mode 100644 index 0000000..a4c7574 --- /dev/null +++ b/app/web/src/components/UserProfile/userProfileConfig.tsx @@ -0,0 +1,189 @@ +import React, { ReactNode, useRef } from 'react' +import KeyValue from 'src/components/KeyValue/KeyValue' +import InputText from '../InputText/InputText' +import Editor from 'rich-markdown-editor' +import ImageUploader from 'src/components/ImageUploader' +import { User } from 'types/graphql' + + +export interface UserProfileType { + user: User, + isEditable: 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, +} + +const ProfileKeyValue = ({ field, dispatch, user, save, hasEditPermission, children, bottom = false }) => { + return ( + { + 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 } + + ) + } + +const bioField : FieldConfigType = { + editable: true, + needsRef: true, + component: (props) => { + const ref = useRef(null) + + const { dispatch, field } = props + + return +
      + e?.target?.id === 'bio-wrap' && + ref?.current?.focusAtEnd() + } + > + dispatch({ type: "SET_NEW_VALUE", payload: { field: field.bio, value: bio() }})} + /> +
      +
      + }, +} + +const createdAtField : FieldConfigType = { + editable: false, + component: (props) => { + const { field } = props + + return +

      { new Date(field.currentValue).toLocaleDateString() }

      +
      + }, +} + +const imageField : FieldConfigType = { + editable: true, + component: (props) => { + const { field, user, save, hasEditPermission } = props + return ( + { + 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 + { (!field.isEditing) + ?

      { user?.name }

      + : dispatch({ type: "SET_NEW_VALUE", payload: { field: field.name, value }})} + isEditable={!field.isEditable} + /> + } +
      + }, +} + +const userNameField : FieldConfigType = { + editable: true, + component: (props) => { + const { dispatch, field } = props + + return + { (!field.isEditing) + ?

      @{ field?.currentValue?.replace(/([^a-zA-Z\d_:])/g, '-') }

      + : dispatch({ type: "SET_NEW_VALUE", payload: { field: field.name, value }})} + isEditable={!field.isEditable} + /> + } +
      + }, +} + +export const fieldsConfig = { + bio: bioField, + createdAt: createdAtField, + id: { + editable: false, + }, + image: imageField, + name: nameField, + updatedAt: { + editable: false, + }, + userName: userNameField, +} + +export function fieldReducer(state, action) { + switch (action.type) { + case "TOGGLE_EDITING": + return { + ...state, + [action.payload]: { + ...state[action.payload], + isEditing: (state[action.payload].editable && !state[action.payload].isEditing) ? true : false, + } + } + case "SET_NEW_VALUE": + const newState = { + ...state, + [action.payload.field]: { + ...state[action.payload.field], + newValue: action.payload.value, + } + } + return newState + default: + return state + } +} \ No newline at end of file diff --git a/app/web/src/index.css b/app/web/src/index.css index 0b8ef1d..17e159d 100644 --- a/app/web/src/index.css +++ b/app/web/src/index.css @@ -17,6 +17,19 @@ body { font-family: 'Fira Sans', ui-sans-serif, system-ui, -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } + + /* custom scrollbar */ + .ch-scrollbar::-webkit-scrollbar { + @apply w-3; + } + + .ch-scrollbar::-webkit-scrollbar-track { + @apply bg-ch-gray-700; + } + + .ch-scrollbar::-webkit-scrollbar-thumb { + @apply bg-ch-pink-800 bg-opacity-30 hover:bg-opacity-60; + } } diff --git a/app/web/src/pages/EditUserPage/EditUserPage.js b/app/web/src/pages/EditUserPage/EditUserPage.js index 23623a9..3d52cc3 100644 --- a/app/web/src/pages/EditUserPage/EditUserPage.js +++ b/app/web/src/pages/EditUserPage/EditUserPage.js @@ -1,14 +1,13 @@ -import MainLayout from 'src/layouts/MainLayout' import EditUserCell from 'src/components/EditUserCell' import Seo from 'src/components/Seo/Seo' const UserPage = ({ userName }) => { return ( - + <> - + ) } diff --git a/app/web/src/pages/UserPage/UserPage.js b/app/web/src/pages/UserPage/UserPage.js index c2b027b..38072c9 100644 --- a/app/web/src/pages/UserPage/UserPage.js +++ b/app/web/src/pages/UserPage/UserPage.js @@ -4,11 +4,11 @@ import Seo from 'src/components/Seo/Seo' const UserPage = ({ userName }) => { return ( - + <> - + ) } From 34757cf53544d82e662239d95748c2cc2dd414a2 Mon Sep 17 00:00:00 2001 From: Frank Johnson Date: Sun, 12 Sep 2021 12:38:16 -0400 Subject: [PATCH 05/12] Finished fixing nav, tweaked KeyValue edit btn --- app/web/config/tailwind.config.js | 1 + .../src/components/IdeHeader/IdeHeader.tsx | 176 ++++++++++-------- .../components/ImageUploader/ImageUploader.js | 17 +- app/web/src/components/KeyValue/KeyValue.tsx | 6 +- .../ProfileSlashLogin/ProfileSlashLogin.tsx | 6 +- .../components/ProjectCard/ProjectCard.tsx | 6 +- .../components/UserProfile/UserProfile.tsx | 13 +- app/web/src/layouts/MainLayout/MainLayout.js | 4 +- 8 files changed, 125 insertions(+), 104 deletions(-) diff --git a/app/web/config/tailwind.config.js b/app/web/config/tailwind.config.js index 7031fa3..90382d8 100644 --- a/app/web/config/tailwind.config.js +++ b/app/web/config/tailwind.config.js @@ -22,6 +22,7 @@ module.exports = { 800: '#1A1A1D', 750: '#222222', 760: '#232532', + 710: '#2B303C', // TODO: Use HSL so I stop adding grays to fix the warm/cool problem 700: '#2A3038', 600: '#3B3E4B', 550: '#63636A', diff --git a/app/web/src/components/IdeHeader/IdeHeader.tsx b/app/web/src/components/IdeHeader/IdeHeader.tsx index 43ac8f3..b9a34ca 100644 --- a/app/web/src/components/IdeHeader/IdeHeader.tsx +++ b/app/web/src/components/IdeHeader/IdeHeader.tsx @@ -11,6 +11,7 @@ import ProfileSlashLogin from 'src/components/ProfileSlashLogin' import Gravatar from 'src/components/Gravatar/Gravatar' import EditableProjectTitle from 'src/components/EditableProjecTitle/EditableProjecTitle' import CaptureButton from 'src/components/CaptureButton/CaptureButton' +import { ReactNode } from 'react' const TopButton = ({ onClick, @@ -44,6 +45,7 @@ interface IdeHeaderProps { projectOwnerId?: string projectOwnerImage?: string projectId?: string + children?: ReactNode } const IdeHeader = ({ @@ -53,6 +55,7 @@ const IdeHeader = ({ projectOwnerImage, projectId, projectOwnerId, + children, }: IdeHeaderProps) => { const { currentUser } = useAuth() const { project } = useIdeContext() @@ -92,87 +95,16 @@ const IdeHeader = ({
      )}
      - {canEdit && !projectTitle && ( - ( - - - - )} /> - )} - {!projectTitle && ( - - - - )} - {projectTitle && ( - - navigate(routes.ide({ userName: _projectOwner, projectTitle })) - } - name="Editor" - > - - - )} - - - {({ open }) => { - return ( - <> - - - - - - {open && ( - - - - - - - - - - - encoded script - external script - - - - )} - - ) - }} - + : children + } {/* Fork */}
      @@ -184,3 +116,89 @@ const IdeHeader = ({ } export default IdeHeader + + +function DefaultTopButtons({ project, projectTitle, _projectOwner, handleRender, canEdit }) { + return (<> + {canEdit && !projectTitle && ( + ( + + + + )} + /> + )} + {!projectTitle && ( + + + + )} + {projectTitle && ( + + navigate(routes.ide({ userName: _projectOwner, projectTitle })) + } + name="Editor" + > + + + )} + + {({ open }) => { + return ( + <> + + + + + + {open && ( + + + + + + + + + + + encoded script + external script + + + + )} + + ) + }} + + ) +} \ No newline at end of file diff --git a/app/web/src/components/ImageUploader/ImageUploader.js b/app/web/src/components/ImageUploader/ImageUploader.js index aa757cd..a121d80 100644 --- a/app/web/src/components/ImageUploader/ImageUploader.js +++ b/app/web/src/components/ImageUploader/ImageUploader.js @@ -11,13 +11,13 @@ import Svg from 'src/components/Svg' const CLOUDINARY_UPLOAD_PRESET = 'CadHub_project_images' const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/irevdev/upload' -export function ImageFallback({ width = 100, cloudinaryId = 'CadHub/eia1kwru54g2kf02s2xx', className = '' }) { +export function ImageFallback({ width = 100, imageId = 'CadHub/eia1kwru54g2kf02s2xx', className = '' }) { return (
      @@ -78,20 +78,19 @@ export default function ImageUploader({ } style={{ paddingBottom: `${(1 / aspectRatio) * 100}%` }} > -
      +
      {cloudinaryId && isEditable && ( - )} {isEditable && } {(cloudinaryId || !isEditable) && ( - + )} {!cloudinaryId && } {!cloudinaryId && isEditable && ( diff --git a/app/web/src/components/KeyValue/KeyValue.tsx b/app/web/src/components/KeyValue/KeyValue.tsx index 2323701..ed7c2d8 100644 --- a/app/web/src/components/KeyValue/KeyValue.tsx +++ b/app/web/src/components/KeyValue/KeyValue.tsx @@ -25,17 +25,17 @@ const KeyValue = ({ return (
      - {keyName} + {keyName} {canEdit && (isEditable ? ( )} {isEditable && } diff --git a/app/web/src/components/KeyValue/KeyValue.tsx b/app/web/src/components/KeyValue/KeyValue.tsx index ed7c2d8..f05aeea 100644 --- a/app/web/src/components/KeyValue/KeyValue.tsx +++ b/app/web/src/components/KeyValue/KeyValue.tsx @@ -12,43 +12,46 @@ interface KeyValueType { } const KeyValue = ({ - keyName, - children, - hide = false, - canEdit = false, - onEdit, - isEditable = false, - bottom = false, - className = "", - } : KeyValueType) => { - if (!children || hide) return null - return ( -
      -
      - {keyName} - {canEdit && - (isEditable ? ( - - ) : ( - - ))} -
      -
      {children}
      + keyName, + children, + hide = false, + canEdit = false, + onEdit, + isEditable = false, + bottom = false, + className = '', +}: KeyValueType) => { + if (!children || hide) return null + return ( +
      +
      + {keyName} + {canEdit && + (isEditable ? ( + + ) : ( + + ))}
      - ) - } +
      + {children} +
      +
      + ) +} - export default KeyValue \ No newline at end of file +export default KeyValue diff --git a/app/web/src/components/NavPlusButton/NavPlusButton.tsx b/app/web/src/components/NavPlusButton/NavPlusButton.tsx index 73ea57d..b5ea48f 100644 --- a/app/web/src/components/NavPlusButton/NavPlusButton.tsx +++ b/app/web/src/components/NavPlusButton/NavPlusButton.tsx @@ -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' } > -
      +
      {name}
      {sub}
      diff --git a/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx b/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx index ba6a615..f90ecf7 100644 --- a/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx +++ b/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx @@ -46,7 +46,8 @@ const ProfileSlashLogin = () => { + className="h-full w-full outline-none border-ch-gray-400 border-2 rounded-full" + > {!loading && ( { /> )} - { currentUser && ( - + {currentUser && ( +

      Hello {user?.name} @@ -65,14 +66,18 @@ const ProfileSlashLogin = () => {
      + to={routes.user({ userName: user?.userName })} + >
      View Your Profile
      - + Logout - + )} ) : ( diff --git a/app/web/src/components/ProjectCard/ProjectCard.tsx b/app/web/src/components/ProjectCard/ProjectCard.tsx index bb446e8..9f6fbd4 100644 --- a/app/web/src/components/ProjectCard/ProjectCard.tsx +++ b/app/web/src/components/ProjectCard/ProjectCard.tsx @@ -34,7 +34,7 @@ const ProjectCard = ({ title, mainImage, user, Reaction, cadPackage }) => (
      diff --git a/app/web/src/components/ProjectProfile/ProjectProfile.tsx b/app/web/src/components/ProjectProfile/ProjectProfile.tsx index b3d1d26..d015a09 100644 --- a/app/web/src/components/ProjectProfile/ProjectProfile.tsx +++ b/app/web/src/components/ProjectProfile/ProjectProfile.tsx @@ -257,4 +257,3 @@ const ProjectProfile = ({ } export default ProjectProfile - diff --git a/app/web/src/components/UserProfile/UserProfile.tsx b/app/web/src/components/UserProfile/UserProfile.tsx index 7b18bfe..9f97fdc 100644 --- a/app/web/src/components/UserProfile/UserProfile.tsx +++ b/app/web/src/components/UserProfile/UserProfile.tsx @@ -4,18 +4,24 @@ import { Link, navigate, routes } from '@redwoodjs/router' import ProjectsOfUser from 'src/components/ProjectsOfUserCell' import IdeHeader from 'src/components/IdeHeader/IdeHeader' import Svg from 'src/components/Svg/Svg' -import { fieldsConfig, fieldReducer, UserProfileType, FieldConfigType } from './userProfileConfig' +import { + fieldsConfig, + fieldReducer, + UserProfileType, + FieldConfigType, +} from './userProfileConfig' function buildFieldsConfig(fieldsConfig, user) { - Object.entries(fieldsConfig).forEach(([key, field] : [string, FieldConfigType]) => { - field.currentValue = field.newValue = user[key] - field.name = key - }) + Object.entries(fieldsConfig).forEach( + ([key, field]: [string, FieldConfigType]) => { + field.currentValue = field.newValue = user[key] + field.name = key + } + ) return fieldsConfig } - const UserProfile = ({ user, isEditable, @@ -23,11 +29,13 @@ const UserProfile = ({ onSave, error, projects, -} : UserProfileType) => { +}: UserProfileType) => { const { currentUser } = useAuth() const hasEditPermission = currentUser?.sub === user.id useEffect(() => { - isEditable && !hasEditPermission && navigate(routes.user({ userName: user.userName })) + isEditable && + !hasEditPermission && + navigate(routes.user({ userName: user.userName })) }, [currentUser]) const initializedFields = buildFieldsConfig(fieldsConfig, user) @@ -49,8 +57,7 @@ const UserProfile = ({ projectOwnerImage={user?.image} projectOwnerId={user?.id} > - - +
      @@ -63,7 +70,7 @@ const UserProfile = ({
      @@ -95,14 +102,14 @@ const UserProfile = ({ />

      - +
    {/* Viewer */}
    -

    Projects

    +

    + Projects +

    diff --git a/app/web/src/components/UserProfile/userProfileConfig.tsx b/app/web/src/components/UserProfile/userProfileConfig.tsx index a4c7574..666843b 100644 --- a/app/web/src/components/UserProfile/userProfileConfig.tsx +++ b/app/web/src/components/UserProfile/userProfileConfig.tsx @@ -5,149 +5,187 @@ import Editor from 'rich-markdown-editor' import ImageUploader from 'src/components/ImageUploader' import { User } from 'types/graphql' - export interface UserProfileType { - user: User, - isEditable: boolean, - loading: boolean, - error: boolean, - onSave: Function, - projects: {}[], + user: User + isEditable: 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, + name?: string // introspection ugh + editable: boolean + component?: ReactNode + needsRef?: boolean + isEditing?: boolean | undefined + onSave?: Function + currentValue?: any + newValue?: any } -const ProfileKeyValue = ({ field, dispatch, user, save, hasEditPermission, children, bottom = false }) => { +const ProfileKeyValue = ({ + field, + dispatch, + user, + save, + hasEditPermission, + children, + bottom = false, +}) => { + return ( + { + 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} + + ) +} + +const bioField: FieldConfigType = { + editable: true, + needsRef: true, + component: (props) => { + const ref = useRef(null) + + const { dispatch, field } = props + return ( - { - if (field.isEditing) { - save(user.userName, { [field.name]: field.newValue }) + +
    - { children } + onClick={(e) => + e?.target?.id === 'bio-wrap' && ref?.current?.focusAtEnd() + } + > + + dispatch({ + type: 'SET_NEW_VALUE', + payload: { field: field.bio, value: bio() }, + }) + } + /> +
    +
    + ) + }, +} + +const createdAtField: FieldConfigType = { + editable: false, + component: (props) => { + const { field } = props + + return ( + +

    + {new Date(field.currentValue).toLocaleDateString()} +

    ) - } + }, +} -const bioField : FieldConfigType = { - editable: true, - needsRef: true, - component: (props) => { - const ref = useRef(null) +const imageField: FieldConfigType = { + editable: true, + component: (props) => { + const { field, user, save, hasEditPermission } = props + return ( + { + 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 -
    + {!field.isEditing ? ( +

    {user?.name}

    + ) : ( + + dispatch({ + type: 'SET_NEW_VALUE', + payload: { field: field.name, value }, + }) } - onClick={(e) => - e?.target?.id === 'bio-wrap' && - ref?.current?.focusAtEnd() + isEditable={!field.isEditable} + /> + )} + + ) + }, +} + +const userNameField: FieldConfigType = { + editable: true, + component: (props) => { + const { dispatch, field } = props + + return ( + + {!field.isEditing ? ( +

    + @{field?.currentValue?.replace(/([^a-zA-Z\d_:])/g, '-')} +

    + ) : ( + + dispatch({ + type: 'SET_NEW_VALUE', + payload: { field: field.name, value }, + }) } - > - dispatch({ type: "SET_NEW_VALUE", payload: { field: field.bio, value: bio() }})} - /> -
    -
    - }, -} - -const createdAtField : FieldConfigType = { - editable: false, - component: (props) => { - const { field } = props - - return -

    { new Date(field.currentValue).toLocaleDateString() }

    -
    - }, -} - -const imageField : FieldConfigType = { - editable: true, - component: (props) => { - const { field, user, save, hasEditPermission } = props - return ( - { - 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 - { (!field.isEditing) - ?

    { user?.name }

    - : dispatch({ type: "SET_NEW_VALUE", payload: { field: field.name, value }})} - isEditable={!field.isEditable} - /> - } -
    - }, -} - -const userNameField : FieldConfigType = { - editable: true, - component: (props) => { - const { dispatch, field } = props - - return - { (!field.isEditing) - ?

    @{ field?.currentValue?.replace(/([^a-zA-Z\d_:])/g, '-') }

    - : dispatch({ type: "SET_NEW_VALUE", payload: { field: field.name, value }})} - isEditable={!field.isEditable} - /> - } -
    - }, + isEditable={!field.isEditable} + /> + )} + + ) + }, } export const fieldsConfig = { @@ -166,24 +204,27 @@ export const fieldsConfig = { export function fieldReducer(state, action) { switch (action.type) { - case "TOGGLE_EDITING": + case 'TOGGLE_EDITING': return { ...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 = { ...state, [action.payload.field]: { ...state[action.payload.field], newValue: action.payload.value, - } + }, } return newState default: return state } -} \ No newline at end of file +} diff --git a/app/web/src/layouts/MainLayout/MainLayout.js b/app/web/src/layouts/MainLayout/MainLayout.js index 1050c47..811eff0 100644 --- a/app/web/src/layouts/MainLayout/MainLayout.js +++ b/app/web/src/layouts/MainLayout/MainLayout.js @@ -135,7 +135,8 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => { + className="h-full w-full outline-none border-ch-gray-400 border-2 rounded-full" + > {!loading && ( { /> )} - { currentUser && ( - + {currentUser && ( +

    Hello {user?.name} @@ -154,14 +155,18 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
    + to={routes.user({ userName: user?.userName })} + >
    View Your Profile
    - + Logout - + )} diff --git a/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js b/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js index eb5a5b3..519e3bb 100644 --- a/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js +++ b/app/web/src/pages/AccountRecoveryPage/AccountRecoveryPage.js @@ -37,9 +37,7 @@ const AccountRecoveryPage = () => { className="grid items-center gap-2" style={{ gridTemplateColumns: 'auto 1fr' }} > - - email - + email { required: true, }} /> - - confirm - + confirm Date: Sun, 12 Sep 2021 13:11:12 -0400 Subject: [PATCH 07/12] Minor tweak to Sign In/Up in IDE --- app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx b/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx index f90ecf7..055eeed 100644 --- a/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx +++ b/app/web/src/components/ProfileSlashLogin/ProfileSlashLogin.tsx @@ -84,7 +84,7 @@ const ProfileSlashLogin = () => {
    Sign In/Up From 88326ed57352f9557d46bcfbfab03cbc48314732 Mon Sep 17 00:00:00 2001 From: Frank Johnson Date: Sun, 12 Sep 2021 13:40:47 -0400 Subject: [PATCH 08/12] Style tweaks to ImageUploader no-image state --- app/web/src/components/ImageUploader/ImageUploader.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/web/src/components/ImageUploader/ImageUploader.js b/app/web/src/components/ImageUploader/ImageUploader.js index 2980a2d..ce96622 100644 --- a/app/web/src/components/ImageUploader/ImageUploader.js +++ b/app/web/src/components/ImageUploader/ImageUploader.js @@ -98,11 +98,11 @@ export default function ImageUploader({ )} {!cloudinaryId && } {!cloudinaryId && isEditable && ( -
    -
    - Drop files here ... or{' '} - - +
    +
    + Drop files here or{' '} + + upload From fc6cded59e5ce3909a5a55d0c49a2238fad7f1b9 Mon Sep 17 00:00:00 2001 From: Frank Johnson Date: Sun, 12 Sep 2021 17:47:59 -0400 Subject: [PATCH 09/12] Merge issues trying to look at a stashed commit --- app/web/src/components/Hero/AssetWithGooey.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/web/src/components/Hero/AssetWithGooey.tsx b/app/web/src/components/Hero/AssetWithGooey.tsx index 8a45ce7..dc8f83f 100644 --- a/app/web/src/components/Hero/AssetWithGooey.tsx +++ b/app/web/src/components/Hero/AssetWithGooey.tsx @@ -43,8 +43,7 @@ export default function AssetWithGooey({ + ) } @@ -73,22 +73,22 @@ function Gooey() { const dist = Math.random() * 3 + 2.5 const x = randomSign(Math.random() * dist) const y = randomSign(Math.sqrt(dist * dist - x * x)) - const z = randomSign(Math.random() * 3) + const z = randomSign(Math.random() * 2) const position: [number, number, number] = [x, z, y] const size = Math.random() * 0.8 + 0.1 - const distort = Math.random() * 0.8 + 0.1 - const speed = (Math.random() * 0.8) / size / size + 0.1 + const distort = (size > .1) ? Math.random() * .6 * size + 0.2 : 0 + const speed = (size > .1) ? (Math.random() * 0.8) / size / size + 0.1 : 0 return { position, size, distort, speed } }) const secondSet = Array.from({ length: 5 }).map((_, index) => { const dist = Math.random() * 3 + 1.5 const x = randomSign(Math.random() * dist) const y = randomSign(Math.sqrt(dist * dist - x * x)) - const z = randomSign(Math.random() * 3) + const z = randomSign(Math.random() * 2) const position: [number, number, number] = [x, z, y] const size = Math.random() * 0.2 + 0.05 - const distort = Math.random() * 0.8 + 0.1 - const speed = (Math.random() * 0.5) / size / size + 0.1 + const distort = (size > .1) ? Math.random() * .8 * size + 0.2 : 0 + const speed = (size > .1) ? (Math.random() * 0.5) / size / size + 0.1 : 0 return { position, size, distort, speed } }) return [...firstSet, ...secondSet] From 9d9e3c49574d129a7c3ceee8d7161e8b9f16ea39 Mon Sep 17 00:00:00 2001 From: Kurt Hutten Date: Wed, 15 Sep 2021 05:38:08 +1000 Subject: [PATCH 10/12] Update homepage meta tag --- app/web/src/pages/HomePage/HomePage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/web/src/pages/HomePage/HomePage.tsx b/app/web/src/pages/HomePage/HomePage.tsx index e215150..710e89b 100644 --- a/app/web/src/pages/HomePage/HomePage.tsx +++ b/app/web/src/pages/HomePage/HomePage.tsx @@ -6,7 +6,7 @@ const ProjectsPage = () => { return ( Date: Wed, 15 Sep 2021 01:53:44 -0400 Subject: [PATCH 11/12] Fix up KeyValue component, fix save issue of Bio, simplify UserProfile --- app/web/src/components/KeyValue/KeyValue.tsx | 56 ++-- .../components/ProjectCard/ProjectCard.tsx | 4 +- .../ProjectProfile/ProjectProfile.tsx | 48 +-- .../components/UserProfile/UserProfile.tsx | 65 ++-- .../UserProfile/userProfileConfig.tsx | 310 +++++++++--------- app/web/src/pages/UserPage/UserPage.js | 2 + 6 files changed, 245 insertions(+), 240 deletions(-) diff --git a/app/web/src/components/KeyValue/KeyValue.tsx b/app/web/src/components/KeyValue/KeyValue.tsx index f05aeea..5d93378 100644 --- a/app/web/src/components/KeyValue/KeyValue.tsx +++ b/app/web/src/components/KeyValue/KeyValue.tsx @@ -1,27 +1,47 @@ import Svg from 'src/components/Svg/Svg' +interface EditToggleType { + hasPermissionToEdit: boolean + onEdit?: React.MouseEventHandler + isEditing?: boolean +} + +const EditToggle = ({ + onEdit = () => { console.error('Field declared editable without edit action.') }, + isEditing = false, +} : EditToggleType) => ( + (isEditing ? ( + + ) : ( + + )) +) + interface KeyValueType { keyName: string children: React.ReactNode - hide?: boolean - canEdit?: boolean - onEdit?: () => void - isEditable?: boolean bottom?: boolean className?: string + edit?: EditToggleType } const KeyValue = ({ keyName, children, - hide = false, - canEdit = false, - onEdit, - isEditable = false, bottom = false, className = '', + edit = { hasPermissionToEdit: false }, }: KeyValueType) => { - if (!children || hide) return null + if (!children) return null return (
    - {keyName} - {canEdit && - (isEditable ? ( - - ) : ( - - ))} + {keyName} + {edit && edit.hasPermissionToEdit && }
    {children} diff --git a/app/web/src/components/ProjectCard/ProjectCard.tsx b/app/web/src/components/ProjectCard/ProjectCard.tsx index 9f6fbd4..fa0f303 100644 --- a/app/web/src/components/ProjectCard/ProjectCard.tsx +++ b/app/web/src/components/ProjectCard/ProjectCard.tsx @@ -33,8 +33,8 @@ const ProjectCard = ({ title, mainImage, user, Reaction, cadPackage }) => (
    diff --git a/app/web/src/components/ProjectProfile/ProjectProfile.tsx b/app/web/src/components/ProjectProfile/ProjectProfile.tsx index d015a09..3d4c83b 100644 --- a/app/web/src/components/ProjectProfile/ProjectProfile.tsx +++ b/app/web/src/components/ProjectProfile/ProjectProfile.tsx @@ -27,7 +27,7 @@ const ProjectProfile = ({ onComment, }) => { const [comment, setComment] = useState('') - const [isEditable, setIsEditable] = useState(false) + const [isEditing, setIsEditing] = useState(false) const onCommentClear = () => { onComment(comment) setComment('') @@ -36,14 +36,14 @@ const ProjectProfile = ({ const [isReactionsModalOpen, setIsReactionsModalOpen] = useState(false) const { currentUser } = useAuth() const editorRef = useRef(null) - const canEdit = + const hasPermissionToEdit = currentUser?.sub === userProject.id || currentUser?.roles.includes('admin') const project = userProject?.Project const emotes = countEmotes(project?.Reaction) const userEmotes = project?.userReactions.map(({ emote }) => emote) useEffect(() => { - isEditable && - !canEdit && + isEditing && + !hasPermissionToEdit && navigate( routes.project({ userName: userProject.userName, @@ -55,7 +55,7 @@ const ProjectProfile = ({ const [newDescription, setNewDescription] = useState(project?.description) const onDescriptionChange = (description) => setNewDescription(description()) const onEditSaveClick = () => { - if (isEditable) { + if (isEditing) { onSave(project?.id, { description: newDescription }) return } @@ -108,26 +108,27 @@ const ProjectProfile = ({ className="px-3 py-2 rounded" />
    - { - if (!isEditable) { - setIsEditable(true) - } else { - onEditSaveClick() - setIsEditable(false) - } + edit={{ + hasPermissionToEdit, + isEditing, + onEdit: () => { + if (!isEditing) { + setIsEditing(true) + } else { + onEditSaveClick() + setIsEditing(false) + } + }, }} - isEditable={isEditable} >
    e?.target?.id === 'description-wrap' && @@ -137,11 +138,11 @@ const ProjectProfile = ({
    -
    + }
    {new Date(project?.createdAt).toDateString()} @@ -156,10 +157,11 @@ const ProjectProfile = ({ userEmotes={userEmotes} onEmote={onReaction} onShowProjectReactions={() => setIsReactionsModalOpen(true)} + className="" /> - - {!isEditable && ( + { currentUser && + {!isEditing && ( <> {currentUser && ( <> @@ -215,8 +217,8 @@ const ProjectProfile = ({ )} - - {canEdit && ( + } + {hasPermissionToEdit && ( <>

    Danger Zone