From 82dd3d25550139e5ad73cd2c81845ec6e36d75f8 Mon Sep 17 00:00:00 2001 From: Frank Noirot Date: Tue, 11 Jan 2022 01:30:15 -0500 Subject: [PATCH] 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 --- app/api/db/schema.prisma | 2 +- app/web/src/Routes.js | 1 + .../components/EmbedProject/EmbedProject.tsx | 31 +++++++++++ .../EmbedProjectCell/EmbedProjectCell.mock.ts | 6 +++ .../EmbedProjectCell.stories.tsx | 16 ++++++ .../EmbedProjectCell.test.tsx | 21 ++++++++ .../EmbedProjectCell/EmbedProjectCell.tsx | 47 +++++++++++++++++ .../components/EmbedViewer/EmbedViewer.tsx | 34 ++++++++++++ .../src/components/IdeViewer/IdeViewer.tsx | 7 ++- .../components/IdeViewer/PureIdeViewer.tsx | 2 +- app/web/src/components/LogoType/LogoType.js | 43 +++++++++++++++ .../ProjectProfile/ProjectProfile.tsx | 1 + app/web/src/layouts/MainLayout/MainLayout.js | 52 +++++-------------- .../EmbedProjectPage.test.tsx | 11 ++++ .../EmbedProjectPage/EmbedProjectPage.tsx | 11 ++++ docs/blog/2021-08-14-ux-studies-timeline.mdx | 43 +++++++++++++++ 16 files changed, 284 insertions(+), 44 deletions(-) create mode 100644 app/web/src/components/EmbedProject/EmbedProject.tsx create mode 100644 app/web/src/components/EmbedProjectCell/EmbedProjectCell.mock.ts create mode 100644 app/web/src/components/EmbedProjectCell/EmbedProjectCell.stories.tsx create mode 100644 app/web/src/components/EmbedProjectCell/EmbedProjectCell.test.tsx create mode 100644 app/web/src/components/EmbedProjectCell/EmbedProjectCell.tsx create mode 100644 app/web/src/components/EmbedViewer/EmbedViewer.tsx create mode 100644 app/web/src/components/LogoType/LogoType.js create mode 100644 app/web/src/pages/EmbedProjectPage/EmbedProjectPage.test.tsx create mode 100644 app/web/src/pages/EmbedProjectPage/EmbedProjectPage.tsx create mode 100644 docs/blog/2021-08-14-ux-studies-timeline.mdx diff --git a/app/api/db/schema.prisma b/app/api/db/schema.prisma index e62813d..9b02179 100644 --- a/app/api/db/schema.prisma +++ b/app/api/db/schema.prisma @@ -5,7 +5,7 @@ datasource db { generator client { provider = "prisma-client-js" - binaryTargets = "native" + binaryTargets = ["native", "darwin-arm64", "darwin"] } // sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode diff --git a/app/web/src/Routes.js b/app/web/src/Routes.js index a2f053b..58578bc 100644 --- a/app/web/src/Routes.js +++ b/app/web/src/Routes.js @@ -56,6 +56,7 @@ const Routes = () => { + diff --git a/app/web/src/components/EmbedProject/EmbedProject.tsx b/app/web/src/components/EmbedProject/EmbedProject.tsx new file mode 100644 index 0000000..a8ef33c --- /dev/null +++ b/app/web/src/components/EmbedProject/EmbedProject.tsx @@ -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 ( +
+ + + +
+ ) +} + +export default EmbedProject diff --git a/app/web/src/components/EmbedProjectCell/EmbedProjectCell.mock.ts b/app/web/src/components/EmbedProjectCell/EmbedProjectCell.mock.ts new file mode 100644 index 0000000..2e297e0 --- /dev/null +++ b/app/web/src/components/EmbedProjectCell/EmbedProjectCell.mock.ts @@ -0,0 +1,6 @@ +// Define your own mock data here: +export const standard = (/* vars, { ctx, req } */) => ({ + ideProject: { + id: 42, + }, +}) diff --git a/app/web/src/components/EmbedProjectCell/EmbedProjectCell.stories.tsx b/app/web/src/components/EmbedProjectCell/EmbedProjectCell.stories.tsx new file mode 100644 index 0000000..77b02cf --- /dev/null +++ b/app/web/src/components/EmbedProjectCell/EmbedProjectCell.stories.tsx @@ -0,0 +1,16 @@ +import { Loading, Empty, Success } from './EmbedProjectCell' +import { standard } from './EmbedProjectCell.mock' + +export const loading = () => { + return Loading ? : null +} + +export const empty = () => { + return Empty ? : null +} + +export const success = () => { + return Success ? : null +} + +export default { title: 'Cells/IdeProjectCell' } diff --git a/app/web/src/components/EmbedProjectCell/EmbedProjectCell.test.tsx b/app/web/src/components/EmbedProjectCell/EmbedProjectCell.test.tsx new file mode 100644 index 0000000..9e770d4 --- /dev/null +++ b/app/web/src/components/EmbedProjectCell/EmbedProjectCell.test.tsx @@ -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() + // Use screen.debug() to see output + expect(screen.getByText('Loading...')).toBeInTheDocument() + }) + + test('Empty renders successfully', async () => { + render() + expect(screen.getByText('Empty')).toBeInTheDocument() + }) + + test('Success renders successfully', async () => { + render() + expect(screen.getByText(/42/i)).toBeInTheDocument() + }) +}) diff --git a/app/web/src/components/EmbedProjectCell/EmbedProjectCell.tsx b/app/web/src/components/EmbedProjectCell/EmbedProjectCell.tsx new file mode 100644 index 0000000..d55e518 --- /dev/null +++ b/app/web/src/components/EmbedProjectCell/EmbedProjectCell.tsx @@ -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 = () =>
Loading...
+ +export const Empty = () =>
Project not found
+ +interface SaveCodeArgs { + input: any + id: string + isFork: boolean +} + +export const Success = ({ + project, + refetch, +}: { + project: Project + refetch: any +}) => { + const [state, thunkDispatch] = useIdeState() + + + return ( + + + + ) +} diff --git a/app/web/src/components/EmbedViewer/EmbedViewer.tsx b/app/web/src/components/EmbedViewer/EmbedViewer.tsx new file mode 100644 index 0000000..77d3ed8 --- /dev/null +++ b/app/web/src/components/EmbedViewer/EmbedViewer.tsx @@ -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 ( +
+ +
+

+ {project?.title.replace(/-/g, ' ')} +

+

by @{ project?.user?.userName }

+

built with

+
+
+ View on +
+
+ ) +} + +export default EmbedViewer diff --git a/app/web/src/components/IdeViewer/IdeViewer.tsx b/app/web/src/components/IdeViewer/IdeViewer.tsx index d1045c3..a5d2ce0 100644 --- a/app/web/src/components/IdeViewer/IdeViewer.tsx +++ b/app/web/src/components/IdeViewer/IdeViewer.tsx @@ -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 ( ) } diff --git a/app/web/src/components/IdeViewer/PureIdeViewer.tsx b/app/web/src/components/IdeViewer/PureIdeViewer.tsx index 33f5e46..8b0f56f 100644 --- a/app/web/src/components/IdeViewer/PureIdeViewer.tsx +++ b/app/web/src/components/IdeViewer/PureIdeViewer.tsx @@ -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" /> )} diff --git a/app/web/src/components/LogoType/LogoType.js b/app/web/src/components/LogoType/LogoType.js new file mode 100644 index 0000000..450ac4f --- /dev/null +++ b/app/web/src/components/LogoType/LogoType.js @@ -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 ( +
    +
  • + { (wrappedInLink + ? +
    + +
    + + :
    +
    + +
    +
    + )} +
  • +
  • + +
    + {/* 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. */} +

    + CadHub +

    +
    + pre-alpha +
    +
    +
    +
  • +
+ ) +} \ No newline at end of file diff --git a/app/web/src/components/ProjectProfile/ProjectProfile.tsx b/app/web/src/components/ProjectProfile/ProjectProfile.tsx index 6c893f3..65019c0 100644 --- a/app/web/src/components/ProjectProfile/ProjectProfile.tsx +++ b/app/web/src/components/ProjectProfile/ProjectProfile.tsx @@ -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()) diff --git a/app/web/src/layouts/MainLayout/MainLayout.js b/app/web/src/layouts/MainLayout/MainLayout.js index fd17a0f..7061590 100644 --- a/app/web/src/layouts/MainLayout/MainLayout.js +++ b/app/web/src/layouts/MainLayout/MainLayout.js @@ -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 (