JoinMastodon Documentation
repository·main·Indexed 42 days ago
https://github.com/mastodon/joinmastodonDocumentation for joinmastodon.org, the informational landing portal for the Mastodon project. Built with Next.js, the site features internationalization via react-intl and Crowdin, real-time statistics fetching from the joinmastodon.org API, and a donation system integrated with Stripe.
What's inside JoinMastodon
- When adding raster images, provide them at the maximum resolution they will be displayed at. If you do not require specific art direction, Next.js will automatically generate optimized mobile versions of the images.
Deploy joinmastodon.org
mainThe project must be hosted using a Node.js server (self-hosting) rather than a static HTML export. This requirement is necessary to support the following features:
- Image Optimization: Automatic WebP conversion and compression.
- Internationalized Routing: Localized versions of pages hosted at dedicated URLs for better SEO and crawling.
- Redirects: Custom redirect logic configured in
next.config.js.
Manage translations with react-intl and Crowdin
mainTranslations are managed via Crowdin and distributed using
react-intl. The project follows a specific workflow for adding and syncing translations:- Declare Messages: Use the
<FormattedMessage>component in your source files to declare translatable strings. - Extract Defaults: Run
yarn extractto pull default messages intolocales/en.json. - Sync with Crowdin: Sync the
locales/en.jsonfile with the Crowdin project. New messages from the source will appear in Crowdin for translators, and new translations will be downloaded back into corresponding locale files (e.g.,locales/es.json). - Build: During the build process, each locale loads its messages via
getStaticProps().
yarn extract- Declare Messages: Use the
Install and run joinmastodon.org locally
mainTo develop the site locally, ensure you have Node.js version 20 installed. This project uses
yarnfor dependency management. Ifyarnis not available, you can install it usingcorepack enableandcorepack prepare yarn@1.Follow these steps to install dependencies and start the development server:
yarn yarn run devServer-side data fetching for DonatePage
mainThe
getServerSidePropsfunction handles the initialization of the donation page by:- Stripe Validation: Ensures
process.env.STRIPE_PUBLIC_KEYis present. - Seed Management: Checks for a
seedcookie. If missing, it generates a random string (0-98) and sets it as anhttpOnlycookie with a 15-minute expiry. - API Request: Fetches active campaigns from the
v1/donations/campaigns/activeendpoint usingfetchEndpoint. The request includeslocale,platform(defaults toandroid),seed,source(defaults tomenu), and areturn_url. - Environment Handling: If
process.env.APP_ENVis notproduction, it addsenvironment=stagingto the query parameters. - Error Handling: If the API fetch fails, it returns
notFound: true.
Required Environment Variables:
STRIPE_PUBLIC_KEY: Required for the page to function.APP_ENV: Used to determine if the request should target thestagingenvironment.NODE_ENV: Used to determine if theseedcookie should besecure.
- Stripe Validation: Ensures
Use getStaticProps for static generation
mainThe landing page uses
getStaticPropsto enable static site generation (SSG). It is exported using a wrapper functionwithDefaultStaticPropsfrom the project's utilities.export const getStaticProps = withDefaultStaticProps()Fetch Mastodon statistics via API
mainThe
Metricscomponent in the About page retrieves real-time statistics by fetching data from thehttps://api.joinmastodon.org/statisticsendpoint. The returned data is an array of objects representing different time periods. To calculate growth or changes, the component compares the second-to-last entry (days.data[days.data.length - 2]) as thecurrentDayagainst the first entry (days.data[0]) as thecompareDay.// The underlying data fetch used by the Metrics component fetch("https://api.joinmastodon.org/statistics").then((res) => res.json())Implement a donation handler for the DonatePage
mainWhen building or extending a donation page using the
DonatePagepattern, you can implement theOnDonateFncallback to redirect users to a checkout flow. The handler should construct a URL with the following parameters:url: The base donation URL.frequency: The donation frequency (e.g., one-time or recurring).amount: The amount as a string, formatted with zero decimal places (e.g.,amount.toFixed(0)).currency: The selected currency.theme: The current UI theme.
Example implementation using
next/navigation:const handleDonate: OnDonateFn = useCallback( async (amount, frequency, currency) => { const params = new URLSearchParams({ url: donation_url, frequency, amount: amount.toFixed(0), currency, theme, }) router.push(`/donate/checkout?${params.toString()}`) }, [donation_url, router, theme] )const handleDonate: OnDonateFn = useCallback( async (amount, frequency, currency) => { const params = new URLSearchParams({ url: donation_url, frequency, amount: amount.toFixed(0), currency, theme, }) router.push(`/donate/checkout?${params.toString()}`) }, [donation_url, router, theme] )Query parameters for the DonatePage
mainThe
DonatePageaccepts several query parameters which are validated against a schema. These parameters allow for pre-configuring the donation widget:Parameter Type Description themethemeSchemaThe visual theme of the widget campaignstring(optional)Specific campaign identifier callbackstring(optional)Callback URL frequencyDONATION_FREQUENCIES(enum, optional)The donation frequency amountnumber(optional)An integer $\ge 100$ representing the amount currencyCURRENCIES(enum, optional)The selected currency Note:
amountis coerced to an integer and must be positive and at least 100.Exported Home component
mainThe
Homecomponent is the main landing page entrypoint for the application. It is a React component that renders the primary landing page layout, including the Hero section, Features, Why Mastodon, Testimonials, and Sponsors sections. It utilizesreact-intlfor internationalization andnext/headfor SEO metadata.export default HomeUse mapBoardPositionToLabel for board member roles
mainWhen rendering the Board of Directors section, use the
mapBoardPositionToLabelutility to transform raw position strings from the board data into human-readable labels for the UI.// Usage within the board mapping loop <span className="b2 block flex-grow !font-semibold text-gray-1"> {mapBoardPositionToLabel(member.position)} </span>