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