diff --git a/.env.qa b/.env.qa index 9358379..cf375c7 100644 --- a/.env.qa +++ b/.env.qa @@ -1,10 +1,10 @@ mode=qa VITE_BACKEND_API_URL=http://35.208.209.92:3000/ VITE_REDUX_DEBUG=true -#NPM_TOKEN=secret -#GSAP_TOKEN=secret -#VITE_RPC_PROVIDER_ALCHEMY=secret -#VITE_WALLET_CONNECT_ID=secret +NPM_TOKEN=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 +GSAP_TOKEN=5b8296c2-d882-401b-be86-87707a83f99d +VITE_RPC_PROVIDER_ALCHEMY=y9Gx-Wglr_8PQ67heuNUZ18rwoUEip9B +VITE_WALLET_CONNECT_ID=210ab6fbbd6f9d07fd69a357b4c39a13 # fire base configs VITE_FIREBASE_API_KEY=AIzaSyD6NTn82GLXFGWGWKNsmmwhohOHJXaHaoQ @@ -14,3 +14,4 @@ VITE_FIREBASE_PROJECT_ID=inspired-bebop-399607 VITE_FIREBASE_STORAGE_BUCKET=inspired-bebop-399607.appspot.com VITE_FIREBASE_MESSAGING_SENDER_ID=368714610861 VITE_FIREBASE_APP_ID=1:368714610861:web:25a05efd0381cbd552ee24 + \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..2757241 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "blackboxapp.blackbox" + ] +} \ No newline at end of file diff --git a/src/app/blockchainApiSlice.ts b/src/app/blockchainApiSlice.ts index e09962b..76e3179 100644 --- a/src/app/blockchainApiSlice.ts +++ b/src/app/blockchainApiSlice.ts @@ -9,6 +9,4 @@ export const blockchainApiSlice = createApi({ baseQuery, reducerPath: baseUrl, endpoints: () => ({}) -}); - - \ No newline at end of file +}); \ No newline at end of file diff --git a/src/common/CoinSide/index.tsx b/src/common/CoinSide/index.tsx index fe42b56..118bf4a 100644 --- a/src/common/CoinSide/index.tsx +++ b/src/common/CoinSide/index.tsx @@ -1,10 +1,10 @@ -import { CoinSideHeadsIcon, CoinSideTailsIcon } from "../../assets/icons"; +import { CoinSideHeadsIcon, CoinSideTailsIconWithCircle } from "../../assets/icons"; import { CoinSideTypes, CoinSideTypesEnum, AllPossibleCoinSides } from "../../models"; import 'twin.macro'; export const COIN_SIDE_COMPONENTS: any = { [CoinSideTypes[CoinSideTypesEnum.HEADS]]: CoinSideHeadsIcon, - [CoinSideTypes[CoinSideTypesEnum.TAILS]]: CoinSideTailsIcon, + [CoinSideTypes[CoinSideTypesEnum.TAILS]]: CoinSideTailsIconWithCircle, } export interface CoinSideProps extends React.Attributes, React.AllHTMLAttributes { diff --git a/src/common/ToggleCoinFlipSides/index.tsx b/src/common/ToggleCoinFlipSides/index.tsx index 5d132e0..341e963 100644 --- a/src/common/ToggleCoinFlipSides/index.tsx +++ b/src/common/ToggleCoinFlipSides/index.tsx @@ -46,7 +46,7 @@ export const ToggleCoinFlipSides: React.FC = ({ setCur // replace gradient border with other approach return (
{/* */} { - isWagerApproved ? - : } {/* button approve */} diff --git a/src/components/CreateGameCard/blockchainApi.ts b/src/components/CreateGameCard/blockchainApi.ts deleted file mode 100644 index 5189256..0000000 --- a/src/components/CreateGameCard/blockchainApi.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -import { utils } from 'ethers'; -import { prepareWriteContract } from '@wagmi/core'; -import { gameControllerConfig } from "../../utils"; -import { HexishString, AllPossibleWegaTypes } from '../../models'; -import { BlockchainAPIBase } from '../../libs/wagmi' - -export class CreateGameBlockchainApi extends BlockchainAPIBase { - private gameControllerConfig = { - address: gameControllerConfig.address[this.chain?.id as keyof typeof gameControllerConfig.address] as HexishString, - abi: gameControllerConfig.abi, - } - public functions = { - createWagerAndDeposit: async ({ tokenAddress, gameType, wager }: { - tokenAddress: HexishString - wager: number, - gameType: AllPossibleWegaTypes, - }) => { - const wagerAsBigint = utils.parseEther(String(wager)).toBigInt() - const config = await prepareWriteContract({ - ...this.gameControllerConfig, - functionName: 'createGame', - args: [gameType, tokenAddress, wagerAsBigint] - }) - return await this.handleWriteRequest(config); - } - } - constructor(baseUrl: string | undefined = undefined) { - super(baseUrl); - } -} diff --git a/src/components/Dice/animations.ts b/src/components/Dice/animations.ts index d30da40..9e7e4aa 100644 --- a/src/components/Dice/animations.ts +++ b/src/components/Dice/animations.ts @@ -33,7 +33,7 @@ export function useRoll(diceRef: any, onBegin?: Callback, onEnd?: Callback) { setAnimationTarget(String(rollDestination * 100)); setStrigger(s => !s); if(isGameOver) { - setTimeout(() => setRolled(true), 3000); + setTimeout(() => setRolled(true), 4000); } }; diff --git a/src/components/Dice/images/WegaDice.tsx b/src/components/Dice/images/WegaDice.tsx index 5665591..88c62ba 100644 --- a/src/components/Dice/images/WegaDice.tsx +++ b/src/components/Dice/images/WegaDice.tsx @@ -270,4 +270,6 @@ const SvgComponent = forwardRef((props: SVGProps, ref: any) => ( )) -export default SvgComponent; \ No newline at end of file +export default SvgComponent; + +// transform: translateY(69px); \ No newline at end of file diff --git a/src/components/Dice/types.ts b/src/components/Dice/types.ts index 7b9b083..ad20cdf 100644 --- a/src/components/Dice/types.ts +++ b/src/components/Dice/types.ts @@ -13,10 +13,7 @@ export const DiceContainer = styled.div` background: rgba(75, 75, 75, 0.30 ); backdrop-filter: blur(15px); ${tw`border border-blanc border-[1.5px]`} - - > svg { - border-radius: 5px; - } + border-radius: 5px; ` export const DiceWrapper = styled.div` position: relative; diff --git a/src/components/JoinGameCard/JoinCoinFlipGameCard.tsx b/src/components/JoinGameCard/JoinCoinFlipGameCard.tsx index ab8c6eb..a1c70be 100644 --- a/src/components/JoinGameCard/JoinCoinFlipGameCard.tsx +++ b/src/components/JoinGameCard/JoinCoinFlipGameCard.tsx @@ -18,7 +18,8 @@ import { AllPossibleWegaTypes, AllPossibleCoinSides, WegaAttributes, - WegaState + WegaState, + Network } from "../../models"; import { BadgeIcon, @@ -32,10 +33,11 @@ import { ArrowDownIcon, StarLoaderIcon } from '../../assets/icons'; import tw from 'twin.macro'; import { useForm } from 'react-hook-form'; import { useBalance } from 'wagmi'; -import { useBlockchainApiHooks, useAppSelector, useNavigateTo } from '../../hooks'; -import { selectWagerApproved } from '../../api/blockchain/blockchainSlice'; +import { useNavigateTo } from '../../hooks'; +import { useDepositAndJoinCoinflipMutation } from './blockchainApiSlice'; +import { useAllowanceQuery, useApproveERC20Mutation } from '../CreateGameCard/blockchainApiSlice'; import toast from 'react-hot-toast'; -import { toastSettings } from '../../utils'; +import { toastSettings, escrowConfig, toBigIntInWei } from '../../utils'; import Button from '../../common/Button'; import { useFormReveal } from '../CreateGameCard/animations'; import { useJoinGameMutation, useUpdateGameMutation } from '../../containers/App/api'; @@ -54,6 +56,7 @@ export interface JoinCoinFlipGameCardProps extends React.Attributes, React.AllHT escrowId: HexishString; gameId: number; gameAttributes: WegaAttributes; + network: Network; } const JoinCoinFlipGameCard = ({ @@ -68,6 +71,7 @@ const JoinCoinFlipGameCard = ({ escrowId, gameId, gameAttributes, + network, // eslint-disable-next-line @typescript-eslint/no-unused-vars css, ...rest @@ -77,17 +81,9 @@ const JoinCoinFlipGameCard = ({ const detailsBlock = useRef(null) const [currentWagerType] = useState(wagerType); const [currentCurrencyType] = useState(currencyType); - const isWagerApproved = useAppSelector(state => selectWagerApproved(state)); const {revealed, triggerRevealAnimation} = useFormReveal(false, formRef, detailsBlock); const [currentCoinSide] = useState(gameAttributes && Number(gameAttributes[0].value) === 1 ? 2 : 1); - - const { - useAllowanceQuery, - useApproveERC20Mutation, - useDepositWagerMutation, - } = useBlockchainApiHooks; - const { register, formState: { errors }, handleSubmit } = useForm({ mode: 'onChange', resolver: joiResolver(createGameSchema('wager', wagerAmount)) , @@ -98,7 +94,12 @@ const JoinCoinFlipGameCard = ({ }); // approval for allowance - const { isLoading: isGetAllowanceLoading, allowance } = useAllowanceQuery(); + const isWagerApproved = (allowance: number, wagerAmount: number) => allowance >= wagerAmount; + const allowanceQuery = useAllowanceQuery({ + spender: escrowConfig.address[network.id as keyof typeof escrowConfig.address], + owner: playerAddress, + tokenAddress, + }); // get token balance of userP const { data: userWagerBalance, isLoading: isWagerbalanceLoading } = useBalance({ @@ -106,14 +107,18 @@ const JoinCoinFlipGameCard = ({ token: tokenAddress, }) - const { isLoading: isDepositWagerLoading, depositWager } = useDepositWagerMutation(); - const [ joinGame, { isLoading: isJoinGameLoading } ] = useJoinGameMutation(); - const [ updateGame, { isLoading: isUpdateGameLoading } ] = useUpdateGameMutation(); + const [updateGame, updateGameQuery] = useUpdateGameMutation(); + const [depositAndJoinCoinflip, depositAndJoinQuery] = useDepositAndJoinCoinflipMutation(); + const [joinGame, joinGameQuery] = useJoinGameMutation(); + const [approveERC20, approveERC20Query] = useApproveERC20Mutation(); const handleDepositWagerClick = async () => { try { + if(!isWagerApproved(allowanceQuery.data, wagerAmount)) { + await approveERC20({ spender: escrowConfig.address[network.id as keyof typeof escrowConfig.address], wagerAsBigint: toBigIntInWei(wagerAmount), tokenAddress }).unwrap(); + } const playerChoices = [Number(gameAttributes[0].value), currentCoinSide]; - await depositWager(escrowId, playerChoices).unwrap(); + await depositAndJoinCoinflip({escrowHash: escrowId, playerChoices }).unwrap(); await joinGame({ newPlayerUuid: playerUuid, gameUuid }).unwrap(); await updateGame({ uuid: gameUuid, @@ -136,22 +141,16 @@ const JoinCoinFlipGameCard = ({ toast.error(message, { ...toastSettings('error', 'bottom-center') as any }); } } - - // approve token - const { isLoading: isApproveERC20Loading, approveERC20 } = useApproveERC20Mutation(); - const handleApproveWagerClick = ({ wager }: { wager: number }) => { - approveERC20(tokenAddress, wager); - }; - // + const navigateToGameUi = useNavigateTo() useEffect(() => { - allowance(tokenAddress, playerAddress, wagerAmount); + allowanceQuery.refetch(); }, [tokenAddress, wagerAmount, isWagerApproved]); return ( { @@ -187,12 +186,14 @@ const JoinCoinFlipGameCard = ({ {/* */} { - isWagerApproved ? : } {/* details */} diff --git a/src/components/JoinGameCard/JoinDiceGamecard.tsx b/src/components/JoinGameCard/JoinDiceGamecard.tsx index be72954..172deea 100644 --- a/src/components/JoinGameCard/JoinDiceGamecard.tsx +++ b/src/components/JoinGameCard/JoinDiceGamecard.tsx @@ -14,7 +14,7 @@ import { import { AllPossibleCurrencyTypes, AllPossibleWagerTypes, - WegaState + WegaState, } from "../../models"; import { BadgeIcon, @@ -28,10 +28,11 @@ import { ArrowDownIcon, StarLoaderIcon } from '../../assets/icons'; import tw from 'twin.macro'; import { useForm } from 'react-hook-form'; import { useBalance } from 'wagmi'; -import { useBlockchainApiHooks, useAppSelector, useNavigateTo } from '../../hooks'; -import { selectWagerApproved } from '../../api/blockchain/blockchainSlice'; +import { useNavigateTo } from '../../hooks'; +import { useDepositAndJoinDiceMutation } from './blockchainApiSlice'; +import { useAllowanceQuery, useApproveERC20Mutation } from '../CreateGameCard/blockchainApiSlice'; import toast from 'react-hot-toast'; -import { toastSettings } from '../../utils'; +import { toastSettings, escrowConfig, toBigIntInWei } from '../../utils'; import Button from '../../common/Button'; import { useFormReveal } from '../CreateGameCard/animations'; import { useJoinGameMutation, useUpdateGameMutation } from '../../containers/App/api'; @@ -50,6 +51,7 @@ const JoinGameDiceCard: React.FC = ({ wagerAmount, escrowId, gameId, + network, // eslint-disable-next-line @typescript-eslint/no-unused-vars css, ...rest @@ -59,16 +61,9 @@ const JoinGameDiceCard: React.FC = ({ const detailsBlock = useRef(null) const [currentWagerType] = useState(wagerType); const [currentCurrencyType] = useState(currencyType); - const isWagerApproved = useAppSelector(state => selectWagerApproved(state)); const {revealed, triggerRevealAnimation} = useFormReveal(false, formRef, detailsBlock); // should go into blockchain api slice - const { - useAllowanceQuery, - useApproveERC20Mutation, - useDepositWagerMutation, - } = useBlockchainApiHooks; - const { register, formState: { errors }, handleSubmit } = useForm({ mode: 'onChange', resolver: joiResolver(createGameSchema('wager', wagerAmount)) , @@ -79,20 +74,30 @@ const JoinGameDiceCard: React.FC = ({ }); // approval for allowance - const { isLoading: isGetAllowanceLoading, allowance } = useAllowanceQuery(); - + const isWagerApproved = (allowance: number, wagerAmount: number) => allowance >= wagerAmount; + const allowanceQuery = useAllowanceQuery({ + spender: escrowConfig.address[network.id as keyof typeof escrowConfig.address], + owner: playerAddress, + tokenAddress, + }); + // get token balance of user const { data: userWagerBalance, isLoading: isWagerbalanceLoading } = useBalance({ address: playerAddress, token: tokenAddress, }) - const [ updateGame, { isLoading: isUpdateGameLoading } ] = useUpdateGameMutation(); - const { isLoading: isDepositWagerLoading, depositWager } = useDepositWagerMutation(); - const [ joinGame, { isLoading: isJoinGameLoading } ] = useJoinGameMutation(); + const [updateGame, updateGameQuery] = useUpdateGameMutation(); + const [depositAndJoinDice, depositAndJoinQuery] = useDepositAndJoinDiceMutation(); + const [joinGame, joinGameQuery] = useJoinGameMutation(); + const [approveERC20, approveERC20Query] = useApproveERC20Mutation(); + const handleDepositWagerClick = async () => { try { - await depositWager(escrowId).unwrap(); + if(!isWagerApproved(allowanceQuery.data, wagerAmount)) { + await approveERC20({ spender: escrowConfig.address[network.id as keyof typeof escrowConfig.address], wagerAsBigint: toBigIntInWei(wagerAmount), tokenAddress }).unwrap(); + } + await depositAndJoinDice({ escrowHash: escrowId }).unwrap(); await joinGame({ newPlayerUuid: playerUuid, gameUuid }).unwrap(); await updateGame({ uuid: gameUuid, state: WegaState.PLAYING }).unwrap(); navigateToGameUi(`/${gameType.toLowerCase()}/play/${gameUuid}`, 1500, { replace: true, state: { gameId: gameId, gameUuid } }); @@ -104,21 +109,17 @@ const JoinGameDiceCard: React.FC = ({ } } - // approve token - const { isLoading: isApproveERC20Loading, approveERC20 } = useApproveERC20Mutation(); - const handleApproveWagerClick = ({ wager }: { wager: number }) => { - approveERC20(tokenAddress, wager); - }; + const navigateToGameUi = useNavigateTo() useEffect(() => { - allowance(tokenAddress, playerAddress, wagerAmount); + allowanceQuery.refetch(); }, [tokenAddress, wagerAmount, isWagerApproved]); return ( @@ -150,13 +151,15 @@ const JoinGameDiceCard: React.FC = ({ {/* */} { - isWagerApproved ? : + } {/* details */} {/* wager */} diff --git a/src/components/JoinGameCard/blockchainApiSlice.ts b/src/components/JoinGameCard/blockchainApiSlice.ts new file mode 100644 index 0000000..88372ca --- /dev/null +++ b/src/components/JoinGameCard/blockchainApiSlice.ts @@ -0,0 +1,33 @@ +import { blockchainApiSlice } from '../../app/blockchainApiSlice'; +import { HexishString } from '../../models'; +import { ContractTypes } from '../../libs/wagmi'; + +// Todo + // write function names with type safety +export const joinGameBlockchainApiSlice = blockchainApiSlice.injectEndpoints({ + endpoints: (builder) => ({ + depositAndJoinDice: builder.mutation({ + query: ({ escrowHash }) => ({ + functionName: 'depositOrPlay', + contract: ContractTypes.GAMECONTROLLER, + method: 'WRITE', + args: [escrowHash] + }) + }), + depositAndJoinCoinflip: builder.mutation({ + query: ({ escrowHash, playerChoices }) => ({ + functionName: 'depositOrPlay', + contract: ContractTypes.GAMECONTROLLER, + method: 'WRITE', + args: [escrowHash, playerChoices] + }) + }), + }) +}); + +export const { + useDepositAndJoinDiceMutation, + useDepositAndJoinCoinflipMutation, +} = joinGameBlockchainApiSlice; + + \ No newline at end of file diff --git a/src/components/JoinGameCard/index.tsx b/src/components/JoinGameCard/index.tsx index 2461e48..072fa35 100644 --- a/src/components/JoinGameCard/index.tsx +++ b/src/components/JoinGameCard/index.tsx @@ -5,7 +5,8 @@ import { AllPossibleWegaTypes, WegaTypesEnum, WegaTypes, - WegaAttributes + WegaAttributes, + Network, } from "../../models"; import JoinDiceGameCard from './JoinDiceGamecard'; import JoinCoinFlipGameCard from './JoinCoinFlipGameCard'; @@ -22,6 +23,7 @@ export interface JoinGameCardProps extends React.Attributes, React.AllHTMLAttrib escrowId: HexishString; gameId: number; gameAttributes?: WegaAttributes; + network: Network; } const JOIN_GAME_CARD_COMPONENTS: any = { @@ -41,6 +43,7 @@ const JoinGameCard = ({ escrowId, gameId, children, + network, ...rest }: JoinGameCardProps ) => { const renderCard = () => { @@ -61,6 +64,7 @@ const JoinGameCard = ({ wagerAmount, escrowId, gameId, + network, ...rest, } }>{children} } else { @@ -74,7 +78,8 @@ const JoinGameCard = ({ wagerAmount, escrowId, gameUuid, - gameId, + gameId, + network, ...rest } } diff --git a/src/components/MainContainer/index.tsx b/src/components/MainContainer/index.tsx index 36273ec..2032ce0 100644 --- a/src/components/MainContainer/index.tsx +++ b/src/components/MainContainer/index.tsx @@ -3,8 +3,10 @@ import 'twin.macro'; const MainContainer: React.FC = ({ children, ...rest }: MainPropsContainerProps) => { return ( -
- {children} +
+
+ {children} +
) } diff --git a/src/components/PlayGamePlayerCard/index.tsx b/src/components/PlayGamePlayerCard/index.tsx index 57368ae..2f0a8e0 100644 --- a/src/components/PlayGamePlayerCard/index.tsx +++ b/src/components/PlayGamePlayerCard/index.tsx @@ -32,6 +32,7 @@ export const PlayGamePlayerCard = ({ coinFlipChoice, isGamePlayable, }: PlayGamePlayerCardProps) => { + console.log(coinFlipChoice) return status !== 'connecting' ? ( { @@ -50,7 +51,9 @@ export const PlayGamePlayerCard = ({ } { coinFlipChoice &&
- + { + coinFlipChoice == 1 ? : + }
}
diff --git a/src/components/PlayGameSection/PlayCoinFlipGameSection.tsx b/src/components/PlayGameSection/PlayCoinFlipGameSection.tsx index ec01f3c..ca203d4 100644 --- a/src/components/PlayGameSection/PlayCoinFlipGameSection.tsx +++ b/src/components/PlayGameSection/PlayCoinFlipGameSection.tsx @@ -19,7 +19,7 @@ import { PlayGamePlayerCard } from "../PlayGamePlayerCard" import { CoinFlip } from "../CoinFlip" import { useRoll } from "../CoinFlip/animations" import Button from "../../common/Button" -import { useUpdateGameMutation } from "../../containers/App/api" +import { useUpdateGameMutation } from "./apiSlice" import { useGlobalModalContext, MODAL_TYPES } from "../../common/modals" interface PlayGameSectionProps { diff --git a/src/components/PlayGameSection/PlayDiceGameSection.tsx b/src/components/PlayGameSection/PlayDiceGameSection.tsx index 2e72100..9b855b7 100644 --- a/src/components/PlayGameSection/PlayDiceGameSection.tsx +++ b/src/components/PlayGameSection/PlayDiceGameSection.tsx @@ -1,5 +1,4 @@ import { useEffect, useState, useRef } from 'react'; -import 'twin.macro'; import { Wega, HexishString, GameInfoType, User, Player, Wallet, WegaState } from "../../models" import { HelpCircleIcon, ClockIcon, SparkleIcon } from '../../assets/icons'; import { NormalText } from '../CreateGameCard/types'; @@ -7,9 +6,10 @@ import { PlayGameContainer, MinimumGameRounds } from './types'; import { PlayGamePlayerCard } from "../PlayGamePlayerCard"; import { Dice } from "../Dice"; import { useRoll } from '../Dice/animations'; -import Button from "../../common/Button"; -import { useUpdateGameMutation } from "../../containers/App/api"; +import { useUpdateGameMutation } from "./apiSlice"; import { useGlobalModalContext, MODAL_TYPES } from "../../common/modals"; +import Button from "../../common/Button"; +import 'twin.macro'; interface PlayGameSectionProps { game: Wega; @@ -104,14 +104,13 @@ const PlayDiceGameSection: React.FC= ({ triggerRoll(animationTarget, gameInfo.currentTurn >= maxTurns); } }, [ - gameResults.length, - players.length, + gameResults.length, + players.length, wallet?.address, gameInfo?.currentTurn, gameInfo?.currentRound, rolled ]); - console.log(gameInfo.rollerIndex, shouldCurrentPlayerRoll) return players && gameInfo && {/* orbs */} {/* timer icon row */} @@ -157,7 +156,4 @@ const PlayDiceGameSection: React.FC= ({ } } -export default PlayDiceGameSection; - -// who should roll - // is the rollerIndex \ No newline at end of file +export default PlayDiceGameSection; \ No newline at end of file diff --git a/src/components/PlayGameSection/apiSlice.ts b/src/components/PlayGameSection/apiSlice.ts new file mode 100644 index 0000000..58b15b6 --- /dev/null +++ b/src/components/PlayGameSection/apiSlice.ts @@ -0,0 +1,23 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +import { appApiSlice } from '../../app/apiSlice'; +import { type Wega } from '../../models' + +export const playGameApiSlice = appApiSlice.injectEndpoints({ + endpoints: (builder) => ({ + updateGame: builder.mutation>({ + query: ({ uuid, ...updates }) => { + return ({ + url: `/games/${uuid}`, + method: 'PATCH', + body: { uuid, ...updates } + }) + }, + }), + }), +}) +appApiSlice.enhanceEndpoints({ + addTagTypes: ['Games'], + endpoints: { createGame: { invalidatesTags: [ { type: 'Games', id: 'LIST' } ]} } +}); + +export const { useUpdateGameMutation } = playGameApiSlice; diff --git a/src/components/PlayGameSection/blockchainApiSlice.ts b/src/components/PlayGameSection/blockchainApiSlice.ts new file mode 100644 index 0000000..d0be1d7 --- /dev/null +++ b/src/components/PlayGameSection/blockchainApiSlice.ts @@ -0,0 +1,36 @@ +import { blockchainApiSlice } from '../../app/blockchainApiSlice'; +import { + AllPossibleWegaTypes, + HexishString +} from '../../models'; +import { ContractTypes } from '../../libs/wagmi'; + +// Todo + // write function names with type safety +export const playGameBlockchainApiSlice = blockchainApiSlice.injectEndpoints({ + endpoints: (builder) => ({ + getGameResults: builder.query({ + query: ({ gameType, escrowHash, player }) => ({ + functionName: 'gameResults', + contract: ContractTypes.GAMECONTROLLER, + method: 'READ', + args: [gameType.toUpperCase(), escrowHash, player] + }), + transformResponse: (response: bigint[]) => response.map(r => Number(r)) + }), + getGameWinners: builder.query({ + query: ({ gameType, escrowHash }) => ({ + functionName: 'winners', + method: 'READ', + contract: ContractTypes.GAMECONTROLLER, + args: [gameType.toUpperCase(), escrowHash] + }), + }), + }) +}); +export const { + useGetGameResultsQuery, + useGetGameWinnersQuery, +} = playGameBlockchainApiSlice; + + \ No newline at end of file diff --git a/src/components/PlayGameSection/index.tsx b/src/components/PlayGameSection/index.tsx index bb7803b..3872d81 100644 --- a/src/components/PlayGameSection/index.tsx +++ b/src/components/PlayGameSection/index.tsx @@ -1,28 +1,36 @@ +import { useEffect } from 'react'; +import { useGetSet } from 'react-use'; +import { useAppDispatch } from '../../hooks'; import PlayDiceGameSection from './PlayDiceGameSection'; -import PlayCoinFlipGameSection from './PlayCoinFlipGameSection'; +import PlayCoinFlipGameSection from './PlayCoinFlipGameSection'; +import { + useGetGameWinnersQuery, + playGameBlockchainApiSlice + } from './blockchainApiSlice' + import { Wega, User, WegaTypes, WegaTypesEnum, + GameInfoType, HexishString, - GameInfoType, Wallet, Player, WegaAttributes, PlayerFlipChoices, } from "../../models"; + +import { ComponentLoader } from "../../common/loaders" import 'twin.macro'; export interface PlayGameSectionProps extends React.Attributes, React.AllHTMLAttributes { game: Wega; user: User; players: Player[]; - gameResults: any; gameInfo: GameInfoType; wallet: Wallet; isGamePlayable: boolean; - winners: HexishString[]; gameAttributes?: WegaAttributes; playerFlipChoices?: PlayerFlipChoices; } @@ -37,15 +45,33 @@ const PlayGameSection: React.FC = ({ game, user, players, - gameResults, gameInfo, wallet, isGamePlayable, - winners, gameAttributes, playerFlipChoices, ...rest }) => { + const dispatch = useAppDispatch(); + const [gameResults, setGameResults] = useGetSet(undefined); + // TODO + // convert to hook + const { gameType, wager: { wagerHash: escrowHash } } = game; + const getGameResultsOfAllPlayers = async () => { + const results = (await Promise.all(players.map(async (player) => dispatch(playGameBlockchainApiSlice.endpoints.getGameResults.initiate({ + gameType, + escrowHash: escrowHash as HexishString, + player: player.walletAddress as HexishString + }))))).map((result: any) => result.data); + setGameResults(results as unknown as number[][]); + } + const { data: winners } = useGetGameWinnersQuery({ + gameType, + escrowHash: escrowHash as HexishString, + }); + useEffect(() => { + getGameResultsOfAllPlayers(); + }, [players.length]); const renderGame = () => { let Comp; if(!game) { @@ -53,11 +79,11 @@ const PlayGameSection: React.FC = ({ } else { Comp = GAME_COMPONENTS[game.gameType.toUpperCase()]; if(children) { - return = ({ gameAttributes, playerFlipChoices, ...rest - }}>{children} + }}>{children} : } else { - return = ({ gameAttributes, playerFlipChoices, ...rest - }} /> + }} /> : } } } - return renderGame(); + return renderGame() ; } export default PlayGameSection; diff --git a/src/components/WegaGames/index.tsx b/src/components/WegaGames/index.tsx index e32b91b..b8cd899 100644 --- a/src/components/WegaGames/index.tsx +++ b/src/components/WegaGames/index.tsx @@ -2,17 +2,23 @@ import { useEffect, useState } from 'react'; import GameBar from "../../common/GameBar"; import Section from '../../common/Section'; import { JoinableGamesHeaderBar } from '../../common/JoinableGameBar/types'; +// import ClaimBar from '../../common/ClaimBar'; import { useGetGamesQuery } from './apiSlice'; import { Wega, WegaState } from '../../models'; +// import { useGetGameWinnersQuery, playGameBlockchainApiSlice } from '../PlayGameSection/blockchainApiSlice' + import 'twin.macro'; export interface JoinableAndPlayableGamesProps extends React.Attributes { userUuid: string; gamesCount: number; } -const filterPlayableGames = (data: Wega[], userUuid: string) => data.filter(game => game.state === WegaState.PLAYING && game.players.some(predicate => predicate.uuid === userUuid )); +const filterPlayableGames = (data: Wega[], userUuid: string) => data.filter(game => game.state === WegaState.PLAYING && game.players.some(predicate => predicate.uuid === userUuid )); const filterJoinableGames = (data: Wega[], userUuid: string) => data.filter(game => game.state === WegaState.PENDING && game.players.every(predicate => predicate.uuid !== userUuid )); const sortPlayableGames = (data: Wega[]) => data.sort((a: any, b: any) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); +// const filterClaimableGames = (data: Wega[], userUuid: string) => data.filter(game => game.players.some(predicate => predicate.uuid === userUuid )); +// const attachGameWinners = (data: Wega[], winners: HexishString[]) => data.map(game => ({ ...game, winners })); +// const filterClaimableGames = (data: Wega[], userAddress: HexishString) => data.filter(game => game.players.some(predicate => predicate.walletAddress?.toLowerCase() !== userAddress.toLowerCase())); export const JoinableAndPlayableGames: React.FC = ({ gamesCount, userUuid, ...rest }: JoinableAndPlayableGamesProps) => { const { data, isLoading, isSuccess} = useGetGamesQuery(undefined); @@ -26,16 +32,17 @@ export const JoinableAndPlayableGames: React.FC = setGameIds(sortedGameIds); } }, [data, gamesCount, isSuccess]); - return (
+ + return (
Date created Game Wager Escrow - { - !isLoading && gameIds && gameIds.map((gameId: number) => ( )) - } + { + !isLoading && gameIds && gameIds.map((gameId: number) => ( )) + }
) } @@ -89,3 +96,43 @@ export const PlayableGames: React.FC = ({ gamesCo
) } + +// interface ClaimableGamesProps extends JoinableAndPlayableGamesProps { +// winners: HexishString[]; +// gameType: AllPossibleWegaTypes; +// escrowHash: HexishString; +// } +// export const ClaimableGames: React.FC = ({ gamesCount, userUuid, ...rest }: JoinableAndPlayableGamesProps) => { +// const getGamesQuery = useGetGamesQuery({ state: WegaState.COMPLETED }); +// const [gameIds, setGameIds] = useState(); +// const getGameWinners = async (games: Wega[]) => await Promise.all(games.map(async (game) => { +// const { gameType, wager: { wagerHash } } = game; +// const winnersData = playGameBlockchainApiSlice.endpoints.getGameWinners.useQuery({ gameType, escrowHash: wagerHash as HexishString }); +// })) + +// const { data: winners } = useGetGameWinnersQuery({ }); + +// useEffect(() => { +// if(getGamesQuery.isSuccess && getGamesQuery.data) { +// // once we have the data, + +// } +// const dataArray = getGamesQuery.data.ids.map((id: number) => getGamesQuery.data.entities[id]) as Wega[]; +// const joinableGames = filterJoinableGames(dataArray, userUuid); +// const sortedGameIds = sortPlayableGames(joinableGames).map(game => game.id); +// setGameIds(sortedGameIds ?? []); +// }, [data, gamesCount]); +// return (
+// +// Date created +// Game +// Wager +// Escrow +// +// { +// !isLoading && gameIds && gameIds.map((gameId: number, i) => ()) +// } +//
+// ) +// } + diff --git a/src/containers/CreateGamePage/index.tsx b/src/containers/CreateGamePage/index.tsx index 6a3584c..77f8cd6 100644 --- a/src/containers/CreateGamePage/index.tsx +++ b/src/containers/CreateGamePage/index.tsx @@ -8,9 +8,7 @@ import { WagerTypesEnum, CurrencyTypes, CurrencyTypesEnum, - AllPossibleWegaTypes, - WegaTypesEnum, - WegaTypes + AllPossibleWegaTypes } from '../../models'; import { SupportedWagerTokenAddresses } from '../../models/constants'; import { useWegaStore } from '../../hooks'; @@ -30,15 +28,14 @@ const CreateGamePage = () => { wallet && user.uuid && state - ) ? (
+ ) ? (<> Create - {BADGE_TEXTS[state.gameType.toUpperCase()]}
@@ -57,7 +54,7 @@ const CreateGamePage = () => { />
-
) : + ) : } export default CreateGamePage; diff --git a/src/containers/Footer/index.tsx b/src/containers/Footer/index.tsx index 9d8b33a..cf5c833 100644 --- a/src/containers/Footer/index.tsx +++ b/src/containers/Footer/index.tsx @@ -3,10 +3,9 @@ import { TwitterIcon, WhitepaperIcon, GithubIcon } from '../../assets/icons'; import { SmallText } from '../../components/CreateGameCard/types'; import 'twin.macro'; - const Footer = () => { return ( -