feat(ui): batch initial showcase/gallery loads into smaller chunks

Showcase and gallery were fetching the full initial batch (60 / 50
items) in a single API call. Operator-flagged 2026-05-30: items should
stream in as small batches so they render progressively rather than
blocking on one big response.

- showcase store: PAGE 60 → 5, INITIAL_BATCHES = 12 (5 × 12 = 60, same
  total). loadInitial() fetches PAGE chunks in sequence; shuffle()
  delegates. fetchPage() still returns one PAGE-sized chunk so
  infinite-scroll also pulls 5 per trigger.
- gallery store: loadMore() limit 50 → PAGE (5), INITIAL_BATCHES = 10.
  loadInitial() loops loadMore() up to INITIAL_BATCHES times, stopping
  early when nextCursor becomes null (end of data).
- ShowcaseView: onMounted calls loadInitial() instead of fetchPage().
  Gallery/setTagFilter/setPostFilter already routed through loadInitial.

Net visual effect: each ~5-item batch lands and renders independently;
combined with MasonryGrid's animateFromIndex logic, the showcase now
cascades batches in as they arrive instead of one big pop-in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 14:51:58 -04:00
parent 99b66aa85f
commit adeee64a2d
2 changed files with 16 additions and 3 deletions
+15 -2
View File
@@ -2,6 +2,13 @@ import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useApi } from '../composables/useApi.js'
// Operator-confirmed 2026-05-30: fetch PAGE-sized chunks instead of one
// 50-item request so items render as each batch lands. Total initial
// count is unchanged (PAGE * INITIAL_BATCHES = 50). Infinite-scroll also
// pulls PAGE per trigger to keep appends progressive.
const PAGE = 5
const INITIAL_BATCHES = 10
export const useGalleryStore = defineStore('gallery', () => {
const api = useApi()
@@ -21,7 +28,13 @@ export const useGalleryStore = defineStore('gallery', () => {
images.value = []
dateGroups.value = []
nextCursor.value = null
await loadMore()
// Sequentially fetch INITIAL_BATCHES chunks so items render as each
// batch lands rather than blocking on one big response. Stop early
// when the backend reports no more pages.
for (let i = 0; i < INITIAL_BATCHES; i++) {
if (i > 0 && nextCursor.value === null) break
await loadMore()
}
}
async function loadMore() {
@@ -30,7 +43,7 @@ export const useGalleryStore = defineStore('gallery', () => {
error.value = null
const myId = ++inflightId
try {
const params = { limit: 50, ...activeFilterParam() }
const params = { limit: PAGE, ...activeFilterParam() }
if (nextCursor.value) params.cursor = nextCursor.value
const body = await api.get('/api/gallery/scroll', { params })
if (myId !== inflightId) return // stale response
+1 -1
View File
@@ -70,7 +70,7 @@ function onKeydown(e) {
}
onMounted(() => {
if (store.images.length === 0) store.fetchPage()
if (store.images.length === 0) store.loadInitial()
window.addEventListener('keydown', onKeydown)
})
onUnmounted(() => window.removeEventListener('keydown', onKeydown))