Skip to content

Commit

Permalink
feat: implement useSendSol hook for sending SOL transactions and add …
Browse files Browse the repository at this point in the history
…AboutSection component
  • Loading branch information
yarre-uk committed Jan 10, 2025
1 parent a281b9c commit d3ba606
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 16 deletions.
41 changes: 41 additions & 0 deletions src/components/page/game/about.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';

export const AboutSection = () => {
return (
<div className="grid w-full grid-cols-2 bg-[url('/about.png')] bg-repeat font-inknut *:pb-[60px] *:pt-[80px]">
<div className="flex h-full min-h-full w-full flex-col justify-between border-r border-black pl-[140px] pr-[100px]">
<div className="space-y-8">
<p className="text-[60px] font-light leading-[72px]">About Tarot</p>
<p className="text-[20px] font-light leading-[28px]">
Lorem ipsum dolor sit amet consectetur. Posuere auctor vivamus sed leo non pellentesque. Massa sed eget est
porta facilisis rhoncus mauris. Pellentesque malesuada morbi volutpat dictum. Sollicitudin suspendisse
aliquam imperdiet rutrum interdum. Dignissim in diam vestibulum sodales nibh nec scelerisque id. Sit
facilisi vestibulum etiam nunc a. Ullamcorper amet sed etiam ac adipiscing mauris cursus blandit vitae.
Lacus eget gravida morbi condimentum lobortis a. Blandit gravida dictum mollis scelerisque feugiat dis.
</p>
</div>

<img src="/sun.png" alt="tarot1" className="!mt-auto ml-auto h-[320px] w-[305px]" />
</div>

<div className="w-full space-y-8 pl-[100px] pr-[140px]">
<p className="text-[60px] font-light leading-[72px]">Rules</p>

<div className="flex w-full max-w-full flex-row justify-between overflow-x-hidden *:h-[310px] *:w-[217px]">
<img src="/libra-card.png" alt="tarot1" />
<img src="/libra-card.png" alt="tarot2" />
<img src="/libra-card.png" alt="tarot3" />
</div>

<Accordion type="single" defaultValue="item-0">
{new Array(6).fill(0).map((_, i) => (
<AccordionItem value={`item-${i}`} key={i}>
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>Yes. It adheres to the WAI-ARIA design pattern.</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
</div>
);
};
67 changes: 62 additions & 5 deletions src/components/pages/game/game.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useWallet } from '@solana/wallet-adapter-react';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { toast } from 'react-toastify';
import { z } from 'zod';

import Solana from '@/components/common/Svg/Solana.tsx';
import useMakePrediction from '@/hooks/contracts/write/use-make-prediction';
import useSendSol from '@/hooks/contracts/write/use-send-sol';
import { cn } from '@/lib/utils';
import { useWalletModalStore } from '@/store/wallet-modal.tsx';

const TarotRequestSchema = z.object({
Expand All @@ -23,6 +26,9 @@ export const GameSection = () => {
const { publicKey } = useWallet();
const { setIsOpen } = useWalletModalStore();
const { mutateAsync: transfer, isSuccess, isPending, data: predictionAnswer } = useMakePrediction();
const { mutateAsync: transferSol, isPending: isSolPending } = useSendSol();

const [selectedTip, setSelectedTip] = useState<number>(0);

const {
register,
Expand All @@ -40,6 +46,20 @@ export const GameSection = () => {
await transfer(watch('question').trim());
};

const handleTip = async () => {
if (!publicKey) {
toast.error('Connect wallet first');
return;
}

if (!selectedTip) {
toast.error('Select tip first');
return;
}

await transferSol(selectedTip);
};

useEffect(() => {
if (predictionAnswer) {
setValue('question', predictionAnswer.answer);
Expand All @@ -49,6 +69,7 @@ export const GameSection = () => {
return (
<div className="container flex flex-col gap-[20px] py-[20px] font-inknut">
<div className="text-center font-bona-nova-sc text-[50px]">Your Future In One Bet</div>

<div className="relative -z-50">
{predictionAnswer && (
<div className="absolute flex h-full w-full flex-row justify-evenly py-4">
Expand All @@ -59,32 +80,36 @@ export const GameSection = () => {
)}
<img src="images/tarot-game/bord.png" alt="bord" className="relative -z-50" />
</div>

<div className="flex flex-row items-center justify-between">
<div className="text-[24px]">Type your question and ask the cards</div>
<button className="rounded-[8px] border border-[#3A3939] bg-[#E8D5AF] px-[20px] py-[13px] text-[22px]">
Suggest question
</button>
</div>

<div className="grid">
<div className="text-red-700"> {errors.question?.message ? 'min 3 symbols' : '⠀'} </div>
<textarea
{...register('question')}
className="rounded-[8px] border border-[#3A3939] bg-transparent p-4 placeholder-[#3A3939]"
className="rounded-[8px] border border-[#3A3939] bg-transparent p-4 placeholder-[#3A3939] outline-none"
placeholder="Type your question here"
disabled={isPending}
rows={7}
/>
</div>

<div className="grid grid-cols-2 gap-10">
<div className="flex flex-row items-center gap-4 rounded-[8px] border border-[#3A3939] bg-[#D0C7A3] p-[14px] text-[20px]">
<Solana />
<div className="font-poppins">0.002 $SOL</div>
<div className="font-poppins">0.002 SOL</div>
</div>

{publicKey ? (
<button
onClick={handleSubmit(onSubmit)}
disabled={isPending}
className="rounded-[8px] border border-[#3A3939] bg-[#9DA990] text-[22px] text-[#4F5548]"
className="rounded-[8px] border border-[#3A3939] bg-[#9DA990] text-[22px] text-black disabled:text-[#4F5548]"
>
Make a Forecast
</button>
Expand All @@ -93,12 +118,44 @@ export const GameSection = () => {
onClick={() => {
setIsOpen(true);
}}
className="rounded-[8px] border border-[#3A3939] bg-[#9DA990] text-[22px] text-[#4F5548]"
className="rounded-[8px] border border-[#3A3939] bg-[#9DA990] text-[22px] text-black disabled:text-[#4F5548]"
>
Connect Wallet
</button>
)}
</div>

<div className="grid grid-rows-[auto_auto] gap-5 lg:grid-cols-2 lg:gap-10">
<div className="grid grid-cols-2 gap-[20px] md:grid-cols-5">
<div className="flex w-full items-center justify-center rounded-[8px] border border-[#3A3939] bg-[#D0C7A3] p-[14px] text-[20px] max-md:col-span-2">
<Solana />
</div>

{[0.002, 0.004, 0.02, 0.5].map((tip) => (
<div
key={tip}
onClick={() => {
setSelectedTip(selectedTip === tip ? 0 : tip);
}}
className={cn(
selectedTip === tip ? '!bg-[#9DA990]' : '',
'flex w-full items-center justify-center rounded-[8px] border border-[#3A3939] bg-[#D0C7A3] p-[14px] font-poppins text-[20px]',
'cursor-pointer select-none',
)}
>
{tip}
</div>
))}
</div>

<button
onClick={handleTip}
disabled={isSolPending || !publicKey}
className="h-[60px] rounded-[8px] border border-[#3A3939] bg-[#9DA990] text-[22px] text-black disabled:text-[#4F5548]"
>
Thank the Oracle
</button>
</div>
</div>
);
};
28 changes: 20 additions & 8 deletions src/hooks/contracts/write/use-make-prediction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useWallet } from '@solana/wallet-adapter-react';
import { SystemProgram, Transaction } from '@solana/web3.js';
import { useMutation } from '@tanstack/react-query';
import { useState } from 'react';
import { toast } from 'react-toastify';

import { OwnerAddress } from '@/constants/addresses';
Expand All @@ -10,19 +11,18 @@ import { network } from '@/lib/solana';
import { sendAndConfirmTransaction } from '@/lib/solana/utils';
import { getRandomTarotCards } from '@/lib/utils';

let toastId: string | number | null = null;

const notify = () => {
toastId = toast('Making prediction...', {
const notify = (setToastId: React.Dispatch<React.SetStateAction<string | number | null>>) => {
const id = toast('Making prediction...', {
autoClose: false,
closeOnClick: false,
draggable: false,
isLoading: true,
type: 'default',
});
setToastId(id);
};

const updateToast = () => {
const updateToast = (toastId: string | number | null) => {
if (toastId !== null) {
toast.update(toastId, {
render: 'Done!',
Expand All @@ -33,17 +33,29 @@ const updateToast = () => {
}
};

const handleErrorToast = (toastId: string | number | null) => {
if (toastId !== null) {
toast.update(toastId, {
render: 'Error occurred!',
type: 'error',
autoClose: 3000,
isLoading: false,
});
}
};

const useMakePrediction = () => {
const { publicKey, sendTransaction } = useWallet();
const { mutateAsync: submitCards } = useSubmitTarotCards();
const [toastId, setToastId] = useState<string | number | null>(null);

return useMutation({
async mutationFn(question: string) {
if (!publicKey) {
return;
}

notify();
notify(setToastId);

const rawTx = new Transaction();

Expand All @@ -56,13 +68,12 @@ const useMakePrediction = () => {
);

const txHash = await sendAndConfirmTransaction(publicKey, rawTx, sendTransaction);
console.log('txHash', txHash);

const tarots = getRandomTarotCards(txHash + publicKey.toBase58());

const result = await submitCards({ tarots, hash: txHash, question });

updateToast();
updateToast(toastId);

return {
tarots,
Expand All @@ -72,6 +83,7 @@ const useMakePrediction = () => {

onError(error) {
console.trace(error);
handleErrorToast(toastId);
},
});
};
Expand Down
39 changes: 39 additions & 0 deletions src/hooks/contracts/write/use-send-sol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useWallet } from '@solana/wallet-adapter-react';
import { SystemProgram, Transaction } from '@solana/web3.js';
import { useMutation } from '@tanstack/react-query';

import { OwnerAddress } from '@/constants/addresses';
import { network } from '@/lib/solana';
import { sendAndConfirmTransaction } from '@/lib/solana/utils';
import { showTxToast } from '@/lib/utils';

const useSendSol = () => {
const { publicKey, sendTransaction } = useWallet();

return useMutation({
async mutationFn(amount: number) {
if (!publicKey) {
return;
}

await showTxToast('Tipping the Oracle', async () => {
const rawTx = new Transaction();

rawTx.add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: OwnerAddress[network],
lamports: Number(amount) * 1e9,
}),
);

await sendAndConfirmTransaction(publicKey, rawTx, sendTransaction);
});
},
onError(error) {
console.trace(error);
},
});
};

export default useSendSol;
6 changes: 3 additions & 3 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ export const showTxToast = (methodName: string, promise: () => Promise<void>) =>
export const getRandomTarotCards = (hash: string): TarotCard[] => {
const TAROT_CARDS_AMOUNT = 78;

const splitedHash = splitStringIntoEqualParts(hash, Math.floor(hash.length / 3));
const splittedHash = splitStringIntoEqualParts(hash, Math.floor(hash.length / 3));

const tarotIds: number[] = [];

while (tarotIds.length < 3) {
let tarotId = calculateByteSum(splitedHash[tarotIds.length]) % TAROT_CARDS_AMOUNT;
let tarotId = calculateByteSum(splittedHash[tarotIds.length]) % TAROT_CARDS_AMOUNT;

while (tarotIds.includes(tarotId)) {
tarotId = (tarotId + 1) % TAROT_CARDS_AMOUNT;
Expand All @@ -37,7 +37,7 @@ export const getRandomTarotCards = (hash: string): TarotCard[] => {
tarotIds.push(tarotId);
}

const isReverted = splitedHash
const isReverted = splittedHash
.map((part) => calculateByteSum(part.slice(0, 3)))
.map((sum) => sum % 7 === 0)
.slice(0, 3);
Expand Down

0 comments on commit d3ba606

Please sign in to comment.