Skip to content

Commit

Permalink
refactor to have app context
Browse files Browse the repository at this point in the history
  • Loading branch information
od41 committed Mar 10, 2024
1 parent f7b6ef1 commit 6759d75
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 203 deletions.
14 changes: 13 additions & 1 deletion frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
'use client'

import { useEffect } from 'react'
import { useContext, useEffect } from 'react'

import { AppContext } from '@/context/app-context'
import { useInkathon } from '@scio-labs/use-inkathon'
import { toast } from 'react-hot-toast'

import { Spinner } from '@/components/ui/spinner'
import { ChatMessages } from '@/components/web3/chat-messages'
import { ConnectButton } from '@/components/web3/connect-button'

import { HomePageTitle } from './components/home-page-title'

export default function HomePage() {
const { isAppLoading } = useContext(AppContext)

// Display `useInkathon` error messages (optional)
const { error } = useInkathon()
useEffect(() => {
if (!error) return
toast.error(error.message)
}, [error])

// Connection Loading Indicator
if (isAppLoading)
return (
<div className="my-8 flex h-full w-full flex-col items-center justify-center space-y-3 text-center font-mono text-sm text-gray-400 sm:flex-row sm:space-x-3 sm:space-y-0">
<Spinner />
</div>
)

return (
<>
<div className="container relative flex grow flex-col items-center justify-center py-10 pt-0">
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { PropsWithChildren } from 'react'

import { AppProvider } from '@/context/app-context'
import { getDeployments } from '@/deployments/deployments'
import { UseInkathonProvider } from '@scio-labs/use-inkathon'

Expand All @@ -15,7 +16,7 @@ export default function ClientProviders({ children }: PropsWithChildren) {
defaultChain={env.defaultChain}
deployments={getDeployments()}
>
{children}
<AppProvider>{children}</AppProvider>
</UseInkathonProvider>
)
}
123 changes: 20 additions & 103 deletions frontend/src/components/web3/chat-messages.tsx
Original file line number Diff line number Diff line change
@@ -1,121 +1,38 @@
'use client'

import { FC, useEffect, useState } from 'react'
import { FC, useContext } from 'react'

import { ContractIds } from '@/deployments/deployments'
import ChatroomContract from '@inkathon/contracts/typed-contracts/contracts/chatroom'
import {
contractQuery,
decodeOutput,
useInkathon,
useRegisteredContract,
useRegisteredTypedContract,
} from '@scio-labs/use-inkathon'
import toast from 'react-hot-toast'
import { AppContext } from '@/context/app-context'
import { useInkathon } from '@scio-labs/use-inkathon'
import { BsChatSquareQuote } from 'react-icons/bs'

import { Card, CardContent } from '@/components/ui/card'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Spinner } from '@/components/ui/spinner'

import { Message, MessageProps } from './message'
import { Message } from './message'
import { SendMessage } from './send-message'

export const ChatMessages: FC = () => {
const { api, activeChain, activeAccount, activeSigner } = useInkathon()
const { contract, address: contractAddress } = useRegisteredContract(ContractIds.Chatroom)
const { typedContract } = useRegisteredTypedContract(ContractIds.Chatroom, ChatroomContract)
const [fetchIsLoading, setFetchIsLoading] = useState<boolean>()
const [activeChatroom, setActiveChatroom] = useState(false)
const [chatroomId, setChatroomId] = useState('5CqRGE6QMZUxh8anBchE69P8gt3sojPtwNQkmpKwWPz9yPRB')
const [messages, setMessages] = useState<MessageProps[]>([])

// fetch chatroom
const fetchChatroom = async () => {
if (!activeAccount || !contract || !activeSigner || !api) {
toast.error('Wallet not connected. Try again…')
return
}

setFetchIsLoading(true)

try {
const result = await contractQuery(api, activeAccount.address, contract, 'getChatroom', {}, [
chatroomId,
])
const { output, isError, decodedOutput } = decodeOutput(result, contract, 'getChatroom')
if (isError) throw new Error(decodedOutput)
console.log('chatroom: ', output)
fetchMessages()
setActiveChatroom(true)
} catch (e) {
console.error(e)
toast.error('Error while loading chatroom. Try again…')
setFetchIsLoading(false)
} finally {
setFetchIsLoading(false)
}
}

useEffect(() => {
console.log('chatroom status before', activeChatroom)
fetchChatroom()
console.log('chatroom status', activeChatroom)
}, [api, activeAccount, contract, activeSigner])

// const fetchGreeting = async () => {
// if (!contract || !typedContract || !api) return

// setFetchIsLoading(true)
// try {
// const result = await contractQuery(api, '', contract, 'greet')
// const { output, isError, decodedOutput } = decodeOutput(result, contract, 'greet')
// if (isError) throw new Error(decodedOutput)
// setGreeterMessage(output)

// // Alternatively: Fetch it with typed contract instance
// const typedResult = await typedContract.query.greet()
// console.log('Result from typed contract: ', typedResult.value)
// } catch (e) {
// console.error(e)
// toast.error('Error while fetching greeting. Try again…')
// setGreeterMessage(undefined)
// } finally {
// setFetchIsLoading(false)
// }
// }

// Fetch messages
const fetchMessages = async () => {
if (!activeAccount || !contract || !activeSigner || !api) {
toast.error('Wallet not connected. Try again…')
return
}

try {
const result = await contractQuery(api, activeAccount.address, contract, 'getMessages', {}, [
chatroomId,
])
const { output, isError, decodedOutput } = decodeOutput(result, contract, 'getMessages')
if (isError) throw new Error(decodedOutput)
console.log('messages: ', output)
setMessages(output)
} catch (e) {
console.error(e)
toast.error('Error while loading chatroom. Try again…')
setFetchIsLoading(false)
} finally {
setFetchIsLoading(false)
}
}
const { activeChain } = useInkathon()
const { isChatroomLoading, isChatroomActive, messages, isMessagesLoading } =
useContext(AppContext)
console.log('ischatroomactive', isChatroomActive)

// Connection Loading Indicator
if (fetchIsLoading || !activeChatroom)
if (isChatroomLoading)
return (
<div className="mb-4 mt-8 flex flex-col items-center justify-center space-y-3 text-center font-mono text-sm text-gray-400 sm:flex-row sm:space-x-3 sm:space-y-0">
<div className="my-8 flex w-full flex-col items-center justify-center space-y-3 text-center font-mono text-sm text-gray-400 sm:flex-row sm:space-x-3 sm:space-y-0">
<Spinner />
<div>
Connecting to {activeChain?.name} ({activeChain?.rpcUrls?.[0]})
</div>
<div>Connecting to chatrooms</div>
</div>
)
if (!isChatroomLoading && !isChatroomActive)
// No chatroom found
return (
<div className="my-8 flex w-full flex-col items-center justify-center space-y-3 text-center font-mono text-sm text-gray-400 sm:flex-row sm:space-x-3 sm:space-y-0">
<BsChatSquareQuote />
<div>No chatroom found</div>
</div>
)

Expand Down
86 changes: 15 additions & 71 deletions frontend/src/components/web3/new-chatroom-button.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import { useState } from 'react'
import { useContext, useState } from 'react'

import { ContractIds } from '@/deployments/deployments'
import ChatroomContract from '@inkathon/contracts/typed-contracts/contracts/chatroom'
import {
useInkathon,
useRegisteredContract,
useRegisteredTypedContract,
} from '@scio-labs/use-inkathon'
import toast from 'react-hot-toast'
import { AppContext } from '@/context/app-context'
import { useInkathon, useRegisteredContract } from '@scio-labs/use-inkathon'
import { FiChevronDown } from 'react-icons/fi'

import { contractTxWithToast } from '@/utils/contract-tx-with-toast'
import { env } from '@/config/environment'

import { Button } from '../ui/button'
import {
Expand All @@ -22,81 +16,31 @@ import {
import { Input } from '../ui/input'

const NewChatRoomButton = () => {
const { api, activeAccount, activeSigner } = useInkathon()
const { contract, address: contractAddress } = useRegisteredContract(ContractIds.Chatroom)
const { typedContract } = useRegisteredTypedContract(ContractIds.Chatroom, ChatroomContract)
const [chatroomId, setChatroomId] = useState('5CqRGE6QMZUxh8anBchE69P8gt3sojPtwNQkmpKwWPz9yPRB')
const { activeAccount } = useInkathon()
const [participantId, setParticipantId] = useState<string>()

const supportedChains: any[] = [
{
name: 'Aleph Zero',
description: 'Lorem ipsum',
},
{
name: 'Accurast',
description: 'Lorem ipsum',
},
]
const { isChatroomActive, createChatroom, deleteChatroom, inviteFriends } = useContext(AppContext)

const handleCreateChat = async (chain: any) => {
if (!activeAccount || !contract || !activeSigner || !api) {
toast.error('Wallet not connected. Try again…')
return
}

try {
await contractTxWithToast(api, activeAccount.address, contract, 'createChatroom', {}, [])
} catch (e) {
console.error(e)
} finally {
// fetchMessages()
if (chain.name == 'Aleph Zero') {
await createChatroom()
} else if (chain.name == 'Accurast') {
await createChatroom()
}
}

const handleDeleteChatroom = async () => {
if (!activeAccount || !contract || !activeSigner || !api) {
toast.error('Wallet not connected. Try again…')
return
}

// TODO include check that caller must be owner

try {
await contractTxWithToast(api, activeAccount.address, contract, 'deleteChatroom', {}, [
chatroomId,
])
} catch (e) {
console.error(e)
} finally {
// fetchMessages()
}
await deleteChatroom()
}

const handleInviteFriends = async () => {
if (!activeAccount || !contract || !activeSigner || !api) {
toast.error('Wallet not connected. Try again…')
return
}
if (!participantId) return

// TODO include check that caller must be owner
console.log('invite', chatroomId, participantId)

try {
await contractTxWithToast(api, activeAccount.address, contract, 'invite', {}, [
chatroomId,
participantId,
])
} catch (e) {
console.error(e)
} finally {
// fetchMessages()
}
await inviteFriends([participantId])
}

return (
<div className="mt-8 w-full">
{api ? ( // TODO get value of active chatroom
{activeAccount && isChatroomActive ? ( //
<DropdownMenu>
<DropdownMenuTrigger
asChild
Expand All @@ -119,7 +63,7 @@ const NewChatRoomButton = () => {
className="no-scrollbar max-h-[40vh] w-full min-w-[20rem] overflow-scroll rounded-2xl"
>
{/* Supported Chains */}
{supportedChains.map((chain) => (
{env.chatroomChains.map((chain) => (
<DropdownMenuItem
key={`network-${chain.name}`}
className="cursor-pointer"
Expand Down
36 changes: 9 additions & 27 deletions frontend/src/components/web3/send-message.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
'use client'

import { FC, useState } from 'react'
import { FC, useContext } from 'react'

import { AppContext } from '@/context/app-context'
import { ContractIds } from '@/deployments/deployments'
import { zodResolver } from '@hookform/resolvers/zod'
import ChatroomContract from '@inkathon/contracts/typed-contracts/contracts/chatroom'
import {
useInkathon,
useRegisteredContract,
useRegisteredTypedContract,
} from '@scio-labs/use-inkathon'
import { useInkathon, useRegisteredContract } from '@scio-labs/use-inkathon'
import { SubmitHandler, useForm } from 'react-hook-form'
import toast from 'react-hot-toast'
import * as z from 'zod'

import { Button } from '@/components/ui/button'
import { Form, FormControl, FormItem } from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { contractTxWithToast } from '@/utils/contract-tx-with-toast'

const formSchema = z.object({
newMessage: z.string().min(1).max(400),
})

export const SendMessage: FC = () => {
const { api, activeAccount, activeSigner } = useInkathon()
const { contract, address: contractAddress } = useRegisteredContract(ContractIds.Chatroom)
const { typedContract } = useRegisteredTypedContract(ContractIds.Chatroom, ChatroomContract)
const [chatroomId, setChatroomId] = useState('5CqRGE6QMZUxh8anBchE69P8gt3sojPtwNQkmpKwWPz9yPRB')
const { chatroomId, sendMessage } = useContext(AppContext)

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
Expand All @@ -36,23 +29,12 @@ export const SendMessage: FC = () => {
const { register, reset, handleSubmit } = form

// send a message
const sendMessage: SubmitHandler<z.infer<typeof formSchema>> = async ({ newMessage }) => {
if (!activeAccount || !contract || !activeSigner || !api) {
toast.error('Wallet not connected. Try again…')
const handleSendMessage: SubmitHandler<z.infer<typeof formSchema>> = async ({ newMessage }) => {
toast.error('Please type a message')
if (newMessage == '') {
return
}

try {
await contractTxWithToast(api, activeAccount.address, contract, 'sendMessage', {}, [
chatroomId,
newMessage,
])
reset()
} catch (e) {
console.error(e)
} finally {
// refresh messages
}
await sendMessage(newMessage)
}

// if (!api) return null
Expand All @@ -66,7 +48,7 @@ export const SendMessage: FC = () => {
{/* <Card> */}
{/* <CardContent className="pt-6"> */}
<form
onSubmit={handleSubmit(sendMessage)}
onSubmit={handleSubmit(handleSendMessage)}
className="flex w-full flex-col justify-end gap-2"
>
<FormItem>
Expand Down
Loading

0 comments on commit 6759d75

Please sign in to comment.