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.
This commit is contained in:
@@ -1,34 +1,47 @@
|
|||||||
import { useAsyncState } from "@vueuse/core"
|
import { computed, ref } from "vue"
|
||||||
|
|
||||||
import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook"
|
import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook"
|
||||||
import { RepoBase } from "@/modules/repo/interfaces/RepoBase"
|
import { RepoBase } from "@/modules/repo/interfaces/RepoBase"
|
||||||
import { getOctokit } from "@/modules/repo/services/octo"
|
import { getOctokit } from "@/modules/repo/services/octo"
|
||||||
|
|
||||||
|
const PER_PAGE = 30
|
||||||
|
|
||||||
export const useRepos = () => {
|
export const useRepos = () => {
|
||||||
const { username, accessToken } = useGitHubLogin()
|
const { username, accessToken } = useGitHubLogin()
|
||||||
const repos = useAsyncState<RepoBase[]>(async () => {
|
|
||||||
|
const repos = ref<RepoBase[]>([])
|
||||||
|
const isReady = ref(false)
|
||||||
|
const currentPage = ref(0)
|
||||||
|
const totalCount = ref(0)
|
||||||
|
|
||||||
|
const loadMore = async () => {
|
||||||
if (!accessToken.value || !username.value) {
|
if (!accessToken.value || !username.value) {
|
||||||
return []
|
isReady.value = true
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const octokit = await getOctokit()
|
const octokit = await getOctokit()
|
||||||
|
const nextPage = currentPage.value + 1
|
||||||
const repoList = await octokit.request("GET /search/repositories", {
|
const repoList = await octokit.request("GET /search/repositories", {
|
||||||
q: `user:${username.value}`,
|
q: `user:${username.value}`,
|
||||||
per_page: 100
|
per_page: PER_PAGE,
|
||||||
|
page: nextPage
|
||||||
})
|
})
|
||||||
|
currentPage.value = nextPage
|
||||||
return repoList.data.items
|
totalCount.value = repoList.data.total_count
|
||||||
.map((item) => ({
|
const newItems = repoList.data.items.map((item) => ({
|
||||||
id: `${item.id}`,
|
id: `${item.id}`,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
isPrivate: item.private
|
isPrivate: item.private
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => (a.name < b.name ? -1 : 1))
|
repos.value = [...repos.value, ...newItems].sort((a, b) =>
|
||||||
}, [])
|
a.name < b.name ? -1 : 1
|
||||||
|
)
|
||||||
return {
|
isReady.value = true
|
||||||
repos: repos.state,
|
|
||||||
isReady: repos.isReady
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const canLoadMore = computed(() => repos.value.length < totalCount.value)
|
||||||
|
|
||||||
|
loadMore()
|
||||||
|
|
||||||
|
return { repos, isReady, canLoadMore, loadMore }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { RepoBase } from "@/modules/repo/interfaces/RepoBase"
|
|||||||
|
|
||||||
export const useRepoList = () => {
|
export const useRepoList = () => {
|
||||||
const { savedFavoriteRepos, addFavorite, removeFavorite } = useFavoriteRepos()
|
const { savedFavoriteRepos, addFavorite, removeFavorite } = useFavoriteRepos()
|
||||||
const { repos } = useRepos()
|
const { repos, canLoadMore, loadMore } = useRepos()
|
||||||
|
|
||||||
const favoriteRepos = computed(() => {
|
const favoriteRepos = computed(() => {
|
||||||
return repos.value.filter((repo) =>
|
return repos.value.filter((repo) =>
|
||||||
@@ -38,6 +38,8 @@ export const useRepoList = () => {
|
|||||||
favoriteRepos,
|
favoriteRepos,
|
||||||
otherRepos,
|
otherRepos,
|
||||||
favoriteCheckboxes,
|
favoriteCheckboxes,
|
||||||
toggleCheckbox
|
toggleCheckbox,
|
||||||
|
canLoadMore,
|
||||||
|
loadMore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { vInfiniteScroll } from "@vueuse/components"
|
||||||
|
|
||||||
import GoBack from "@/components/GoBack.vue"
|
import GoBack from "@/components/GoBack.vue"
|
||||||
import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook"
|
import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook"
|
||||||
import { useRepos } from "@/hooks/useRepos.hook"
|
import { useRepos } from "@/hooks/useRepos.hook"
|
||||||
@@ -6,12 +8,15 @@ import { useRepoList } from "@/modules/repo/hooks/useRepoList.hook"
|
|||||||
|
|
||||||
const { username } = useGitHubLogin()
|
const { username } = useGitHubLogin()
|
||||||
const { isReady } = useRepos()
|
const { isReady } = useRepos()
|
||||||
const { favoriteRepos, otherRepos, favoriteCheckboxes, toggleCheckbox } =
|
const { favoriteRepos, otherRepos, favoriteCheckboxes, toggleCheckbox, canLoadMore, loadMore } =
|
||||||
useRepoList()
|
useRepoList()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="repo-list">
|
<div
|
||||||
|
class="repo-list"
|
||||||
|
v-infinite-scroll="[loadMore, { canLoadMore: () => canLoadMore }]"
|
||||||
|
>
|
||||||
<h1 class="title is-1">Repositories</h1>
|
<h1 class="title is-1">Repositories</h1>
|
||||||
<go-back />
|
<go-back />
|
||||||
<div v-if="!isReady">loading...</div>
|
<div v-if="!isReady">loading...</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user