snowballtools-base/packages/frontend/src/context/OctokitContext.tsx
2024-04-11 21:49:14 -05:00

116 lines
2.7 KiB
TypeScript

import {
createContext,
useContext,
ReactNode,
useState,
useMemo,
useCallback,
useEffect,
} from 'react';
import { Octokit, RequestError } from 'octokit';
import { useNavigate, useParams } from 'react-router-dom';
import { useDebounceCallback } from 'usehooks-ts';
import { useGQLClient } from './GQLClientContext';
import { useToast } from 'components/shared/Toast';
const UNAUTHORIZED_ERROR_CODE = 401;
interface ContextValue {
octokit: Octokit;
isAuth: boolean;
updateAuth: () => void;
}
const OctokitContext = createContext<ContextValue>({
octokit: new Octokit(),
isAuth: false,
updateAuth: () => {},
});
export const OctokitProvider = ({ children }: { children: ReactNode }) => {
const [authToken, setAuthToken] = useState<string | null>(null);
const [isAuth, setIsAuth] = useState(false);
const navigate = useNavigate();
const { orgSlug } = useParams();
const { toast, dismiss } = useToast();
const client = useGQLClient();
const fetchUser = useCallback(async () => {
const { user } = await client.getUser();
setAuthToken(user.gitHubToken);
}, []);
const updateAuth = useCallback(() => {
fetchUser();
}, []);
const octokit = useMemo(() => {
if (!authToken) {
setIsAuth(false);
return new Octokit();
}
setIsAuth(true);
return new Octokit({ auth: authToken });
}, [authToken]);
useEffect(() => {
fetchUser();
}, []);
const debouncedUnauthorizedGithubHandler = useDebounceCallback(
useCallback(
(error: RequestError) => {
toast({
id: 'unauthorized-github-token',
title: `GitHub authentication error: ${error.message}`,
variant: 'error',
onDismiss: dismiss,
});
navigate(`/${orgSlug}/projects/create`);
},
[toast, navigate, orgSlug],
),
500,
);
useEffect(() => {
// TODO: Handle React component error
const interceptor = async (error: RequestError | Error) => {
if (
error instanceof RequestError &&
error.status === UNAUTHORIZED_ERROR_CODE
) {
await client.unauthenticateGithub();
await fetchUser();
debouncedUnauthorizedGithubHandler(error);
}
throw error;
};
octokit.hook.error('request', interceptor);
return () => {
// Remove the interceptor when the component unmounts
octokit.hook.remove('request', interceptor);
};
}, [octokit, client, debouncedUnauthorizedGithubHandler]);
return (
<OctokitContext.Provider value={{ octokit, updateAuth, isAuth }}>
{children}
</OctokitContext.Provider>
);
};
export const useOctokit = () => {
const { octokit, updateAuth, isAuth } = useContext(OctokitContext);
return { octokit, updateAuth, isAuth };
};