Add project forking #533

Merged
franknoirot merged 7 commits from frank/add-project-forking into main 2021-09-28 14:51:36 +02:00
17 changed files with 351 additions and 252 deletions

View File

@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "Project" ADD COLUMN "forkedFromId" TEXT;
-- AddForeignKey
ALTER TABLE "Project" ADD FOREIGN KEY ("forkedFromId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@@ -53,9 +53,12 @@ model Project {
deleted Boolean @default(false)
cadPackage CadPackage @default(openscad)
socialCard SocialCard?
forkedFromId String?
forkedFrom Project? @relation("Fork", fields: [forkedFromId], references: [id])
Comment Comment[]
Reaction ProjectReaction[]
childForks Project[] @relation("Fork")
Comment Comment[]
Reaction ProjectReaction[]
@@unique([title, userId])
}

View File

@@ -14,6 +14,9 @@ export const schema = gql`
socialCard: SocialCard
Comment: [Comment]!
Reaction(userId: String): [ProjectReaction]!
forkedFromId: String
forkedFrom: Project
childForks: [Project]!
}
enum CadPackage {
@@ -37,6 +40,11 @@ export const schema = gql`
cadPackage: CadPackage!
}
input ForkProjectInput {
userId: String!
forkedFromId: String
}
input UpdateProjectInput {
title: String
description: String
@@ -47,7 +55,7 @@ export const schema = gql`
type Mutation {
createProject(input: CreateProjectInput!): Project!
forkProject(input: CreateProjectInput!): Project!
forkProject(input: ForkProjectInput!): Project!
updateProject(id: String!, input: UpdateProjectInput!): Project!
updateProjectImages(
id: String!

View File

@@ -78,13 +78,27 @@ export const createProject = async ({ input }: CreateProjectArgs) => {
}
export const forkProject = async ({ input }) => {
// Only difference between create and fork project is that fork project will generate a unique title
// (for the user) if there is a conflict
requireAuth()
const projectData = await db.project.findUnique({
where: {
id: input.forkedFromId,
},
})
const isUniqueCallback = isUniqueProjectTitle(input.userId)
const title = await generateUniqueString(input.title, isUniqueCallback)
// TODO change the description to `forked from userName/projectName ${rest of description}`
let title = projectData.title
title = await generateUniqueString(title, isUniqueCallback)
const { code, description, cadPackage } = projectData
return db.project.create({
data: foreignKeyReplacement({ ...input, title }),
data: foreignKeyReplacement({
...input,
title,
code,
description,
cadPackage,
}),
})
}
@@ -252,6 +266,11 @@ export const deleteProject = async ({ id }: Prisma.ProjectWhereUniqueInput) => {
}
export const Project = {
forkedFrom: (_obj, { root }) =>
root.forkedFromId &&
db.project.findUnique({ where: { id: root.forkedFromId } }),
childForks: (_obj, { root }) =>
db.project.findMany({ where: { forkedFromId: root.id } }),
user: (_obj, { root }: ResolverArgs<ReturnType<typeof project>>) =>
db.project.findUnique({ where: { id: root.id } }).user(),
socialCard: (_obj, { root }: ResolverArgs<ReturnType<typeof project>>) =>

View File

@@ -1,17 +1,30 @@
import { useAuth } from '@redwoodjs/auth'
import { useMutation } from '@redwoodjs/web'
import { Popover } from '@headlessui/react'
import { Link, navigate, routes } from '@redwoodjs/router'
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'
import FullScriptEncoding from 'src/components/EncodedUrl/FullScriptEncoding'
import ExternalScript from 'src/components/EncodedUrl/ExternalScript'
import Svg from 'src/components/Svg/Svg'
import NavPlusButton from 'src/components/NavPlusButton'
import ProfileSlashLogin from 'src/components/ProfileSlashLogin'
import { toast } from '@redwoodjs/web/toast'
import CaptureButton from 'src/components/CaptureButton/CaptureButton'
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
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 FORK_PROJECT_MUTATION = gql`
mutation ForkProjectMutation($input: ForkProjectInput!) {
forkProject(input: $input) {
id
title
user {
id
userName
}
}
}
`
const TopButton = ({
onClick,
@@ -38,180 +51,170 @@ const TopButton = ({
)
}
interface IdeHeaderProps {
handleRender: () => void
projectTitle?: string
projectOwner?: string
projectOwnerId?: string
projectOwnerImage?: string
projectId?: string
children?: ReactNode
}
const IdeHeader = ({
export default function IdeHeader({
handleRender,
projectOwner,
projectTitle,
projectOwnerImage,
projectId,
projectOwnerId,
children,
}: IdeHeaderProps) => {
context,
}: {
handleRender?: () => void
context: 'ide' | 'profile'
}) {
const { currentUser } = useAuth()
const { project } = useIdeContext()
const isProfile = context === 'profile'
const canEdit =
(currentUser &&
currentUser?.sub === (project?.user?.id || projectOwnerId)) ||
currentUser?.roles.includes('admin')
const _projectId = projectId || project?.id
const _projectOwner = project?.user?.userName || projectOwner
(currentUser && currentUser?.sub === project?.user?.id) ||
currentUser?.roles?.includes('admin')
const projectOwner = project?.user?.userName
return (
<div className="h-16 w-full bg-ch-gray-900 flex justify-between items-center text-lg">
<div className="h-full text-gray-300 flex items-center">
<div className="w-14 h-16 flex items-center justify-center bg-ch-gray-900">
<Link to={routes.home()}>
<Svg className="w-12 p-0.5" name="favicon" />
</Link>
</div>
{_projectId && (
<>
<span className="bg-ch-gray-700 h-full grid grid-flow-col-dense items-center gap-2 px-4">
<Gravatar
image={project?.user?.image || projectOwnerImage}
className="w-10"
/>
<Link
to={routes.user({
userName: _projectOwner,
})}
>
{_projectOwner}
</Link>
</span>
<EditableProjectTitle
id={_projectId}
userName={_projectOwner}
projectTitle={project?.title || projectTitle}
canEdit={canEdit}
shouldRouteToIde={!projectTitle}
/>
</>
)}
</div>
<div className="text-gray-200 grid grid-flow-col-dense gap-4 mr-4 items-center">
{!children ? (
<DefaultTopButtons
project={project}
projectTitle={projectTitle}
_projectOwner={_projectOwner}
handleRender={handleRender}
canEdit={canEdit}
/>
) : (
children
)}
{/* <TopButton>Fork</TopButton> */}
<div className="h-8 w-8">
<NavPlusButton />
</div>
<ProfileSlashLogin />
</div>
</div>
)
}
const [createFork] = useMutation(FORK_PROJECT_MUTATION, {
onCompleted: ({ forkProject }) => {
const params = {
userName: forkProject?.user?.userName,
projectTitle: forkProject?.title,
}
navigate(!isProfile ? routes.ide(params) : routes.project(params))
},
})
const handleFork = () => {
const prom = createFork({
variables: {
input: {
userId: currentUser.sub,
forkedFromId: project?.id,
},
},
})
toast.promise(prom, {
loading: 'Forking...',
success: <b>Forked successfully!</b>,
error: <b>Problem forking.</b>,
})
}
export default IdeHeader
function DefaultTopButtons({
project,
projectTitle,
_projectOwner,
handleRender,
canEdit,
}) {
return (
<>
{canEdit && !projectTitle && (
<CaptureButton
canEdit={canEdit}
projectTitle={project?.title}
userName={project?.user?.userName}
shouldUpdateImage={!project?.mainImage}
TheButton={({ onClick }) => (
<div className="flex justify-between flex-grow h-full">
<div className="flex h-full items-center text-gray-300">
{project?.id && (
<>
<span className="bg-ch-gray-700 h-full grid grid-flow-col-dense items-center gap-2 px-4">
<Gravatar image={project?.user?.image} className="w-10" />
<Link
to={routes.user({
userName: projectOwner,
})}
>
{projectOwner}
</Link>
</span>
<EditableProjectTitle
id={project?.id}
userName={projectOwner}
projectTitle={project?.title}
canEdit={canEdit}
shouldRouteToIde={!isProfile}
/>
</>
)}
</div>
<div className="grid grid-flow-col-dense gap-4 items-center mr-4">
{canEdit && !isProfile && (
<CaptureButton
canEdit={canEdit}
projectTitle={project?.title}
userName={project?.user?.userName}
shouldUpdateImage={!project?.mainImage}
TheButton={({ onClick }) => (
<TopButton
onClick={onClick}
name="Save Project Image"
className=" bg-ch-blue-650 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
>
<Svg name="camera" className="w-6 h-6 text-ch-blue-400" />
</TopButton>
)}
/>
)}
{!isProfile && (
<TopButton
onClick={onClick}
name="Save Project Image"
className=" bg-ch-blue-650 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}
franknoirot commented 2021-09-28 00:23:45 +02:00 (Migrated from github.com)
Review

Navigate to either the IDE or the details views to match wherever you are now.

Navigate to either the IDE or the details views to match wherever you are now.
franknoirot commented 2021-09-28 00:25:37 +02:00 (Migrated from github.com)
Review

TODO regularize these params when the Header component gets regularized.

TODO regularize these params when the Header component gets regularized.
Irev-Dev commented 2021-09-28 12:32:39 +02:00 (Migrated from github.com)
Review

This seems a little brittle to me, as I would hope that the component would be able be able to know what page it's on without having to look at the route. What if someone names their project "idea" than it would match on "user/idea" I had a look at seeing what it would take to pass this information to the component instead and realised that it could be done but it would be adding more mess to the messy situation that is this component so I did a big refactor aimed at this branch.

I've put you as a review, but the diff is not super meaningful as it's really just huge chunks of code moved around 🤷

https://github.com/Irev-Dev/cadhub/pull/534

This seems a little brittle to me, as I would hope that the component would be able be able to know what page it's on without having to look at the route. What if someone names their project "idea" than it would match on "user/idea" I had a look at seeing what it would take to pass this information to the component instead and realised that it could be done but it would be adding more mess to the messy situation that is this component so I did a big refactor aimed at this branch. I've put you as a review, but the diff is not super meaningful as it's really just huge chunks of code moved around 🤷 https://github.com/Irev-Dev/cadhub/pull/534
name={canEdit ? 'Save' : 'Preview'}
>
<Svg name="camera" className="w-6 h-6 text-ch-blue-400" />
<Svg
name={canEdit ? 'floppy-disk' : 'photograph'}
className="w-6 h-6 text-ch-pink-500"
/>
</TopButton>
)}
/>
)}
{!projectTitle && (
<TopButton
className="bg-ch-pink-800 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
onClick={handleRender}
name={canEdit ? 'Save' : 'Preview'}
>
<Svg
name={canEdit ? 'floppy-disk' : 'photograph'}
className="w-6 h-6 text-ch-pink-500"
/>
</TopButton>
)}
{projectTitle && (
<TopButton
className="bg-ch-pink-800 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
onClick={() =>
navigate(routes.ide({ userName: _projectOwner, projectTitle }))
}
name="Editor"
>
<Svg name="terminal" className="w-6 h-6 text-ch-pink-500" />
</TopButton>
)}
<Popover className="relative outline-none w-full h-full">
{({ open }) => {
return (
<>
<Popover.Button className="h-full w-full outline-none">
<TopButton
Tag="div"
name="Share"
className=" bg-ch-purple-400 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
>
<Svg
name="share"
className="w-6 h-6 text-ch-purple-500 mt-1"
/>
</TopButton>
</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>
{isProfile && (
<TopButton
className="bg-ch-pink-800 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
onClick={() =>
navigate(
routes.ide({
userName: projectOwner,
projectTitle: project.title,
})
)
}
name="Editor"
>
<Svg name="terminal" className="w-6 h-6 text-ch-pink-500" />
</TopButton>
)}
<Popover className="relative outline-none w-full h-full">
{({ open }) => {
return (
<>
<Popover.Button className="h-full outline-none">
<TopButton
Tag="div"
name="Share"
className=" bg-ch-purple-400 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
>
<Svg
name="share"
className="w-6 h-6 text-ch-purple-500 mt-1"
/>
</TopButton>
</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">
<Tab className="p-3 px-5">encoded script</Tab>
<Tab className="p-3 px-5">external script</Tab>
</TabList>
</Tabs>
</Popover.Panel>
)}
</>
)
}}
</Popover>
<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">external script</Tab>
</TabList>
</Tabs>
</Popover.Panel>
)}
</>
)
}}
</Popover>
{currentUser?.sub && (
<TopButton
onClick={handleFork}
name="Fork"
className=" bg-ch-blue-650 bg-opacity-30 hover:bg-opacity-80 text-ch-gray-300"
>
<Svg name="fork-new" className="w-6 h-6 text-ch-blue-400" />
</TopButton>
)}
</div>
</div>
</>
)
}

View File

@@ -1,17 +1,17 @@
import { useEffect, useState } from 'react'
import { useState } from 'react'
import IdeContainer from 'src/components/IdeContainer/IdeContainer'
import { useRender } from './useRender'
import OutBound from 'src/components/OutBound/OutBound'
import IdeSideBar from 'src/components/IdeSideBar/IdeSideBar'
import IdeHeader from 'src/components/IdeHeader/IdeHeader'
import Svg from 'src/components/Svg/Svg'
import TopNav from 'src/components/TopNav/TopNav'
import { useIdeInit } from 'src/components/EncodedUrl/helpers'
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
import { useSaveCode } from 'src/components/IdeWrapper/useSaveCode'
import { ShortcutsModalContext } from 'src/components/EditorMenu/AllShortcutsModal'
import IdeHeader from 'src/components/IdeHeader/IdeHeader'
import type { CadPackageType } from 'src/components/CadPackage/CadPackage'
interface Props {
cadPackage: string
cadPackage: CadPackageType
}
const IdeWrapper = ({ cadPackage }: Props) => {
@@ -33,7 +33,9 @@ const IdeWrapper = ({ cadPackage }: Props) => {
<div className="h-full flex flex-col">
<ShortcutsModalContext.Provider value={shortcutModalContextValues}>
<nav className="flex">
<IdeHeader handleRender={onRender} />
<TopNav>
<IdeHeader handleRender={onRender} context="ide" />
</TopNav>
</nav>
<div className="h-full flex flex-grow bg-ch-gray-900">
<div className="flex-shrink-0">

View File

@@ -6,7 +6,14 @@ import { countEmotes } from 'src/helpers/emote'
import ImageUploader from 'src/components/ImageUploader'
import { ImageFallback } from '../ImageUploader/ImageUploader'
const ProjectCard = ({ title, mainImage, user, Reaction, cadPackage }) => (
const ProjectCard = ({
title,
mainImage,
user,
Reaction,
cadPackage,
childForks,
}) => (
<li
className="rounded p-1.5 bg-ch-gray-760 hover:bg-ch-gray-710 shadow-ch"
key={`${user?.userName}--${title}`}
@@ -45,7 +52,8 @@ const ProjectCard = ({ title, mainImage, user, Reaction, cadPackage }) => (
{countEmotes(Reaction).reduce((prev, { count }) => prev + count, 0)}
</div>
<div className="px-2 flex items-center bg-ch-blue-650 bg-opacity-30 text-ch-gray-300 rounded-sm">
<Svg name="fork-new" className="w-4 mr-2" />0
<Svg name="fork-new" className="w-4 mr-2" />
{childForks?.length || 0}
</div>
</div>
</Link>

View File

@@ -2,6 +2,8 @@ import { useMutation } from '@redwoodjs/web'
import { toast } from '@redwoodjs/web/toast'
import { navigate, routes } from '@redwoodjs/router'
import { useAuth } from '@redwoodjs/auth'
import { useIdeState } from 'src/helpers/hooks/useIdeState'
import { IdeContext } from 'src/helpers/hooks/useIdeContext'
import ProjectProfile from 'src/components/ProjectProfile/ProjectProfile'
import { QUERY as PROJECT_REACTION_QUERY } from 'src/components/ProjectReactionsCell'
@@ -26,8 +28,14 @@ export const QUERY = gql`
mainImage
createdAt
updatedAt
userId
cadPackage
forkedFrom {
id
title
user {
userName
}
}
Reaction {
emote
}
@@ -114,26 +122,20 @@ export const Empty = () => <div className="h-full">Empty</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Success = ({
userProject,
variables: { isEditable },
refetch,
}) => {
export const Success = ({ userProject, refetch }) => {
const { currentUser } = useAuth()
const [updateProject, { loading, error }] = useMutation(
UPDATE_PROJECT_MUTATION,
{
onCompleted: ({ updateProject }) => {
navigate(
routes.project({
userName: updateProject.user.userName,
projectTitle: updateProject.title,
})
)
toast.success('Project updated.')
},
}
)
const [state, thunkDispatch] = useIdeState()
const [updateProject] = useMutation(UPDATE_PROJECT_MUTATION, {
onCompleted: ({ updateProject }) => {
navigate(
routes.project({
userName: updateProject.user.userName,
projectTitle: updateProject.title,
})
)
toast.success('Project updated.')
},
})
const [createProject] = useMutation(CREATE_PROJECT_MUTATION, {
onCompleted: ({ createProject }) => {
navigate(
@@ -154,7 +156,7 @@ export const Success = ({
refetch()
}
const [deleteProject] = useMutation(DELETE_PROJECT_MUTATION, {
onCompleted: ({ deleteProject }) => {
onCompleted: () => {
navigate(routes.home())
toast.success('Project deleted.')
},
@@ -200,15 +202,27 @@ export const Success = ({
})
return (
<ProjectProfile
userProject={userProject}
onSave={onSave}
onDelete={onDelete}
loading={loading}
error={error}
isEditable={isEditable}
onReaction={onReaction}
onComment={onComment}
/>
<IdeContext.Provider
value={{
state,
thunkDispatch,
project: {
...userProject?.Project,
user: {
id: userProject.id,
image: userProject.image,
userName: userProject.userName,
},
},
}}
>
<ProjectProfile
userProject={userProject}
onSave={onSave}
onDelete={onDelete}
onReaction={onReaction}
onComment={onComment}
/>
</IdeContext.Provider>
)
}

View File

@@ -10,6 +10,7 @@ import Button from 'src/components/Button/Button'
import ProjectReactionsCell from '../ProjectReactionsCell'
import { countEmotes } from 'src/helpers/emote'
import { getActiveClasses } from 'get-active-classes'
import TopNav from 'src/components/TopNav/TopNav'
import IdeHeader from 'src/components/IdeHeader/IdeHeader'
import CadPackage from 'src/components/CadPackage/CadPackage'
import Gravatar from 'src/components/Gravatar/Gravatar'
@@ -38,6 +39,7 @@ const ProjectProfile = ({
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(() => {
@@ -49,7 +51,7 @@ const ProjectProfile = ({
projectTitle: project?.title,
})
)
}, [currentUser])
}, [currentUser, project?.title, userProject.userName])
useIdeInit(project?.cadPackage, project?.code, 'viewer')
const [newDescription, setNewDescription] = useState(project?.description)
const onDescriptionChange = (description) => setNewDescription(description())
@@ -69,14 +71,9 @@ const ProjectProfile = ({
<>
<div className="h-screen flex flex-col text-lg font-fira-sans">
<div className="flex">
<IdeHeader
handleRender={() => {}}
projectTitle={project?.title}
projectOwner={userProject?.userName}
projectOwnerImage={userProject?.image}
projectOwnerId={userProject?.id}
projectId={project?.id}
/>
<TopNav>
<IdeHeader context="profile" />
</TopNav>
</div>
<div className="relative flex-grow h-full">
<div className="grid grid-cols-1 md:auto-cols-preview-layout grid-flow-row-dense absolute inset-0 h-full">
@@ -145,6 +142,20 @@ const ProjectProfile = ({
<KeyValue keyName="Updated on">
{new Date(project?.updatedAt).toDateString()}
</KeyValue>
{project.forkedFrom && (
<KeyValue keyName="Forked from">
<Link
className="pink-link"
to={routes.project({
userName: project.forkedFrom.user.userName,
projectTitle: project.forkedFrom.title,
})}
>
{project.forkedFrom.title}
</Link>{' '}
by {project.forkedFrom.user.userName}
</KeyValue>
)}
</div>
<KeyValue keyName="Reactions">
<EmojiReaction

View File

@@ -40,7 +40,10 @@ const ProjectsList = ({
style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(16rem, 1fr))' }}
>
{filteredProjects.map(
({ title, mainImage, user, Reaction, cadPackage }, index) => (
(
{ title, mainImage, user, Reaction, cadPackage, childForks },
index
) => (
<ProjectCard
key={index}
title={title}
@@ -48,6 +51,7 @@ const ProjectsList = ({
user={user}
Reaction={Reaction}
cadPackage={cadPackage}
childForks={childForks}
/>
)
)}

View File

@@ -9,6 +9,9 @@ export const QUERY = gql`
title
cadPackage
mainImage
childForks {
id
}
createdAt
updatedAt
user {

View File

@@ -9,6 +9,9 @@ export const QUERY = gql`
title
mainImage
cadPackage
childForks {
id
}
createdAt
updatedAt
user {

View File

@@ -0,0 +1,31 @@
import { Link, routes } from '@redwoodjs/router'
import Svg from 'src/components/Svg/Svg'
import NavPlusButton from 'src/components/NavPlusButton'
import ProfileSlashLogin from 'src/components/ProfileSlashLogin'
import { ReactNode } from 'react'
interface IdeHeaderProps {
children?: ReactNode
}
const TopNav = ({ children }: IdeHeaderProps) => {
return (
<div className="h-16 w-full bg-ch-gray-900 flex justify-between items-center text-lg">
<div className="h-full text-gray-300 flex items-center">
<div className="w-14 h-16 flex items-center justify-center bg-ch-gray-900">
<Link to={routes.home()}>
<Svg className="w-12 p-0.5" name="favicon" />
</Link>
</div>
</div>
{children}
<div className="text-gray-200 grid grid-flow-col-dense gap-4 mr-4 items-center">
<div className="h-8 w-8">
<NavPlusButton />
</div>
<ProfileSlashLogin />
</div>
</div>
)
}
export default TopNav

View File

@@ -1,9 +1,8 @@
import { useEffect, useReducer } from 'react'
import { useAuth } from '@redwoodjs/auth'
import { Link, navigate, routes } from '@redwoodjs/router'
import { 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 TopNav from 'src/components/TopNav/TopNav'
import {
fieldComponents,
fieldReducer,
@@ -27,13 +26,7 @@ function buildFieldsConfig(fieldsConfig, user, hasPermissionToEdit) {
)
}
const UserProfile = ({
user,
isEditing,
loading,
onSave,
error,
}: UserProfileType) => {
const UserProfile = ({ user, isEditing, onSave }: UserProfileType) => {
const { currentUser } = useAuth()
const hasPermissionToEdit = currentUser?.sub === user.id
useEffect(() => {
@@ -60,14 +53,7 @@ const UserProfile = ({
<>
<div className="md:h-screen flex flex-col text-lg font-fira-sans">
<div className="flex">
<IdeHeader
handleRender={() => {}}
projectOwner={user?.userName}
projectOwnerImage={user?.image}
projectOwnerId={user?.id}
>
<span></span>
</IdeHeader>
<TopNav />
</div>
<div className="relative flex-grow h-full">
<div className="grid md:grid-cols-profile-layout grid-flow-row-dense absolute inset-0">

View File

@@ -52,6 +52,7 @@
.markdown-overrides div {
@apply text-ch-gray-300 bg-transparent;
}
.pink-link,
franknoirot commented 2021-09-28 00:29:12 +02:00 (Migrated from github.com)
Review

Made this a utility class and naming things is hard.

Made this a utility class and naming things is hard.
.markdown-overrides a,
.markdown-overrides div a {
@apply text-ch-pink-500 underline bg-transparent;

View File

@@ -4,6 +4,7 @@ import { Toaster } from '@redwoodjs/web/toast'
import { useIdeState } from 'src/helpers/hooks/useIdeState'
import type { Project } from 'src/components/IdeProjectCell/IdeProjectCell'
import { IdeContext } from 'src/helpers/hooks/useIdeContext'
import type { CadPackageType } from 'src/components/CadPackage/CadPackage'
interface Props {
cadPackage: string
@@ -21,7 +22,9 @@ const DevIdePage = ({ cadPackage, project }: Props) => {
/>
<Toaster timeout={9000} />
<IdeContext.Provider value={{ state, thunkDispatch, project }}>
<IdeWrapper cadPackage={cadPackage.toLocaleLowerCase()} />
<IdeWrapper
cadPackage={cadPackage.toLocaleLowerCase() as CadPackageType}
/>
</IdeContext.Provider>
</div>
)

View File

@@ -2,14 +2,11 @@ import { useAuth } from '@redwoodjs/auth'
import ProjectCell from 'src/components/ProjectCell'
import Seo from 'src/components/Seo/Seo'
import { useIdeState } from 'src/helpers/hooks/useIdeState'
import { IdeContext } from 'src/helpers/hooks/useIdeContext'
import { Toaster } from '@redwoodjs/web/toast'
import { makeSocialPublicId } from 'src/helpers/hooks/useUpdateProjectImages'
const ProjectPage = ({ userName, projectTitle }) => {
const { currentUser } = useAuth()
const [state, thunkDispatch] = useIdeState()
const socialImageUrl = `http://res.cloudinary.com/irevdev/image/upload/c_scale,w_1200/v1/CadHub/${makeSocialPublicId(
userName,
projectTitle
@@ -23,13 +20,11 @@ const ProjectPage = ({ userName, projectTitle }) => {
lang="en-US"
/>
<Toaster timeout={1500} />
<IdeContext.Provider value={{ state, thunkDispatch, project: null }}>
<ProjectCell
userName={userName}
projectTitle={projectTitle}
currentUserId={currentUser?.sub}
/>
</IdeContext.Provider>
<ProjectCell
userName={userName}
projectTitle={projectTitle}
currentUserId={currentUser?.sub}
/>
</>
)
}