diff --git a/src/typescript/frontend/src/app/arena/page.tsx b/src/typescript/frontend/src/app/arena/page.tsx index 6c14182f1..ebe68d3b7 100644 --- a/src/typescript/frontend/src/app/arena/page.tsx +++ b/src/typescript/frontend/src/app/arena/page.tsx @@ -1,26 +1,47 @@ -import { fetchMarketStateByAddress, fetchMelee } from "@/queries/arena"; +import { type fetchArenaInfo, fetchMarketStateByAddress } from "@/queries/arena"; import { ArenaClient } from "components/pages/arena/ArenaClient"; import { redirect } from "next/navigation"; export const revalidate = 2; export default async function Arena() { - let melee: Awaited> = null; + let arenaInfo: Awaited> = null; try { - melee = await fetchMelee({}); + // arenaInfo = await fetchArenaInfo({}); } catch (e) { - console.warn("Could not get melee data. This probably means that the backend is running an outdated version of the processor, without the arena processing. Please update.") + console.warn( + "Could not get melee data. This probably means that the backend is running an outdated version of the processor, without the arena processing. Please update." + ); redirect("/home"); } - if (!melee) { - redirect("/home"); - } + //if (!melee) { + // redirect("/home"); + //} + + arenaInfo = { + arenaInfo: { + duration: 120n * 1000n * 1000n, + startTime: BigInt(new Date().getTime() * 1000 - 1000 * 1000 * 60), + volume: 123n * 10n ** 8n, + meleeId: 2n, + aptLocked: 12n * 10n ** 8n, + maxMatchAmount: 5n * 10n ** 8n, + rewardsRemaining: 12345n * 10n ** 6n, + maxMatchPercentage: 50n, + emojicoin0MarketAddress: "0x43dcf02dcc0f3759d00486052585bf1694acf85c7e3e7c4b4770c5216d58eb67", + emojicoin1MarketAddress: "0x43dcf02dcc0f3759d00486052585bf1694acf85c7e3e7c4b4770c5216d58eb67", + }, + }; const [market0, market1] = await Promise.all([ - fetchMarketStateByAddress({ address: melee.arenaMelee.emojicoin0MarketAddress }), - fetchMarketStateByAddress({ address: melee.arenaMelee.emojicoin1MarketAddress }), + fetchMarketStateByAddress({ + address: "0x43dcf02dcc0f3759d00486052585bf1694acf85c7e3e7c4b4770c5216d58eb67", + }), + fetchMarketStateByAddress({ + address: "0x43dcf02dcc0f3759d00486052585bf1694acf85c7e3e7c4b4770c5216d58eb67", + }), ]); - return ; + return ; } diff --git a/src/typescript/frontend/src/components/Countdown.tsx b/src/typescript/frontend/src/components/Countdown.tsx new file mode 100644 index 000000000..674756e41 --- /dev/null +++ b/src/typescript/frontend/src/components/Countdown.tsx @@ -0,0 +1,63 @@ +import { useMemo, useState } from "react"; +import { useInterval } from "react-use"; +import darkTheme from "theme/dark"; + +const CountdownNumber = ({ n }: { n: string }) => ( +
+ {n} +
+); + +export const Countdown = ({ startTime, duration }: { startTime: bigint; duration: bigint }) => { + const getRemaining = () => Number(duration) - (new Date().getTime() / 1000 - Number(startTime)); + const [remaining, setRemaining] = useState(getRemaining()); + useInterval(() => { + setRemaining(getRemaining()); + }, 1000); + + const seconds = useMemo( + () => + Math.max(Math.floor(remaining) % 60, 0) + .toString() + .padStart(2, "0"), + [remaining] + ); + const minutes = useMemo( + () => + Math.max(Math.floor(remaining / 60) % 60, 0) + .toString() + .padStart(2, "0"), + [remaining] + ); + const hours = useMemo( + () => + Math.max(Math.floor(remaining / 60 / 60), 0) + .toString() + .padStart(2, "0"), + [remaining] + ); + + return ( +
+ + +
:
+ + +
:
+ + +
+ ); +}; diff --git a/src/typescript/frontend/src/components/pages/arena/ArenaClient.tsx b/src/typescript/frontend/src/components/pages/arena/ArenaClient.tsx index cd4626f19..debc2f00f 100644 --- a/src/typescript/frontend/src/components/pages/arena/ArenaClient.tsx +++ b/src/typescript/frontend/src/components/pages/arena/ArenaClient.tsx @@ -1,22 +1,177 @@ "use client"; -import type { ArenaMeleeModel, MarketStateModel } from "@sdk/indexer-v2/types"; +import { useMatchBreakpoints } from "@hooks/index"; +import type { ArenaInfoModel, MarketStateModel } from "@sdk/indexer-v2/types"; +import { Countdown } from "components/Countdown"; +import { FormattedNumber } from "components/FormattedNumber"; +import type { CSSProperties } from "react"; +import { createPortal } from "react-dom"; +import { emoji } from "utils"; +import { Emoji } from "utils/emoji"; -export const ArenaClient = ({ - melee, - market0, - market1, -}: { - melee: ArenaMeleeModel; +type Props = { + arenaInfo: ArenaInfoModel; market0: MarketStateModel; market1: MarketStateModel; +}; + +const Box: React.FC> = ({ + children, + className, + style, +}) => { + return ( +
+ {children} +
+ ); +}; + +const EmojiTitle = ({ + market0Symbols, + market1Symbols, +}: { + market0Symbols: string; + market1Symbols: string; }) => { + const { isTablet, isLaptop } = useMatchBreakpoints(); + const emojiCount = market0Symbols.length + market1Symbols.length; + // Formula to get a good font size for the amount of emojis to display. + // Works great for up to 6 emojis (not tested for more, but might work as well). + const baseFontSize = + 72 * (isTablet ? 0.6 : 1) * (isLaptop ? 0.69 : 1) * (1 - (emojiCount * 5) / 6 / 10); return ( -
-
ID: {melee.arenaMelee.meleeId.toString()}
-
- {market0.market.symbolEmojis.join("")} vs {market1.market.symbolEmojis.join("")} +
+ {" "} + + vs + {" "} + +
+ ); +}; + +const RewardsRemainingBox = ({ rewardsRemaining }: { rewardsRemaining: bigint }) => { + const { isMobile } = useMatchBreakpoints(); + return ( + +
+ Rewards remaining
+
+ +
+
+ ); +}; + +const Desktop: React.FC = ({ arenaInfo, market0, market1 }) => { + return ( +
+ + + + + + + + +
); }; + +const BottomNavigationItem = ({ + emoji, + text, + onClick, +}: { + emoji: string; + text: string; + onClick?: () => void; +}) => { + return ( +
+ +
{text}
+
+ ); +}; + +const BottomNavigation = () => { + return ( +
+ + + + +
+ ); +}; + +const Mobile: React.FC = ({ arenaInfo, market0, market1 }) => { + return ( + <> +
+ + +
+ +
+
+ + + +
+ {createPortal(, document.body)} + + ); +}; + +export const ArenaClient = (props: Props) => { + const { isMobile } = useMatchBreakpoints(); + return isMobile ? : ; +}; diff --git a/src/typescript/frontend/src/components/pages/home/components/arena-card/index.tsx b/src/typescript/frontend/src/components/pages/home/components/arena-card/index.tsx index 4320cde46..4db5fc5f5 100644 --- a/src/typescript/frontend/src/components/pages/home/components/arena-card/index.tsx +++ b/src/typescript/frontend/src/components/pages/home/components/arena-card/index.tsx @@ -7,11 +7,9 @@ import Link from "next/link"; import { ROUTES } from "router/routes"; import { Emoji } from "utils/emoji"; import "./module.css"; -import { useInterval } from "react-use"; -import { useMemo, useState } from "react"; -import darkTheme from "theme/dark"; import { useMatchBreakpoints } from "@hooks/index"; import { getEmojisInString } from "@sdk/emoji_data"; +import { Countdown } from "components/Countdown"; type ArenaCardProps = { market0Symbol: string; @@ -23,66 +21,6 @@ type ArenaCardProps = { duration: bigint; }; -const TimerNumber = ({ n }: { n: string }) => ( -
- {n} -
-); - -const Timer = ({ startTime, duration }: { startTime: bigint; duration: bigint }) => { - const getRemaining = () => Number(duration) - (new Date().getTime() / 1000 - Number(startTime)); - const [remaining, setRemaining] = useState(getRemaining()); - useInterval(() => { - setRemaining(getRemaining()); - }, 1000); - - const seconds = useMemo( - () => - Math.max(Math.round(remaining) % 60, 0) - .toString() - .padStart(2, "0"), - [remaining] - ); - const minutes = useMemo( - () => - Math.max(Math.round(remaining / 60) % 60, 0) - .toString() - .padStart(2, "0"), - [remaining] - ); - const hours = useMemo( - () => - Math.max(Math.round(remaining / 60 / 60), 0) - .toString() - .padStart(2, "0"), - [remaining] - ); - - return ( -
- - -
:
- - -
:
- - -
- ); -}; - const GlowingEmoji = ({ emoji }: { emoji: string }) => (
{!isMobile && headerText} - +