hts/apps/migrant/components/deploy-frontend-button.tsx

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
}