Skip to content

Commit 109c75e

Browse files
authored
Merge pull request #560 from PotLock/staging
Staging to prod
2 parents 7f9a8c8 + b6342f9 commit 109c75e

File tree

1 file changed

+12
-141
lines changed

1 file changed

+12
-141
lines changed
Lines changed: 12 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,24 @@
11
import { ReactElement, useMemo } from "react";
22

3-
import type { GetStaticPaths, GetStaticProps } from "next";
43
import { useRouter } from "next/router";
54

6-
import { CAMPAIGNS_CONTRACT_ACCOUNT_ID, NETWORK } from "@/common/_config";
75
import { APP_METADATA } from "@/common/constants";
8-
import type { Campaign as ContractCampaign } from "@/common/contracts/core/campaigns/interfaces";
96
import { CampaignDonorsTable, CampaignSettings } from "@/entities/campaign";
107
import { CampaignLayout } from "@/layout/campaign/components/layout";
118
import { RootLayout } from "@/layout/components/root-layout";
129

13-
type SeoProps = {
14-
seoTitle: string;
15-
seoDescription: string;
16-
seoImage?: string;
17-
};
18-
19-
type CampaignPageProps = {
20-
seo: SeoProps;
21-
campaignId: number;
22-
};
23-
24-
export default function CampaignPage({ seo, campaignId }: CampaignPageProps) {
10+
export default function CampaignPage() {
2511
const router = useRouter();
26-
const { tab } = router.query as { tab?: string };
12+
const { tab, campaignId } = router.query as { tab?: string; campaignId?: string };
2713

28-
const parsedCampaignId = campaignId;
14+
const parsedCampaignId = campaignId ? parseInt(campaignId) : undefined;
2915

3016
// Determine which content to show based on tab param
3117
const content = useMemo(() => {
18+
if (!parsedCampaignId || Number.isNaN(parsedCampaignId)) {
19+
return null;
20+
}
21+
3222
switch (tab) {
3323
case "settings":
3424
return <CampaignSettings campaignId={parsedCampaignId} />;
@@ -39,7 +29,11 @@ export default function CampaignPage({ seo, campaignId }: CampaignPageProps) {
3929
}, [tab, parsedCampaignId]);
4030

4131
return (
42-
<RootLayout title={seo.seoTitle} description={seo.seoDescription} image={seo.seoImage}>
32+
<RootLayout
33+
title={`Campaign ${parsedCampaignId}`}
34+
description={APP_METADATA.description}
35+
image={APP_METADATA.openGraph.images.url}
36+
>
4337
{content}
4438
</RootLayout>
4539
);
@@ -48,126 +42,3 @@ export default function CampaignPage({ seo, campaignId }: CampaignPageProps) {
4842
CampaignPage.getLayout = function getLayout(page: ReactElement) {
4943
return <CampaignLayout>{page}</CampaignLayout>;
5044
};
51-
52-
export const getStaticPaths: GetStaticPaths = async () => {
53-
return {
54-
paths: [],
55-
fallback: "blocking",
56-
};
57-
};
58-
59-
export const getStaticProps: GetStaticProps<CampaignPageProps> = async (context) => {
60-
const { campaignId } = context.params as { campaignId: string };
61-
62-
const parsedCampaignId = parseInt(campaignId);
63-
64-
if (Number.isNaN(parsedCampaignId) || parsedCampaignId <= 0) {
65-
return {
66-
notFound: true,
67-
};
68-
}
69-
70-
// Fallback SEO in case of error
71-
const defaultSeo: SeoProps = {
72-
seoTitle: `Campaign ${campaignId} | Potlock`,
73-
seoDescription: APP_METADATA.description,
74-
seoImage: APP_METADATA.openGraph.images.url,
75-
};
76-
77-
try {
78-
const rpcUrl =
79-
NETWORK === "mainnet" ? "https://free.rpc.fastnear.com" : "https://test.rpc.fastnear.com";
80-
81-
const controller = new AbortController();
82-
const timeoutMs = 5000;
83-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
84-
85-
let campaign: ContractCampaign | null | undefined;
86-
87-
try {
88-
const response = await fetch(rpcUrl, {
89-
method: "POST",
90-
headers: { "content-type": "application/json" },
91-
signal: controller.signal,
92-
body: JSON.stringify({
93-
jsonrpc: "2.0",
94-
id: `campaign-${parsedCampaignId}`,
95-
method: "query",
96-
params: {
97-
request_type: "call_function",
98-
account_id: CAMPAIGNS_CONTRACT_ACCOUNT_ID,
99-
method_name: "get_campaign",
100-
args_base64: Buffer.from(JSON.stringify({ campaign_id: parsedCampaignId })).toString(
101-
"base64",
102-
),
103-
finality: "optimistic",
104-
},
105-
}),
106-
});
107-
108-
if (!response.ok) {
109-
throw new Error(`RPC response not ok: ${response.status}`);
110-
}
111-
112-
const payload = (await response.json()) as {
113-
error?: { message?: string };
114-
result?: { result?: number[] };
115-
};
116-
117-
if (payload.error?.message) {
118-
throw new Error(payload.error.message);
119-
}
120-
121-
const resultBytes = payload.result?.result
122-
? Uint8Array.from(payload.result.result)
123-
: undefined;
124-
125-
if (!resultBytes) {
126-
throw new Error("RPC returned empty result");
127-
}
128-
129-
campaign = JSON.parse(Buffer.from(resultBytes).toString()) as ContractCampaign;
130-
} finally {
131-
clearTimeout(timeoutId);
132-
}
133-
134-
if (!campaign) {
135-
return { notFound: true, revalidate: 60 };
136-
}
137-
138-
const seo: SeoProps = {
139-
seoTitle: `${campaign.name} | Potlock`,
140-
seoDescription:
141-
campaign.description && campaign.description.trim()
142-
? campaign.description.substring(0, 160)
143-
: APP_METADATA.description,
144-
seoImage: campaign.cover_image_url ?? APP_METADATA.openGraph.images.url,
145-
};
146-
147-
return {
148-
props: {
149-
seo,
150-
campaignId: parsedCampaignId,
151-
},
152-
revalidate: 300, // 5 minutes
153-
};
154-
} catch (error) {
155-
// Handle timeout or other errors by returning fallback SEO
156-
// This ensures the page doesn't break
157-
const message = (error as Error)?.message ?? "Unknown error";
158-
159-
if (message.toLowerCase().includes("not found")) {
160-
return { notFound: true, revalidate: 60 };
161-
}
162-
163-
console.error(`Error fetching campaign ${campaignId} for SEO:`, { message });
164-
165-
return {
166-
props: {
167-
seo: defaultSeo,
168-
campaignId: parsedCampaignId,
169-
},
170-
revalidate: 60, // Try again sooner on error
171-
};
172-
}
173-
};

0 commit comments

Comments
 (0)