Files
cadhub/app/web/src/components/CaptureButton/CaptureButton.tsx
2021-08-14 18:16:28 +10:00

233 lines
7.3 KiB
TypeScript

import { useState } from 'react'
import { toast } from '@redwoodjs/web/toast'
import Popover from '@material-ui/core/Popover'
import Svg from 'src/components/Svg/Svg'
import Button from 'src/components/Button/Button'
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
import { canvasToBlob, blobTo64 } from 'src/helpers/canvasToBlob'
import { useUpdateProject } from 'src/helpers/hooks/useUpdateProject'
import {
useUpdateSocialCard,
makeSocialPublicId,
} from 'src/helpers/hooks/useUpdateSocialCard'
import {
uploadToCloudinary,
serverVerifiedImageUpload,
} from 'src/helpers/cloudinary'
import SocialCardCell from 'src/components/SocialCardCell/SocialCardCell'
import { toJpeg } from 'html-to-image'
import { useAuth } from '@redwoodjs/auth'
const anchorOrigin = {
vertical: 'bottom',
horizontal: 'center',
}
const transformOrigin = {
vertical: 'top',
horizontal: 'center',
}
const CaptureButton = ({
canEdit,
TheButton,
shouldUpdateImage,
projectTitle,
userName,
}) => {
const [captureState, setCaptureState] = useState<any>({})
const [anchorEl, setAnchorEl] = useState(null)
const [whichPopup, setWhichPopup] = useState(null)
const { state, project } = useIdeContext()
const ref = React.useRef<HTMLDivElement>(null)
const { updateProject } = useUpdateProject({
onCompleted: () => toast.success('Image updated'),
})
const { updateSocialCard } = useUpdateSocialCard({})
const { getToken } = useAuth()
const getSocialBlob = async (): Promise<string> => {
const tokenPromise = getToken()
const blob = await toJpeg(ref.current, { cacheBust: true, quality: 0.75 })
const token = await tokenPromise
const { publicId } = await serverVerifiedImageUpload(
blob,
project?.id,
token,
makeSocialPublicId(userName, projectTitle)
)
return publicId
}
const onCapture = async () => {
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 = {
image: await imgBlob,
currImage: project?.mainImage,
imageObjectURL: window.URL.createObjectURL(await imgBlob),
callback: uploadAndUpdateImage,
cloudinaryImgURL: '',
updated: false,
image64: await image64,
}
setCaptureState(config)
async function uploadAndUpdateImage() {
const [cloudinaryImgURL, socialCloudinaryURL] = await Promise.all([
uploadToCloudinary(config.image),
getSocialBlob(),
])
updateSocialCard({
variables: {
projectId: project?.id,
url: socialCloudinaryURL,
},
})
// Save the screenshot as the mainImage
updateProject({
variables: {
id: project?.id,
input: {
mainImage: cloudinaryImgURL.public_id,
},
},
})
return cloudinaryImgURL
}
// if there isn't a screenshot saved yet, just go ahead and save right away
if (shouldUpdateImage) {
config.cloudinaryImgURL = (await uploadAndUpdateImage()).public_id
config.updated = true
setCaptureState(config)
}
}
const handleDownload = (url) => {
const aTag = document.createElement('a')
document.body.appendChild(aTag)
aTag.href = url
aTag.style.display = 'none'
aTag.download = `${project?.title}-${new Date().toISOString()}.jpg`
aTag.click()
document.body.removeChild(aTag)
}
const handleClick = ({ event, whichPopup }) => {
setAnchorEl(event.currentTarget)
setWhichPopup(whichPopup)
}
const handleClose = () => {
setAnchorEl(null)
setWhichPopup(null)
}
return (
<div>
{canEdit && (
<div>
<TheButton
onClick={async (event) => {
handleClick({ event, whichPopup: 'capture' })
onCapture()
}}
/>
<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-2 text-gray-500">
{!captureState ? (
'Loading...'
) : (
<div className="grid grid-cols-2">
<div
className="rounded m-auto"
style={{ width: 'fit-content', overflow: 'hidden' }}
>
<img src={captureState.imageObjectURL} className="w-32" />
</div>
<div className="p-2 text-indigo-800">
{captureState.currImage && !captureState.updated ? (
<button
className="flex justify-center mb-4"
onClick={async () => {
const cloudinaryImg = await captureState.callback()
setCaptureState({
...captureState,
currImage: cloudinaryImg.public_id,
updated: true,
})
}}
>
<Svg
name="refresh"
className="mr-2 w-4 text-indigo-600"
/>{' '}
Update Project Image
</button>
) : (
<div className="flex justify-center mb-4">
<Svg
name="checkmark"
className="mr-2 w-6 text-indigo-600"
/>{' '}
Part Image Updated
</div>
)}
<Button
iconName="save"
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={() =>
handleDownload(captureState.imageObjectURL)
}
>
Download
</Button>
</div>
</div>
)}
<div className="rounded-lg shadow-md mt-4 overflow-hidden">
<div
className="transform scale-50 origin-top-left"
style={{ width: '600px', height: '315px' }}
>
<div style={{ width: '1200px', height: '630px' }} ref={ref}>
<SocialCardCell
userName={userName}
projectTitle={projectTitle}
image64={captureState.image64}
/>
</div>
</div>
</div>
</div>
</Popover>
</div>
)}
</div>
)
}
export default CaptureButton