Merge pull request #458 from Irev-Dev/kurt/97-clean-up
social card clean up
This commit was merged in pull request #458.
This commit is contained in:
@@ -5,18 +5,10 @@ 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 { useUpdateProjectImages } from 'src/helpers/hooks/useUpdateProjectImages'
|
||||
|
||||
import SocialCardCell from 'src/components/SocialCardCell/SocialCardCell'
|
||||
import { toJpeg } from 'html-to-image'
|
||||
import { useAuth } from '@redwoodjs/auth'
|
||||
|
||||
const anchorOrigin = {
|
||||
vertical: 'bottom',
|
||||
@@ -39,24 +31,7 @@ const CaptureButton = ({
|
||||
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 { updateProjectImages } = useUpdateProjectImages({})
|
||||
|
||||
const onCapture = async () => {
|
||||
const threeInstance = state.threeInstance
|
||||
@@ -64,7 +39,7 @@ const CaptureButton = ({
|
||||
let imgBlob
|
||||
let image64
|
||||
if (!isOpenScadImage) {
|
||||
imgBlob = canvasToBlob(threeInstance, { width: 400, height: 300 })
|
||||
imgBlob = canvasToBlob(threeInstance, { width: 500, height: 375 })
|
||||
image64 = blobTo64(
|
||||
await canvasToBlob(threeInstance, { width: 500, height: 522 })
|
||||
)
|
||||
@@ -84,49 +59,45 @@ const CaptureButton = ({
|
||||
setCaptureState(config)
|
||||
|
||||
async function uploadAndUpdateImage() {
|
||||
const [cloudinaryImgURL, socialCloudinaryURL] = await Promise.all([
|
||||
uploadToCloudinary(config.image),
|
||||
getSocialBlob(),
|
||||
])
|
||||
const upload = async () => {
|
||||
const socialCard64 = toJpeg(ref.current, {
|
||||
cacheBust: true,
|
||||
quality: 0.7,
|
||||
})
|
||||
|
||||
updateSocialCard({
|
||||
variables: {
|
||||
projectId: project?.id,
|
||||
url: socialCloudinaryURL,
|
||||
},
|
||||
})
|
||||
|
||||
// Save the screenshot as the mainImage
|
||||
updateProject({
|
||||
variables: {
|
||||
id: project?.id,
|
||||
input: {
|
||||
mainImage: cloudinaryImgURL.public_id,
|
||||
// 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({
|
||||
variables: {
|
||||
id: project?.id,
|
||||
socialCard64: await socialCard64,
|
||||
},
|
||||
})
|
||||
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>,
|
||||
})
|
||||
|
||||
return cloudinaryImgURL
|
||||
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()).public_id
|
||||
config.cloudinaryImgURL = await uploadAndUpdateImage()
|
||||
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)
|
||||
@@ -155,59 +126,22 @@ const CaptureButton = ({
|
||||
transformOrigin={transformOrigin}
|
||||
className="material-ui-overrides transform translate-y-4"
|
||||
>
|
||||
<div className="text-sm p-2 text-gray-500">
|
||||
<div className="text-sm p-4 text-gray-500">
|
||||
{!captureState ? (
|
||||
'Loading...'
|
||||
) : (
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="">
|
||||
<div className="text-lg">Thumbnail</div>
|
||||
<div
|
||||
className="rounded m-auto"
|
||||
className="rounded"
|
||||
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="text-lg mt-4">Social Media Card</div>
|
||||
<div className="rounded-lg shadow-md overflow-hidden">
|
||||
<div
|
||||
className="transform scale-50 origin-top-left"
|
||||
style={{ width: '600px', height: '315px' }}
|
||||
@@ -221,6 +155,33 @@ const CaptureButton = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 text-indigo-800">
|
||||
{captureState.currImage && !captureState.updated ? (
|
||||
<Button
|
||||
iconName="refresh"
|
||||
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({
|
||||
...captureState,
|
||||
currImage: cloudinaryImg,
|
||||
updated: true,
|
||||
})
|
||||
}}
|
||||
>
|
||||
Update Project Images
|
||||
</Button>
|
||||
) : (
|
||||
<div className="flex justify-center mb-4">
|
||||
<Svg
|
||||
name="checkmark"
|
||||
className="mr-2 w-6 text-indigo-600"
|
||||
/>{' '}
|
||||
Project Images Updated
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
@@ -2,8 +2,8 @@ import { Helmet } from 'react-helmet'
|
||||
import { useIsBrowser } from '@redwoodjs/prerender/browserUtils'
|
||||
|
||||
const Seo = ({
|
||||
title = "CadHub",
|
||||
description = "Edit this part of CadHub",
|
||||
title = 'CadHub',
|
||||
description = 'Edit this part of CadHub',
|
||||
lang = 'en-US',
|
||||
socialImageUrl,
|
||||
}: {
|
||||
@@ -22,25 +22,33 @@ const Seo = ({
|
||||
title={title}
|
||||
titleTemplate={`Cadhub - ${title}`}
|
||||
>
|
||||
<title>{title || 'cadhub'}</title>
|
||||
<meta name="description" content={description} />
|
||||
{title && <title>{title || 'cadhub'}</title>}
|
||||
{description && <meta name="description" content={description} />}
|
||||
|
||||
{/* Facebook Meta Tags */}
|
||||
{browser && <meta property="og:url" content={location.href} />}
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={socialImageUrl} />
|
||||
{title && <meta property="og:title" content={title} />}
|
||||
{description && (
|
||||
<meta property="og:description" content={description} />
|
||||
)}
|
||||
{socialImageUrl && (
|
||||
<meta property="og:image" content={socialImageUrl} />
|
||||
)}
|
||||
|
||||
{/* Twitter Meta Tags */}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:domain" content="cadhub.xyz" />
|
||||
{browser && <meta property="twitter:url" content={location.href} />}
|
||||
<meta name="twitter:title" content={title} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
<meta name="twitter:image" content={socialImageUrl} />
|
||||
{title && <meta name="twitter:title" content={title} />}
|
||||
{description && (
|
||||
<meta name="twitter:description" content={description} />
|
||||
)}
|
||||
{socialImageUrl && (
|
||||
<meta name="twitter:image" content={socialImageUrl} />
|
||||
)}
|
||||
|
||||
<meta property="og:locale" content={lang} />
|
||||
{lang && <meta property="og:locale" content={lang} />}
|
||||
</Helmet>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -41,6 +41,10 @@ export const Success = ({
|
||||
}: CellSuccessProps<FindSocialCardQuery>) => {
|
||||
const image = userProject?.Project?.mainImage
|
||||
const gravatar = userProject?.image
|
||||
const truncatedDescription =
|
||||
userProject?.Project?.description?.length > 150
|
||||
? (userProject?.Project?.description || '').slice(0, 145) + ' . . .'
|
||||
: userProject?.Project?.description || ''
|
||||
return (
|
||||
<div
|
||||
className="flex-col flex h-full bg-ch-gray-800 text-ch-gray-300"
|
||||
@@ -51,37 +55,30 @@ export const Success = ({
|
||||
style={{ gridTemplateColumns: '7fr 5fr' }}
|
||||
>
|
||||
<div className="bg-ch-gray-800 relative">
|
||||
<div className="absolute bottom-0 left-0 transform scale-200 aspect-h-1 h-full -translate-x-24 translate-y-24 rotate-45 rounded-full overflow-hidden">
|
||||
{/* <CloudinaryImage
|
||||
cloudName="irevdev"
|
||||
publicId={image || 'CadHub/eia1kwru54g2kf02s2xx'}
|
||||
width={500}
|
||||
crop="scale"
|
||||
/> */}
|
||||
</div>
|
||||
<div className="absolute bottom-0 left-0 transform scale-200 aspect-h-1 h-full -translate-x-24 translate-y-24 rotate-45 rounded-full overflow-hidden"></div>
|
||||
|
||||
<div className="relative bg-ch-gray-760 bg-opacity-90 pt-10 pl-20 pr-12 h-full backdrop-filter backdrop-blur">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center">
|
||||
{gravatar && (
|
||||
<Gravatar image={gravatar} className="w-14 h-14" size={60} />
|
||||
<Gravatar image={gravatar} className="w-18 h-18" size={60} />
|
||||
)}
|
||||
<div className="text-2xl font-fira-sans ml-6 whitespace-nowrap">
|
||||
<div className="text-3xl font-fira-sans ml-6 whitespace-nowrap">
|
||||
{userProject?.userName}
|
||||
</div>
|
||||
</div>
|
||||
<CadPackage
|
||||
cadPackage={userProject?.Project?.cadPackage}
|
||||
className="p-2 rounded px-4"
|
||||
className="p-2 rounded px-4 transform scale-150 origin-right"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h1 className="text-6xl font-fira-sans mt-16 capitalize">
|
||||
<h1 className="text-6xl font-fira-sans mt-12 capitalize">
|
||||
{userProject?.Project?.title.replace(/-/g, ' ')}
|
||||
</h1>
|
||||
|
||||
<p className="mt-10 text-2xl font-fira-sans text-ch-gray-400">
|
||||
{(userProject?.Project?.description || '').slice(0, 150)}
|
||||
<p className="mt-10 text-3xl font-fira-sans text-ch-gray-400">
|
||||
{truncatedDescription}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -104,7 +101,7 @@ export const Success = ({
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="h-24 grid bg-ch-gray-900 relative"
|
||||
className="h-28 grid bg-ch-gray-900 relative"
|
||||
style={{ gridTemplateColumns: '7fr 5fr' }}
|
||||
>
|
||||
<div className="grid grid-flow-col-dense items-center justify-center gap-16">
|
||||
@@ -121,10 +118,10 @@ export const Success = ({
|
||||
},
|
||||
].map(({ svg, title, count }, index) => (
|
||||
<div className="grid grid-flow-col-dense gap-4" key={index}>
|
||||
<Svg className="w-10" name={svg} />
|
||||
<Svg className="w-12" name={svg} />
|
||||
<div className="flex flex-col">
|
||||
<div className="text-3xl">{count}</div>
|
||||
<div className="text-xl text-ch-gray-400">{title}</div>
|
||||
<div className="text-4xl">{count}</div>
|
||||
<div className="text-3xl text-ch-gray-400">{title}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -134,17 +131,11 @@ export const Success = ({
|
||||
<div className="ml-2 md:ml-6 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"
|
||||
className="text-indigo-300 text-3xl 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 whitespace-nowrap"
|
||||
style={{ paddingBottom: '2rem', marginLeft: '-1.8rem' }}
|
||||
>
|
||||
pre-alpha
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user