ENH - overhaul App Store experience

This commit is contained in:
smx.pusha
2026-08-14 12:53:39 +02:00
parent 3fc0ebb08f
commit 54924daca0
19 changed files with 4165 additions and 71 deletions
@@ -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),
)
})
})
+38
View File
@@ -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
}
+5 -1
View File
@@ -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) {