FEAT: Create basic model embed (#588)

* initial commit, issue with OpenSCAD embed viewing

* initial implementation

* Fix openscad size bug

* Add overlays to embed

* Remove console.log and reuse exact query

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
This commit was merged in pull request #588.
This commit is contained in:
Frank Noirot
2022-01-11 01:30:15 -05:00
committed by GitHub
parent e7dec57644
commit 82dd3d2555
16 changed files with 284 additions and 44 deletions

View File

@@ -56,6 +56,7 @@ const Routes = () => {
<Route path="/u/{userName}" page={UserPage} name="user" />
<Route path="/u/{userName}/{projectTitle}" page={ProjectPage} name="project" />
<Route path="/u/{userName}/{projectTitle}/ide" page={IdeProjectPage} name="ide" />
<Route path="/u/{userName}/{projectTitle}/embed" page={EmbedProjectPage} name="embed" />
<Route path="/u/{userName}/{projectTitle}/social-card" page={SocialCardPage} name="socialCard" />
<Private unauthenticated="home" role="admin">

View File

@@ -0,0 +1,31 @@
import Seo from 'src/components/Seo/Seo'
import IdeViewer from 'src/components/IdeViewer/IdeViewer'
import { useIdeState } from 'src/helpers/hooks/useIdeState'
import type { Project } from 'src/components/EmbedProjectCell/EmbedProjectCell'
import { IdeContext } from 'src/helpers/hooks/useIdeContext'
import { use3dViewerResize } from 'src/helpers/hooks/use3dViewerResize'
import { useEffect } from 'react'
interface Props {
project?: Project
}
const EmbedProject = ({ project }: Props) => {
const [state, thunkDispatch] = useIdeState()
const { viewerDomRef, handleViewerSizeUpdate } = use3dViewerResize()
useEffect(() => {
handleViewerSizeUpdate()
}, [])
return (
<div className="flex flex-col h-screen" ref={viewerDomRef} >
<IdeContext.Provider value={{ state, thunkDispatch, project }}>
<IdeViewer />
</IdeContext.Provider>
</div>
)
}
export default EmbedProject

View File

@@ -0,0 +1,6 @@
// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
ideProject: {
id: 42,
},
})

View File

@@ -0,0 +1,16 @@
import { Loading, Empty, Success } from './EmbedProjectCell'
import { standard } from './EmbedProjectCell.mock'
export const loading = () => {
return Loading ? <Loading /> : null
}
export const empty = () => {
return Empty ? <Empty /> : null
}
export const success = () => {
return Success ? <Success {...standard()} /> : null
}
export default { title: 'Cells/IdeProjectCell' }

View File

@@ -0,0 +1,21 @@
import { render, screen } from '@redwoodjs/testing'
import { Loading, Empty, Success } from './EmbedProjectCell'
import { standard } from './EmbedProjectCell.mock'
describe('IdeProjectCell', () => {
test('Loading renders successfully', () => {
render(<Loading />)
// Use screen.debug() to see output
expect(screen.getByText('Loading...')).toBeInTheDocument()
})
test('Empty renders successfully', async () => {
render(<Empty />)
expect(screen.getByText('Empty')).toBeInTheDocument()
})
test('Success renders successfully', async () => {
render(<Success ideProject={standard().ideProject} />)
expect(screen.getByText(/42/i)).toBeInTheDocument()
})
})

View File

@@ -0,0 +1,47 @@
import { useIdeState } from 'src/helpers/hooks/useIdeState'
import { IdeContext } from 'src/helpers/hooks/useIdeContext'
import EmbedViewer from '../EmbedViewer/EmbedViewer'
import { QUERY as IdeQuery } from 'src/components/IdeProjectCell'
export const QUERY = IdeQuery
export interface Project {
id: string
title: string
code: string
description: string
mainImage: string
createdAt: string
cadPackage: 'openscad' | 'cadquery'
user: {
id: string
userName: string
image: string
}
}
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Project not found</div>
interface SaveCodeArgs {
input: any
id: string
isFork: boolean
}
export const Success = ({
project,
refetch,
}: {
project: Project
refetch: any
}) => {
const [state, thunkDispatch] = useIdeState()
return (
<IdeContext.Provider value={{ state, thunkDispatch, project }}>
<EmbedViewer project={project} />
</IdeContext.Provider>
)
}

View File

@@ -0,0 +1,34 @@
import { useIdeInit } from 'src/components/EncodedUrl/helpers'
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
import IdeViewer from 'src/components/IdeViewer/IdeViewer'
import { use3dViewerResize } from 'src/helpers/hooks/use3dViewerResize'
import CadPackage from '../CadPackage/CadPackage'
import LogoType from '../LogoType/LogoType'
import { Link, routes } from '@redwoodjs/router'
function EmbedViewer() {
const { state, project } = useIdeContext()
useIdeInit(project?.cadPackage, project?.code || state?.code, "viewer")
const { viewerDomRef } = use3dViewerResize()
return (
<div className="relative flex flex-col h-screen group" ref={viewerDomRef}>
<IdeViewer isMinimal={true} />
<div className="absolute top-5 left-5 text-ch-gray-300">
<h1 className="mb-4 text-4xl font-normal capitalize ">
{project?.title.replace(/-/g, ' ')}
</h1>
<h2 className="mb-2 transition-opacity duration-100 group-hover:opacity-0">by @{ project?.user?.userName }</h2>
<h2 className="transition-opacity duration-100 group-hover:opacity-0">built with <div className="inline-block"><CadPackage cadPackage={project?.cadPackage} className="px-3 py-2"/></div></h2>
</div>
<div className="absolute grid items-center grid-flow-col-dense gap-2 bottom-5 right-5 text-ch-gray-300">
View on <Link className="inline-block" to={routes.project({
userName: project?.user?.userName,
projectTitle: project?.title.toString(),
})}><LogoType className="inline-block" wrappedInLink={true}/></Link>
</div>
</div>
)
}
export default EmbedViewer

View File

@@ -4,8 +4,10 @@ import { PureIdeViewer } from './PureIdeViewer'
const IdeViewer = ({
handleOwnCamera = false,
isMinimal = false,
}: {
handleOwnCamera?: boolean
handleOwnCamera?: boolean,
isMinimal?: boolean,
}) => {
const { state, thunkDispatch } = useIdeContext()
const dataType = state.objectData?.type
@@ -41,7 +43,7 @@ const IdeViewer = ({
}
})
}
return (
<PureIdeViewer
dataType={dataType}
@@ -51,6 +53,7 @@ const IdeViewer = ({
isLoading={state.isLoading}
camera={state?.camera}
ideType={ideType}
isMinimal={isMinimal}
/>
)
}

View File

@@ -212,7 +212,7 @@ export function PureIdeViewer({
alt="code-cad preview"
id="special"
src={URL.createObjectURL(image)}
className="h-full w-full"
className="w-full h-full"
/>
</div>
)}

View File

@@ -0,0 +1,43 @@
import Tooltip from '@material-ui/core/Tooltip'
import { Link, routes } from '@redwoodjs/router'
import Svg from 'src/components/Svg'
export default function LogoType({ className="", wrappedInLink=false }) {
return (
<ul className={"flex items-center " + className}>
<li>
{ (wrappedInLink
? <Link to={routes.home()}>
<div className="ml-2 overflow-hidden rounded-full">
<Svg className="w-10" name="favicon" />
</div>
</Link>
: <div>
<div className="ml-2 overflow-hidden rounded-full">
<Svg className="w-10" name="favicon" />
</div>
</div>
)}
</li>
<li>
<Tooltip title="Very alpha, there's lots of work todo">
<div className="flex ml-4">
{/* Because of how specific these styles are to this heading/logo and it doesn't need to be replicated else where as well as it's very precise with the placement of "pre-alpha" I think it's appropriate. */}
<h2
className="py-1 text-2xl text-indigo-300 md:text-5xl font-ropa-sans md:tracking-wider"
style={{ letterSpacing: '0.3em' }}
>
CadHub
</h2>
<div
className="hidden text-sm font-bold text-pink-400 font-ropa-sans md:block"
style={{ paddingBottom: '2rem', marginLeft: '-1.8rem' }}
>
pre-alpha
</div>
</div>
</Tooltip>
</li>
</ul>
)
}

View File

@@ -53,6 +53,7 @@ const ProjectProfile = ({
})
)
}, [currentUser, project?.title, userProject.userName])
console.log('from ProjectProfile', { cadPackage: project.cadPackage, code: project.code })
useIdeInit(project?.cadPackage, project?.code, 'viewer')
const [newDescription, setNewDescription] = useState(project?.description)
const onDescriptionChange = (description) => setNewDescription(description())

View File

@@ -2,7 +2,6 @@ import { useState, useEffect } from 'react'
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 '@headlessui/react'
import { getActiveClasses } from 'get-active-classes'
import Footer from 'src/components/Footer'
@@ -12,11 +11,11 @@ import NavPlusButton from 'src/components/NavPlusButton'
import ReactGA from 'react-ga'
import { isBrowser } from '@redwoodjs/prerender/browserUtils'
import Svg from 'src/components/Svg'
import { ImageFallback } from 'src/components/ImageUploader'
import useUser from 'src/helpers/hooks/useUser'
import './MainLayout.css'
import RecentProjectsCell from 'src/components/RecentProjectsCell'
import LogoType from 'src/components/LogoType'
let previousSubmission = ''
@@ -72,39 +71,12 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
}, [hash, client])
return (
<div
className="h-full flex flex-col ch-scrollbar overflow-y-scroll overflow-x-hidden"
className="flex flex-col h-full overflow-x-hidden overflow-y-scroll ch-scrollbar"
style={{ perspective: '1px', perspectiveOrigin: 'top center' }}
>
<header id="cadhub-main-header">
<nav className="flex justify-between h-16 sm:px-4 bg-ch-gray-900">
<ul className="flex items-center">
<li>
<Link to={routes.home()}>
<div className="rounded-full overflow-hidden ml-2">
<Svg className="w-10" name="favicon" />
</div>
</Link>
</li>
<li>
<Tooltip title="Very alpha, there's lots of work todo">
<div className="ml-4 flex">
{/* Because of how specific these styles are to this heading/logo and it doesn't need to be replicated else where as well as it's very precise with the placement of "pre-alpha" I think it's appropriate. */}
<h2
className="text-indigo-300 text-2xl md:text-5xl font-ropa-sans py-1 md:tracking-wider"
style={{ letterSpacing: '0.3em' }}
>
CadHub
</h2>
<div
className="text-pink-400 text-sm font-bold font-ropa-sans hidden md:block"
style={{ paddingBottom: '2rem', marginLeft: '-1.8rem' }}
>
pre-alpha
</div>
</div>
</Tooltip>
</li>
</ul>
<LogoType />
<ul className="flex items-center">
<li
className={getActiveClasses(
@@ -114,29 +86,29 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
<NavPlusButton />
</li>
{isAuthenticated ? (
<li className="h-10 w-10">
<Popover className="relative outline-none w-full h-full">
<li className="w-10 h-10">
<Popover className="relative w-full h-full outline-none">
<Popover.Button
disabled={!isAuthenticated || !currentUser}
className="h-full w-full outline-none border-ch-gray-400 border-2 rounded-full"
className="w-full h-full border-2 rounded-full outline-none border-ch-gray-400"
>
{!loading && (
<ImageFallback
width={80}
className="rounded-full object-cover"
className="object-cover rounded-full"
imageId={user?.image}
/>
)}
</Popover.Button>
{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="absolute right-0 z-10 w-48 px-3 py-2 mt-4 overflow-hidden rounded shadow-md bg-ch-gray-700 text-ch-gray-300">
<Link to={routes.user({ userName: user?.userName })}>
<p className="my-2 text-ch-blue-400 font-fira-code leading-4 text-sm">
<p className="my-2 text-sm leading-4 text-ch-blue-400 font-fira-code">
Hello {user?.name}
</p>
</Link>
<Link
className="my-2 block hover:text-ch-pink-300"
className="block my-2 hover:text-ch-pink-300"
to={routes.user({ userName: user?.userName })}
>
<div>View Your Profile</div>
@@ -149,7 +121,7 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
Logout
</a>
<hr className="my-4" />
<p className="text-ch-blue-400 font-fira-code leading-4 text-sm">
<p className="text-sm leading-4 text-ch-blue-400 font-fira-code">
Recent Projects
</p>
<RecentProjectsCell userName={user?.userName} />
@@ -161,7 +133,7 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
<li>
<a
href="#"
className="text-ch-gray-300 mr-1 sm:mr-2 px-2 sm:px-4 py-2 border-2 border-ch-gray-400 rounded-full hover:bg-ch-gray-600"
className="px-2 py-2 mr-1 border-2 rounded-full text-ch-gray-300 sm:mr-2 sm:px-4 border-ch-gray-400 hover:bg-ch-gray-600"
onClick={recordedLogin}
>
Sign In/Up

View File

@@ -0,0 +1,11 @@
import { render } from '@redwoodjs/testing'
import EmbedProjectPage from './EmbedProjectPage'
describe('EmbedProjectPage', () => {
it('renders successfully', () => {
expect(() => {
render(<EmbedProjectPage />)
}).not.toThrow()
})
})

View File

@@ -0,0 +1,11 @@
import EmbedProjectCell from 'src/components/EmbedProjectCell'
const EmbedProjectPage = ({ userName, projectTitle }) => {
return (
<>
<EmbedProjectCell userName={userName} projectTitle={projectTitle} />
</>
)
}
export default EmbedProjectPage