xref: /expo/docs/providers/page-api-version.tsx (revision dfd15ebd)
1import { NextRouter } from 'next/router';
2import React, { createContext, PropsWithChildren, useCallback, useContext } from 'react';
3
4import navigation from '~/public/static/constants/navigation.json';
5
6export const PageApiVersionContext = createContext({
7  /** The version selected in the URL, or the default version */
8  version: 'latest',
9  /** If the current URL has a version defined */
10  hasVersion: false,
11  /** Change the URL to the select version  */
12  setVersion: newVersion => {
13    throw new Error('PageApiVersionContext not found');
14  },
15} as PageApiVersionContextType);
16
17export type PageApiVersionContextType = {
18  version: keyof typeof navigation.reference;
19  hasVersion: boolean;
20  setVersion: (newVersion: string) => void;
21};
22
23type Props = PropsWithChildren<{
24  /** The router containing the current URL info of the page, possibly containing the API version */
25  router: NextRouter;
26}>;
27
28export function PageApiVersionProvider(props: Props) {
29  const version = getVersionFromPath(props.router.pathname);
30  const hasVersion = version !== null;
31
32  // note: if the page doesn't exists, the error page will handle it
33  const setVersion = useCallback((newVersion: string) => {
34    props.router.push(replaceVersionInPath(props.router.pathname, newVersion));
35  }, []);
36
37  return (
38    <PageApiVersionContext.Provider
39      value={{ setVersion, hasVersion, version: version || 'latest' }}>
40      {props.children}
41    </PageApiVersionContext.Provider>
42  );
43}
44
45export function usePageApiVersion() {
46  return useContext(PageApiVersionContext);
47}
48
49/**
50 * Determine if there is a version within the pathname of the URL.
51 * Versioned pages always starts with /versions/<version>.
52 */
53export function isVersionedPath(path: string) {
54  return path.startsWith('/versions/');
55}
56
57/**
58 * Find the version within the pathname of the URL.
59 * This only accepts pathnames, without hashes or query strings.
60 */
61export function getVersionFromPath(path: string): PageApiVersionContextType['version'] | null {
62  return !isVersionedPath(path)
63    ? null
64    : (path.split('/', 3).pop()! as PageApiVersionContextType['version']);
65}
66
67/**
68 * Replace the version in the pathname from the URL.
69 * If no version was found, the path is returned as is.
70 */
71export function replaceVersionInPath(path: string, newVersion: string) {
72  const version = getVersionFromPath(path);
73  return version ? path.replace(version, newVersion) : path;
74}
75