ENH - paginate Gallery in batches of 30

This commit is contained in:
Eichenholz
2026-08-06 16:57:29 +02:00
parent 4a3e526879
commit 8ce0e65ccd
5 changed files with 35 additions and 20 deletions
+7
View File
@@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest'
import {
filterMedia,
formatRecordingDuration,
hasNextMediaPage,
mediaErrorKey,
mergeMedia,
} from './media'
@@ -32,6 +33,12 @@ describe('media utilities', () => {
])
})
it('loads another gallery page only after a full 30-item batch', () => {
expect(hasNextMediaPage(30)).toBe(true)
expect(hasNextMediaPage(29)).toBe(false)
expect(hasNextMediaPage(0)).toBe(false)
})
it('formats unlimited recording durations', () => {
expect(formatRecordingDuration(0)).toBe('00:00')
expect(formatRecordingDuration(3_725_000)).toBe('62:05')
+6
View File
@@ -1,5 +1,7 @@
import type { GalleryFilter, MediaType, PhoneMedia } from '@/types/media'
export const MEDIA_PAGE_SIZE = 30
export function isMediaType(value: unknown): value is MediaType {
return value === 'photo' || value === 'video'
}
@@ -24,6 +26,10 @@ export function mergeMedia(
)
}
export function hasNextMediaPage(pageLength: number): boolean {
return pageLength === MEDIA_PAGE_SIZE
}
export function formatRecordingDuration(elapsedMs: number): string {
const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1000))
const minutes = Math.floor(totalSeconds / 60)
+8 -4
View File
@@ -17,14 +17,18 @@ import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { usePhoneStore } from '@/stores/phone'
import type { DeleteResult, GalleryFilter, PhoneMedia } from '@/types/media'
import { mediaErrorKey, mergeMedia } from '@/utils/media'
import {
hasNextMediaPage,
MEDIA_PAGE_SIZE,
mediaErrorKey,
mergeMedia,
} from '@/utils/media'
import { nuiCall } from '@/utils/nui'
const isDevelopment = import.meta.env.DEV
const developmentGalleryState = isDevelopment
? new URLSearchParams(window.location.search).get('galleryMock')
: null
const pageSize = 36
const filterItems = [
{ id: 'all', label: 'all' },
{ id: 'photo', label: 'photos' },
@@ -135,14 +139,14 @@ async function fetchMore(): Promise<void> {
fetching.value = true
const offset = media.value.length
const response = await nuiCall<PhoneMedia[]>('gallery:list', {
limit: pageSize,
limit: MEDIA_PAGE_SIZE,
mediaType: filter.value === 'all' ? undefined : filter.value,
mockState: developmentGalleryState ?? undefined,
offset,
})
if (response.success && Array.isArray(response.data)) {
media.value = mergeMedia(media.value, response.data)
hasMore.value = response.data.length === pageSize
hasMore.value = hasNextMediaPage(response.data.length)
} else if (
isDevelopment &&
developmentGalleryState !== 'error' &&
+13 -15
View File
@@ -11,20 +11,18 @@ let authenticated = false
let draft = null
let linkedAccount = null
let mockNotes = []
let mockMedia = [
{
createdAt: Date.now() - 60_000,
id: 1,
mediaType: 'photo',
url: 'https://picsum.photos/seed/sky-phone-1/600/800',
},
{
createdAt: Date.now() - 120_000,
id: 2,
mediaType: 'video',
url: 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4',
},
]
let mockMedia = Array.from({ length: 65 }, (_, index) => {
const id = index + 1
const video = id % 10 === 0
return {
createdAt: Date.now() - id * 60_000,
id,
mediaType: video ? 'video' : 'photo',
url: video
? 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4'
: `https://picsum.photos/seed/sky-phone-${id}/600/800`,
}
})
const deviceData = {}
const accountDevices = [
{
@@ -145,7 +143,7 @@ app.post('/api/:endpoint', (request, response) => {
? mockMedia.filter((item) => item.mediaType === request.body.mediaType)
: mockMedia
const offset = Number(request.body.offset) || 0
const limit = Number(request.body.limit) || 36
const limit = Number(request.body.limit) || 30
response.json({
success: true,
data: filtered.slice(offset, offset + limit),
+1 -1
View File
@@ -13,5 +13,5 @@ Config.Media = {
BitrateKbps = 1500,
},
UploadSessionTimeoutMs = 60000,
PageSize = 36,
PageSize = 30,
}