Add project forking #533

Merged
franknoirot merged 7 commits from frank/add-project-forking into main 2021-09-28 14:51:36 +02:00
5 changed files with 79 additions and 9 deletions
Showing only changes of commit 02463db741 - Show all commits

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,26 @@ 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,
}),
})
}

View File

@@ -8,11 +8,24 @@ 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 { useMutation } from '@redwoodjs/web'
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
description
code
}
}
`
const TopButton = ({
onClick,
children,
@@ -111,7 +124,6 @@ const IdeHeader = ({
) : (
children
)}
{/* <TopButton>Fork</TopButton> */}
<div className="h-8 w-8">
<NavPlusButton />
</div>
@@ -130,6 +142,26 @@ function DefaultTopButtons({
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
canEdit,
}) {
const { currentUser } = useAuth()
const [createFork] = useMutation(FORK_PROJECT_MUTATION, {
onCompleted: () => {},
})
const handleFork = () => {
const prom = createFork({
variables: {
input: {
userId: currentUser.sub,
forkedFromId: project.id,
},
},
})
// toast.promise(prom, {
// loading: 'Saving Image/s',
// success: <b>Image/s saved!</b>,
// error: <b>Problem saving.</b>,
// })
}
return (
<>
{canEdit && !projectTitle && (
@@ -212,6 +244,15 @@ function DefaultTopButtons({
)
}}
</Popover>
{currentUser?.id && (
<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>
)}
</>
)
}