From da4fada8a1e2d212824dd86f90c824245be92555 Mon Sep 17 00:00:00 2001 From: Julien Calixte Date: Mon, 27 Apr 2026 10:32:37 +0200 Subject: [PATCH] fix(repoList): handle Bad credentials error from GitHub API Catch 401 responses in useRepos loadMore and expose hasCredentialError, then show a sign-in prompt in RepoList instead of an unhandled rejection. --- src/hooks/useRepos.hook.ts | 54 ++++++++++++++++++++++++-------------- src/views/RepoList.vue | 7 ++++- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/hooks/useRepos.hook.ts b/src/hooks/useRepos.hook.ts index 096a2a1..213dde1 100644 --- a/src/hooks/useRepos.hook.ts +++ b/src/hooks/useRepos.hook.ts @@ -9,6 +9,7 @@ const STALE_TIME_MS = 20 * 60 * 1000 const repos = ref([]) const isReady = ref(false) +const hasCredentialError = ref(false) const currentPage = ref(0) const totalCount = ref(0) let lastFetchedAt = 0 @@ -21,24 +22,38 @@ export const useRepos = () => { isReady.value = true return } - const octokit = await getOctokit() - const nextPage = currentPage.value + 1 - const repoList = await octokit.request("GET /search/repositories", { - q: `user:${username.value}`, - per_page: PER_PAGE, - page: nextPage - }) - currentPage.value = nextPage - totalCount.value = repoList.data.total_count - const newItems = repoList.data.items.map((item) => ({ - id: `${item.id}`, - name: item.name, - isPrivate: item.private - })) - repos.value = [...repos.value, ...newItems].sort((a, b) => - a.name < b.name ? -1 : 1 - ) - isReady.value = true + try { + const octokit = await getOctokit() + const nextPage = currentPage.value + 1 + const repoList = await octokit.request("GET /search/repositories", { + q: `user:${username.value}`, + per_page: PER_PAGE, + page: nextPage + }) + currentPage.value = nextPage + totalCount.value = repoList.data.total_count + const newItems = repoList.data.items.map((item) => ({ + id: `${item.id}`, + name: item.name, + isPrivate: item.private + })) + repos.value = [...repos.value, ...newItems].sort((a, b) => + a.name < b.name ? -1 : 1 + ) + } catch (err: unknown) { + if ( + typeof err === "object" && + err !== null && + "status" in err && + (err as { status: number }).status === 401 + ) { + hasCredentialError.value = true + } else { + throw err + } + } finally { + isReady.value = true + } } const canLoadMore = computed(() => repos.value.length < totalCount.value) @@ -50,10 +65,11 @@ export const useRepos = () => { currentPage.value = 0 totalCount.value = 0 isReady.value = false + hasCredentialError.value = false } lastFetchedAt = Date.now() loadMore() } - return { repos, isReady, canLoadMore, loadMore } + return { repos, isReady, hasCredentialError, canLoadMore, loadMore } } diff --git a/src/views/RepoList.vue b/src/views/RepoList.vue index 3cc4a6a..8e1d0ae 100644 --- a/src/views/RepoList.vue +++ b/src/views/RepoList.vue @@ -2,12 +2,13 @@ import { vInfiniteScroll } from "@vueuse/components" import GoBack from "@/components/GoBack.vue" +import SignInGithub from "@/components/SignInGithub.vue" import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook" import { useRepos } from "@/hooks/useRepos.hook" import { useRepoList } from "@/modules/repo/hooks/useRepoList.hook" const { username } = useGitHubLogin() -const { isReady } = useRepos() +const { isReady, hasCredentialError } = useRepos() const { favoriteRepos, otherRepos, @@ -26,6 +27,10 @@ const {

Repositories

loading...
+
+

Your GitHub credentials are invalid or expired.

+ +