Skip to content

Commit

Permalink
[ECO-2664] Add home page arena update (#508)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt <[email protected]>
  • Loading branch information
CRBl69 and xbtmatt authored Feb 10, 2025
1 parent 5e8b8ae commit cd70d2c
Show file tree
Hide file tree
Showing 14 changed files with 556 additions and 26 deletions.
1 change: 1 addition & 0 deletions cfg/cspell-frontend-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ bytea
nominalize
dexscreener
clippyts
xlarge
8 changes: 1 addition & 7 deletions src/typescript/frontend/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ const withBundleAnalyzer = analyzer({
});

const DEBUG = process.env.BUILD_DEBUG === "true";
const styledComponentsConfig = {
displayName: true,
ssr: true,
fileName: true,
minify: false,
};
/** @type {import('next').NextConfig} */
const debugConfigOptions = {
productionBrowserSourceMaps: true,
Expand All @@ -36,7 +30,7 @@ const nextConfig = {
ignoreBuildErrors: process.env.IGNORE_BUILD_ERRORS === "true",
},
compiler: {
styledComponents: DEBUG ? styledComponentsConfig : true,
styledComponents: true,
},
...(DEBUG ? debugConfigOptions : {}),
// Log full fetch URLs if we're in a specific environment.
Expand Down
28 changes: 24 additions & 4 deletions src/typescript/frontend/src/app/home/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { type DatabaseModels } from "@sdk/indexer-v2/types";
import { ARENA_MODULE_ADDRESS } from "@sdk/const";
import type { ArenaInfoModel, MarketStateModel, DatabaseModels } from "@sdk/indexer-v2/types";
import { ArenaCard } from "components/pages/home/components/arena-card";
import EmojiTable from "components/pages/home/components/emoji-table";
import MainCard from "components/pages/home/components/main-card/MainCard";
import { PriceFeed } from "components/price-feed";
Expand All @@ -13,6 +15,11 @@ export interface HomePageProps {
searchBytes?: string;
children?: React.ReactNode;
priceFeed: DatabaseModels["price_feed"][];
meleeData: {
melee: ArenaInfoModel;
market0: MarketStateModel;
market1: MarketStateModel;
} | null;
}

export default async function HomePageComponent({
Expand All @@ -23,13 +30,26 @@ export default async function HomePageComponent({
searchBytes,
children,
priceFeed,
meleeData,
}: HomePageProps) {
return (
<>
<div className="relative">
<div className="flex-col mb-[31px]">
{priceFeed.length > 0 ? <PriceFeed data={priceFeed} /> : <TextCarousel />}
<div className="flex justify-center items-center px-[16px] mobile-lg:px-[24px] mx-auto w-full max-w-full max-h-[60dvh]">
<MainCard featuredMarkets={priceFeed} page={page} sortBy={sortBy} />
{ARENA_MODULE_ADDRESS && meleeData ? (
<ArenaCard
market0Symbol={meleeData.market0.market.symbolEmojis.join("")}
market1Symbol={meleeData.market1.market.symbolEmojis.join("")}
rewardsRemaining={meleeData.melee.rewardsRemaining}
meleeVolume={meleeData.melee.volume}
aptLocked={meleeData.melee.aptLocked}
startTime={meleeData.melee.startTime / 1000n / 1000n}
duration={meleeData.melee.duration / 1000n / 1000n}
/>
) : (
<MainCard featuredMarkets={priceFeed} page={page} sortBy={sortBy} />
)}
</div>
{children}
<TextCarousel />
Expand All @@ -42,6 +62,6 @@ export default async function HomePageComponent({
sortBy={sortBy}
searchBytes={searchBytes}
/>
</>
</div>
);
}
29 changes: 28 additions & 1 deletion src/typescript/frontend/src/app/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { SortMarketsBy } from "@sdk/indexer-v2/types/common";
import { getAptPrice } from "lib/queries/get-apt-price";
import { AptPriceContextProvider } from "context/AptPrice";
import { ORDER_BY } from "@sdk/indexer-v2/const";
import { ARENA_MODULE_ADDRESS } from "@sdk/const";
import { fetchArenaInfo, fetchMarketStateByAddress } from "@/queries/arena";

export const revalidate = 2;

Expand Down Expand Up @@ -79,18 +81,42 @@ export default async function Home({ searchParams }: HomePageParams) {

const aptPricePromise = getAptPrice();

const [priceFeedData, markets, numMarkets, aptPrice] = await Promise.all([
const meleeDataPromise = (async () => {
if (ARENA_MODULE_ADDRESS) {
const melee = await fetchArenaInfo({});
if (!melee) {
console.error("Arena is enabled, but arena info couldn't be fetched from the database.");
return null;
}
const [market0, market1] = await Promise.all([
fetchMarketStateByAddress({ address: melee.emojicoin0MarketAddress }),
fetchMarketStateByAddress({ address: melee.emojicoin1MarketAddress }),
]);
if (!market0 || !market1) {
console.error(
"Arena info found, but one or both of the arena markets aren't in the market state table."
);
return null;
}
return { melee, market0, market1 };
}
return null;
})();

const [priceFeedData, markets, numMarkets, aptPrice, meleeData] = await Promise.all([
priceFeedPromise,
marketsPromise,
numMarketsPromise,
aptPricePromise,
meleeDataPromise,
]).catch((e) => {
console.error(e);
return [
[] as DatabaseModels["price_feed"][],
[] as DatabaseModels["market_state"][],
0,
undefined,
null,
] as const;
});

Expand All @@ -103,6 +129,7 @@ export default async function Home({ searchParams }: HomePageParams) {
sortBy={sortBy}
searchBytes={q}
priceFeed={priceFeedData}
meleeData={meleeData}
/>
</AptPriceContextProvider>
);
Expand Down
2 changes: 2 additions & 0 deletions src/typescript/frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import DisplayDebugData from "@/store/server-to-client/FetchFromServer";
import { fontsStyle, notoColorEmoji } from "styles/fonts";
import { headers } from "next/headers";
import "@react95/core/themes/win95.css";
import { RandomEmojiBg } from "components/RandomEmojiBg";

export const metadata: Metadata = getDefaultMetadata();
export const viewport: Viewport = {
Expand All @@ -23,6 +24,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
<style dangerouslySetInnerHTML={{ __html: fontsStyle }} />
<StyledComponentsRegistry>
<Providers userAgent={userAgent}>
<RandomEmojiBg />
<DisplayDebugData />
{children}
</Providers>
Expand Down
99 changes: 99 additions & 0 deletions src/typescript/frontend/src/components/RandomEmojiBg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"use client";

import useNodeDimensions from "@hooks/use-node-dimensions";
import { getRandomSymbolEmoji } from "@sdk/emoji_data";
import { useEffect, useMemo, useRef, useState } from "react";
import { Emoji } from "utils/emoji";

function OneRandomEmoji({ emoji }: { emoji: string | undefined }) {
const mt = useMemo(() => Math.random() * 100, []);
const ml = useMemo(() => Math.random() * 100, []);
const rotate = useMemo(() => Math.round(Math.random() * 50 - 25), []);
const [blur, setBlur] = useState(true);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const mouseenter = (e: MouseEvent) => {
const elements = document.elementsFromPoint(e.clientX, e.clientY);
if (elements.find((e) => e === ref.current)) {
setBlur(false);
}
};
const mouseleave = (e: MouseEvent) => {
const elements = document.elementsFromPoint(e.clientX, e.clientY);
if (!elements.find((e) => e === ref.current)) {
setBlur(true);
}
};

if (emoji) {
window.addEventListener("mousemove", mouseenter, { passive: true, capture: true });
window.addEventListener("mousemove", mouseleave, { passive: true, capture: true });

return () => {
window.removeEventListener("mousemove", mouseenter);
window.removeEventListener("mousemove", mouseleave);
};
}
}, [emoji]);
if (!emoji) return <div></div>;
return (
<div
ref={ref}
className="flex flex-col w-min h-min text-8xl grid place-items-center select-none transition-all"
style={{
marginTop: `${mt}%`,
marginLeft: `${ml}%`,
transform: `rotate(${rotate}deg)`,
filter: blur ? "blur(15px)" : "",
opacity: blur ? "0.6" : "1",
}}
>
<Emoji emojis={emoji} />
</div>
);
}

export function RandomEmojiBg() {
const ref = useRef<HTMLDivElement>(null);
const { height, width } = useNodeDimensions(ref);
const nRows = useMemo(() => Math.ceil(height / 150), [height]);
const nCols = useMemo(() => Math.ceil(width / 150), [width]);
const rows = useMemo(
() =>
Array.from({ length: nCols * nRows }).map(() =>
Math.random() < 1 / 8 ? getRandomSymbolEmoji() : undefined
),
[nRows, nCols]
);

const [mt, setMt] = useState<number>(0);

useEffect(() => {
const fn = (e: Event) => {
setMt(((e.target as HTMLElement | null)?.scrollTop ?? 0) * -0.4);
};

document.addEventListener("scroll", fn, { passive: true, capture: true });

return () => document.removeEventListener("scroll", fn);
}, []);

return (
<div className="z-[-1] absolute top-0 left-0 h-[100%] w-[100vw] overflow-hidden">
<div
ref={ref}
className="absolute top-0 left-0 h-[300%] w-[100%] grid"
style={{
gridTemplateRows: `repeat(${nRows}, 1fr)`,
gridTemplateColumns: `repeat(${nCols}, 1fr)`,
perspective: "20px",
top: mt,
}}
>
{rows.map((row, i) => (
<OneRandomEmoji key={`random-emoji-bg-${i}`} emoji={row?.emoji} />
))}
</div>
</div>
);
}
3 changes: 2 additions & 1 deletion src/typescript/frontend/src/components/button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ const Button = <E extends React.ElementType = "button">({
textScale: "pixelHeading4" as const,
color: isDisabled || fakeDisabled ? ("darkGray" as const) : ("econiaBlue" as const),
textTransform: "uppercase" as const,
fontSize: scale === "sm" ? ("20px" as const) : ("24px" as const),
fontSize:
scale === "sm" ? ("20px" as const) : scale === "lg" ? ("24px" as const) : ("36px" as const),
};

return (
Expand Down
1 change: 1 addition & 0 deletions src/typescript/frontend/src/components/button/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const variants = {
export const scales = {
SMALL: "sm",
LARGE: "lg",
XLARGE: "xl",
} as const;

export type Scale = (typeof scales)[keyof typeof scales];
Expand Down
Loading

0 comments on commit cd70d2c

Please sign in to comment.