@@ -15,7 +15,7 @@ const orgName = computed(() => route.params.org.toLowerCase())
1515const { isConnected } = useConnector ()
1616
1717// Fetch packages progressively (first 250 on SSR, rest on demand via loadAll)
18- const { data : results, status, error, loadAll } = useOrgPackages (orgName )
18+ const { data : results, status, error, loadMore, loadAll } = useOrgPackages (orgName )
1919
2020// Handle 404 errors reactively (since we're not awaiting)
2121watch (
@@ -32,28 +32,41 @@ watch(
3232 { immediate: true },
3333)
3434
35- const allPackages = computed (() => results .value ?.objects ?? [])
35+ const packages = computed (() => results .value ?.objects ?? [])
3636const totalPackages = computed (() => results .value ?.totalPackages ?? 0 )
37-
38- // Show first 250 packages initially; expanding triggers loadAll() to fetch remaining data
39- const {
40- visibleItems : packages,
41- hasMore : hasMoreVisible,
42- isExpanding,
43- expand,
44- } = useVisibleItems (allPackages , 250 , { onExpand: loadAll })
4537const packageCount = computed (() => packages .value .length )
4638
47- // hasMore combines both: hidden in-memory items AND unfetched server data
48- const hasMore = computed (
49- () => hasMoreVisible .value || allPackages .value .length < totalPackages .value ,
50- )
39+ // Progressive loading: first 50 on SSR, rest fetched on demand via loadAll()
40+ const hasMore = computed (() => packages .value .length < totalPackages .value )
41+ const isLoadingMore = shallowRef (false )
42+
43+ /** Fetch the next batch of packages (1000 at a time). */
44+ async function fetchNextBatch() {
45+ if (! hasMore .value || isLoadingMore .value ) return
46+ isLoadingMore .value = true
47+ try {
48+ await loadMore ()
49+ } finally {
50+ isLoadingMore .value = false
51+ }
52+ }
53+
54+ /** Fetch ALL remaining packages (needed for client-side filtering). */
55+ async function fetchAllRemaining() {
56+ if (! hasMore .value || isLoadingMore .value ) return
57+ isLoadingMore .value = true
58+ try {
59+ await loadAll ()
60+ } finally {
61+ isLoadingMore .value = false
62+ }
63+ }
5164
5265// Preferences (persisted to localStorage)
5366const { viewMode, paginationMode, pageSize, columns, toggleColumn, resetColumns } =
5467 usePackageListPreferences ()
5568
56- // Structured filters and sorting (operates on visible set; expand() widens it )
69+ // Structured filters and sorting (operates on all loaded packages )
5770const {
5871 filters,
5972 sortOption,
@@ -77,20 +90,25 @@ const {
7790// Pagination state
7891const currentPage = shallowRef (1 )
7992
93+ // Total items accounts for unfetched packages so pagination shows the real count
94+ const paginationTotal = computed (() =>
95+ hasMore .value ? totalPackages .value : sortedPackages .value .length ,
96+ )
97+
8098// Calculate total pages
8199const totalPages = computed (() => {
82- return Math .ceil (sortedPackages .value . length / pageSize .value )
100+ return Math .ceil (paginationTotal .value / pageSize .value )
83101})
84102
85- // Reset to page 1 when filters change; expand so filtering works across all packages
103+ // Reset to page 1 when filters change; load all packages so client-side filtering works
86104watch ([filters , sortOption ], () => {
87105 currentPage .value = 1
88106 if (
89107 filters .value .text ||
90108 filters .value .keywords .length > 0 ||
91109 sortOption .value !== ' updated-desc'
92110 ) {
93- expand ()
111+ fetchAllRemaining ()
94112 }
95113})
96114
@@ -101,6 +119,14 @@ watch(totalPages, newTotal => {
101119 }
102120})
103121
122+ // Load next batch when user navigates beyond what's loaded
123+ watch (currentPage , page => {
124+ const loadedPages = Math .ceil (sortedPackages .value .length / pageSize .value )
125+ if (page > loadedPages && hasMore .value ) {
126+ fetchNextBatch ()
127+ }
128+ })
129+
104130// Debounced URL update for filter/sort
105131const updateUrl = debounce ((updates : { filter? : string ; sort? : string }) => {
106132 router .replace ({
@@ -273,7 +299,7 @@ defineOgImageComponent('Default', {
273299
274300 <!-- Loading state (only when no packages loaded yet) -->
275301 <LoadingSpinner
276- v-if =" status === 'pending' && allPackages .length === 0"
302+ v-if =" status === 'pending' && packages .length === 0"
277303 :text =" $t('common.loading_packages')"
278304 />
279305
@@ -288,7 +314,7 @@ defineOgImageComponent('Default', {
288314 </div >
289315
290316 <!-- Empty state -->
291- <div v-else-if =" allPackages.length === 0" class =" py-12 text-center" >
317+ <div v-else-if =" packageCount === 0 && totalPackages === 0" class =" py-12 text-center" >
292318 <p class =" text-fg-muted font-mono" >
293319 {{ $t('org.page.no_packages') }} <span class =" text-fg" >@{{ orgName }}</span >
294320 </p >
@@ -337,15 +363,15 @@ defineOgImageComponent('Default', {
337363 <PackageList
338364 :results =" sortedPackages"
339365 :has-more =" hasMore"
340- :is-loading =" isExpanding "
366+ :is-loading =" isLoadingMore "
341367 :view-mode =" viewMode"
342368 :columns =" columns"
343369 :filters =" filters"
344370 v-model:sort-option =" sortOption"
345371 :pagination-mode =" paginationMode"
346372 :page-size =" pageSize"
347373 :current-page =" currentPage"
348- @load-more =" expand "
374+ @load-more =" fetchNextBatch "
349375 @click-keyword =" toggleKeyword"
350376 />
351377
@@ -354,7 +380,7 @@ defineOgImageComponent('Default', {
354380 v-model:mode =" paginationMode"
355381 v-model:page-size =" pageSize"
356382 v-model:current-page =" currentPage"
357- :total-items =" sortedPackages.length "
383+ :total-items =" paginationTotal "
358384 :view-mode =" viewMode"
359385 />
360386 </template >
0 commit comments