Put social media save popover into editor tab (#541)
and make them live
This commit was merged in pull request #541.
This commit is contained in:
@@ -1,14 +1,15 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { toast } from '@redwoodjs/web/toast'
|
import { toast } from '@redwoodjs/web/toast'
|
||||||
import Popover from '@material-ui/core/Popover'
|
import { toJpeg } from 'html-to-image'
|
||||||
import Svg from 'src/components/Svg/Svg'
|
|
||||||
import Button from 'src/components/Button/Button'
|
|
||||||
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||||
import { canvasToBlob, blobTo64 } from 'src/helpers/canvasToBlob'
|
import { canvasToBlob, blobTo64 } from 'src/helpers/canvasToBlob'
|
||||||
import { useUpdateProjectImages } from 'src/helpers/hooks/useUpdateProjectImages'
|
import { useUpdateProjectImages } from 'src/helpers/hooks/useUpdateProjectImages'
|
||||||
|
import { requestRenderStateless } from 'src/helpers/hooks/useIdeState'
|
||||||
|
import { PureIdeViewer } from 'src/components/IdeViewer/IdeViewer'
|
||||||
import SocialCardCell from 'src/components/SocialCardCell/SocialCardCell'
|
import SocialCardCell from 'src/components/SocialCardCell/SocialCardCell'
|
||||||
import { toJpeg } from 'html-to-image'
|
|
||||||
|
export const captureSize = { width: 500, height: 522 }
|
||||||
|
|
||||||
const anchorOrigin = {
|
const anchorOrigin = {
|
||||||
vertical: 'bottom',
|
vertical: 'bottom',
|
||||||
@@ -19,175 +20,247 @@ const transformOrigin = {
|
|||||||
horizontal: 'center',
|
horizontal: 'center',
|
||||||
}
|
}
|
||||||
|
|
||||||
const CaptureButton = ({
|
export const CaptureButtonViewer = ({
|
||||||
canEdit,
|
onInit,
|
||||||
TheButton,
|
onScadImage,
|
||||||
shouldUpdateImage,
|
canvasRatio = 1,
|
||||||
projectTitle,
|
}: {
|
||||||
userName,
|
onInit: (a: any) => void
|
||||||
|
onScadImage: (a: any) => void
|
||||||
|
canvasRatio: number
|
||||||
}) => {
|
}) => {
|
||||||
const [captureState, setCaptureState] = useState<any>({})
|
const { state } = useIdeContext()
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
const threeInstance = React.useRef(null)
|
||||||
const [whichPopup, setWhichPopup] = useState(null)
|
const [dataType, dataTypeSetter] = useState(state?.objectData?.type)
|
||||||
const { state, project } = useIdeContext()
|
const [artifact, artifactSetter] = useState(state?.objectData?.data)
|
||||||
const ref = React.useRef<HTMLDivElement>(null)
|
const [isLoading, isLoadingSetter] = useState(false)
|
||||||
const { updateProjectImages } = useUpdateProjectImages({})
|
const getThreeInstance = (_threeInstance) => {
|
||||||
|
threeInstance.current = _threeInstance
|
||||||
const onCapture = async () => {
|
onInit(_threeInstance)
|
||||||
const threeInstance = state.threeInstance
|
|
||||||
const isOpenScadImage = state?.objectData?.type === 'png'
|
|
||||||
let imgBlob
|
|
||||||
let image64
|
|
||||||
if (!isOpenScadImage) {
|
|
||||||
imgBlob = canvasToBlob(threeInstance, { width: 500, height: 375 })
|
|
||||||
image64 = blobTo64(
|
|
||||||
await canvasToBlob(threeInstance, { width: 500, height: 522 })
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
imgBlob = state.objectData.data
|
|
||||||
image64 = blobTo64(state.objectData.data)
|
|
||||||
}
|
}
|
||||||
const config = {
|
const onCameraChange = (camera) => {
|
||||||
image: await imgBlob,
|
const renderPromise =
|
||||||
currImage: project?.mainImage,
|
state.ideType === 'openscad' &&
|
||||||
imageObjectURL: window.URL.createObjectURL(await imgBlob),
|
requestRenderStateless({
|
||||||
callback: uploadAndUpdateImage,
|
state,
|
||||||
cloudinaryImgURL: '',
|
camera,
|
||||||
updated: false,
|
viewerSize: {
|
||||||
image64: await image64,
|
width: threeInstance.current.size.width * canvasRatio,
|
||||||
}
|
height: threeInstance.current.size.height * canvasRatio,
|
||||||
setCaptureState(config)
|
|
||||||
|
|
||||||
async function uploadAndUpdateImage() {
|
|
||||||
const upload = async () => {
|
|
||||||
const socialCard64 = toJpeg(ref.current, {
|
|
||||||
cacheBust: true,
|
|
||||||
quality: 0.7,
|
|
||||||
})
|
|
||||||
|
|
||||||
// uploading in two separate mutations because of the 100kb limit of the lambda functions
|
|
||||||
const imageUploadPromise1 = updateProjectImages({
|
|
||||||
variables: {
|
|
||||||
id: project?.id,
|
|
||||||
mainImage64: await config.image64,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const imageUploadPromise2 = updateProjectImages({
|
if (!renderPromise) {
|
||||||
variables: {
|
return
|
||||||
id: project?.id,
|
}
|
||||||
socialCard64: await socialCard64,
|
isLoadingSetter(true)
|
||||||
},
|
renderPromise.then(async ({ objectData }) => {
|
||||||
|
isLoadingSetter(false)
|
||||||
|
dataTypeSetter(objectData?.type)
|
||||||
|
artifactSetter(objectData?.data)
|
||||||
|
if (objectData?.type === 'png') {
|
||||||
|
onScadImage(await blobTo64(objectData?.data))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return Promise.all([imageUploadPromise2, imageUploadPromise1])
|
|
||||||
}
|
|
||||||
const promise = upload()
|
|
||||||
toast.promise(promise, {
|
|
||||||
loading: 'Saving Image/s',
|
|
||||||
success: <b>Image/s saved!</b>,
|
|
||||||
error: <b>Problem saving.</b>,
|
|
||||||
})
|
|
||||||
const [{ data }] = await promise
|
|
||||||
return data?.updateProjectImages?.mainImage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there isn't a screenshot saved yet, just go ahead and save right away
|
|
||||||
if (shouldUpdateImage) {
|
|
||||||
config.cloudinaryImgURL = await uploadAndUpdateImage()
|
|
||||||
config.updated = true
|
|
||||||
setCaptureState(config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClick = ({ event, whichPopup }) => {
|
|
||||||
setAnchorEl(event.currentTarget)
|
|
||||||
setWhichPopup(whichPopup)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setAnchorEl(null)
|
|
||||||
setWhichPopup(null)
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<PureIdeViewer
|
||||||
{canEdit && (
|
scadRatio={canvasRatio}
|
||||||
<div>
|
dataType={dataType}
|
||||||
<TheButton
|
artifact={artifact}
|
||||||
onClick={async (event) => {
|
onInit={getThreeInstance}
|
||||||
handleClick({ event, whichPopup: 'capture' })
|
onCameraChange={onCameraChange}
|
||||||
onCapture()
|
isLoading={isLoading}
|
||||||
}}
|
isMinimal
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabContent() {
|
||||||
|
return (
|
||||||
|
<div className="bg-ch-gray-800 h-full overflow-y-auto px-8 pb-16">
|
||||||
|
<IsolatedCanvas
|
||||||
|
size={{ width: 500, height: 375 }}
|
||||||
|
uploadKey="mainImage64"
|
||||||
|
RenderComponent={ThumbnailViewer}
|
||||||
|
/>
|
||||||
|
<IsolatedCanvas
|
||||||
|
canvasRatio={2}
|
||||||
|
size={captureSize}
|
||||||
|
uploadKey="socialCard64"
|
||||||
|
RenderComponent={SocialCardLiveViewer}
|
||||||
/>
|
/>
|
||||||
<Popover
|
|
||||||
id={'capture-popover'}
|
|
||||||
open={whichPopup === 'capture'}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
onClose={handleClose}
|
|
||||||
anchorOrigin={anchorOrigin}
|
|
||||||
transformOrigin={transformOrigin}
|
|
||||||
className="material-ui-overrides transform translate-y-4"
|
|
||||||
>
|
|
||||||
<div className="text-sm p-4 text-gray-500">
|
|
||||||
{!captureState ? (
|
|
||||||
'Loading...'
|
|
||||||
) : (
|
|
||||||
<div className="">
|
|
||||||
<div className="text-lg">Thumbnail</div>
|
|
||||||
<div
|
|
||||||
className="rounded"
|
|
||||||
style={{ width: 'fit-content', overflow: 'hidden' }}
|
|
||||||
>
|
|
||||||
<img src={captureState.imageObjectURL} className="w-32" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
)}
|
}
|
||||||
<div className="text-lg mt-4">Social Media Card</div>
|
|
||||||
<div className="rounded-lg shadow-md overflow-hidden">
|
function SocialCardLiveViewer({
|
||||||
|
forwardRef,
|
||||||
|
onUpload,
|
||||||
|
children,
|
||||||
|
partSnapShot64,
|
||||||
|
}) {
|
||||||
|
const { project } = useIdeContext()
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h3 className="text-2xl text-ch-gray-300 pt-4">Set social Image</h3>
|
||||||
|
<div className="flex py-4">
|
||||||
|
<div className="rounded-md shadow-ch border border-gray-400 overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="transform scale-50 origin-top-left"
|
className="transform scale-50 origin-top-left"
|
||||||
style={{ width: '600px', height: '315px' }}
|
style={{ width: '600px', height: '315px' }}
|
||||||
>
|
>
|
||||||
<div style={{ width: '1200px', height: '630px' }} ref={ref}>
|
<div style={{ width: '1200px', height: '630px' }} ref={forwardRef}>
|
||||||
<SocialCardCell
|
<SocialCardCell
|
||||||
userName={userName}
|
userName={project.user.userName}
|
||||||
projectTitle={projectTitle}
|
projectTitle={project.title}
|
||||||
image64={captureState.image64}
|
image64={partSnapShot64}
|
||||||
|
LiveProjectViewer={() => children}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 text-indigo-800">
|
</div>
|
||||||
{captureState.currImage && !captureState.updated ? (
|
<button className="bg-gray-200 p-2 rounded-sm" onClick={onUpload}>
|
||||||
<Button
|
save image
|
||||||
iconName="refresh"
|
</button>
|
||||||
className="shadow-md hover:shadow-lg border-indigo-600 border-2 border-opacity-0 hover:border-opacity-100 bg-indigo-200 text-indigo-100 text-opacity-100 bg-opacity-80"
|
</>
|
||||||
shouldAnimateHover
|
)
|
||||||
onClick={async () => {
|
}
|
||||||
const cloudinaryImg = await captureState.callback()
|
|
||||||
setCaptureState({
|
function ThumbnailViewer({ forwardRef, onUpload, children, partSnapShot64 }) {
|
||||||
...captureState,
|
return (
|
||||||
currImage: cloudinaryImg,
|
<>
|
||||||
updated: true,
|
<h3 className="text-2xl text-ch-gray-300 pt-4">Set thumbnail</h3>
|
||||||
|
<div
|
||||||
|
style={{ width: '500px', height: '375px' }}
|
||||||
|
className="rounded-md shadow-ch border border-gray-400 overflow-hidden my-4"
|
||||||
|
>
|
||||||
|
<div className="h-full w-full relative" ref={forwardRef}>
|
||||||
|
{children}
|
||||||
|
{partSnapShot64 && (
|
||||||
|
<img src={partSnapShot64} className="absolute inset-0" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button className="bg-gray-200 p-2 rounded-sm" onClick={onUpload}>
|
||||||
|
save thumbnail
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsolatedCanvas({
|
||||||
|
RenderComponent,
|
||||||
|
canvasRatio = 1,
|
||||||
|
size,
|
||||||
|
uploadKey,
|
||||||
|
}: {
|
||||||
|
canvasRatio?: number
|
||||||
|
uploadKey: 'socialCard64' | 'mainImage64'
|
||||||
|
size: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}
|
||||||
|
RenderComponent: React.FC<{
|
||||||
|
forwardRef: React.Ref<any>
|
||||||
|
children: React.ReactNode
|
||||||
|
partSnapShot64: string
|
||||||
|
onUpload: (a: any) => void
|
||||||
|
}>
|
||||||
|
}) {
|
||||||
|
const { project } = useIdeContext()
|
||||||
|
const { updateProjectImages } = useUpdateProjectImages({})
|
||||||
|
const [partSnapShot64, partSnapShot64Setter] = React.useState('')
|
||||||
|
const [scadSnapShot64, scadSnapShot64Setter] = React.useState('')
|
||||||
|
|
||||||
|
const captureRef = React.useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const threeInstance = React.useRef(null)
|
||||||
|
const onInit = (_threeInstance) => (threeInstance.current = _threeInstance)
|
||||||
|
const upload = async () => {
|
||||||
|
const uploadPromise = new Promise((resolve, reject) => {
|
||||||
|
const asyncHelper = async () => {
|
||||||
|
if (!scadSnapShot64) {
|
||||||
|
partSnapShot64Setter(
|
||||||
|
await blobTo64(await canvasToBlob(threeInstance.current, size))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
partSnapShot64Setter(scadSnapShot64)
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
const capturedImage = await toJpeg(captureRef.current, {
|
||||||
|
cacheBust: true,
|
||||||
|
quality: 0.7,
|
||||||
})
|
})
|
||||||
|
await updateProjectImages({
|
||||||
|
variables: {
|
||||||
|
id: project?.id,
|
||||||
|
[uploadKey]: capturedImage,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
partSnapShot64Setter('')
|
||||||
|
resolve(capturedImage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
asyncHelper()
|
||||||
|
})
|
||||||
|
toast.promise(uploadPromise, {
|
||||||
|
loading: 'Saving Image',
|
||||||
|
success: (finalImg: string) => (
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<b className="py-2">Image saved!</b>
|
||||||
|
<img src={finalImg} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
error: <b>Problem saving.</b>,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<RenderComponent
|
||||||
|
forwardRef={captureRef}
|
||||||
|
onUpload={upload}
|
||||||
|
partSnapShot64={partSnapShot64}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: `${size.width * canvasRatio}px`,
|
||||||
|
height: `${size.height * canvasRatio}px`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Update Project Images
|
<CaptureButtonViewer
|
||||||
</Button>
|
onInit={onInit}
|
||||||
) : (
|
onScadImage={scadSnapShot64Setter}
|
||||||
<div className="flex justify-center mb-4">
|
canvasRatio={canvasRatio}
|
||||||
<Svg
|
/>
|
||||||
name="checkmark"
|
|
||||||
className="mr-2 w-6 text-indigo-600"
|
|
||||||
/>{' '}
|
|
||||||
Project Images Updated
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</RenderComponent>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CaptureButton
|
export default function CaptureButton({ TheButton }) {
|
||||||
|
const { state, thunkDispatch } = useIdeContext()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TheButton
|
||||||
|
onClick={() => {
|
||||||
|
thunkDispatch({
|
||||||
|
type: 'addEditorModel',
|
||||||
|
payload: {
|
||||||
|
type: 'component',
|
||||||
|
label: 'Social Media Card',
|
||||||
|
Component: TabContent,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
thunkDispatch({
|
||||||
|
type: 'switchEditorModel',
|
||||||
|
payload: state.editorTabs.length,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const EditorMenu = () => {
|
|||||||
})
|
})
|
||||||
thunkDispatch({
|
thunkDispatch({
|
||||||
type: 'switchEditorModel',
|
type: 'switchEditorModel',
|
||||||
payload: state.models.length,
|
payload: state.editorTabs.length,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -74,12 +74,8 @@ export const makeStlDownloadHandler =
|
|||||||
requestRender({
|
requestRender({
|
||||||
state,
|
state,
|
||||||
dispatch,
|
dispatch,
|
||||||
code: state.code,
|
|
||||||
viewerSize: state.viewerSize,
|
|
||||||
camera: state.camera,
|
|
||||||
quality: 'high',
|
quality: 'high',
|
||||||
specialCadProcess,
|
specialCadProcess,
|
||||||
parameters: state.currentParameters,
|
|
||||||
}).then(
|
}).then(
|
||||||
(result) => result && saveFile(makeStlBlobFromGeo(result.data))
|
(result) => result && saveFile(makeStlBlobFromGeo(result.data))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import PanelToolbar from 'src/components/PanelToolbar/PanelToolbar'
|
|||||||
import { use3dViewerResize } from 'src/helpers/hooks/use3dViewerResize'
|
import { use3dViewerResize } from 'src/helpers/hooks/use3dViewerResize'
|
||||||
|
|
||||||
const IdeEditor = lazy(() => import('src/components/IdeEditor/IdeEditor'))
|
const IdeEditor = lazy(() => import('src/components/IdeEditor/IdeEditor'))
|
||||||
const IdeViewer = lazy(() => import('src/components/IdeViewer/IdeViewer'))
|
import IdeViewer from 'src/components/IdeViewer/IdeViewer'
|
||||||
|
|
||||||
const SmallLoadingPing = (
|
const SmallLoadingPing = (
|
||||||
<div className="bg-ch-gray-800 text-gray-200 font-ropa-sans relative w-full h-full flex justify-center items-center">
|
<div className="bg-ch-gray-800 text-gray-200 font-ropa-sans relative w-full h-full flex justify-center items-center">
|
||||||
@@ -33,11 +33,7 @@ const ELEMENT_MAP = {
|
|||||||
<IdeEditor Loading={SmallLoadingPing} />
|
<IdeEditor Loading={SmallLoadingPing} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
),
|
),
|
||||||
Viewer: (
|
Viewer: <IdeViewer />,
|
||||||
<Suspense fallback={BigLoadingPing}>
|
|
||||||
<IdeViewer Loading={BigLoadingPing} />
|
|
||||||
</Suspense>
|
|
||||||
),
|
|
||||||
Console: <IdeConsole />,
|
Console: <IdeConsole />,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,24 +60,21 @@ const IdeEditor = ({ Loading }) => {
|
|||||||
requestRender({
|
requestRender({
|
||||||
state,
|
state,
|
||||||
dispatch,
|
dispatch,
|
||||||
code: state.code,
|
|
||||||
viewerSize: state.viewerSize,
|
|
||||||
camera: state.camera,
|
|
||||||
parameters: state.currentParameters,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
localStorage.setItem(makeCodeStoreKey(state.ideType), state.code)
|
localStorage.setItem(makeCodeStoreKey(state.ideType), state.code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const currentTab = state.editorTabs[state.currentModel]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
||||||
className="h-full"
|
className="h-full"
|
||||||
onKeyDown={handleSaveHotkey}
|
onKeyDown={handleSaveHotkey}
|
||||||
>
|
>
|
||||||
{state.models.length > 1 && (
|
{state.editorTabs.length > 1 && (
|
||||||
<fieldset className="bg-ch-gray-700 text-ch-gray-300 flex m-0 p-0">
|
<fieldset className="bg-ch-gray-700 text-ch-gray-300 flex m-0 p-0">
|
||||||
{state.models.map((model, i) => (
|
{state.editorTabs.map((model, i) => (
|
||||||
<label
|
<label
|
||||||
key={model.type + '-' + i}
|
key={model.type + '-' + i}
|
||||||
className={
|
className={
|
||||||
@@ -117,7 +114,7 @@ const IdeEditor = ({ Loading }) => {
|
|||||||
))}
|
))}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
)}
|
)}
|
||||||
{state.models[state.currentModel].type === 'code' ? (
|
{currentTab.type === 'code' && (
|
||||||
<Editor
|
<Editor
|
||||||
defaultValue={state.code}
|
defaultValue={state.code}
|
||||||
value={state.code}
|
value={state.code}
|
||||||
@@ -128,11 +125,13 @@ const IdeEditor = ({ Loading }) => {
|
|||||||
language={ideTypeToLanguageMap[state.ideType] || 'cpp'}
|
language={ideTypeToLanguageMap[state.ideType] || 'cpp'}
|
||||||
onChange={handleCodeChange}
|
onChange={handleCodeChange}
|
||||||
/>
|
/>
|
||||||
) : (
|
)}
|
||||||
|
{currentTab.type === 'guide' && (
|
||||||
<div className="bg-ch-gray-800 h-full">
|
<div className="bg-ch-gray-800 h-full">
|
||||||
<EditorGuide content={state.models[state.currentModel].content} />
|
<EditorGuide content={currentTab.content} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{currentTab.type === 'component' && <currentTab.Component />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import CaptureButton from 'src/components/CaptureButton/CaptureButton'
|
|||||||
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||||
import Gravatar from 'src/components/Gravatar/Gravatar'
|
import Gravatar from 'src/components/Gravatar/Gravatar'
|
||||||
import EditableProjectTitle from 'src/components/EditableProjecTitle/EditableProjecTitle'
|
import EditableProjectTitle from 'src/components/EditableProjecTitle/EditableProjecTitle'
|
||||||
|
import SocialCardModal from 'src/components/SocialCardModal/SocialCardModal'
|
||||||
|
|
||||||
const FORK_PROJECT_MUTATION = gql`
|
const FORK_PROJECT_MUTATION = gql`
|
||||||
mutation ForkProjectMutation($input: ForkProjectInput!) {
|
mutation ForkProjectMutation($input: ForkProjectInput!) {
|
||||||
@@ -121,10 +122,6 @@ export default function IdeHeader({
|
|||||||
<div className="grid grid-flow-col-dense gap-4 items-center mr-4">
|
<div className="grid grid-flow-col-dense gap-4 items-center mr-4">
|
||||||
{canEdit && !isProfile && (
|
{canEdit && !isProfile && (
|
||||||
<CaptureButton
|
<CaptureButton
|
||||||
canEdit={canEdit}
|
|
||||||
projectTitle={project?.title}
|
|
||||||
userName={project?.user?.userName}
|
|
||||||
shouldUpdateImage={!project?.mainImage}
|
|
||||||
TheButton={({ onClick }) => (
|
TheButton={({ onClick }) => (
|
||||||
<TopButton
|
<TopButton
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { useRef, useState, useEffect } from 'react'
|
import { useRef, useState, useEffect, Suspense } from 'react'
|
||||||
import { Canvas, useThree } from '@react-three/fiber'
|
import { Canvas, useThree } from '@react-three/fiber'
|
||||||
import {
|
import {
|
||||||
PerspectiveCamera,
|
PerspectiveCamera,
|
||||||
GizmoHelper,
|
GizmoHelper,
|
||||||
GizmoViewport,
|
GizmoViewport,
|
||||||
OrbitControls,
|
OrbitControls,
|
||||||
Environment,
|
|
||||||
useTexture,
|
useTexture,
|
||||||
} from '@react-three/drei'
|
} from '@react-three/drei'
|
||||||
import { useEdgeSplit } from 'src/helpers/hooks/useEdgeSplit'
|
import { useEdgeSplit } from 'src/helpers/hooks/useEdgeSplit'
|
||||||
@@ -16,6 +15,7 @@ import { requestRender } from 'src/helpers/hooks/useIdeState'
|
|||||||
import texture from './dullFrontLitMetal.png'
|
import texture from './dullFrontLitMetal.png'
|
||||||
import Customizer from 'src/components/Customizer/Customizer'
|
import Customizer from 'src/components/Customizer/Customizer'
|
||||||
import DelayedPingAnimation from 'src/components/DelayedPingAnimation/DelayedPingAnimation'
|
import DelayedPingAnimation from 'src/components/DelayedPingAnimation/DelayedPingAnimation'
|
||||||
|
import type { ArtifactTypes } from 'src/helpers/cadPackages/common'
|
||||||
|
|
||||||
const thresholdAngle = 12
|
const thresholdAngle = 12
|
||||||
|
|
||||||
@@ -35,13 +35,13 @@ function Asset({ geometry: incomingGeo }) {
|
|||||||
<group dispose={null}>
|
<group dispose={null}>
|
||||||
<mesh ref={mesh} scale={[1, 1, 1]} geometry={incomingGeo}>
|
<mesh ref={mesh} scale={[1, 1, 1]} geometry={incomingGeo}>
|
||||||
<meshPhysicalMaterial
|
<meshPhysicalMaterial
|
||||||
envMapIntensity={2}
|
envMapIntensity={0.1}
|
||||||
color="#F472B6"
|
color="#F472B6"
|
||||||
map={colorMap}
|
map={colorMap}
|
||||||
clearcoat={0.1}
|
clearcoat={0.1}
|
||||||
clearcoatRoughness={0.2}
|
clearcoatRoughness={0.2}
|
||||||
roughness={10}
|
roughness={10}
|
||||||
metalness={0.9}
|
metalness={0.7}
|
||||||
smoothShading
|
smoothShading
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
@@ -148,42 +148,53 @@ function Sphere(props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const IdeViewer = ({ Loading }) => {
|
export function PureIdeViewer({
|
||||||
const { state, thunkDispatch } = useIdeContext()
|
dataType,
|
||||||
|
artifact,
|
||||||
|
onInit,
|
||||||
|
onCameraChange,
|
||||||
|
isLoading,
|
||||||
|
isMinimal = false,
|
||||||
|
scadRatio = 1,
|
||||||
|
}: {
|
||||||
|
dataType: 'INIT' | ArtifactTypes
|
||||||
|
artifact: any
|
||||||
|
isLoading: boolean
|
||||||
|
onInit: Function
|
||||||
|
onCameraChange: Function
|
||||||
|
isMinimal?: boolean
|
||||||
|
scadRatio?: number
|
||||||
|
}) {
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
const [image, setImage] = useState()
|
const [image, setImage] = useState()
|
||||||
|
|
||||||
const onInit = (threeInstance) => {
|
|
||||||
thunkDispatch({ type: 'setThreeInstance', payload: threeInstance })
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setImage(state.objectData?.type === 'png' && state.objectData?.data)
|
setImage(dataType === 'png' && artifact)
|
||||||
setIsDragging(false)
|
setIsDragging(false)
|
||||||
}, [state.objectData?.type, state.objectData?.data])
|
}, [dataType, artifact])
|
||||||
const PrimitiveArray = React.useMemo(
|
const PrimitiveArray = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
state.objectData?.type === 'primitive-array' && state.objectData?.data,
|
dataType === 'primitive-array' && artifact?.map((mesh) => mesh.clone()),
|
||||||
[state.objectData?.type, state.objectData?.data]
|
[dataType, artifact]
|
||||||
)
|
)
|
||||||
|
|
||||||
// the following are tailwind colors in hex, can't use these classes to color three.js meshes.
|
// the following are tailwind colors in hex, can't use these classes to color three.js meshes.
|
||||||
const pink400 = '#F472B6'
|
const pink400 = '#F472B6'
|
||||||
const indigo300 = '#A5B4FC'
|
const indigo300 = '#A5B4FC'
|
||||||
const indigo900 = '#312E81'
|
const indigo900 = '#312E81'
|
||||||
const jscadLightIntensity =
|
const jscadLightIntensity = PrimitiveArray ? 0.5 : 1.1
|
||||||
state.objectData?.type === 'geometry' &&
|
|
||||||
state.objectData?.data &&
|
|
||||||
state.objectData?.data.length
|
|
||||||
? 0.5
|
|
||||||
: 1.2
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full bg-ch-gray-800">
|
<div className="relative h-full bg-ch-gray-800 cursor-grab">
|
||||||
{image && (
|
{image && (
|
||||||
<div
|
<div
|
||||||
className={`absolute inset-0 transition-opacity duration-500 ${
|
className={`absolute inset-0 transition-opacity duration-500 ${
|
||||||
isDragging ? 'opacity-25' : 'opacity-100'
|
isDragging ? 'opacity-25' : 'opacity-100'
|
||||||
}`}
|
}`}
|
||||||
|
style={{
|
||||||
|
transform: `translate(${
|
||||||
|
scadRatio !== 1 ? '-250px, -261px' : '0px, 0px'
|
||||||
|
})`,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="code-cad preview"
|
alt="code-cad preview"
|
||||||
@@ -195,7 +206,7 @@ const IdeViewer = ({ Loading }) => {
|
|||||||
)}
|
)}
|
||||||
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
||||||
className={`opacity-0 absolute inset-0 transition-opacity duration-500 ${
|
className={`opacity-0 absolute inset-0 transition-opacity duration-500 ${
|
||||||
!(isDragging || state.objectData?.type !== 'png')
|
!(isDragging || dataType !== 'png')
|
||||||
? 'hover:opacity-50'
|
? 'hover:opacity-50'
|
||||||
: 'opacity-100'
|
: 'opacity-100'
|
||||||
}`}
|
}`}
|
||||||
@@ -205,26 +216,7 @@ const IdeViewer = ({ Loading }) => {
|
|||||||
<Controls
|
<Controls
|
||||||
onDragStart={() => setIsDragging(true)}
|
onDragStart={() => setIsDragging(true)}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
onCameraChange={(camera) => {
|
onCameraChange={onCameraChange}
|
||||||
thunkDispatch({
|
|
||||||
type: 'updateCamera',
|
|
||||||
payload: { camera },
|
|
||||||
})
|
|
||||||
thunkDispatch((dispatch, getState) => {
|
|
||||||
const state = getState()
|
|
||||||
if (['png', 'INIT'].includes(state.objectData?.type)) {
|
|
||||||
dispatch({ type: 'setLoading' })
|
|
||||||
requestRender({
|
|
||||||
state,
|
|
||||||
dispatch,
|
|
||||||
code: state.code,
|
|
||||||
viewerSize: state.viewerSize,
|
|
||||||
camera,
|
|
||||||
parameters: state.currentParameters,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<PerspectiveCamera makeDefault up={[0, 0, 1]}>
|
<PerspectiveCamera makeDefault up={[0, 0, 1]}>
|
||||||
<pointLight
|
<pointLight
|
||||||
@@ -232,17 +224,16 @@ const IdeViewer = ({ Loading }) => {
|
|||||||
intensity={jscadLightIntensity}
|
intensity={jscadLightIntensity}
|
||||||
/>
|
/>
|
||||||
</PerspectiveCamera>
|
</PerspectiveCamera>
|
||||||
<ambientLight intensity={0.3} />
|
<ambientLight intensity={2 * jscadLightIntensity} />
|
||||||
<Environment preset="warehouse" />
|
|
||||||
<pointLight
|
<pointLight
|
||||||
position={[-1000, -1000, -1000]}
|
position={[-1000, -1000, -1000]}
|
||||||
color="#5555FF"
|
color="#5555FF"
|
||||||
intensity={0.5}
|
intensity={1 * jscadLightIntensity}
|
||||||
/>
|
/>
|
||||||
<pointLight
|
<pointLight
|
||||||
position={[-1000, 0, 1000]}
|
position={[-1000, 0, 1000]}
|
||||||
color="#5555FF"
|
color="#5555FF"
|
||||||
intensity={0.5}
|
intensity={1 * jscadLightIntensity}
|
||||||
/>
|
/>
|
||||||
<gridHelper
|
<gridHelper
|
||||||
args={[200, 20, 0xff5555, 0x555555]}
|
args={[200, 20, 0xff5555, 0x555555]}
|
||||||
@@ -250,13 +241,15 @@ const IdeViewer = ({ Loading }) => {
|
|||||||
material-transparent
|
material-transparent
|
||||||
rotation-x={Math.PI / 2}
|
rotation-x={Math.PI / 2}
|
||||||
/>
|
/>
|
||||||
|
{!isMinimal && (
|
||||||
<GizmoHelper alignment={'top-left'} margin={[80, 80]}>
|
<GizmoHelper alignment={'top-left'} margin={[80, 80]}>
|
||||||
<GizmoViewport
|
<GizmoViewport
|
||||||
axisColors={['red', 'green', 'blue']}
|
axisColors={['red', 'green', 'blue']}
|
||||||
labelColor="black"
|
labelColor="black"
|
||||||
/>
|
/>
|
||||||
</GizmoHelper>
|
</GizmoHelper>
|
||||||
{state.objectData?.type === 'png' && (
|
)}
|
||||||
|
{dataType === 'png' && (
|
||||||
<>
|
<>
|
||||||
<Sphere position={[0, 0, 0]} color={pink400} />
|
<Sphere position={[0, 0, 0]} color={pink400} />
|
||||||
<Box position={[0, 50, 0]} size={[1, 100, 1]} color={indigo900} />
|
<Box position={[0, 50, 0]} size={[1, 100, 1]} color={indigo900} />
|
||||||
@@ -264,8 +257,10 @@ const IdeViewer = ({ Loading }) => {
|
|||||||
<Box position={[50, 0, 0]} size={[100, 1, 1]} color={pink400} />
|
<Box position={[50, 0, 0]} size={[100, 1, 1]} color={pink400} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{state.objectData?.type === 'geometry' && state.objectData?.data && (
|
{dataType === 'geometry' && artifact && (
|
||||||
<Asset geometry={state.objectData?.data} />
|
<Suspense fallback={null}>
|
||||||
|
<Asset geometry={artifact} />
|
||||||
|
</Suspense>
|
||||||
)}
|
)}
|
||||||
{PrimitiveArray &&
|
{PrimitiveArray &&
|
||||||
PrimitiveArray.map((mesh, index) => (
|
PrimitiveArray.map((mesh, index) => (
|
||||||
@@ -273,10 +268,55 @@ const IdeViewer = ({ Loading }) => {
|
|||||||
))}
|
))}
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</div>
|
</div>
|
||||||
<DelayedPingAnimation isLoading={state.isLoading} />
|
<DelayedPingAnimation isLoading={isLoading} />
|
||||||
<Customizer />
|
{!isMinimal && <Customizer />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const IdeViewer = ({
|
||||||
|
handleOwnCamera = false,
|
||||||
|
}: {
|
||||||
|
handleOwnCamera?: boolean
|
||||||
|
}) => {
|
||||||
|
const { state, thunkDispatch } = useIdeContext()
|
||||||
|
const dataType = state.objectData?.type
|
||||||
|
const artifact = state.objectData?.data
|
||||||
|
|
||||||
|
const onInit = (threeInstance) => {
|
||||||
|
thunkDispatch({ type: 'setThreeInstance', payload: threeInstance })
|
||||||
|
}
|
||||||
|
const onCameraChange = (camera) => {
|
||||||
|
if (handleOwnCamera) {
|
||||||
|
console.log('yo')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
thunkDispatch({
|
||||||
|
type: 'updateCamera',
|
||||||
|
payload: { camera },
|
||||||
|
})
|
||||||
|
thunkDispatch((dispatch, getState) => {
|
||||||
|
const state = getState()
|
||||||
|
if (['png', 'INIT'].includes(state?.objectData?.type)) {
|
||||||
|
dispatch({ type: 'setLoading' })
|
||||||
|
requestRender({
|
||||||
|
state,
|
||||||
|
dispatch,
|
||||||
|
camera,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PureIdeViewer
|
||||||
|
dataType={dataType}
|
||||||
|
artifact={artifact}
|
||||||
|
onInit={onInit}
|
||||||
|
onCameraChange={onCameraChange}
|
||||||
|
isLoading={state.isLoading}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default IdeViewer
|
export default IdeViewer
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ export const useRender = () => {
|
|||||||
requestRender({
|
requestRender({
|
||||||
state,
|
state,
|
||||||
dispatch,
|
dispatch,
|
||||||
code: state.code,
|
|
||||||
viewerSize: state.viewerSize,
|
|
||||||
camera: state.camera,
|
|
||||||
parameters: disableParams ? {} : state.currentParameters,
|
parameters: disableParams ? {} : state.currentParameters,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
import { lazy, Suspense } from 'react'
|
import IdeViewer from 'src/components/IdeViewer/IdeViewer'
|
||||||
const IdeViewer = lazy(() => import('src/components/IdeViewer/IdeViewer'))
|
|
||||||
import { use3dViewerResize } from 'src/helpers/hooks/use3dViewerResize'
|
import { use3dViewerResize } from 'src/helpers/hooks/use3dViewerResize'
|
||||||
import { BigLoadingPing } from 'src/components/IdeContainer/IdeContainer'
|
|
||||||
|
|
||||||
const ProfileViewer = () => {
|
const ProfileViewer = () => {
|
||||||
const { viewerDomRef } = use3dViewerResize()
|
const { viewerDomRef } = use3dViewerResize()
|
||||||
return (
|
return (
|
||||||
<div className="h-full" ref={viewerDomRef}>
|
<div className="h-full" ref={viewerDomRef}>
|
||||||
<Suspense fallback={BigLoadingPing}>
|
<IdeViewer />
|
||||||
<IdeViewer Loading={BigLoadingPing} />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export const Failure = ({ error }: CellFailureProps) => (
|
|||||||
|
|
||||||
export const Success = ({
|
export const Success = ({
|
||||||
userProject,
|
userProject,
|
||||||
variables: { image64 },
|
variables: { image64, LiveProjectViewer },
|
||||||
}: CellSuccessProps<FindSocialCardQuery>) => {
|
}: CellSuccessProps<FindSocialCardQuery>) => {
|
||||||
const image = userProject?.Project?.mainImage
|
const image = userProject?.Project?.mainImage
|
||||||
const gravatar = userProject?.image
|
const gravatar = userProject?.image
|
||||||
@@ -47,7 +47,7 @@ export const Success = ({
|
|||||||
: userProject?.Project?.description || ''
|
: userProject?.Project?.description || ''
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="grid h-screen bg-ch-gray-800 text-ch-gray-300"
|
className="grid h-full bg-ch-gray-800 text-ch-gray-300"
|
||||||
id="social-card-loaded"
|
id="social-card-loaded"
|
||||||
style={{ gridTemplateRows: ' 555fr 18fr' }}
|
style={{ gridTemplateRows: ' 555fr 18fr' }}
|
||||||
>
|
>
|
||||||
@@ -96,6 +96,18 @@ export const Success = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`absolute inset-0 flex items-center justify-center ${
|
||||||
|
image64 && 'opacity-0'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{LiveProjectViewer && (
|
||||||
|
<div className="w-full h-full" id="social-card-canvas">
|
||||||
|
<LiveProjectViewer />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const canvasToBlob = async (
|
|||||||
(blob) => {
|
(blob) => {
|
||||||
resolve(blob)
|
resolve(blob)
|
||||||
},
|
},
|
||||||
'image/jpeg',
|
'image/png',
|
||||||
0.75
|
0.75
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,10 +23,7 @@ export const use3dViewerResize = () => {
|
|||||||
requestRender({
|
requestRender({
|
||||||
state,
|
state,
|
||||||
dispatch,
|
dispatch,
|
||||||
code: state.code,
|
|
||||||
viewerSize: { width, height },
|
viewerSize: { width, height },
|
||||||
camera: state.camera,
|
|
||||||
parameters: state.currentParameters,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -25,19 +25,33 @@ interface XYZ {
|
|||||||
z: number
|
z: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EditorModel {
|
interface CodeTab {
|
||||||
type: 'code' | 'guide'
|
type: 'code'
|
||||||
label: string
|
label: string
|
||||||
content?: string
|
content?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GuideTab {
|
||||||
|
type: 'guide'
|
||||||
|
label: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ComponentTab {
|
||||||
|
type: 'component'
|
||||||
|
label: string
|
||||||
|
Component: React.FC
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditorTab = GuideTab | CodeTab | ComponentTab
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
ideType: 'INIT' | CadPackageType
|
ideType: 'INIT' | CadPackageType
|
||||||
viewerContext: 'ide' | 'viewer'
|
viewerContext: 'ide' | 'viewer'
|
||||||
ideGuide?: string
|
ideGuide?: string
|
||||||
consoleMessages: { type: 'message' | 'error'; message: string; time: Date }[]
|
consoleMessages: { type: 'message' | 'error'; message: string; time: Date }[]
|
||||||
code: string
|
code: string
|
||||||
models: EditorModel[]
|
editorTabs: EditorTab[]
|
||||||
currentModel: number
|
currentModel: number
|
||||||
objectData: {
|
objectData: {
|
||||||
type: 'INIT' | ArtifactTypes
|
type: 'INIT' | ArtifactTypes
|
||||||
@@ -78,7 +92,7 @@ export const initialState: State = {
|
|||||||
{ type: 'message', message: 'Initialising', time: new Date() },
|
{ type: 'message', message: 'Initialising', time: new Date() },
|
||||||
],
|
],
|
||||||
code,
|
code,
|
||||||
models: [{ type: 'code', label: 'Code' }],
|
editorTabs: [{ type: 'code', label: 'Code' }],
|
||||||
currentModel: 0,
|
currentModel: 0,
|
||||||
objectData: {
|
objectData: {
|
||||||
type: 'INIT',
|
type: 'INIT',
|
||||||
@@ -240,14 +254,14 @@ const reducer = (state: State, { type, payload }): State => {
|
|||||||
case 'addEditorModel':
|
case 'addEditorModel':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
models: [...state.models, payload],
|
editorTabs: [...state.editorTabs, payload],
|
||||||
}
|
}
|
||||||
case 'removeEditorModel':
|
case 'removeEditorModel':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
models: [
|
editorTabs: [
|
||||||
...state.models.slice(0, payload),
|
...state.editorTabs.slice(0, payload),
|
||||||
...state.models.slice(payload + 1),
|
...state.editorTabs.slice(payload + 1),
|
||||||
],
|
],
|
||||||
currentModel: payload === 0 ? 0 : payload - 1,
|
currentModel: payload === 0 ? 0 : payload - 1,
|
||||||
}
|
}
|
||||||
@@ -284,43 +298,57 @@ export const useIdeState = (): [State, (actionOrThunk: any) => any] => {
|
|||||||
return [state, thunkDispatch]
|
return [state, thunkDispatch]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RequestRenderArgs {
|
interface RequestRenderArgsStateless {
|
||||||
state: State
|
state: State
|
||||||
dispatch: any
|
camera?: State['camera']
|
||||||
parameters: any
|
viewerSize?: State['viewerSize']
|
||||||
code: State['code']
|
|
||||||
camera: State['camera']
|
|
||||||
viewerSize: State['viewerSize']
|
|
||||||
quality?: State['objectData']['quality']
|
quality?: State['objectData']['quality']
|
||||||
specialCadProcess?: string
|
specialCadProcess?: string
|
||||||
|
parameters?: { [key: string]: any }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requestRender = ({
|
export const requestRenderStateless = ({
|
||||||
state,
|
state,
|
||||||
dispatch,
|
|
||||||
code,
|
|
||||||
camera,
|
camera,
|
||||||
viewerSize,
|
viewerSize,
|
||||||
quality = 'low',
|
quality = 'low',
|
||||||
specialCadProcess = null,
|
specialCadProcess = null,
|
||||||
parameters,
|
parameters,
|
||||||
}: RequestRenderArgs) => {
|
}: RequestRenderArgsStateless): null | Promise<any> => {
|
||||||
if (
|
if (
|
||||||
|
!(
|
||||||
state.ideType !== 'INIT' &&
|
state.ideType !== 'INIT' &&
|
||||||
(!state.isLoading || state.objectData?.type === 'INIT')
|
(!state.isLoading || state.objectData?.type === 'INIT')
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
const renderFn = specialCadProcess
|
const renderFn = specialCadProcess
|
||||||
? cadPackages[state.ideType][specialCadProcess]
|
? cadPackages[state.ideType][specialCadProcess]
|
||||||
: cadPackages[state.ideType].render
|
: cadPackages[state.ideType].render
|
||||||
return renderFn({
|
return renderFn({
|
||||||
code,
|
code: state.code,
|
||||||
settings: {
|
settings: {
|
||||||
parameters: state.isCustomizerOpen ? parameters : {},
|
parameters: state.isCustomizerOpen
|
||||||
camera,
|
? parameters || state.currentParameters
|
||||||
viewerSize,
|
: {},
|
||||||
|
camera: camera || state.camera,
|
||||||
|
viewerSize: viewerSize || state.viewerSize,
|
||||||
quality,
|
quality,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RequestRenderArgs extends RequestRenderArgsStateless {
|
||||||
|
dispatch: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export const requestRender = ({ dispatch, ...rest }: RequestRenderArgs) => {
|
||||||
|
const renderPromise = requestRenderStateless(rest)
|
||||||
|
if (!renderPromise) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
renderPromise
|
||||||
.then(
|
.then(
|
||||||
({
|
({
|
||||||
objectData,
|
objectData,
|
||||||
@@ -351,4 +379,3 @@ export const requestRender = ({
|
|||||||
)
|
)
|
||||||
.catch(() => dispatch({ type: 'resetLoading' })) // TODO should probably display something to the user here
|
.catch(() => dispatch({ type: 'resetLoading' })) // TODO should probably display something to the user here
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user