From 006cd63388f22e74f9e2e5f085631fb2dc46627d Mon Sep 17 00:00:00 2001 From: Julien Calixte Date: Sun, 5 Apr 2026 11:56:36 +0200 Subject: [PATCH] feat: paginate repo list with infinite scroll Load 30 repos at a time instead of 100 at once, showing data sooner. Adds v-infinite-scroll to RepoList.vue to fetch subsequent pages on scroll. --- src/hooks/useRepos.hook.ts | 51 ++++++++++++++-------- src/modules/repo/hooks/useRepoList.hook.ts | 6 ++- src/views/RepoList.vue | 9 +++- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/hooks/useRepos.hook.ts b/src/hooks/useRepos.hook.ts index 3b27fb0..220debb 100644 --- a/src/hooks/useRepos.hook.ts +++ b/src/hooks/useRepos.hook.ts @@ -1,34 +1,47 @@ -import { useAsyncState } from "@vueuse/core" +import { computed, ref } from "vue" import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook" import { RepoBase } from "@/modules/repo/interfaces/RepoBase" import { getOctokit } from "@/modules/repo/services/octo" +const PER_PAGE = 30 + export const useRepos = () => { const { username, accessToken } = useGitHubLogin() - const repos = useAsyncState(async () => { + + const repos = ref([]) + const isReady = ref(false) + const currentPage = ref(0) + const totalCount = ref(0) + + const loadMore = async () => { if (!accessToken.value || !username.value) { - return [] + 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: 100 + per_page: PER_PAGE, + page: nextPage }) - - return repoList.data.items - .map((item) => ({ - id: `${item.id}`, - name: item.name, - isPrivate: item.private - })) - .sort((a, b) => (a.name < b.name ? -1 : 1)) - }, []) - - return { - repos: repos.state, - isReady: repos.isReady + 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 } + + const canLoadMore = computed(() => repos.value.length < totalCount.value) + + loadMore() + + return { repos, isReady, canLoadMore, loadMore } } diff --git a/src/modules/repo/hooks/useRepoList.hook.ts b/src/modules/repo/hooks/useRepoList.hook.ts index 2eb8619..fd9200e 100644 --- a/src/modules/repo/hooks/useRepoList.hook.ts +++ b/src/modules/repo/hooks/useRepoList.hook.ts @@ -6,7 +6,7 @@ import { RepoBase } from "@/modules/repo/interfaces/RepoBase" export const useRepoList = () => { const { savedFavoriteRepos, addFavorite, removeFavorite } = useFavoriteRepos() - const { repos } = useRepos() + const { repos, canLoadMore, loadMore } = useRepos() const favoriteRepos = computed(() => { return repos.value.filter((repo) => @@ -38,6 +38,8 @@ export const useRepoList = () => { favoriteRepos, otherRepos, favoriteCheckboxes, - toggleCheckbox + toggleCheckbox, + canLoadMore, + loadMore } } diff --git a/src/views/RepoList.vue b/src/views/RepoList.vue index 20f9351..d9ccf03 100644 --- a/src/views/RepoList.vue +++ b/src/views/RepoList.vue @@ -1,4 +1,6 @@