150 lines
3.8 KiB
TypeScript
150 lines
3.8 KiB
TypeScript
'use client'
|
|
|
|
import Link from 'next/link'
|
|
import { useState } from 'react'
|
|
|
|
import { Button } from '@/components/ui/button'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger
|
|
} from '@/components/ui/dialog'
|
|
import { IconExternalLink } from '@/components/ui/icons'
|
|
import { useGlobalStore } from '@/app/state/global-store'
|
|
import toast from 'react-hot-toast'
|
|
|
|
export function DeployFrontendButton({
|
|
sourceCode,
|
|
address,
|
|
abi
|
|
}: {
|
|
sourceCode: string
|
|
address?: string
|
|
abi?: any
|
|
}) {
|
|
const { lastDeploymentData } = useGlobalStore()
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
const [deployResults, setDeployResults] = useState<{
|
|
junoUrl?: string
|
|
icpUrl?: string
|
|
isError?: boolean
|
|
}>({})
|
|
const { junoUrl, icpUrl, isError } = deployResults
|
|
const _address = address || lastDeploymentData?.address || ''
|
|
const _abi = abi || lastDeploymentData?.abi
|
|
|
|
const disabled = !sourceCode || !_address || !_abi
|
|
|
|
async function handleDeployFrontend() {
|
|
const deployment = await deployFrontend({
|
|
sourceCode,
|
|
contractAddress: _address,
|
|
abi: _abi
|
|
})
|
|
|
|
setDeployResults(deployment)
|
|
}
|
|
|
|
return (
|
|
<Dialog onOpenChange={setIsOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button
|
|
className="mr-2 text-primary-foreground"
|
|
variant="default"
|
|
disabled={disabled}
|
|
size="sm"
|
|
>
|
|
<p className="hidden sm:flex">Deploy Frontend</p>
|
|
<p className="flex sm:hidden">Deploy</p>
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className=" sm:max-w-[425px] ">
|
|
<DialogHeader>
|
|
<DialogTitle>Deploy Frontend</DialogTitle>
|
|
<DialogDescription className="pt-2">
|
|
Deploy your frontend on chain. One deployment will be on the Juno
|
|
blockchain the other will be on ICP as a frontend canister.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className=" mx-auto flex w-full items-center justify-center gap-4 py-4">
|
|
{isError && (
|
|
<p className="text-sm text-destructive">
|
|
Error deploying frontend.
|
|
</p>
|
|
)}
|
|
{junoUrl && (
|
|
<Link
|
|
href={junoUrl}
|
|
target="_blank"
|
|
className="text-sm text-green-500 "
|
|
>
|
|
View on Juno <IconExternalLink className="ml-1" />
|
|
</Link>
|
|
)}
|
|
{icpUrl && (
|
|
<Link
|
|
href={icpUrl}
|
|
target="_blank"
|
|
className="text-sm text-blue-500"
|
|
>
|
|
View on ICP <IconExternalLink className="ml-1" />
|
|
</Link>
|
|
)}
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button
|
|
className="mb-4 p-6 sm:p-4"
|
|
disabled={isError}
|
|
onClick={handleDeployFrontend}
|
|
variant="secondary"
|
|
>
|
|
Deploy Frontend
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|
|
|
|
async function deployFrontend({
|
|
sourceCode,
|
|
contractAddress,
|
|
abi
|
|
}: {
|
|
sourceCode: string
|
|
contractAddress: string
|
|
abi: any
|
|
}) {
|
|
// replace CONTRACT_ABI and CONTRACT_ADDRESS with the actual contract abi and address in the source code
|
|
const abiString = JSON.stringify(abi)
|
|
sourceCode = sourceCode.replace('CONTRACT_ABI', abiString)
|
|
sourceCode = sourceCode.replace('CONTRACT_ADDRESS', contractAddress)
|
|
|
|
const res = await toast.promise(
|
|
fetch('http://localhost:4040/deploy-html', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
sourceCode
|
|
})
|
|
}),
|
|
{
|
|
loading: 'Deploying frontend...',
|
|
success: 'Frontend deployed!',
|
|
error: 'Failed to deploy frontend'
|
|
}
|
|
)
|
|
|
|
const deployResult = await res.json()
|
|
|
|
return deployResult
|
|
}
|