320 lines
10 KiB
TypeScript
320 lines
10 KiB
TypeScript
'use client'
|
|
|
|
import { usePathname, useRouter } from 'next/navigation'
|
|
import { PrefetchKind } from 'next/dist/client/components/router-reducer/router-reducer-types'
|
|
import { ChatRequest, FunctionCallHandler } from 'ai'
|
|
// import { useChat, type Message } from 'ai/react'
|
|
import toast from 'react-hot-toast'
|
|
|
|
import { cn } from '@/lib/utils'
|
|
import { ChatList } from '@/components/chat-list'
|
|
import { ChatPanel } from '@/components/chat-panel'
|
|
import { ChatScrollAnchor } from '@/components/chat-scroll-anchor'
|
|
import { nanoid } from '@/lib/utils'
|
|
import { functionSchemas } from '@/lib/functions/schemas'
|
|
import { Landing } from '@/components/landing'
|
|
import { useGlobalStore } from '@/app/state/global-store'
|
|
import { useW3GPTDeploy } from '@/lib/hooks/use-w3gpt-deploy'
|
|
// import { useNetwork } from 'wagmi'
|
|
import { useEffect, useState } from 'react'
|
|
import { FadeIn } from './landing/fade-in'
|
|
|
|
import Image from 'next/image';
|
|
import logoImage from '@/components/images/logo.png';
|
|
import { useTranslation } from 'react-i18next'
|
|
import { Button, Modal } from 'antd'
|
|
import { type Message, useChat } from '@/lib/hooks/use-chat'
|
|
|
|
export interface ChatProps extends React.ComponentProps<'div'> {
|
|
initialMessages?: Message[]
|
|
id?: string
|
|
showLanding?: boolean
|
|
avatarUrl?: string | null | undefined
|
|
}
|
|
|
|
export function Chat({
|
|
id,
|
|
initialMessages,
|
|
className,
|
|
showLanding = false,
|
|
avatarUrl
|
|
}: ChatProps) {
|
|
const router = useRouter()
|
|
const path = usePathname()
|
|
const { t, i18n } = useTranslation()
|
|
|
|
const isChatPage = path.includes('chat')
|
|
const {
|
|
setIsGenerating,
|
|
setIsDeploying,
|
|
setDeployContractConfig,
|
|
verifyContractConfig,
|
|
lastDeploymentData,
|
|
setLastDeploymentData
|
|
} = useGlobalStore()
|
|
|
|
// const { chain } = useNetwork()
|
|
// const fallbackChainId = chain?.unsupported === false ? chain.id : 5001
|
|
// const activeChainId = chain?.id ?? fallbackChainId
|
|
// const { deploy } = useW3GPTDeploy({ chainId: activeChainId })
|
|
|
|
|
|
useEffect(() => {
|
|
let isLounted = true
|
|
|
|
async function verifyContract() {
|
|
|
|
}
|
|
|
|
verifyContract()
|
|
|
|
|
|
// try {
|
|
// let data = localStorage.getItem("UserData");
|
|
// if (data == null) return
|
|
|
|
// data = JSON.parse(data)
|
|
|
|
// console.log("UserData", data)
|
|
|
|
// } catch (error) {
|
|
// console.log(error)
|
|
// }
|
|
|
|
return () => {
|
|
isLounted = false
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
let isMounted = true
|
|
|
|
async function verifyContract() {
|
|
if (
|
|
!verifyContractConfig?.deployHash ||
|
|
lastDeploymentData?.verificationStatus === 'success'
|
|
) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/verify-contract', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(verifyContractConfig)
|
|
})
|
|
|
|
const data = await response.json()
|
|
|
|
if (typeof data === 'string' && data.startsWith('0x') && isMounted) {
|
|
toast.success('Contract verified successfully!')
|
|
lastDeploymentData &&
|
|
setLastDeploymentData({
|
|
...lastDeploymentData,
|
|
verificationStatus: 'success'
|
|
})
|
|
} else {
|
|
setTimeout(verifyContract, 15000) // Retry after 15 seconds
|
|
}
|
|
} catch (error) {
|
|
console.error('Verification failed', error)
|
|
setTimeout(verifyContract, 15000) // Retry after 15 seconds
|
|
}
|
|
}
|
|
|
|
verifyContract()
|
|
|
|
return () => {
|
|
isMounted = false
|
|
}
|
|
}, [lastDeploymentData, verifyContractConfig, setLastDeploymentData])
|
|
|
|
const functionCallHandler: FunctionCallHandler = async (
|
|
chatMessages,
|
|
functionCall
|
|
) => {
|
|
if (functionCall.name === 'deploy_contract') {
|
|
setIsDeploying(true)
|
|
const { chainId, contractName, sourceCode, constructorArgs } = JSON.parse(
|
|
functionCall.arguments || '{}'
|
|
)
|
|
|
|
// setDeployContractConfig({
|
|
// chainId: chainId ?? activeChainId,
|
|
// contractName,
|
|
// sourceCode,
|
|
// constructorArgs
|
|
// })
|
|
|
|
// const verifiedContractAddress = await deploy({
|
|
// chainId: chainId ?? activeChainId,
|
|
// contractName,
|
|
// sourceCode,
|
|
// constructorArgs
|
|
// })
|
|
|
|
// const functionResponse: ChatRequest = {
|
|
// messages: [
|
|
// ...chatMessages,
|
|
// {
|
|
// id: nanoid(),
|
|
// name: 'deploy_contract',
|
|
// role: 'function',
|
|
// content: JSON.stringify({ verifiedContractAddress })
|
|
// }
|
|
// ],
|
|
// functions: functionSchemas
|
|
// }
|
|
// return functionResponse
|
|
} else if (functionCall.name === 'text_to_image') {
|
|
const response = await fetch('/api/text-to-image', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ text: functionCall.arguments })
|
|
})
|
|
if (!response.ok) {
|
|
throw new Error(response.statusText)
|
|
}
|
|
const { imageUrl, metadataUrl } = await response.json()
|
|
|
|
const functionResponse: ChatRequest = {
|
|
messages: [
|
|
...chatMessages,
|
|
{
|
|
id: nanoid(),
|
|
name: 'text-to-image',
|
|
role: 'function',
|
|
content: JSON.stringify({ imageUrl, metadataUrl })
|
|
}
|
|
],
|
|
functions: functionSchemas
|
|
}
|
|
return functionResponse
|
|
}
|
|
}
|
|
|
|
const { messages, append, reload, stop, isLoading, input, setInput } =
|
|
useChat({
|
|
experimental_onFunctionCall: functionCallHandler,
|
|
initialMessages,
|
|
id,
|
|
body: {
|
|
id
|
|
},
|
|
onResponse(response) {
|
|
setIsGenerating(true)
|
|
if (!isChatPage) {
|
|
router.prefetch(`${i18n.language}/chat/${id}`, {
|
|
kind: PrefetchKind.FULL
|
|
})
|
|
}
|
|
if (response.status === 401) {
|
|
toast.error(response.statusText)
|
|
}
|
|
},
|
|
onFinish() {
|
|
setIsGenerating(false)
|
|
if (!isChatPage) {
|
|
history.pushState({}, '', `${i18n.language}/chat/${id}`)
|
|
history.go(1)
|
|
}
|
|
}
|
|
})
|
|
|
|
return (
|
|
<>
|
|
<div className={cn('px-4 pb-[200px] pt-4 md:pt-10', className)}>
|
|
<FadeIn >
|
|
{/* {showLanding && <Landing disableAnimations={isChatPage} />} */}
|
|
{/* {showLanding && <h3 className='text-center text-2xl font-bold py-4'>What do you want to ask about AI or Blockchain?</h3>} */}
|
|
{
|
|
showLanding &&
|
|
// <Image src={logoImage} height={40} alt="show" className="cursor-pointer m-auto" onClick={() => { }} />
|
|
<LogoAI
|
|
className='flex text-center m-auto'
|
|
/>
|
|
}
|
|
|
|
</FadeIn>
|
|
<ChatList messages={messages} avatarUrl={avatarUrl} />
|
|
|
|
|
|
<ChatScrollAnchor trackVisibility={isLoading} />
|
|
|
|
{!!messages && <ChatPanel
|
|
id={id}
|
|
stop={stop}
|
|
isLoading={isLoading}
|
|
append={append}
|
|
reload={reload}
|
|
messages={messages}
|
|
input={input}
|
|
setInput={setInput}
|
|
|
|
/>}
|
|
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
|
|
|
|
export const LogoAI: React.FC<{ className?: string; width?: string }> = ({ className, width }) => {
|
|
return (
|
|
<svg
|
|
className={className}
|
|
width={width ? width : "32"}
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 328.72 271.03"
|
|
>
|
|
<polygon
|
|
points="215.08 83.41 177.33 147.81 235.8 244.7 251.67 271.03 328.72 271.03 215.08 83.41"
|
|
fill="#3e67b1"
|
|
strokeWidth="0"
|
|
/>
|
|
<polygon
|
|
points="164.56 0 126.22 63.11 164.56 126.65 202.79 63.13 164.56 0"
|
|
fill="#46b972"
|
|
strokeWidth="0"
|
|
/>
|
|
<polygon
|
|
points="0 271.03 77.05 271.03 137.01 172.44 152.09 147.39 113.83 83.63 0 271.03"
|
|
fill="#3e67b1"
|
|
strokeWidth="0"
|
|
/>
|
|
</svg>
|
|
);
|
|
};
|
|
|
|
// export const LogoAI: React.FC<{ className?: string, width?: string }> = ({ className, width }) => {
|
|
|
|
// return (
|
|
// <svg
|
|
// className={className}
|
|
// width={width ? width : "64"}
|
|
// xmlns="http://www.w3.org/2000/svg" viewBox="0 0 328.72 271.03">
|
|
// <defs>
|
|
// <style>
|
|
// {`.cls-1{fill:#2565d5;}.cls-2{fill:#36c072;}.cls-3{fill:#191919;}`}
|
|
// </style>
|
|
// </defs>
|
|
// <polygon className="cls-1" points="297.11 149.31 259.36 213.72 317.82 310.6 333.7 336.93 410.75 336.93 297.11 149.31" />
|
|
// <polygon className="cls-2" points="246.58 65.9 208.25 129.01 246.59 192.55 284.82 129.03 246.58 65.9" />
|
|
// <polygon className="cls-1" points="82.03 336.93 159.07 336.93 219.04 238.34 234.11 213.29 195.86 149.53 82.03 336.93" />
|
|
// <path className="cls-3" d="M456.65,338.19V310.72a99.7,99.7,0,0,0,14.88,1.14q12.83,0,18-7.21t5.15-21.41V137.39h34.57V284.84q0,27-14.19,40.76t-40.3,13.74A137.42,137.42,0,0,1,456.65,338.19Z" />
|
|
// <path className="cls-3" d="M669.13,241.8l-79.68,5q.91,13.29,8.93,19.47t24.5,6.18a104.74,104.74,0,0,0,22.78-2.63q11.79-2.64,18.21-6.07v26.1q-6,3.44-19,6.07a144.09,144.09,0,0,1-28.39,2.63q-19.92,0-33.09-5.72t-20.15-19.81q-7-14.08-7-38.58t7.1-38.58q7.1-14.08,20.38-19.81t33.66-5.72q20.6,0,32.28,6.64a35.08,35.08,0,0,1,16.14,18.43q4.47,11.79,4.47,29.19A132.2,132.2,0,0,1,669.13,241.8Zm-29.08-22q0-11.69-4.58-18.09t-18.08-6.41q-15.12,0-21.18,6.52t-6.76,21.87Z" />
|
|
// <path className="cls-3" d="M704.51,290.11q-7.9-7.56-7.9-24.5V122.28h33.2V257.6q0,8,2.29,10.87t8,2.87a28.15,28.15,0,0,0,6.64-.69v25.64a67.59,67.59,0,0,1-14.2,1.38Q712.41,297.67,704.51,290.11Z" />
|
|
// <path className="cls-3" d="M774.11,290.11q-7.9-7.56-7.9-24.5V122.28h33.2V257.6q0,8,2.29,10.87c1.52,1.91,4.2,2.87,8,2.87a28.09,28.09,0,0,0,6.64-.69v25.64a67.5,67.5,0,0,1-14.19,1.38Q782,297.67,774.11,290.11Z" />
|
|
// <path className="cls-3" d="M834.67,313.47q15.11,0,22.1-4.36t10-14l-48.09-122.5h36.87l29.08,88.15H886l27.48-88.15h35.71L907.71,284.84Q899.23,308,890.65,320a47.28,47.28,0,0,1-21.4,17.06q-12.84,5-34.58,5Z" />
|
|
// <path className="cls-3" d="M1065.47,256.91H1000.9l-13.74,39.38H951l59.31-158.9H1057l58.85,158.9H1079Zm-10.08-28.85-20.6-60.22H1032L1011,228.06Z" />
|
|
// <path className="cls-3" d="M1132.32,122.28h33.2v174h-33.2Z" />
|
|
// </svg>
|
|
|
|
// )
|
|
// };
|
|
|