diff --git a/clients/react-vite/package.json b/clients/react-vite/package.json index 2ced2b5..8626a81 100644 --- a/clients/react-vite/package.json +++ b/clients/react-vite/package.json @@ -12,18 +12,26 @@ "build-storybook": "storybook build" }, "dependencies": { + "@cartridge/connector": "^0.3.36", + "@lootsurvivor/core": "workspace:^", + "@lootsurvivor/react": "workspace:^", "@radix-ui/react-slider": "^1.2.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", + "@starknet-react/chains": "^0.1.7", + "@starknet-react/core": "^2.8.3", + "@tanstack/react-query": "^5.51.15", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "framer-motion": "^11.3.19", + "get-starknet-core": "^3.3.2", + "graphql-request": "^7.1.0", "lucide-react": "^0.416.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "starknet": "^6.11.0", "tailwind-merge": "^2.4.0", - "tailwindcss-animate": "^1.0.7", - "@lootsurvivor/core": "workspace:^" + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@chromatic-com/storybook": "^1.6.1", diff --git a/clients/react-vite/public/images/bg-skulls.png b/clients/react-vite/public/images/bg-skulls.png new file mode 100644 index 0000000..997df8a Binary files /dev/null and b/clients/react-vite/public/images/bg-skulls.png differ diff --git a/clients/react-vite/src/App.css b/clients/react-vite/src/App.css index b9d355d..e69de29 100644 --- a/clients/react-vite/src/App.css +++ b/clients/react-vite/src/App.css @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/clients/react-vite/src/App.tsx b/clients/react-vite/src/App.tsx index badfa89..28d696f 100644 --- a/clients/react-vite/src/App.tsx +++ b/clients/react-vite/src/App.tsx @@ -1,15 +1,17 @@ -import { useState } from "react"; +import { LandingPage } from "./components/pages/LandingPage"; +import { Button } from "./components/ui/button"; -import "./App.css"; - -import { useGameStateStore } from "@lootsurvivor/core"; +import { useSurvivorState } from "./hooks/useSurvivorState"; +import { useConnect } from "@starknet-react/core"; function App() { - const [count, setCount] = useState(0); - - const { gameState } = useGameStateStore.getState(); + const { survivor } = useSurvivorState(); - return <>; + return ( + <> + + + ); } export default App; diff --git a/clients/react-vite/src/components/pages/LandingPage.tsx b/clients/react-vite/src/components/pages/LandingPage.tsx new file mode 100644 index 0000000..0b123b0 --- /dev/null +++ b/clients/react-vite/src/components/pages/LandingPage.tsx @@ -0,0 +1,123 @@ +import { useAdventurersByXPWithScores } from "@/hooks"; +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from "@/components/ui/pagination"; + +import { useConnect } from "@starknet-react/core"; +import { useState } from "react"; +import { AnimatedNumber } from "../ui/animatedNumber"; + +export const LandingPage = () => { + const { connect, connectors } = useConnect(); + + const [page, setPage] = useState(1); + + const handlePreviousPage = () => { + setPage((prev) => Math.max(prev - 1, 1)); + }; + + const handleNextPage = () => { + setPage((prev) => Math.min(prev + 1, totalPages)); + }; + + const { adventurersWithScores, isLoading, isError } = + useAdventurersByXPWithScores({ page }); + + const itemsPerPage = 10; + const totalPages = 5; + const startingRank = (page - 1) * itemsPerPage + 1; + + return ( +
+
+
Leaderboard
+
+
+ + + + + + + + + + + {isLoading &&
Loading...
} + {isError &&
Error
} + {adventurersWithScores?.slice(0, 1).map((adventurer, index) => ( + + + + + + + ))} + {adventurersWithScores + ?.slice(1, 12) + .map((adventurer, index) => ( + + + + + + + ))} + +
+ Rank + + Player + + Score + + Lords Payout +
+ {startingRank} + + {adventurer.name} + + {adventurer.xp} + + +
+ {startingRank + index + 1} + + {adventurer.name} + + {adventurer.xp} + + +
+
+ + + + + + {[...Array(totalPages)].map((_, i) => ( + + setPage(i + 1)} + isActive={page === i + 1} + > + {i + 1} + + + ))} + + + + + +
+
{" "} +
+ ); +}; diff --git a/clients/react-vite/src/components/providers/QueryProvider.tsx b/clients/react-vite/src/components/providers/QueryProvider.tsx new file mode 100644 index 0000000..87c4d08 --- /dev/null +++ b/clients/react-vite/src/components/providers/QueryProvider.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { + useQuery, + useMutation, + useQueryClient, + QueryClient, + QueryClientProvider, +} from "@tanstack/react-query"; + +const queryClient = new QueryClient(); + +export function QueryProvider({ children }: { children: React.ReactNode }) { + return ( + {children} + ); +} diff --git a/clients/react-vite/src/components/providers/StarknetProvider.tsx b/clients/react-vite/src/components/providers/StarknetProvider.tsx new file mode 100644 index 0000000..809734b --- /dev/null +++ b/clients/react-vite/src/components/providers/StarknetProvider.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import CartridgeConnector from "@cartridge/connector"; +import { sepolia, mainnet } from "@starknet-react/chains"; +import { StarknetConfig, publicProvider, voyager } from "@starknet-react/core"; + +export function StarknetProvider({ children }: { children: React.ReactNode }) { + const cartridge = new CartridgeConnector([]); + + return ( + + {children} + + ); +} diff --git a/clients/react-vite/src/components/ui/animatedNumber.tsx b/clients/react-vite/src/components/ui/animatedNumber.tsx new file mode 100644 index 0000000..cdd9ec5 --- /dev/null +++ b/clients/react-vite/src/components/ui/animatedNumber.tsx @@ -0,0 +1,13 @@ +import { motion, useMotionValue, useTransform, animate } from "framer-motion"; +import { useEffect } from "react"; +export const AnimatedNumber = ({ value }: { value: number }) => { + const count = useMotionValue(0); + const rounded = useTransform(count, (latest) => latest.toFixed(2)); + + useEffect(() => { + const controls = animate(count, value, { duration: 1 }); + return controls.stop; + }, [count, value]); + + return {rounded}; +}; diff --git a/clients/react-vite/src/components/ui/button.tsx b/clients/react-vite/src/components/ui/button.tsx index b906594..5f25ecc 100644 --- a/clients/react-vite/src/components/ui/button.tsx +++ b/clients/react-vite/src/components/ui/button.tsx @@ -17,7 +17,7 @@ const buttonVariants = cva( "border border-input bg-transparent border-primary text-primary hover:bg-primary/40 ", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", + ghost: "hover:bg-primary hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", }, size: { diff --git a/clients/react-vite/src/components/ui/pagination.tsx b/clients/react-vite/src/components/ui/pagination.tsx new file mode 100644 index 0000000..ea40d19 --- /dev/null +++ b/clients/react-vite/src/components/ui/pagination.tsx @@ -0,0 +1,117 @@ +import * as React from "react" +import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" +import { ButtonProps, buttonVariants } from "@/components/ui/button" + +const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( +