diff --git a/src/app/apiSlice.ts b/src/app/apiSlice.ts index 9f83b09..147534e 100644 --- a/src/app/apiSlice.ts +++ b/src/app/apiSlice.ts @@ -10,6 +10,6 @@ const baseQuery = fetchBaseQuery({ export const appApiSlice = createApi({ baseQuery, - reducerPath: 'wega-api', + reducerPath: 'wegaApi', endpoints: () => ({}) }); \ No newline at end of file diff --git a/src/app/blockchainApiSlice.ts b/src/app/blockchainApiSlice.ts index 76e3179..e58af63 100644 --- a/src/app/blockchainApiSlice.ts +++ b/src/app/blockchainApiSlice.ts @@ -3,7 +3,7 @@ import { createApi } from '@reduxjs/toolkit/query/react'; import { customBlockchainBaseQuery } from '../libs/rtk'; import { BlockchainAPIBase } from '../libs/wagmi'; -const baseUrl = 'wega-blockchain-api' +const baseUrl = 'wegaBlockchainApi' const baseQuery = customBlockchainBaseQuery({ baseUrl }, BlockchainAPIBase); export const blockchainApiSlice = createApi({ baseQuery, diff --git a/src/app/store.ts b/src/app/store.ts index 05b61f1..8750993 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -1,25 +1,22 @@ import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'; -import appReducer from '../containers/App/AppSlice' -import { appApiSlice } from '../containers/App/api'; +import walletConnectionReducer from '../components/RainBowConnectButton/connectionSlice' import { blockchainApiSlice } from './blockchainApiSlice'; -import { appApiSlice as newAppApiSlice } from './apiSlice'; +import { appApiSlice } from './apiSlice'; import intlReducer from '../containers/LanguageProvider/intlSlice' import blockchainReducer from '../api/blockchain/blockchainSlice' export const store = configureStore({ reducer: { + connection: walletConnectionReducer, [appApiSlice.reducerPath]: appApiSlice.reducer, - [newAppApiSlice.reducerPath]: newAppApiSlice.reducer, [blockchainApiSlice.reducerPath]: blockchainApiSlice.reducer, blockchain: blockchainReducer, - app: appReducer, language: intlReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false }).concat( appApiSlice.middleware, - newAppApiSlice.middleware, blockchainApiSlice.middleware ), devTools: import.meta.env.VITE_REDUX_DEBUG === "true" ? true : false diff --git a/src/assets/images/arbitrum-logo.png b/src/assets/images/arbitrum-logo.png new file mode 100644 index 0000000..42adf43 Binary files /dev/null and b/src/assets/images/arbitrum-logo.png differ diff --git a/src/assets/images/optimisim-logo.png b/src/assets/images/optimisim-logo.png new file mode 100644 index 0000000..6fb5fe4 Binary files /dev/null and b/src/assets/images/optimisim-logo.png differ diff --git a/src/common/ClaimBar/index.tsx b/src/common/ClaimBar/index.tsx index 9644a93..a8bdf3e 100644 --- a/src/common/ClaimBar/index.tsx +++ b/src/common/ClaimBar/index.tsx @@ -17,13 +17,13 @@ import { AllPossibleCurrencyTypes, AllPossibleWagerTypes, HexishString, + Wega } from '../../models'; import { dateFromTs, parseBarCount } from '../../utils'; import { Count } from './types' import{ BarDiceIcon, BarCoinIcon, USDCIcon, USDTIcon, ArrowTrSquareIcon} from '../../assets/icons'; -import { selectGameById } from '../../containers/App/api'; +import { selectGameById } from '../../components/WegaGames/apiSlice'; import { useSelector } from 'react-redux' -import { useWegaStore } from '../../hooks' import { ButtonForClaiming } from '../ButtonForClaiming'; import { constructBlockExplorerHash } from '../GameBar/utils'; import { Link } from 'react-router-dom' @@ -40,35 +40,37 @@ export const BADGE_TEXTS: any = { interface ClaimBarProps { gameId: number; count: number; + networkId: number; + game: Wega; } function ClaimBar({ gameId, + networkId, // eslint-disable-next-line @typescript-eslint/no-unused-vars - css, + css, + game, count, ...rest }: { gameId: number, count: number } & React.Attributes & Partial> & ClaimBarProps) { - const game = useSelector(state => selectGameById(state, gameId)); - const { user, wallet, network } = useWegaStore(); - - return game && wallet?.address && user?.uuid && network?.id && ( - + const claimableGame = useSelector(state => selectGameById(state, gameId)) ?? game; + return ( + {parseBarCount(count)} {/* date */} - {dateFromTs(new Date(game.createdAt as string).getTime() * 1000)} + {dateFromTs(new Date(claimableGame.createdAt as string).getTime() * 1000)} - {formatEther(game.wager.wagerAmount)} - {renderWagerBadge(game.wager.wagerType, game.wager.wagerCurrency)} - {game.wager.wagerCurrency} + {formatEther(claimableGame.wager.wagerAmount)} + {renderWagerBadge(claimableGame.wager.wagerType, claimableGame.wager.wagerCurrency)} + {claimableGame.wager.wagerCurrency}
- {renderGameTypeBadge(game.gameType)} - {BADGE_TEXTS[game.gameType]} + {renderGameTypeBadge(claimableGame.gameType)} + {BADGE_TEXTS[claimableGame.gameType]}
@@ -78,7 +80,7 @@ function ClaimBar({
view on explorer - +
{/* render for a joinable game */} diff --git a/src/components/ClaimNFTWinsSection/index.tsx b/src/components/ClaimNFTWinsSection/index.tsx index 3037683..b8adc57 100644 --- a/src/components/ClaimNFTWinsSection/index.tsx +++ b/src/components/ClaimNFTWinsSection/index.tsx @@ -3,7 +3,10 @@ import 'twin.macro'; import { LargeText, } from "../../components/CreateGameCard/types"; -interface ClaimNFTWinsSection extends React.Attributes { +import { + SectionHeader, + } from "../../common/Section/types" +interface ClaimNFTWinsSection extends React.AllHTMLAttributes { gameIds: number[] } @@ -11,8 +14,10 @@ interface ClaimNFTWinsSection extends React.Attributes { function ClaimNFTWinsSection({ gameIds , ...rest }: ClaimNFTWinsSection) { // filter out the games of which the user is not the winner return ( -
- Coming soon. +
NFTs won + } direction="col" tw="gap-y-[24px]" { ...rest } > + Coming soon.
) } diff --git a/src/components/ClaimOnOtherNetworkSection/index.tsx b/src/components/ClaimOnOtherNetworkSection/index.tsx new file mode 100644 index 0000000..695fe73 --- /dev/null +++ b/src/components/ClaimOnOtherNetworkSection/index.tsx @@ -0,0 +1,25 @@ +import 'twin.macro'; +import { NormalText } from '../CreateGameCard/types'; +import Section from '../../common/Section'; +import optimismLogo from '../../assets/images/optimisim-logo.png' +import arbitrumLogo from '../../assets/images/arbitrum-logo.png' + +function ClaimOnOtherNetworkSection(props: React.AllHTMLAttributes) { + // filter out the games of which the user is not the winner + return (
+ +
+
+ optimism-logo + Arbitrum - Coming soon +
+ +
+ arbitrum-logo + Optimisim - Coming soon +
+
+
+ ) +} +export default ClaimOnOtherNetworkSection; \ No newline at end of file diff --git a/src/components/ClaimTokenWinsSection/index.tsx b/src/components/ClaimTokenWinsSection/index.tsx index 0d603a8..96dacf4 100644 --- a/src/components/ClaimTokenWinsSection/index.tsx +++ b/src/components/ClaimTokenWinsSection/index.tsx @@ -1,19 +1,15 @@ -import Section from '../../common/Section'; -import ClaimBar from '../../common/ClaimBar'; import 'twin.macro'; -interface ClaimTokenWinsSectionProps extends React.Attributes { - gameIds: number[] +import { ClaimableGames } from '../WegaGames'; +import { HexishString } from '../../models'; + +interface ClaimTokenWinsSectionProps extends React.AllHTMLAttributes { + userWalletAddress: HexishString; + gamesCount: number; + networkId: number; } -function ClaimTokenWinsSection({ gameIds , ...rest }: ClaimTokenWinsSectionProps) { +function ClaimTokenWinsSection({ networkId, userWalletAddress, gamesCount, ...rest }: ClaimTokenWinsSectionProps) { // filter out the games of which the user is not the winner - return ( -
- { - gameIds.map( - (dg, i) => ( )) - } -
- ) + return ( ) } export default ClaimTokenWinsSection; \ No newline at end of file diff --git a/src/components/ClaimWinsDisconnectedUserSection/index.tsx b/src/components/ClaimWinsDisconnectedUserSection/index.tsx new file mode 100644 index 0000000..4b2cadf --- /dev/null +++ b/src/components/ClaimWinsDisconnectedUserSection/index.tsx @@ -0,0 +1,33 @@ +import Section from '../../common/Section'; +import Button from '../../common/Button'; +import { + LargeText, + NormalText +} from "../CreateGameCard/types"; +import { + useConnectModal, +} from '@rainbow-me/rainbowkit'; +import 'twin.macro'; + +interface ClaimWinsDisconnectedUserSectionProps extends React.AllHTMLAttributes { + gameIds: number[] +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function ClaimWinsDisconnectedUserSection({ gameIds , ...rest }: ClaimWinsDisconnectedUserSectionProps) { + // filter out the games of which the user is not the winner + const { openConnectModal } = useConnectModal(); + return openConnectModal ? ( +
+ Connect your wallet to see your wins. + +
+ ) : <> +} +export default ClaimWinsDisconnectedUserSection; \ No newline at end of file diff --git a/src/components/RainBowConnectButton/apiSlice.ts b/src/components/RainBowConnectButton/apiSlice.ts new file mode 100644 index 0000000..43cf610 --- /dev/null +++ b/src/components/RainBowConnectButton/apiSlice.ts @@ -0,0 +1,21 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +import { User } from '../../models'; +import { appApiSlice } from '../../app/apiSlice'; + +export const connectButtonApiSlice = appApiSlice.injectEndpoints({ + endpoints: (builder) => ({ + createPlayer: builder.mutation & Pick>({ + query: (walletAddress) => ({ + url: '/users', + method: 'POST', + body: { walletAddress } + }), + transformResponse: (response: any) => { + return response.uuid + }, + }), + }) +}) + +export const { useCreatePlayerMutation } = connectButtonApiSlice; + \ No newline at end of file diff --git a/src/components/RainBowConnectButton/connectionSlice.ts b/src/components/RainBowConnectButton/connectionSlice.ts new file mode 100644 index 0000000..72c9e7b --- /dev/null +++ b/src/components/RainBowConnectButton/connectionSlice.ts @@ -0,0 +1,56 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable no-unused-vars */ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { Network, User, Wallet } from '../../models'; +import { RootState } from "../../app/store"; +import { connectButtonApiSlice } from './apiSlice'; + +interface AppState { + loading: boolean; + network: Network | undefined; + user: User; +} + +export const initialState: AppState = { + loading: false, + network: undefined, + user: { + uuid: undefined, + wallet: undefined, + loading: false, + }, +}; + +export const connectionSlice = createSlice({ + name: 'connection', + initialState, + reducers: { + setWallet(state, action: PayloadAction){ + state.user.wallet = action.payload + }, + resetWallet(state) { + state.user.wallet = undefined; + }, + resetNetwork(state) { + state.network = undefined; + }, + setNetworkUnsupported(state, action: PayloadAction) { + state = Object.assign(state, { ...state, network: { unsupported: action.payload } }); + }, + setNetwork(state, action: PayloadAction){ + state.network = action.payload; + }, + }, + extraReducers: (builder) => { + builder.addMatcher(connectButtonApiSlice.endpoints.createPlayer.matchFulfilled, (state, action) => { state.user.uuid = action.payload }); + builder.addMatcher(connectButtonApiSlice.endpoints.createPlayer.matchRejected, (state) => { state.user.loading = false }); + builder.addMatcher(connectButtonApiSlice.endpoints.createPlayer.matchPending, (state) => { state.user.loading = true }); + } +}); + +export const { setWallet, setNetwork, resetWallet, resetNetwork, setNetworkUnsupported } = connectionSlice.actions; +export const selectNetwork = (state: RootState) => state.connection.network; +export const selectUser = (state: RootState) => state.connection.user; +export const selectWallet = (state: RootState) => state.connection.user.wallet; +export const selectAppLoading = (state: RootState) => state.connection.loading; +export default connectionSlice.reducer; diff --git a/src/components/RainBowConnectButton/index.tsx b/src/components/RainBowConnectButton/index.tsx index 811fbdc..0e5fafa 100644 --- a/src/components/RainBowConnectButton/index.tsx +++ b/src/components/RainBowConnectButton/index.tsx @@ -3,10 +3,10 @@ import { ConnectButton } from '@rainbow-me/rainbowkit'; import { ConnectionInformation, Balance, Chain, AvatarWrapper } from './types'; import Button from '../../common/Button'; import WalletAvatar from '../../common/WalletAvatar'; -import { resetWallet, resetNetwork, setNetworkUnsupported, setWallet, setNetwork } from '../../containers/App/AppSlice'; +import { resetWallet, resetNetwork, setNetworkUnsupported, setWallet, setNetwork } from './connectionSlice'; import { useAppDispatch } from '../../hooks'; import { useAccount } from 'wagmi'; -import { useCreatePlayerMutation } from '../../containers/App/api'; +import { useCreatePlayerMutation } from './apiSlice'; import 'twin.macro'; diff --git a/src/components/WegaGames/index.tsx b/src/components/WegaGames/index.tsx index d995b42..4a820f7 100644 --- a/src/components/WegaGames/index.tsx +++ b/src/components/WegaGames/index.tsx @@ -1,9 +1,12 @@ import { useEffect, useState } from 'react'; import GameBar from "../../common/GameBar"; +import ClaimBar from "../../common/ClaimBar"; import Section from '../../common/Section'; import { JoinableGamesHeaderBar } from '../../common/JoinableGameBar/types'; import { useGetGamesQuery } from './apiSlice'; -import { Wega, WegaState } from '../../models'; +import { HexishString, Wega, WegaState } from '../../models'; +import { ComponentLoader } from '../../common/loaders'; + import 'twin.macro'; export interface JoinableAndPlayableGamesProps extends React.Attributes, React.HTMLAttributes { @@ -28,7 +31,7 @@ export const JoinableAndPlayableGames: React.FC = } }, [data, gamesCount, isSuccess]); - return (
+ return !isLoading ? (
Date created Game @@ -37,10 +40,10 @@ export const JoinableAndPlayableGames: React.FC = { - !isLoading && gameIds && gameIds.map((gameId: number) => ( )) + gameIds && gameIds.map((gameId: number) => ( )) }
- ) + ) : } export const JoinableGames: React.FC = ({ gamesCount, userUuid, ...rest }: JoinableAndPlayableGamesProps) => { @@ -53,8 +56,8 @@ export const JoinableGames: React.FC = ({ gamesCo const sortedGameIds = sortPlayableGames(joinableGames).map(game => game.id); setGameIds(sortedGameIds ?? []); } - }, [data, gamesCount]); - return (
+ }, [data, gamesCount, isSuccess]); + return !isLoading ? (
Date created Game @@ -62,24 +65,24 @@ export const JoinableGames: React.FC = ({ gamesCo Escrow { - !isLoading && gameIds && gameIds.map((gameId: number) => ()) + gameIds && gameIds.map((gameId: number) => ()) }
- ) + ) : } export const PlayableGames: React.FC = ({ gamesCount, userUuid, ...rest }: JoinableAndPlayableGamesProps) => { const { data, isLoading, isSuccess } = useGetGamesQuery({ state: WegaState.PLAYING }); const [gameIds, setGameIds] = useState(); useEffect(() => { - if(isSuccess && data) { + if(isSuccess && data && data?.entities) { const dataArray = data.ids.map((id: number) => data.entities[id]) as Wega[]; const joinableGames = filterJoinableGames(dataArray, userUuid); const sortedGameIds = sortPlayableGames(joinableGames).map(game => game.id); setGameIds(sortedGameIds ?? []); } - }, [data, gamesCount]); - return (
+ }, [data, gamesCount, isSuccess]); + return !isLoading ? (
Date created Game @@ -87,48 +90,35 @@ export const PlayableGames: React.FC = ({ gamesCo Escrow { - !isLoading && gameIds && gameIds.map((gameId: number) => ()) + gameIds && gameIds.map((gameId: number) => ()) }
- ) + ) : } -// 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) => ()) -// } -//
-// ) -// } - +// const filterClaimableGames = (data: Wega[], userWalletAddress: HexishString) => data.filter(game => game.gameWinners?.some(predicate => predicate.winner.toLowerCase() === userWalletAddress.toLowerCase() )); +export interface ClaimableGamesProps extends React.Attributes, React.HTMLAttributes { + userWalletAddress: HexishString; + gamesCount: number; + networkId: number; + } +export const ClaimableGames: React.FC = ({ networkId, gamesCount, userWalletAddress, ...rest }: ClaimableGamesProps) => { + const { data, isLoading, isSuccess } = useGetGamesQuery({ state: WegaState.COMPLETED, winners: userWalletAddress }); + const [sortedGames, setSortedGames] = useState(); + useEffect(() => { + if(isSuccess && data && data?.entities) { + const dataArray = data.ids.map((id: number) => data.entities[id]) as Wega[]; + const sortedGameIds = sortPlayableGames(dataArray).map(game => game.id); + // setGameIds(sortedGameIds ?? undefined); + setSortedGames(sortedGameIds.map(id => data.entities[id] as Wega) ?? []); + } + }, [data, gamesCount, isSuccess, userWalletAddress]); + return !isLoading ? (
+ { + sortedGames && sortedGames.length > 0 ? sortedGames.map((game: Wega, i) => ( + )) : <> + } + +
+ ) : +} \ No newline at end of file diff --git a/src/containers/App/api.ts b/src/containers/App/api.ts index 2ada80c..7c015b7 100644 --- a/src/containers/App/api.ts +++ b/src/containers/App/api.ts @@ -105,4 +105,7 @@ export const { selectById: selectGameById, selectIds: selectAllGamesIds, selectTotal: selectGamesCount, -} = gamesAdapter.getSelectors((state: any) => selectGamesData(state) ?? gamesInitialState); \ No newline at end of file +} = gamesAdapter.getSelectors((state: any) => selectGamesData(state) ?? gamesInitialState); + +// TODO + // CLEAN UP \ No newline at end of file diff --git a/src/containers/App/index.tsx b/src/containers/App/index.tsx index 30620da..dad64ab 100644 --- a/src/containers/App/index.tsx +++ b/src/containers/App/index.tsx @@ -1,4 +1,3 @@ -import { useEffect } from 'react'; import { HelmetProvider } from 'react-helmet-async'; import LanguageProvider from '../LanguageProvider'; import { localeSelector } from '../LanguageProvider/intlSlice'; @@ -10,11 +9,6 @@ import 'twin.macro'; function App() { const locale = useAppSelector(state => localeSelector(state)); - - useEffect(()=>{ - const htmlElement = document.documentElement; - if(htmlElement && !htmlElement.classList.contains('dark')) htmlElement.classList.add('dark'); - }) return ( diff --git a/src/containers/PlayPage/index.tsx b/src/containers/PlayPage/index.tsx index 905e78b..7c665bd 100644 --- a/src/containers/PlayPage/index.tsx +++ b/src/containers/PlayPage/index.tsx @@ -42,7 +42,7 @@ const PlayPage = () => {
{/* { user?.uuid ? : } */} - { user?.uuid ? : gamesCount && !user.uuid ? <> : <> } + { user?.uuid ? : gamesCount && !user.uuid ? <> : <> } ) diff --git a/src/containers/WinsPage/index.tsx b/src/containers/WinsPage/index.tsx index 1c0ddc9..7259444 100644 --- a/src/containers/WinsPage/index.tsx +++ b/src/containers/WinsPage/index.tsx @@ -2,34 +2,29 @@ import {Helmet} from 'react-helmet-async' import 'twin.macro'; import Section from '../../common/Section'; import ClaimWinsTokenSection from '../../components/ClaimTokenWinsSection'; +import ClaimWinsDisconnectedUserSection from '../../components/ClaimWinsDisconnectedUserSection' +import ClaimOnOtherNetworkSection from '../../components/ClaimOnOtherNetworkSection' import ClaimNFTWinsSection from '../../components/ClaimNFTWinsSection'; -import { useGetGamesQuery } from '../App/api'; -import { ComponentLoader } from '../../common/loaders'; import MainContainer from '../../components/MainContainer'; import { SectionHeaderTitle, SectionHeaderSubtitle, SectionHeaderContainer } from "../../common/Section/types" -import { useWegaStore } from '../../hooks' +import { useWegaStore, useFirebaseData } from '../../hooks' const WinsPage = () => { - const { user, wallet } = useWegaStore(); - const { claimableGameIds, isLoading } = useGetGamesQuery(undefined, { - selectFromResult: ({ data, isLoading }) => ({ - claimableGameIds: data ? Object.entries(data.entities) - .filter(([, game]: any) => game.players.filter((player: any) => player.walletAddress.toLowerCase() === wallet?.address.toLowerCase()).length > 0) - .map(([id,]: any) => Number(id)) : [], - isLoading, - }) - }) + const { wallet, network } = useWegaStore(); + const { gamesCount } = useFirebaseData(''); + // TODO + // create claimwins disconnected user section for nft claims section return ( <> Claim - +
{ >
{ - !isLoading && wallet && wallet?.address && user?.uuid ? - <> - - - - : + wallet && wallet?.isConnected && network && network.id ? + (
+ + + +
) + : ( + <> + + + + ) }
diff --git a/src/hooks/useWegaStore.ts b/src/hooks/useWegaStore.ts index 9b4b2ba..2363564 100644 --- a/src/hooks/useWegaStore.ts +++ b/src/hooks/useWegaStore.ts @@ -3,7 +3,7 @@ import { selectNetwork, selectUser, selectWallet, -} from '../containers/App/AppSlice'; +} from '../components/RainBowConnectButton/connectionSlice'; export function useWegaStore(){