mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-31 18:28:55 +00:00
ENH - overhaul Photos media experience
This commit is contained in:
@@ -1,17 +1,20 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
bottomRightGridPosition,
|
||||
filterMedia,
|
||||
formatMediaSize,
|
||||
formatRecordingDuration,
|
||||
hasNextMediaPage,
|
||||
mediaErrorKey,
|
||||
mergeMedia,
|
||||
orderMedia,
|
||||
orderMediaOldestFirst,
|
||||
} from './media'
|
||||
|
||||
const media = [
|
||||
{ createdAt: 10, id: 1, mediaType: 'photo' as const, url: 'photo' },
|
||||
{ createdAt: 20, id: 2, mediaType: 'video' as const, url: 'video' },
|
||||
{ createdAt: 10, favorite: false, id: 1, mediaType: 'photo' as const, url: 'photo' },
|
||||
{ createdAt: 20, favorite: false, id: 2, mediaType: 'video' as const, url: 'video' },
|
||||
]
|
||||
|
||||
describe('media utilities', () => {
|
||||
@@ -24,8 +27,8 @@ describe('media utilities', () => {
|
||||
it('merges pages without duplicates and keeps newest first', () => {
|
||||
expect(
|
||||
mergeMedia(media, [
|
||||
{ createdAt: 30, id: 1, mediaType: 'photo', url: 'updated' },
|
||||
{ createdAt: 25, id: 3, mediaType: 'photo', url: 'new' },
|
||||
{ createdAt: 30, favorite: true, id: 1, mediaType: 'photo', url: 'updated' },
|
||||
{ createdAt: 25, favorite: false, id: 3, mediaType: 'photo', url: 'new' },
|
||||
]).map((entry) => [entry.id, entry.url]),
|
||||
).toEqual([
|
||||
[1, 'updated'],
|
||||
@@ -34,6 +37,28 @@ describe('media utilities', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('orders the photo grid from oldest to newest', () => {
|
||||
expect(orderMediaOldestFirst(media).map((entry) => entry.id)).toEqual([
|
||||
1, 2,
|
||||
])
|
||||
})
|
||||
|
||||
it('orders the photo grid in the selected direction', () => {
|
||||
expect(orderMedia(media, 'oldest').map((entry) => entry.id)).toEqual([1, 2])
|
||||
expect(orderMedia(media, 'newest').map((entry) => entry.id)).toEqual([2, 1])
|
||||
})
|
||||
|
||||
it('fills the gallery from bottom-right to top-left', () => {
|
||||
expect(bottomRightGridPosition(0, 1)).toEqual({ column: 3, row: 1 })
|
||||
expect(bottomRightGridPosition(0, 3)).toEqual({ column: 3, row: 1 })
|
||||
expect(bottomRightGridPosition(1, 3)).toEqual({ column: 2, row: 1 })
|
||||
expect(bottomRightGridPosition(2, 3)).toEqual({ column: 1, row: 1 })
|
||||
expect(bottomRightGridPosition(0, 4)).toEqual({ column: 3, row: 2 })
|
||||
expect(bottomRightGridPosition(3, 4)).toEqual({ column: 3, row: 1 })
|
||||
expect(bottomRightGridPosition(4, 5)).toEqual({ column: 2, row: 1 })
|
||||
expect(bottomRightGridPosition(10, 11)).toEqual({ column: 2, row: 1 })
|
||||
})
|
||||
|
||||
it('loads another gallery page only after a full 30-item batch', () => {
|
||||
expect(hasNextMediaPage(30)).toBe(true)
|
||||
expect(hasNextMediaPage(29)).toBe(false)
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import type { GalleryFilter, MediaType, PhoneMedia } from '@/types/media'
|
||||
import type {
|
||||
GalleryFilter,
|
||||
GallerySortOrder,
|
||||
MediaType,
|
||||
PhoneMedia,
|
||||
} from '@/types/media'
|
||||
|
||||
export const MEDIA_PAGE_SIZE = 30
|
||||
|
||||
@@ -26,6 +31,37 @@ export function mergeMedia(
|
||||
)
|
||||
}
|
||||
|
||||
export function orderMediaOldestFirst(media: PhoneMedia[]): PhoneMedia[] {
|
||||
return [...media].sort(
|
||||
(left, right) => left.createdAt - right.createdAt || left.id - right.id,
|
||||
)
|
||||
}
|
||||
|
||||
export function orderMedia(
|
||||
media: PhoneMedia[],
|
||||
sortOrder: GallerySortOrder,
|
||||
): PhoneMedia[] {
|
||||
return sortOrder === 'oldest'
|
||||
? orderMediaOldestFirst(media)
|
||||
: [...media].sort(
|
||||
(left, right) =>
|
||||
right.createdAt - left.createdAt || right.id - left.id,
|
||||
)
|
||||
}
|
||||
|
||||
export function bottomRightGridPosition(
|
||||
itemIndex: number,
|
||||
itemCount: number,
|
||||
columnCount = 3,
|
||||
): { column: number; row: number } {
|
||||
const count = Math.max(1, itemCount)
|
||||
const index = Math.max(0, Math.min(itemIndex, count - 1))
|
||||
return {
|
||||
column: columnCount - (index % columnCount),
|
||||
row: Math.ceil(count / columnCount) - Math.floor(index / columnCount),
|
||||
}
|
||||
}
|
||||
|
||||
export function hasNextMediaPage(pageLength: number): boolean {
|
||||
return pageLength === MEDIA_PAGE_SIZE
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user