Skip to content

Commit

Permalink
Showing 25 changed files with 388 additions and 471 deletions.
8 changes: 3 additions & 5 deletions src/typescript/frontend/src/app/home/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -6,18 +6,16 @@ import TextCarousel from "components/text-carousel/TextCarousel";
import { type MarketDataSortByHomePage } from "lib/queries/sorting/types";

export interface HomePageProps {
featured?: DatabaseModels["market_state"];
markets: Array<DatabaseModels["market_state"]>;
numMarkets: number;
page: number;
sortBy: MarketDataSortByHomePage;
searchBytes?: string;
children?: React.ReactNode;
priceFeed: Array<DatabaseModels["price_feed"]>;
priceFeed: DatabaseModels["price_feed"][];
}

export default async function HomePageComponent({
featured,
markets,
numMarkets,
page,
@@ -30,8 +28,8 @@ export default async function HomePageComponent({
<>
<div className="flex-col mb-[31px]">
{priceFeed.length > 0 ? <PriceFeed data={priceFeed} /> : <TextCarousel />}
<div className="flex justify-center px-[16px] mobile-lg:px-[24px] mx-auto w-full max-w-full max-h-[60dvh]">
<MainCard featured={featured} page={page} sortBy={sortBy} />
<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} />
</div>
{children}
<TextCarousel />
46 changes: 29 additions & 17 deletions src/typescript/frontend/src/app/home/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { type HomePageParams, toHomePageParamsWithDefault } from "lib/routes/home-page-params";
import HomePageComponent from "./HomePage";
import {
fetchFeaturedMarket,
fetchMarkets,
fetchMarketsWithCount,
fetchNumRegisteredMarkets,
fetchPriceFeed,
fetchPriceFeedWithMarketState,
} from "@/queries/home";
import { symbolBytesToEmojis } from "@sdk/emoji_data";
import { MARKETS_PER_PAGE } from "lib/queries/sorting/const";
import { ORDER_BY } from "@sdk/queries";
import { SortMarketsBy } from "@sdk/indexer-v2/types/common";
import { unstable_cache } from "next/cache";
import { parseJSON, stringifyJSON } from "utils";
import { type DatabaseModels, toPriceFeed } from "@sdk/indexer-v2/types";
import { type DatabaseJsonType } from "@sdk/indexer-v2/types/json-types";
import { SortMarketsBy } from "@sdk/indexer-v2/types/common";
import { ORDER_BY } from "@sdk/queries";

export const revalidate = 2;

@@ -21,11 +23,31 @@ const getCachedNumMarketsFromAptosNode = unstable_cache(
{ revalidate: 10 }
);

const NUM_MARKETS_ON_PRICE_FEED = 25;

const stringifiedFetchPriceFeedData = () =>
fetchPriceFeedWithMarketState({
sortBy: SortMarketsBy.DailyVolume,
orderBy: ORDER_BY.DESC,
pageSize: NUM_MARKETS_ON_PRICE_FEED,
}).then((res) => stringifyJSON(res));

const getCachedPriceFeedData = unstable_cache(
stringifiedFetchPriceFeedData,
["price-feed-with-market-data"],
{ revalidate: 10 }
);

export default async function Home({ searchParams }: HomePageParams) {
const { page, sortBy, orderBy, q } = toHomePageParamsWithDefault(searchParams);
const searchEmojis = q ? symbolBytesToEmojis(q).emojis.map((e) => e.emoji) : undefined;

const priceFeedPromise = fetchPriceFeed({});
const priceFeedPromise = getCachedPriceFeedData()
.then((res) => parseJSON<DatabaseJsonType["price_feed"][]>(res).map((p) => toPriceFeed(p)))
.catch((err) => {
console.error(err);
return [] as DatabaseModels["price_feed"][];
});

let marketsPromise: ReturnType<typeof fetchMarkets>;

@@ -53,30 +75,20 @@ export default async function Home({ searchParams }: HomePageParams) {
numMarketsPromise = getCachedNumMarketsFromAptosNode();
}

let featuredPromise: ReturnType<typeof fetchFeaturedMarket>;

if (sortBy === SortMarketsBy.DailyVolume && orderBy === ORDER_BY.DESC) {
featuredPromise = marketsPromise.then((r) => r[0]);
} else {
featuredPromise = fetchFeaturedMarket();
}

const [featured, priceFeed, markets, numMarkets] = await Promise.all([
featuredPromise,
const [priceFeedData, markets, numMarkets] = await Promise.all([
priceFeedPromise,
marketsPromise,
numMarketsPromise,
]);

return (
<HomePageComponent
featured={featured}
markets={markets}
numMarkets={numMarkets}
page={page}
sortBy={sortBy}
searchBytes={q}
priceFeed={priceFeed}
priceFeed={priceFeedData}
/>
);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";

import React, { useEffect, useMemo, useRef } from "react";
import React, { useEffect, useMemo, useRef, useState } from "react";
import { translationFunction } from "context/language-context";
import { Column, Flex, FlexGap } from "@containers";
import { FlexGap } from "@containers";
import { toCoinDecimalString } from "lib/utils/decimals";
import AptosIconBlack from "components/svg/icons/AptosBlack";
import Image from "next/image";
@@ -12,26 +12,52 @@ import { useLabelScrambler } from "../table-card/animation-variants/event-varian
import planetHome from "../../../../../../public/images/planet-home.png";
import { emojiNamesToPath } from "utils/pathname-helpers";
import { type HomePageProps } from "app/home/HomePage";
import "./module.css";
import { emoji } from "utils";
import { Emoji } from "utils/emoji";
import "./module.css";
import { PriceDelta } from "components/price-feed/inner";
import { sortByValue } from "lib/utils/sort-events";
import { AnimatePresence, motion } from "framer-motion";
import { useInterval } from "react-use";

export interface MainCardProps {
featured?: HomePageProps["featured"];
featuredMarkets: HomePageProps["priceFeed"];
page: HomePageProps["page"];
sortBy: HomePageProps["sortBy"];
}

const FEATURED_MARKET_INTERVAL = 5 * 1000;
const MAX_NUM_FEATURED_MARKETS = 5;

const MainCard = (props: MainCardProps) => {
const { featured } = props;
const featuredMarkets = useMemo(() => {
const sorted = props.featuredMarkets.toSorted((a, b) =>
sortByValue(a.deltaPercentage, b.deltaPercentage, "desc")
);
const positives = sorted.filter(({ deltaPercentage }) => deltaPercentage > 0);
const notPositives = sorted.filter(({ deltaPercentage }) => deltaPercentage <= 0);
return positives.length
? positives.slice(0, MAX_NUM_FEATURED_MARKETS)
: notPositives.slice(0, MAX_NUM_FEATURED_MARKETS);
}, [props.featuredMarkets]);

const { t } = translationFunction();
const globeImage = useRef<HTMLImageElement>(null);

const { marketCap, dailyVolume, allTimeVolume } = useMemo(() => {
const [currentIndex, setCurrentIndex] = useState(0);

useInterval(() => {
setCurrentIndex((i) => (i + 1) % Math.min(featuredMarkets.length, MAX_NUM_FEATURED_MARKETS));
}, FEATURED_MARKET_INTERVAL);

const featured = useMemo(() => featuredMarkets.at(currentIndex), [featuredMarkets, currentIndex]);

const { marketCap, dailyVolume, allTimeVolume, priceDelta } = useMemo(() => {
return {
marketCap: BigInt(featured?.state.instantaneousStats.marketCap ?? 0),
dailyVolume: BigInt(featured?.dailyVolume ?? 0),
allTimeVolume: BigInt(featured?.state.cumulativeStats.quoteVolume ?? 0),
priceDelta: featured?.deltaPercentage ?? 0,
};
}, [featured]);

@@ -56,57 +82,69 @@ const MainCard = (props: MainCardProps) => {
const { ref: allTimeVolumeRef } = useLabelScrambler(toCoinDecimalString(allTimeVolume, 2));

return (
<Flex justifyContent="center" width="100%" my={{ _: "20px", tablet: "70px" }} maxWidth="1872px">
<Flex
alignItems="center"
justifyContent="center"
maxWidth="100%"
width="100%"
flexDirection={{ _: "column", tablet: "row" }}
>
<div className="flex flex-col w-full my-[20px] md:my-[70px] max-w-full">
<div className="flex flex-col md:flex-row w-full max-w-full items-center justify-center">
<Link
className="flex relative items-center ml-[-8%]"
href={
featured
? `${ROUTES.market}/${emojiNamesToPath(featured.market.emojis.map((x) => x.name))}`
: ROUTES.home
}
style={{
position: "relative",
alignItems: "center",
marginLeft: "-8%",
display: "flex",
}}
>
<Image
id="hero-image"
alt="Planet"
src={planetHome}
ref={globeImage}
placeholder="empty"
className="z-10"
/>
<Emoji
className={`styled-emoji ${featured?.market.emojis.length === 1 ? "styled-single-emoji" : "styled-double-emoji"}`}
emojis={featured?.market.emojis ?? emoji("black heart")}
/>
<AnimatePresence mode="popLayout">
<div
key={`${featured?.market.symbolData.symbol}-${currentIndex}`}
className={`flex flex-row styled-emoji ${featured?.market.emojis.length === 1 ? "styled-single-emoji" : "styled-double-emoji"} z-[-1]`}
>
<motion.div
className="flex relative"
initial={{
right: -300,
opacity: 0,
}}
animate={{
right: 0,
opacity: 1,
}}
exit={{
right: 300,
opacity: 0,
}}
>
<Emoji emojis={featured?.market.emojis ?? emoji("black heart")} />
</motion.div>
</div>
</AnimatePresence>
</Link>

<Column maxWidth="100%" ellipsis>
<div className="flex flex-col max-w-full ellipses">
<div className="flex flex-row items-center">
<span className="text-medium-gray pixel-heading-text">HOT</span>
<span className="text-medium-gray pixel-heading-text uppercase">Hot</span>
<span>&nbsp;</span>
<div>
<Emoji className="pixel-heading-emoji" emojis={emoji("fire")} />
</div>
{priceDelta > 0 && (
<PriceDelta className="pixel-heading-emoji ml-[0.5ch]" delta={priceDelta} />
)}
</div>
<div
className="display-font-text ellipses font-forma-bold"
title={(featured ? featured.market.symbolData.name : "BLACK HEART").toUpperCase()}
className="display-font-text ellipses font-forma-bold uppercase"
title={featured?.market.symbolData.name ?? "BLACK HEART"}
>
{(featured ? featured.market.symbolData.name : "BLACK HEART").toUpperCase()}
{featured?.market.symbolData.name ?? "BLACK HEART"}
</div>

<FlexGap gap="8px">
{typeof featured !== "undefined" && (
{featured && (
<>
<div className="font-forma text-medium-gray market-data-text uppercase">
{t("Mkt. Cap:")}
@@ -123,7 +161,7 @@ const MainCard = (props: MainCardProps) => {
</FlexGap>

<FlexGap gap="8px">
{typeof featured !== "undefined" && (
{featured && (
<>
<div className="uppercase">
<div className="font-forma text-medium-gray market-data-text uppercase">
@@ -142,7 +180,7 @@ const MainCard = (props: MainCardProps) => {
</FlexGap>

<FlexGap gap="8px">
{typeof featured !== "undefined" && (
{featured && (
<>
<div className="font-forma text-medium-gray market-data-text uppercase">
{t("All-time vol:")}
@@ -157,9 +195,9 @@ const MainCard = (props: MainCardProps) => {
</>
)}
</FlexGap>
</Column>
</Flex>
</Flex>
</div>
</div>
</div>
);
};

Original file line number Diff line number Diff line change
@@ -130,19 +130,19 @@ export const scrambleConfig = {
playOnMount: true,
};

export const useLabelScrambler = (value: string, suffix: string = "") => {
// Ignore all characters in the suffix, as long as they are not numbers.
export const useLabelScrambler = (value: string, suffix: string = "", prefix: string = "") => {
// Ignore all characters in the prefix and the suffix, as long as they are not numbers.
const ignore = ["."];
const numberSet = new Set("0123456789");
const suffixSet = new Set(suffix);
for (const char of suffixSet) {
const suffixesAndPrefixes = new Set(prefix + suffix);
for (const char of suffixesAndPrefixes) {
if (!numberSet.has(char)) {
ignore.push(char);
}
}

const scrambler = useScramble({
text: value + suffix,
text: prefix + value + suffix,
...scrambleConfig,
ignore,
});
6 changes: 3 additions & 3 deletions src/typescript/frontend/src/components/price-feed/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { DatabaseModels } from "@sdk/indexer-v2/types";
import { PriceFeedInner } from "./inner";

export const PriceFeed = async ({ data }: { data: Array<DatabaseModels["price_feed"]> }) => {
return <PriceFeedInner data={data} />;
};
export const PriceFeed = async ({ data }: { data: Array<DatabaseModels["price_feed"]> }) => (
<PriceFeedInner data={data} />
);
Loading

0 comments on commit 8c3322b

Please sign in to comment.