mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 01:08:59 +00:00
35bcb3edbb
# Conflicts: # frontend/src/App.vue # frontend/src/config/apps.test.ts # sky_phone/source/html/index.html
208 lines
6.0 KiB
TypeScript
208 lines
6.0 KiB
TypeScript
import type { LaunchablePhoneAppId } from '@/types/apps'
|
|
|
|
export const APPEARANCE_MODE_IDS = ['automatic', 'light', 'dark'] as const
|
|
export const PHONE_FRAME_IDS = [
|
|
'black',
|
|
'blue',
|
|
'green',
|
|
'lavender',
|
|
'white',
|
|
] as const
|
|
export const RINGTONE_IDS = ['skyline', 'horizon', 'pulse'] as const
|
|
export const NOTIFICATION_SOUND_IDS = ['chime', 'signal', 'soft'] as const
|
|
export const WALLPAPER_IDS = ['midnight', 'aurora', 'ember'] as const
|
|
export const PHONE_SCALE_MIN = 50
|
|
export const PHONE_SCALE_MAX = 150
|
|
export const PHONE_SCALE_STEP = 5
|
|
|
|
export type AppearanceMode = (typeof APPEARANCE_MODE_IDS)[number]
|
|
export type PhoneFrameId = (typeof PHONE_FRAME_IDS)[number]
|
|
export type RingtoneId = (typeof RINGTONE_IDS)[number]
|
|
export type NotificationSoundId = (typeof NOTIFICATION_SOUND_IDS)[number]
|
|
export type WallpaperId = (typeof WALLPAPER_IDS)[number]
|
|
export type AppNotificationPreferences = {
|
|
enabled: boolean
|
|
sounds: boolean
|
|
}
|
|
|
|
export type PhonePreferencesV1 = {
|
|
settings: {
|
|
airplaneMode: boolean
|
|
appearanceMode: AppearanceMode
|
|
frame: PhoneFrameId
|
|
notificationSound: NotificationSoundId
|
|
notificationDurationSeconds: number
|
|
notificationVolume: number
|
|
notifications: Record<LaunchablePhoneAppId, AppNotificationPreferences>
|
|
phoneScale: number
|
|
ringtone: RingtoneId
|
|
ringtoneVolume: number
|
|
streamerMode: boolean
|
|
wallpaper: WallpaperId
|
|
}
|
|
version: 1
|
|
}
|
|
|
|
const DEFAULT_APP_NOTIFICATIONS: Record<
|
|
LaunchablePhoneAppId,
|
|
AppNotificationPreferences
|
|
> = {
|
|
phone: { enabled: true, sounds: true },
|
|
'app-store': { enabled: true, sounds: true },
|
|
calculator: { enabled: true, sounds: true },
|
|
snake: { enabled: true, sounds: true },
|
|
memory: { enabled: true, sounds: true },
|
|
'number-merge': { enabled: true, sounds: true },
|
|
minesweeper: { enabled: true, sounds: true },
|
|
'tower-stack': { enabled: true, sounds: true },
|
|
'sky-flappy': { enabled: true, sounds: true },
|
|
'neon-drop': { enabled: true, sounds: true },
|
|
camera: { enabled: true, sounds: true },
|
|
clock: { enabled: true, sounds: true },
|
|
weather: { enabled: true, sounds: true },
|
|
mail: { enabled: true, sounds: true },
|
|
map: { enabled: true, sounds: true },
|
|
notes: { enabled: true, sounds: true },
|
|
photos: { enabled: true, sounds: true },
|
|
settings: { enabled: true, sounds: true },
|
|
}
|
|
|
|
export const DEFAULT_PHONE_PREFERENCES: PhonePreferencesV1 = {
|
|
settings: {
|
|
airplaneMode: false,
|
|
appearanceMode: 'automatic',
|
|
frame: 'black',
|
|
notificationSound: 'chime',
|
|
notificationDurationSeconds: 10,
|
|
notificationVolume: 70,
|
|
notifications: DEFAULT_APP_NOTIFICATIONS,
|
|
phoneScale: 100,
|
|
ringtone: 'skyline',
|
|
ringtoneVolume: 80,
|
|
streamerMode: false,
|
|
wallpaper: 'midnight',
|
|
},
|
|
version: 1,
|
|
}
|
|
|
|
function readBoolean(value: unknown, fallback: boolean): boolean {
|
|
return typeof value === 'boolean' ? value : fallback
|
|
}
|
|
|
|
function readNumber(
|
|
value: unknown,
|
|
fallback: number,
|
|
minimum: number,
|
|
maximum: number,
|
|
): number {
|
|
return typeof value === 'number' && Number.isFinite(value)
|
|
? Math.min(maximum, Math.max(minimum, value))
|
|
: fallback
|
|
}
|
|
|
|
function readChoice<T extends string>(
|
|
value: unknown,
|
|
choices: readonly T[],
|
|
fallback: T,
|
|
): T {
|
|
return typeof value === 'string' && choices.includes(value as T)
|
|
? (value as T)
|
|
: fallback
|
|
}
|
|
|
|
function readNotifications(
|
|
value: unknown,
|
|
): Record<LaunchablePhoneAppId, AppNotificationPreferences> {
|
|
const source =
|
|
value && typeof value === 'object'
|
|
? (value as Partial<
|
|
Record<LaunchablePhoneAppId, Partial<AppNotificationPreferences>>
|
|
>)
|
|
: {}
|
|
const notifications = structuredClone(DEFAULT_APP_NOTIFICATIONS)
|
|
|
|
for (const appId of Object.keys(notifications) as LaunchablePhoneAppId[]) {
|
|
notifications[appId] = {
|
|
enabled: readBoolean(
|
|
source[appId]?.enabled,
|
|
DEFAULT_APP_NOTIFICATIONS[appId].enabled,
|
|
),
|
|
sounds: readBoolean(
|
|
source[appId]?.sounds,
|
|
DEFAULT_APP_NOTIFICATIONS[appId].sounds,
|
|
),
|
|
}
|
|
}
|
|
|
|
return notifications
|
|
}
|
|
|
|
export function parsePhonePreferences(raw: string | null): PhonePreferencesV1 {
|
|
if (!raw) return structuredClone(DEFAULT_PHONE_PREFERENCES)
|
|
|
|
try {
|
|
const parsed = JSON.parse(raw) as Partial<PhonePreferencesV1>
|
|
const settings = parsed.settings
|
|
if (parsed.version !== 1 || !settings || typeof settings !== 'object') {
|
|
return structuredClone(DEFAULT_PHONE_PREFERENCES)
|
|
}
|
|
|
|
const defaults = DEFAULT_PHONE_PREFERENCES.settings
|
|
return {
|
|
settings: {
|
|
airplaneMode: readBoolean(settings.airplaneMode, defaults.airplaneMode),
|
|
appearanceMode: readChoice(
|
|
settings.appearanceMode,
|
|
APPEARANCE_MODE_IDS,
|
|
defaults.appearanceMode,
|
|
),
|
|
frame: readChoice(settings.frame, PHONE_FRAME_IDS, defaults.frame),
|
|
notificationSound: readChoice(
|
|
settings.notificationSound,
|
|
NOTIFICATION_SOUND_IDS,
|
|
defaults.notificationSound,
|
|
),
|
|
notificationDurationSeconds: readNumber(
|
|
settings.notificationDurationSeconds,
|
|
defaults.notificationDurationSeconds,
|
|
3,
|
|
30,
|
|
),
|
|
notificationVolume: readNumber(
|
|
settings.notificationVolume,
|
|
defaults.notificationVolume,
|
|
0,
|
|
100,
|
|
),
|
|
notifications: readNotifications(settings.notifications),
|
|
phoneScale: readNumber(
|
|
settings.phoneScale,
|
|
defaults.phoneScale,
|
|
PHONE_SCALE_MIN,
|
|
PHONE_SCALE_MAX,
|
|
),
|
|
ringtone: readChoice(
|
|
settings.ringtone,
|
|
RINGTONE_IDS,
|
|
defaults.ringtone,
|
|
),
|
|
ringtoneVolume: readNumber(
|
|
settings.ringtoneVolume,
|
|
defaults.ringtoneVolume,
|
|
0,
|
|
100,
|
|
),
|
|
streamerMode: readBoolean(settings.streamerMode, defaults.streamerMode),
|
|
wallpaper: readChoice(
|
|
settings.wallpaper,
|
|
WALLPAPER_IDS,
|
|
defaults.wallpaper,
|
|
),
|
|
},
|
|
version: 1,
|
|
}
|
|
} catch {
|
|
return structuredClone(DEFAULT_PHONE_PREFERENCES)
|
|
}
|
|
}
|