Skip to content

Commit 489559c

Browse files
committed
add agent banner, fix client prefetch
1 parent d844727 commit 489559c

File tree

8 files changed

+192
-47
lines changed

8 files changed

+192
-47
lines changed

apps/nextjs/public/elizaOS.avif

3.78 KB
Binary file not shown.

apps/nextjs/src/app/(app)/games/GameCard.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1+
import type { CollectionEntry } from "@/utils/keystatic";
12
import Image from "next/image";
23
import Link from "next/link";
3-
4-
import { Button } from "@realms-world/ui/components/ui/button";
54
import { Badge } from "@realms-world/ui/components/ui/badge";
5+
import { Button } from "@realms-world/ui/components/ui/button";
6+
67
import { BaseCard } from "../../_components/BaseCard";
78
import { StatusDot } from "../../_components/StatusDot";
8-
import type { CollectionEntry } from "@/utils/keystatic";
99

10-
export const GameCard = ({ game, slug }: { game: CollectionEntry<'games'>, slug: string }) => {
10+
export const GameCard = ({
11+
game,
12+
slug,
13+
className,
14+
}: {
15+
game: CollectionEntry<"games">;
16+
slug: string;
17+
className?: string;
18+
}) => {
1119
return (
12-
<BaseCard>
20+
<BaseCard className={className}>
1321
<Link
14-
className="group relative flex h-80 flex-col items-center justify-center text-center "
22+
className="group relative flex h-80 flex-col items-center justify-center text-center"
1523
href={`/games/${slug}`} // navigate to a custom game homepage if one is specified, default page otherwise
1624
>
1725
<div className="absolute left-4 top-4 z-10 font-sans">
@@ -29,17 +37,17 @@ export const GameCard = ({ game, slug }: { game: CollectionEntry<'games'>, slug:
2937
alt={game.title}
3038
width={800}
3139
height={400}
32-
className="absolute bottom-0 rounded top-0 h-full w-full object-cover brightness-75 transition-all duration-300 group-hover:filter-none"
40+
className="absolute bottom-0 top-0 h-full w-full rounded object-cover brightness-75 transition-all duration-300 group-hover:filter-none"
3341
/>
34-
<div className="relative flex h-full w-full max-w-full flex-col items-center justify-center object-contain">
42+
<div className="relative flex h-full w-full max-w-full flex-col items-center justify-center object-contain">
3543
<Image
3644
src={`/content/games/${slug}/${game.icon}`}
3745
alt={game.title}
3846
width={80}
3947
height={80}
4048
className="max-h-[180px] w-auto max-w-[75%] object-contain"
4149
/>
42-
<p className="mt-2 text-lg font-semibold">{game.description}</p>
50+
<p className="mt-2 text-lg font-semibold">{game.description}</p>
4351
</div>
4452
<div className="invisible z-10 opacity-0 duration-300 group-hover:visible group-hover:-translate-y-3 group-hover:opacity-100">
4553
<Button variant={"outline"}>Explore</Button>

apps/nextjs/src/app/(app)/page.tsx

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,21 @@ import { Suspense } from "react";
22
import Image from "next/image";
33
import Link from "next/link";
44
import { GameCard } from "@/app/(app)/games/GameCard";
5-
import LordsIcon from "@/icons/lords.svg";
65
import { api, HydrateClient } from "@/trpc/server";
76
import { reader } from "@/utils/keystatic";
87
import { Button } from "@realms-world/ui/components/ui/button";
9-
import {
10-
Card,
11-
CardContent,
12-
CardHeader,
13-
CardTitle,
14-
} from "@realms-world/ui/components/ui/card";
158
import {
169
Carousel,
1710
CarouselContent,
1811
CarouselItem,
1912
CarouselNext,
2013
CarouselPrevious,
2114
} from "@realms-world/ui/components/ui/carousel";
22-
import { formatNumber } from "@realms-world/utils";
2315

16+
import { AgentBanner } from "../_components/AgentBanner";
2417
import { PageLayout } from "../_components/PageLayout";
2518
import { Partners } from "../_components/Partners";
2619
import { VeLordsBanner } from "../_components/VeLordsBanner";
27-
import { VeLordsRewardsChart } from "./account/lords/velords/VeLordsRewardsChart";
2820
import { BlogGrid } from "./blogs/BlogGrid";
2921
import CollectionsList from "./collection/CollectionsList";
3022

@@ -97,23 +89,40 @@ export default async function Home() {
9789
<h2 className="mb-4 font-sans text-xl sm:text-2xl md:text-3xl">
9890
All Games
9991
</h2>
100-
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
101-
{games.map((game, index) => (
102-
<GameCard key={index} game={game.entry} slug={game.slug} />
103-
))}
92+
<div className="">
93+
<Carousel opts={{ dragFree: true }} className="w-full">
94+
<CarouselContent>
95+
{games.map((game, index) => (
96+
<CarouselItem
97+
className="translate-3d flex-[0_0_25%] pl-0"
98+
key={index}
99+
>
100+
<GameCard
101+
className="embla__scale"
102+
key={index}
103+
game={game.entry}
104+
slug={game.slug}
105+
/>
106+
</CarouselItem>
107+
))}
108+
</CarouselContent>
109+
<CarouselPrevious className="left-2 sm:left-4 md:left-6 lg:left-8" />
110+
<CarouselNext className="right-2 sm:right-4 md:right-6 lg:right-8" />
111+
</Carousel>
104112
</div>
105113

114+
<Suspense fallback={<div>Loading...</div>}>
115+
<VeLordsBanner />
116+
</Suspense>
117+
<AgentBanner />
118+
106119
<div className="my-12 sm:my-16 md:my-20 lg:my-24">
107120
<h2 className="mb-4 font-sans text-xl sm:text-2xl md:text-3xl">
108121
News
109122
</h2>
110123
<BlogGrid />
111124
</div>
112125

113-
<Suspense fallback={<div>Loading...</div>}>
114-
<VeLordsBanner />
115-
</Suspense>
116-
117126
<hr className="my-6 border sm:my-8" />
118127
<div className="my-12 sm:my-16 md:my-20">
119128
<h2 className="mb-4 font-sans text-xl sm:text-2xl md:text-3xl">
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"use client";
2+
3+
import { useRef } from "react";
4+
import Image from "next/image";
5+
import { motion, useInView } from "framer-motion"; // Import useInView
6+
7+
export const AgentBanner = () => {
8+
const ref = useRef(null);
9+
const isInView = useInView(ref, { margin: "-250px" }); // Trigger animation once
10+
return (
11+
<div ref={ref} className="my-12 flex flex-col items-center md:flex-row">
12+
<div className="w-full md:w-1/2">
13+
<Image
14+
src="/elizaOS.avif"
15+
alt="Banner Image"
16+
width={800}
17+
height={600}
18+
className="h-full w-full object-cover"
19+
/>
20+
</div>
21+
<div className="w-full p-4 md:w-1/2">
22+
<div className="mx-auto max-w-lg">
23+
<motion.h2
24+
className="text-2xl font-bold sm:text-3xl md:text-4xl"
25+
initial={{ opacity: 0, y: -20 }}
26+
animate={isInView ? { opacity: 1, y: 0 } : {}}
27+
transition={{ delay: 0.3, duration: 1 }}
28+
>
29+
Realms AI x Daydreams x Eliza (AI16z)
30+
</motion.h2>
31+
<motion.p
32+
className="mt-4 text-sm sm:text-base md:text-lg"
33+
initial={{ opacity: 0 }}
34+
animate={isInView ? { opacity: 1 } : {}}
35+
transition={{ delay: 0.6, duration: 1 }}
36+
>
37+
Play with generative onchain game agents on Eternum and other Dojo
38+
games on Starknet. The Eliza Daydreams protocol enables the creation
39+
of agents capable of complex problem solving and adaptation to
40+
thrive in Realms World
41+
</motion.p>
42+
<motion.a
43+
className="mt-4 text-sm sm:text-base md:text-lg"
44+
initial={{ opacity: 0 }}
45+
animate={isInView ? { opacity: 1 } : {}}
46+
transition={{ delay: 0.6, duration: 1 }}
47+
href="https://github.com/daydreamsai/daydreams"
48+
target="_blank"
49+
>
50+
Read more about Eliza Daydreams
51+
</motion.a>
52+
</div>
53+
</div>
54+
</div>
55+
);
56+
};

apps/nextjs/src/app/_components/VeLordsBanner.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function VeLordsBanner() {
3939
<div className="hidden sm:block sm:w-1/2">
4040
<VeLordsRewardsChart
4141
data={data}
42-
//totalSupply={totalSupply.new_supply}
42+
totalSupply={totalSupplyData?.new_supply}
4343
/>
4444
</div>
4545
</div>
@@ -67,7 +67,7 @@ export function VeLordsBanner() {
6767
<CardContent>
6868
<p className="flex text-2xl font-bold">
6969
<LordsIcon className="mr-3 w-5" />
70-
{formatNumber(1234567)}
70+
{formatNumber(Number(totalSupplyData?.new_supply ?? 0))}
7171
</p>
7272
</CardContent>
7373
</Card>

apps/nextjs/src/trpc/react.tsx

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
"use client";
22

3+
import type { AppRouter } from "@realms-world/api";
4+
import type { QueryClient } from "@tanstack/react-query";
35
import { useState } from "react";
4-
import { env } from "env";
5-
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
6+
import { QueryClientProvider } from "@tanstack/react-query";
67
import { loggerLink, unstable_httpBatchStreamLink } from "@trpc/client";
78
import { createTRPCReact } from "@trpc/react-query";
9+
import { env } from "env";
810
import SuperJSON from "superjson";
911

10-
import type { AppRouter } from "@realms-world/api";
11-
12-
const createQueryClient = () =>
13-
new QueryClient({
14-
defaultOptions: {
15-
queries: {
16-
// With SSR, we usually want to set some default staleTime
17-
// above 0 to avoid refetching immediately on the client
18-
staleTime: 30 * 1000,
19-
},
20-
},
21-
});
12+
import { createQueryClient } from "./query-client";
2213

2314
let clientQueryClientSingleton: QueryClient | undefined = undefined;
2415
const getQueryClient = () => {
@@ -33,10 +24,7 @@ const getQueryClient = () => {
3324

3425
export const api = createTRPCReact<AppRouter>();
3526

36-
export function TRPCReactProvider(props: {
37-
children: React.ReactNode;
38-
headersPromise: Promise<Headers>;
39-
}) {
27+
export function TRPCReactProvider(props: { children: React.ReactNode }) {
4028
const queryClient = getQueryClient();
4129

4230
const [trpcClient] = useState(() =>

apps/ui/src/components/ui/carousel.tsx

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
"use client";
22

3-
import type { UseEmblaCarouselType } from "embla-carousel-react";
3+
import type {
4+
EmblaCarouselType,
5+
EmblaEventType,
6+
UseEmblaCarouselType,
7+
} from "embla-carousel";
48
import * as React from "react";
59
import { cn } from "@realms-world/utils";
610
import useEmblaCarousel from "embla-carousel-react";
711
import { ArrowLeft, ArrowRight } from "lucide-react";
812

913
import { Button } from "./button";
1014

15+
const TWEEN_FACTOR_BASE = 0.15;
16+
17+
const numberWithinRange = (number: number, min: number, max: number): number =>
18+
Math.min(Math.max(number, min), max);
19+
1120
type CarouselApi = UseEmblaCarouselType[1];
1221
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
1322
type CarouselOptions = UseCarouselParameters[0];
@@ -119,6 +128,77 @@ const Carousel = React.forwardRef<
119128
};
120129
}, [api, onSelect]);
121130

131+
const tweenFactor = React.useRef(0);
132+
const tweenNodes = React.useRef<HTMLElement[]>([]);
133+
134+
const setTweenNodes = React.useCallback((api: EmblaCarouselType): void => {
135+
tweenNodes.current = api.slideNodes().map((slideNode) => {
136+
return slideNode.querySelector(".embla__scale") as HTMLElement;
137+
});
138+
}, []);
139+
140+
const setTweenFactor = React.useCallback((api: EmblaCarouselType) => {
141+
tweenFactor.current = TWEEN_FACTOR_BASE * api.scrollSnapList().length;
142+
}, []);
143+
144+
const tweenScale = React.useCallback(
145+
(api: EmblaCarouselType, eventName?: EmblaEventType) => {
146+
const engine = api.internalEngine();
147+
const scrollProgress = api.scrollProgress();
148+
const slidesInView = api.slidesInView();
149+
const isScrollEvent = eventName === "scroll";
150+
151+
api.scrollSnapList().forEach((scrollSnap, snapIndex) => {
152+
let diffToTarget = scrollSnap - scrollProgress;
153+
const slidesInSnap = engine.slideRegistry[snapIndex];
154+
155+
slidesInSnap.forEach((slideIndex) => {
156+
if (isScrollEvent && !slidesInView.includes(slideIndex)) return;
157+
158+
if (engine.options.loop) {
159+
engine.slideLooper.loopPoints.forEach((loopItem) => {
160+
const target = loopItem.target();
161+
162+
if (slideIndex === loopItem.index && target !== 0) {
163+
const sign = Math.sign(target);
164+
165+
if (sign === -1) {
166+
diffToTarget = scrollSnap - (1 + scrollProgress);
167+
}
168+
if (sign === 1) {
169+
diffToTarget = scrollSnap + (1 - scrollProgress);
170+
}
171+
}
172+
});
173+
}
174+
175+
const tweenValue = 1 - Math.abs(diffToTarget * tweenFactor.current);
176+
const scale = numberWithinRange(tweenValue, 0, 1).toString();
177+
const tweenNode = tweenNodes.current[slideIndex];
178+
if (tweenNode) {
179+
tweenNode.style.transform = `scale(${scale})`;
180+
}
181+
});
182+
});
183+
},
184+
[],
185+
);
186+
187+
React.useEffect(() => {
188+
if (!api) return;
189+
190+
setTweenNodes(api);
191+
setTweenFactor(api);
192+
tweenScale(api);
193+
194+
api
195+
.on("reInit", setTweenNodes)
196+
.on("reInit", setTweenFactor)
197+
.on("reInit", tweenScale)
198+
.on("scroll", tweenScale)
199+
.on("slideFocus", tweenScale);
200+
}, [api, tweenScale]);
201+
122202
return (
123203
<CarouselContext.Provider
124204
value={{

config/styles/globals.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,7 @@ body::-webkit-scrollbar-thumb,
226226
.mask-transparent {
227227
position: relative;
228228
}
229+
230+
.translate-3d {
231+
transform: translate3d(0, 0, 0);
232+
}

0 commit comments

Comments
 (0)