Skip to content

Commit 9d43907

Browse files
committed
Merge branch 'development'
2 parents 2d9b7be + 034b2e5 commit 9d43907

File tree

11 files changed

+274
-206
lines changed

11 files changed

+274
-206
lines changed

.env.production.devnet

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1-
NEXT_PUBLIC_ENVIRONMENT=devnet
2-
NEXT_PUBLIC_URL=https://devnet-explorer.ratio1.ai
3-
NEXT_PUBLIC_APP_URL=https://devnet-app.ratio1.ai
1+
# NEXT_PUBLIC_ENVIRONMENT=devnet
2+
# NEXT_PUBLIC_URL=https://devnet-explorer.ratio1.ai
3+
# NEXT_PUBLIC_APP_URL=https://devnet-app.ratio1.ai
44

5+
# NEXT_PUBLIC_BACKEND_URL=https://devnet-dapp-api.ratio1.ai
6+
# NEXT_PUBLIC_ORACLES_URL=https://devnet-oracle.ratio1.ai
7+
8+
# NEXT_PUBLIC_R1_CA=0x277CbD0Cf25F4789Bc04035eCd03d811FAf73691
9+
# NEXT_PUBLIC_ND_CA=0x90025B92240E3070d5CdcB3f44D6411855c55a73
10+
# NEXT_PUBLIC_MND_CA=0x17B8934dc5833CdBa1eF42D13D65D677C4727748
11+
# NEXT_PUBLIC_READER_CA=0xFcF04c9A67330431Af75a546615E4881BD8bdC78
12+
# NEXT_PUBLIC_EXPLORER_URL=https://sepolia.basescan.org
13+
14+
# NEXT_PUBLIC_GENESIS_DATE=2025-06-30T07:00:00.000Z
15+
# NEXT_PUBLIC_CONTRACTS_GENESIS_BLOCK=27751462
16+
# NEXT_PUBLIC_EPOCH_DURATION_SECONDS=3600
17+
# NEXT_PUBLIC_ND_LICENSE_CAP=1575188843457943924200
18+
19+
# Leaving old values until the LP is deployed
520
NEXT_PUBLIC_BACKEND_URL=https://devnet-dapp-api.ratio1.ai
621
NEXT_PUBLIC_ORACLES_URL=https://devnet-oracle.ratio1.ai
722

app/page.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ export default async function HomePage(props: {
1919
[key: string]: types.NodeState;
2020
} = response.result.nodes;
2121

22-
// console.log(response);
23-
2422
const pagesCount: number = response.result.nodes_total_pages;
2523

2624
return (

app/server-components/Nodes/Hero.tsx

Lines changed: 74 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,84 @@
11
import HeroEpochCard from '@/components/Hero/HeroEpochCard';
2+
import { fN } from '@/lib/utils';
23
import * as types from '@/typedefs/blockchain';
34
import { Skeleton } from '@heroui/skeleton';
45
import { round } from 'lodash';
5-
import { Suspense } from 'react';
6+
import { Suspense, cache, memo } from 'react';
67
import R1MintedLastEpoch from '../R1MintedLastEpoch';
78
import { BorderedCard } from '../shared/cards/BorderedCard';
89
import { CardHorizontal } from '../shared/cards/CardHorizontal';
910
import R1TotalSupply from '../shared/R1TotalSupply';
1011

12+
// Memoized component for props-dependent parts
13+
const HeroStats = memo(
14+
({ nodesTotalItems, resourcesTotal }: { nodesTotalItems: number; resourcesTotal: types.ResourcesTotal }) => (
15+
<>
16+
<CardHorizontal label="Active Nodes" value={nodesTotalItems} isFlexible widthClasses="min-w-[196px]" />
17+
18+
<CardHorizontal
19+
label="CPU Cores Available"
20+
value={round(resourcesTotal.cpu_cores_avail, 1)}
21+
isFlexible
22+
widthClasses="min-w-[288px]"
23+
/>
24+
25+
<CardHorizontal
26+
label="Memory Available"
27+
value={
28+
<div>
29+
{fN(resourcesTotal.mem_avail)} <span className="text-slate-500">GB</span>
30+
</div>
31+
}
32+
isFlexible
33+
widthClasses="min-w-[314px]"
34+
/>
35+
36+
<CardHorizontal
37+
label="Disk Available"
38+
value={
39+
<div>
40+
{fN(resourcesTotal.disk_avail)} <span className="text-slate-500">GB</span>
41+
</div>
42+
}
43+
isFlexible
44+
widthClasses="min-w-[290px]"
45+
/>
46+
</>
47+
),
48+
);
49+
50+
HeroStats.displayName = 'HeroStats';
51+
52+
const CachedHeroContent = cache(async () => (
53+
<>
54+
<CardHorizontal
55+
label={
56+
<div>
57+
<span className="font-semibold text-primary">$R1</span> total supply
58+
</div>
59+
}
60+
value={<R1TotalSupply />}
61+
isFlexible
62+
widthClasses="min-[420px]:min-w-[330px] md:max-w-[350px]"
63+
/>
64+
65+
<Suspense fallback={<Skeleton className="min-h-[76px] w-full min-w-[300px] flex-1 rounded-xl md:max-w-[320px]" />}>
66+
<CardHorizontal
67+
label={
68+
<div>
69+
<span className="font-semibold text-primary">$R1</span> minted last epoch
70+
</div>
71+
}
72+
value={<R1MintedLastEpoch />}
73+
isFlexible
74+
widthClasses="min-w-[300px]"
75+
/>
76+
</Suspense>
77+
78+
<HeroEpochCard />
79+
</>
80+
));
81+
1182
export default async function Hero({
1283
nodesTotalItems,
1384
resourcesTotal,
@@ -21,68 +92,8 @@ export default async function Hero({
2192
<div className="card-title-big font-bold">Nodes</div>
2293

2394
<div className="flexible-row">
24-
<CardHorizontal label="Active Nodes" value={nodesTotalItems} isFlexible widthClasses="min-w-[200px]" />
25-
26-
<CardHorizontal
27-
label="CPU Cores Available"
28-
value={
29-
<div>
30-
{round(resourcesTotal.cpu_cores_avail, 0)}/{resourcesTotal.cpu_cores}
31-
</div>
32-
}
33-
isFlexible
34-
widthClasses="min-w-[290px]"
35-
/>
36-
37-
<CardHorizontal
38-
label="Memory Available (GB)"
39-
value={
40-
<div>
41-
{round(resourcesTotal.mem_avail, 0)}/{round(resourcesTotal.mem_total, 0)}
42-
</div>
43-
}
44-
isFlexible
45-
widthClasses="min-w-[308px]"
46-
/>
47-
48-
<CardHorizontal
49-
label="Disk Available (GB)"
50-
value={
51-
<div>
52-
{round(resourcesTotal.disk_avail, 0)}/{round(resourcesTotal.disk_total, 0)}
53-
</div>
54-
}
55-
isFlexible
56-
widthClasses="min-w-[296px]"
57-
/>
58-
59-
<CardHorizontal
60-
label={
61-
<div>
62-
<span className="font-semibold text-primary">$R1</span> total supply
63-
</div>
64-
}
65-
value={<R1TotalSupply />}
66-
isFlexible
67-
widthClasses="min-[420px]:min-w-[330px] md:max-w-[350px]"
68-
/>
69-
70-
<Suspense
71-
fallback={<Skeleton className="min-h-[76px] w-full min-w-[340px] flex-1 rounded-xl md:max-w-[320px]" />}
72-
>
73-
<CardHorizontal
74-
label={
75-
<div>
76-
<span className="font-semibold text-primary">$R1</span> minted last epoch
77-
</div>
78-
}
79-
value={<R1MintedLastEpoch />}
80-
isFlexible
81-
widthClasses="min-w-[340px]"
82-
/>
83-
</Suspense>
84-
85-
<HeroEpochCard />
95+
<HeroStats nodesTotalItems={nodesTotalItems} resourcesTotal={resourcesTotal} />
96+
<CachedHeroContent />
8697
</div>
8798
</BorderedCard>
8899
</div>

app/server-components/Nodes/List.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1+
import Node from '@/app/server-components/Nodes/Node';
12
import ParamsPagination from '@/components/Nodes/ParamsPagination';
23
import * as types from '@/typedefs/blockchain';
3-
import { R1Address } from '@/typedefs/blockchain';
44
import { Skeleton } from '@heroui/skeleton';
55
import { Fragment, Suspense } from 'react';
66
import ListHeader from '../shared/ListHeader';
7-
import Node from './Node';
87
import NodeListGNDCard from './NodeListGNDCard';
98

109
export default async function List({
@@ -30,12 +29,16 @@ export default async function List({
3029
<div className="min-w-[152px]">Last Epoch Availability</div>
3130
</ListHeader>
3231

33-
{currentPage === 1 && <NodeListGNDCard />}
32+
{currentPage === 1 && (
33+
<Suspense fallback={<Skeleton className="min-h-[92px] w-full rounded-2xl" />}>
34+
<NodeListGNDCard />
35+
</Suspense>
36+
)}
3437

3538
{Object.entries(nodes).map(([ratio1Addr, node]) => (
3639
<Fragment key={ratio1Addr}>
3740
<Suspense fallback={<Skeleton className="min-h-[92px] w-full rounded-2xl" />}>
38-
<Node ratio1Addr={ratio1Addr as R1Address} node={node} />
41+
<Node ratio1Addr={ratio1Addr as types.R1Address} node={node} />
3942
</Suspense>
4043
</Fragment>
4144
))}

app/server-components/R1MintedLastEpoch.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,38 @@
1+
import { getCurrentEpoch } from '@/config';
12
import { fetchR1MintedLastEpoch } from '@/lib/api/blockchain';
23
import { cache } from 'react';
34
import { formatUnits } from 'viem';
45

5-
// Enable page-level caching
6-
export const revalidate = 60;
7-
export const dynamic = 'force-dynamic';
6+
let cachedValue: string | null = null;
7+
let cachedEpoch: number | null = null;
88

9-
const fetchCachedR1MintedLastEpoch = cache(async () => {
10-
const value: bigint = await fetchR1MintedLastEpoch();
11-
return value;
9+
const getCachedR1MintedLastEpoch = cache(async () => {
10+
const currentEpoch = getCurrentEpoch();
11+
12+
// Check if we have cached data for the current epoch
13+
if (cachedValue !== null && cachedEpoch === currentEpoch) {
14+
// console.log(`[R1MintedLastEpoch] using cached data for epoch ${currentEpoch}`);
15+
return cachedValue;
16+
}
17+
18+
// console.log(`[R1MintedLastEpoch] fetching new data for epoch ${currentEpoch}`);
19+
const value = await fetchR1MintedLastEpoch();
20+
const valueString = value.toString();
21+
22+
// console.log(`[R1MintedLastEpoch] fetched new data for epoch ${currentEpoch}, value: ${valueString}`);
23+
24+
cachedValue = valueString;
25+
cachedEpoch = currentEpoch;
26+
27+
return valueString;
1228
});
1329

1430
export default async function R1MintedLastEpoch() {
1531
let value: bigint | undefined;
1632

1733
try {
18-
value = await fetchCachedR1MintedLastEpoch();
34+
const cachedValue = await getCachedR1MintedLastEpoch();
35+
value = BigInt(cachedValue);
1936
} catch (error) {
2037
console.log('[R1MintedLastEpoch] error', error);
2138
return <div className="text-lg text-slate-600 md:text-[19px]"></div>;

app/server-components/SearchResultsList.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ interface Props {
99
results: SearchResult[];
1010
variant: 'search-page' | 'modal';
1111
getSectionTitle: (value: string) => React.ReactNode;
12+
onClose?: () => void;
1213
}
1314

14-
export default function SearchResultsList({ results, variant, getSectionTitle }: Props) {
15+
export default function SearchResultsList({ results, variant, getSectionTitle, onClose }: Props) {
1516
const linkClassName = clsx('row cursor-pointer px-4 transition-all ', {
1617
'rounded-xl border-2 border-slate-100 hover:border-slate-200 py-3': variant === 'search-page',
1718
'-mx-4 py-2.5 hover:bg-slate-50': variant === 'modal',
@@ -32,7 +33,11 @@ export default function SearchResultsList({ results, variant, getSectionTitle }:
3233
.filter((r): r is Extract<SearchResult, { type: 'node' }> => r.type === 'node')
3334
.map((node, index) => (
3435
<div key={index}>
35-
<Link href={`${routePath.node}/${node.nodeAddress}`} className={linkClassName}>
36+
<Link
37+
href={`${routePath.node}/${node.nodeAddress}`}
38+
className={linkClassName}
39+
onClick={onClose}
40+
>
3641
<div className="row gap-3">
3742
<div className="relative h-8 w-8">
3843
<Identicon value={`node_${node.nodeAddress}`} />
@@ -75,6 +80,7 @@ export default function SearchResultsList({ results, variant, getSectionTitle }:
7580
<Link
7681
href={`${routePath.license}/${license.licenseType}/${license.licenseId}`}
7782
className={linkClassName}
83+
onClick={onClose}
7884
>
7985
<div className="row gap-3">
8086
<div className="relative h-8 w-8">
@@ -114,7 +120,7 @@ export default function SearchResultsList({ results, variant, getSectionTitle }:
114120
.filter((r): r is Extract<SearchResult, { type: 'owner' }> => r.type === 'owner')
115121
.map((owner, index) => (
116122
<div key={index}>
117-
<Link href={`${routePath.owner}/${owner.address}`} className={linkClassName}>
123+
<Link href={`${routePath.owner}/${owner.address}`} className={linkClassName} onClick={onClose}>
118124
<div className="row gap-3">
119125
<div className="relative h-8 w-8">
120126
<Identicon value={`owner_${owner.address}`} />

app/server-components/TopBar.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { CardWithIcon } from '@/app/server-components/shared/cards/CardWithIcon'
22
import Search from '@/components/Search';
33
import { getActiveNodes } from '@/lib/api';
44
import * as types from '@/typedefs/blockchain';
5-
import { lazy } from 'react';
5+
import { lazy, Suspense } from 'react';
66
import { RiCpuLine } from 'react-icons/ri';
77
import { RowWithIcon } from './shared/cards/RowWithIcon';
88
import PriceCard from './shared/PriceCard';
@@ -13,7 +13,7 @@ export default async function TopBar() {
1313
let activeNodes: types.OraclesDefaultResult;
1414

1515
try {
16-
activeNodes = await getActiveNodes();
16+
activeNodes = await getActiveNodes(1); // Only fetch first page data for the count
1717
} catch (error) {
1818
console.error(error);
1919
return null;
@@ -27,19 +27,27 @@ export default async function TopBar() {
2727

2828
<div className="flex-1">
2929
<div className="hidden flex-wrap items-center justify-between gap-2 sm:flex lg:flex-nowrap lg:justify-end lg:gap-3">
30-
<LazyTopBarEpochCard />
30+
<Suspense>
31+
<LazyTopBarEpochCard />
32+
</Suspense>
3133

32-
<PriceCard />
34+
<Suspense>
35+
<PriceCard />
36+
</Suspense>
3337

3438
<CardWithIcon icon={<RiCpuLine />} label="Active Nodes">
3539
{activeNodes.result.nodes_total_items}
3640
</CardWithIcon>
3741
</div>
3842

3943
<div className="flex w-full flex-col gap-1.5 rounded-xl bg-slate-100 px-4 py-4 sm:hidden">
40-
<LazyTopBarEpochCard />
44+
<Suspense>
45+
<LazyTopBarEpochCard />
46+
</Suspense>
4147

42-
<PriceCard />
48+
<Suspense>
49+
<PriceCard />
50+
</Suspense>
4351

4452
<RowWithIcon icon={<RiCpuLine />} label="Active Nodes">
4553
{activeNodes.result.nodes_total_items}

components/Search.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,12 @@ export default function Search() {
149149
{!results.length ? (
150150
getAlert()
151151
) : (
152-
<SearchResultsList results={results} variant="modal" getSectionTitle={getSectionTitle} />
152+
<SearchResultsList
153+
results={results}
154+
variant="modal"
155+
onClose={onClose}
156+
getSectionTitle={getSectionTitle}
157+
/>
153158
)}
154159

155160
{/* Bottom bar */}

0 commit comments

Comments
 (0)