mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 09:13:24 +00:00
ENH - overhaul App Store experience
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { getDailyHighlights } from '@/utils/appStoreHighlights'
|
||||
|
||||
const candidates = [
|
||||
{ id: 'banking' },
|
||||
{ id: 'feather' },
|
||||
{ id: 'snake' },
|
||||
{ id: 'music' },
|
||||
{ id: 'picstagram' },
|
||||
]
|
||||
|
||||
describe('daily App Store highlights', () => {
|
||||
it('keeps the generated order stable throughout one local day', () => {
|
||||
const morning = getDailyHighlights(candidates, new Date(2026, 7, 14, 8, 15))
|
||||
const evening = getDailyHighlights(
|
||||
candidates,
|
||||
new Date(2026, 7, 14, 22, 45),
|
||||
)
|
||||
|
||||
expect(evening).toEqual(morning)
|
||||
expect(morning).toHaveLength(candidates.length)
|
||||
})
|
||||
|
||||
it('generates a different curation for the next local day', () => {
|
||||
const today = getDailyHighlights(candidates, new Date(2026, 7, 14))
|
||||
const tomorrow = getDailyHighlights(candidates, new Date(2026, 7, 15))
|
||||
|
||||
expect(tomorrow.map((app) => app.id)).not.toEqual(
|
||||
today.map((app) => app.id),
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
type HighlightCandidate = {
|
||||
id: string
|
||||
}
|
||||
|
||||
function localDayNumber(date: Date): number {
|
||||
return Math.floor(
|
||||
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / 86_400_000,
|
||||
)
|
||||
}
|
||||
|
||||
function nextRandom(seed: number): number {
|
||||
return (Math.imul(seed, 1_664_525) + 1_013_904_223) >>> 0
|
||||
}
|
||||
|
||||
export function getDailyHighlights<T extends HighlightCandidate>(
|
||||
candidates: readonly T[],
|
||||
date = new Date(),
|
||||
): T[] {
|
||||
const ordered = [...candidates].sort((left, right) =>
|
||||
left.id.localeCompare(right.id),
|
||||
)
|
||||
let seed = localDayNumber(date) >>> 0
|
||||
|
||||
for (let index = ordered.length - 1; index > 0; index -= 1) {
|
||||
seed = nextRandom(seed)
|
||||
const target = seed % (index + 1)
|
||||
const current = ordered[index]
|
||||
ordered[index] = ordered[target]
|
||||
ordered[target] = current
|
||||
}
|
||||
|
||||
if (ordered.length > 1) {
|
||||
const offset = localDayNumber(date) % ordered.length
|
||||
return [...ordered.slice(offset), ...ordered.slice(0, offset)]
|
||||
}
|
||||
|
||||
return ordered
|
||||
}
|
||||
@@ -453,12 +453,16 @@ export function parseHomeLayout(
|
||||
value: unknown,
|
||||
defaults: HomeLayout,
|
||||
installedIds: LaunchablePhoneAppId[],
|
||||
preservePersistedIds = true,
|
||||
): HomeLayout {
|
||||
if (!value || typeof value !== 'object') return defaults
|
||||
|
||||
const source = value as Partial<Record<keyof HomeLayout, unknown>>
|
||||
const availableIds = new Set(installedIds)
|
||||
if (source.version === 3 || source.version === 4 || source.version === 5) {
|
||||
if (
|
||||
preservePersistedIds &&
|
||||
(source.version === 3 || source.version === 4 || source.version === 5)
|
||||
) {
|
||||
for (const collection of [source.dock, source.grid, source.hidden]) {
|
||||
if (!Array.isArray(collection)) continue
|
||||
for (const item of collection) {
|
||||
|
||||
Reference in New Issue
Block a user