116 lines
2.7 KiB
TypeScript
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 };
|
|
};
|