forked from pointer-gg/comments-with-polygon
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1086d47
commit fdaf836
Showing
10 changed files
with
325 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import * as React from "react"; | ||
import { Button, ButtonProps } from "@chakra-ui/react"; | ||
import { useAccount, useConnect } from "wagmi"; | ||
import toast from "react-hot-toast"; | ||
|
||
interface AuthButtonProps extends ButtonProps {} | ||
|
||
const AuthButton: React.FunctionComponent<AuthButtonProps> = (props) => { | ||
const [connectQuery, connect] = useConnect(); | ||
const [accountQuery] = useAccount(); | ||
|
||
React.useEffect(() => { | ||
if (connectQuery.error?.name === "ConnectorNotFoundError") { | ||
toast.error("Metamask extension required to sign in"); | ||
} | ||
}, [connectQuery.error]); | ||
|
||
// If not authenticated, require sign-in | ||
if (!accountQuery.data?.address) { | ||
return ( | ||
<Button | ||
{...props} | ||
onClick={() => { | ||
connect(connectQuery.data.connectors[0]); | ||
}} | ||
> | ||
Sign In | ||
</Button> | ||
); | ||
} | ||
|
||
// If authenticated, show button as usual | ||
return <Button {...props}>{props.children}</Button>; | ||
}; | ||
|
||
export default AuthButton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import * as React from "react"; | ||
import { Text, Heading, HStack, Stack } from "@chakra-ui/react"; | ||
import TimeAgo from "react-timeago"; | ||
import Avatar from "@davatar/react"; | ||
import Username from "./Username"; | ||
import { Comment } from "../hooks/useCommentsContract"; | ||
|
||
interface CommentProps { | ||
comment: Comment; | ||
} | ||
|
||
const Comment: React.FunctionComponent<CommentProps> = ({ comment }) => { | ||
return ( | ||
<HStack spacing={3} alignItems="start"> | ||
<Avatar size={48} address={comment.creator_address} /> | ||
<Stack spacing={1} flex={1} bg="whiteAlpha.100" rounded="2xl" p={3}> | ||
<Heading color="whiteAlpha.900" fontSize="lg"> | ||
<Username address={comment.creator_address} /> | ||
</Heading> | ||
<Text color="whiteAlpha.800" fontSize="lg"> | ||
{comment.message} | ||
</Text> | ||
<Text color="whiteAlpha.500" fontSize="md"> | ||
<TimeAgo date={comment.created_at.toNumber() * 1000} /> | ||
</Text> | ||
</Stack> | ||
</HStack> | ||
); | ||
}; | ||
|
||
export default Comment; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { HStack, Stack, Textarea } from "@chakra-ui/react"; | ||
import * as React from "react"; | ||
import Avatar from "@davatar/react"; | ||
import AuthButton from "./AuthButton"; | ||
import { useAccount } from "wagmi"; | ||
import useAddComment from "../hooks/useAddComment"; | ||
|
||
interface CommentEditorProps { | ||
topic: string; | ||
} | ||
|
||
const CommentEditor: React.FunctionComponent<CommentEditorProps> = ({ | ||
topic, | ||
}) => { | ||
const [message, setMessage] = React.useState(""); | ||
const mutation = useAddComment(); | ||
const [accountQuery] = useAccount(); | ||
|
||
return ( | ||
<Stack spacing={3}> | ||
<HStack spacing={3} alignItems="start"> | ||
<Avatar | ||
size={48} | ||
address={ | ||
accountQuery.data?.address || | ||
"0x0000000000000000000000000000000000000000" | ||
} | ||
/> | ||
<Textarea | ||
value={message} | ||
onChange={(e) => { | ||
setMessage(e.target.value); | ||
}} | ||
placeholder="Write a message.." | ||
p={3} | ||
flex={1} | ||
bg="whiteAlpha.100" | ||
rounded="2xl" | ||
fontSize="lg" | ||
/> | ||
</HStack> | ||
<AuthButton | ||
size="sm" | ||
colorScheme="pink" | ||
alignSelf="flex-end" | ||
onClick={() => { | ||
mutation | ||
.mutateAsync({ | ||
message, | ||
topic, | ||
}) | ||
.then(() => { | ||
setMessage(""); | ||
}); | ||
}} | ||
isLoading={mutation.isLoading} | ||
> | ||
Submit | ||
</AuthButton> | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default CommentEditor; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import * as React from "react"; | ||
import { Box, Spinner, Stack, Center } from "@chakra-ui/react"; | ||
import Comment from "./Comment"; | ||
import CommentEditor from "./CommentEditor"; | ||
import useComments from "../hooks/useComments"; | ||
import useEvents from "../hooks/useEvents"; | ||
|
||
interface CommentsProps { | ||
topic: string; | ||
} | ||
|
||
const Comments: React.FunctionComponent<CommentsProps> = ({ topic }) => { | ||
const query = useComments({ topic }); | ||
|
||
useEvents({ topic }); | ||
|
||
return ( | ||
<Box> | ||
{query.isLoading && ( | ||
<Center p={8}> | ||
<Spinner /> | ||
</Center> | ||
)} | ||
<Stack spacing={4}> | ||
{query.data?.map((comment) => ( | ||
<Comment key={comment.id} comment={comment} /> | ||
))} | ||
{query.isFetched && <CommentEditor topic={topic} />} | ||
</Stack> | ||
</Box> | ||
); | ||
}; | ||
|
||
export default Comments; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import * as React from "react"; | ||
import { Text, TextProps } from "@chakra-ui/react"; | ||
import { useEnsLookup } from "wagmi"; | ||
import truncateMiddle from "truncate-middle"; | ||
|
||
interface UsernameProps extends TextProps { | ||
address: string; | ||
} | ||
|
||
const Username: React.FunctionComponent<UsernameProps> = ({ | ||
address, | ||
...otherProps | ||
}) => { | ||
const [query] = useEnsLookup({ address }); | ||
|
||
// Show ens name if exists, but show truncated address as fallback | ||
return ( | ||
<Text | ||
display="inline" | ||
textTransform={query.data ? "none" : "uppercase"} | ||
{...otherProps} | ||
> | ||
{query.data || truncateMiddle(address || "", 5, 4, "...")} | ||
</Text> | ||
); | ||
}; | ||
|
||
export default Username; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { useMutation } from "react-query"; | ||
import useCommentsContract from "./useCommentsContract"; | ||
|
||
interface UseAddCommentPayload { | ||
topic: string; | ||
message: string; | ||
} | ||
|
||
const useAddComment = () => { | ||
const contract = useCommentsContract(); | ||
|
||
return useMutation(async ({ topic, message }: UseAddCommentPayload) => { | ||
await contract.addComment(topic, message); | ||
}); | ||
}; | ||
|
||
export default useAddComment; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { useQuery } from "react-query"; | ||
import useCommentsContract from "./useCommentsContract"; | ||
|
||
interface UseCommentsQuery { | ||
topic: string; | ||
} | ||
|
||
const useComments = ({ topic }: UseCommentsQuery) => { | ||
const contract = useCommentsContract(); | ||
return useQuery(["comments", { topic, chainId: contract.chainId }], () => | ||
contract.getComments(topic) | ||
); | ||
}; | ||
|
||
export default useComments; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import * as wagmi from "wagmi"; | ||
import { useProvider, useSigner } from "wagmi"; | ||
import type { BigNumber } from "ethers"; | ||
// Import our contract ABI | ||
import CommentsContract from "../artifacts/contracts/Comments.sol/Comments.json"; | ||
|
||
export interface Comment { | ||
id: string; | ||
topic: string; | ||
message: string; | ||
creator_address: string; | ||
created_at: BigNumber; | ||
} | ||
|
||
export enum EventType { | ||
CommentAdded = "CommentAdded", | ||
} | ||
|
||
const useCommentsContract = () => { | ||
// An ethers.Signer instance associated with the signed-in wallet. | ||
// https://docs.ethers.io/v5/api/signer/ | ||
const [signer] = useSigner(); | ||
// An ethers.Provider instance associated with the connected blockchain network. | ||
// (eg localhost, polygon-mumbai, ethereum-mainnet) | ||
// https://docs.ethers.io/v5/api/providers/ | ||
const provider = useProvider(); | ||
|
||
// This returns a new ethers.Contract ready to interact with our comments API. | ||
// We need to pass in the address of our deployed contract as well as its abi. | ||
// We also pass in the signer if there is a signed in wallet, or if there's | ||
// no signed in wallet then we'll pass in the connected provider. | ||
const contract = wagmi.useContract({ | ||
addressOrName: "0x5FbDB2315678afecb367f032d93F642f64180aa3", | ||
contractInterface: CommentsContract.abi, | ||
signerOrProvider: signer.data || provider, | ||
}); | ||
|
||
// Wrapper to add types to our getComments function. | ||
const getComments = async (topic: string): Promise<Comment[]> => { | ||
return contract.getComments(topic).then((comments) => { | ||
// Each comment is represented as array by default so we convert to object | ||
return comments.map((c) => ({ ...c })); | ||
}); | ||
}; | ||
|
||
// Wrapper to add types to our addComment function. | ||
const addComment = async (topic: string, message: string): Promise<void> => { | ||
// Create a new transaction | ||
const tx = await contract.addComment(topic, message); | ||
// Wait for transaction to be mined | ||
await tx.wait(); | ||
}; | ||
|
||
return { | ||
contract, | ||
chainId: contract.provider.network?.chainId, | ||
getComments, | ||
addComment, | ||
}; | ||
}; | ||
|
||
export default useCommentsContract; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { useEffect } from "react"; | ||
import { useQueryClient } from "react-query"; | ||
import useCommentsContract, { EventType } from "./useCommentsContract"; | ||
|
||
interface UseEventsQuery { | ||
topic: string; | ||
} | ||
|
||
// Listen to events and refresh data | ||
const useEvents = ({ topic }: UseEventsQuery) => { | ||
const queryClient = useQueryClient(); | ||
const commentsContract = useCommentsContract(); | ||
|
||
useEffect(() => { | ||
const handler = (comment) => { | ||
if (comment.topic !== topic) { | ||
return; | ||
} | ||
// Invalidates the query whose query key matches the passed array. | ||
// This will cause the useComments hook to re-render the Comments | ||
// component with fresh data. | ||
queryClient.invalidateQueries([ | ||
"comments", | ||
{ topic: comment.topic, chainId: commentsContract.chainId }, | ||
]); | ||
}; | ||
|
||
commentsContract.contract.on(EventType.CommentAdded, handler); | ||
|
||
return () => { | ||
commentsContract.contract.off(EventType.CommentAdded, handler); | ||
}; | ||
}, [queryClient, commentsContract.chainId, topic]); | ||
}; | ||
|
||
export default useEvents; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters