mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 09:13:24 +00:00
ENH - add authentic App Store previews
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import type { BuiltinPhoneAppId } from '@/types/apps'
|
||||
|
||||
type PreviewableBuiltinAppId = Exclude<BuiltinPhoneAppId, 'app-store'>
|
||||
|
||||
const previewModules = import.meta.glob<string>(
|
||||
'../assets/img/app-previews/*.jpg',
|
||||
{
|
||||
eager: true,
|
||||
import: 'default',
|
||||
query: '?url',
|
||||
},
|
||||
)
|
||||
|
||||
const APP_STORE_PREVIEW_IMAGES = Object.fromEntries(
|
||||
Object.entries(previewModules).map(([path, imageUrl]) => {
|
||||
const appId = path.split('/').at(-1)?.replace(/\.jpg$/, '')
|
||||
if (!appId) throw new Error(`Invalid App Store preview path: ${path}`)
|
||||
return [appId, imageUrl]
|
||||
}),
|
||||
) as Partial<Record<PreviewableBuiltinAppId, string>>
|
||||
|
||||
export function getAppStorePreviewImage(appId: string): string | null {
|
||||
return APP_STORE_PREVIEW_IMAGES[appId as PreviewableBuiltinAppId] ?? null
|
||||
}
|
||||
|
||||
export const APP_STORE_PREVIEW_IMAGE_IDS = Object.freeze(
|
||||
Object.keys(APP_STORE_PREVIEW_IMAGES) as PreviewableBuiltinAppId[],
|
||||
)
|
||||
@@ -0,0 +1,68 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { PHONE_APPS } from '@/config/apps'
|
||||
import { APP_STORE_PREVIEW_IMAGE_IDS } from '@/utils/appStorePreviewImages'
|
||||
import {
|
||||
getAppStorePreviewVisual,
|
||||
PREVIEWABLE_BUILTIN_APP_IDS,
|
||||
} from '@/utils/appStorePreviews'
|
||||
|
||||
describe('App Store preview catalog', () => {
|
||||
it('contains a real captured screenshot for every built-in store app', () => {
|
||||
const storeAppIds = PHONE_APPS.filter(
|
||||
(app) => app.kind !== 'external' && app.id !== 'app-store',
|
||||
).map((app) => app.id)
|
||||
|
||||
expect([...APP_STORE_PREVIEW_IMAGE_IDS].sort()).toEqual(storeAppIds.sort())
|
||||
})
|
||||
|
||||
it('provides specialized preview data for every built-in store app', () => {
|
||||
const storeAppIds = PHONE_APPS.filter(
|
||||
(app) => app.kind !== 'external' && app.id !== 'app-store',
|
||||
).map((app) => app.id)
|
||||
|
||||
expect([...PREVIEWABLE_BUILTIN_APP_IDS].sort()).toEqual(
|
||||
storeAppIds.sort(),
|
||||
)
|
||||
const visualSignatures = new Set<string>()
|
||||
for (const appId of storeAppIds) {
|
||||
const preview = getAppStorePreviewVisual(appId)
|
||||
visualSignatures.add(`${preview.accent}:${preview.surface}`)
|
||||
}
|
||||
expect(visualSignatures.size).toBe(storeAppIds.length)
|
||||
})
|
||||
|
||||
it('localizes every specialized preview in the fallback, English and German locales', () => {
|
||||
const localeSources = [
|
||||
readFileSync(
|
||||
fileURLToPath(new URL('../stores/phone.ts', import.meta.url)),
|
||||
'utf8',
|
||||
),
|
||||
readFileSync(
|
||||
fileURLToPath(
|
||||
new URL('../../../sky_phone/config/locales/en.lua', import.meta.url),
|
||||
),
|
||||
'utf8',
|
||||
),
|
||||
readFileSync(
|
||||
fileURLToPath(
|
||||
new URL('../../../sky_phone/config/locales/de.lua', import.meta.url),
|
||||
),
|
||||
'utf8',
|
||||
),
|
||||
]
|
||||
|
||||
for (const appId of PREVIEWABLE_BUILTIN_APP_IDS) {
|
||||
for (const source of localeSources) {
|
||||
expect(source).toMatch(
|
||||
new RegExp(
|
||||
`(?:["']${appId}["']\\]?|${appId.replace(/-/g, '\\-')})\\s*[:=]\\s*\\{\\s*first\\s*[:=]`,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,68 @@
|
||||
import type { BuiltinPhoneAppId } from '@/types/apps'
|
||||
|
||||
export type AppStorePreviewVisual = {
|
||||
accent: string
|
||||
surface: string
|
||||
}
|
||||
|
||||
type PreviewableBuiltinAppId = Exclude<BuiltinPhoneAppId, 'app-store'>
|
||||
|
||||
const APP_STORE_PREVIEW_STYLES = {
|
||||
citywarn: { accent: '#ff4f5e', surface: '#250e17' },
|
||||
crypto: { accent: '#855cff', surface: '#120d26' },
|
||||
health: { accent: '#ff3b6b', surface: '#250d18' },
|
||||
'weazel-news': { accent: '#f4bf32', surface: '#201b0c' },
|
||||
companies: { accent: '#4a92ff', surface: '#0c1728' },
|
||||
music: { accent: '#fa3d71', surface: '#240b19' },
|
||||
picstagram: { accent: '#e54cff', surface: '#210b27' },
|
||||
feather: { accent: '#3c9cff', surface: '#091c2d' },
|
||||
fliptok: { accent: '#24f0d2', surface: '#071d1b' },
|
||||
flare: { accent: '#ff567f', surface: '#260d18' },
|
||||
calendar: { accent: '#ff554d', surface: '#24100d' },
|
||||
radio: { accent: '#47d9ff', surface: '#071c25' },
|
||||
'local-pages': { accent: '#47c98a', surface: '#092118' },
|
||||
crewlink: { accent: '#f3a73a', surface: '#221507' },
|
||||
phone: { accent: '#35d36f', surface: '#082016' },
|
||||
messages: { accent: '#42d66d', surface: '#082117' },
|
||||
darkchat: { accent: '#845eff', surface: '#120c27' },
|
||||
garage: { accent: '#4d9dff', surface: '#0a1828' },
|
||||
house: { accent: '#d4a25c', surface: '#21170c' },
|
||||
map: { accent: '#46c97c', surface: '#091f16' },
|
||||
skyride: { accent: '#f6c43d', surface: '#211a08' },
|
||||
banking: { accent: '#3dcc8b', surface: '#092019' },
|
||||
billing: { accent: '#4aa2ff', surface: '#09192a' },
|
||||
mail: { accent: '#52a8ff', surface: '#091a2b' },
|
||||
notes: { accent: '#ffd34e', surface: '#211b08' },
|
||||
memos: { accent: '#ff4f63', surface: '#250c13' },
|
||||
calculator: { accent: '#ff9e36', surface: '#231407' },
|
||||
camera: { accent: '#b7c0cd', surface: '#11151b' },
|
||||
clock: { accent: '#ff9d32', surface: '#251507' },
|
||||
weather: { accent: '#54baff', surface: '#091b29' },
|
||||
photos: { accent: '#ff5f75', surface: '#251015' },
|
||||
settings: { accent: '#8f99a8', surface: '#141820' },
|
||||
snake: { accent: '#59e16d', surface: '#071f0c' },
|
||||
memory: { accent: '#9c6cff', surface: '#160d29' },
|
||||
'number-merge': { accent: '#f0a451', surface: '#241508' },
|
||||
minesweeper: { accent: '#7a9dba', surface: '#101a23' },
|
||||
'tower-stack': { accent: '#ff6f67', surface: '#260e0c' },
|
||||
'sky-flappy': { accent: '#55cfff', surface: '#081d26' },
|
||||
citymarkt: { accent: '#ff9a45', surface: '#241407' },
|
||||
'neon-drop': { accent: '#f244ff', surface: '#240828' },
|
||||
} satisfies Record<PreviewableBuiltinAppId, AppStorePreviewVisual>
|
||||
|
||||
const EXTERNAL_APP_PREVIEW: AppStorePreviewVisual = {
|
||||
accent: '#4d9dff',
|
||||
surface: '#0a1828',
|
||||
}
|
||||
|
||||
export function getAppStorePreviewVisual(appId: string): AppStorePreviewVisual {
|
||||
if (appId === 'app-store') return EXTERNAL_APP_PREVIEW
|
||||
return (
|
||||
APP_STORE_PREVIEW_STYLES[appId as PreviewableBuiltinAppId] ??
|
||||
EXTERNAL_APP_PREVIEW
|
||||
)
|
||||
}
|
||||
|
||||
export const PREVIEWABLE_BUILTIN_APP_IDS = Object.freeze(
|
||||
Object.keys(APP_STORE_PREVIEW_STYLES) as PreviewableBuiltinAppId[],
|
||||
)
|
||||
Reference in New Issue
Block a user