Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
mastodon
GitHub Repository: mastodon/joinmastodon
Path: blob/main/pages/donate/index.tsx
1006 views
1
import * as cookie from "cookie"
2
import type { GetServerSideProps, InferGetServerSidePropsType } from "next"
3
import { useRouter } from "next/navigation"
4
import { useCallback, useEffect } from "react"
5
import { z } from "zod"
6
7
import { OnDonateFn, DonateWidget } from "../../components/donate/DonateWidget"
8
import { sendMessage, themeSchema } from "../../donate/utils"
9
import {
10
CampaignResponse,
11
CURRENCIES,
12
DONATION_FREQUENCIES,
13
} from "../../types/api"
14
import { fetchEndpoint } from "../../utils/api"
15
import { DonateFooter } from "../../components/donate/DonateFooter"
16
import { DonateWrapper } from "../../components/donate/DonateWrapper"
17
18
export default function DonatePage({
19
theme,
20
default_currency,
21
donation_message,
22
donation_button_text,
23
donation_url,
24
amounts,
25
frequency: defaultFrequency,
26
amount: defaultAmount,
27
currency: defaultCurrency,
28
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
29
const router = useRouter()
30
const handleDonate: OnDonateFn = useCallback(
31
async (amount, frequency, currency) => {
32
const params = new URLSearchParams({
33
url: donation_url,
34
frequency,
35
amount: amount.toFixed(0),
36
currency,
37
theme,
38
})
39
router.push(`/donate/checkout?${params.toString()}`)
40
},
41
[donation_url, router, theme]
42
)
43
44
useEffect(() => {
45
sendMessage("widget-loaded")
46
}, [])
47
48
return (
49
<DonateWrapper theme={theme}>
50
<DonateWidget
51
defaultCurrency={defaultCurrency ?? default_currency}
52
className="p-8 pb-2 grow"
53
messages={{ donation_message, donation_button_text }}
54
amounts={amounts}
55
onDonate={handleDonate}
56
defaultAmount={defaultAmount}
57
defaultFrequency={defaultFrequency}
58
/>
59
<DonateFooter />
60
</DonateWrapper>
61
)
62
}
63
64
type DonatePageProps = DonatePageQuery & CampaignResponse
65
66
const querySchema = z.object({
67
theme: themeSchema,
68
campaign: z.string().optional(),
69
callback: z.string().optional(),
70
frequency: z.enum(DONATION_FREQUENCIES).optional(),
71
amount: z.coerce.number().int().positive().gte(100).optional(),
72
currency: z.enum(CURRENCIES).optional(),
73
})
74
type DonatePageQuery = z.infer<typeof querySchema>
75
76
export const getServerSideProps: GetServerSideProps<DonatePageProps> = async ({
77
query,
78
locale,
79
req,
80
res,
81
}) => {
82
if (!process.env.STRIPE_PUBLIC_KEY) {
83
throw new Error("No Stripe public key set")
84
}
85
86
let seed = req.cookies.seed
87
if (!seed) {
88
seed = Math.floor(Math.random() * 99).toString()
89
res.setHeader(
90
"Set-Cookie",
91
cookie.serialize("seed", seed, {
92
httpOnly: true,
93
maxAge: 60 * 15,
94
secure: process.env.NODE_ENV === "production",
95
})
96
)
97
}
98
const queryParams = new URLSearchParams({
99
locale,
100
platform: "android",
101
seed,
102
source: "menu",
103
return_url: `${req.headers.host}/sponsor`,
104
})
105
if (process.env.APP_ENV !== "production") {
106
queryParams.set("environment", "staging")
107
}
108
109
try {
110
const apiRes = await fetchEndpoint<CampaignResponse>(
111
"v1/donations/campaigns/active",
112
queryParams
113
)
114
return {
115
props: {
116
...apiRes,
117
...querySchema.parse(query),
118
},
119
}
120
} catch {
121
return {
122
notFound: true,
123
}
124
}
125
}
126
127