293 lines
9.9 KiB
TypeScript
293 lines
9.9 KiB
TypeScript
import { type AnimationLevel } from '../../utils/animationLevel'
|
|
import { type TrackColorPreset, type TrackDisplayMode, type TrackStyleProfile, type TrackTailLengthPreset } from '../presentation/trackStyleConfig'
|
|
import { type GpsMarkerColorPreset, type GpsMarkerSizePreset, type GpsMarkerStyleId } from '../presentation/gpsMarkerStyleConfig'
|
|
|
|
export type SideButtonPlacement = 'left' | 'right'
|
|
export type CenterScaleRulerAnchorMode = 'screen-center' | 'compass-center'
|
|
export type UserNorthReferenceMode = 'magnetic' | 'true'
|
|
export type CompassTuningProfile = 'smooth' | 'balanced' | 'responsive'
|
|
|
|
export type SettingLockKey =
|
|
| 'lockAnimationLevel'
|
|
| 'lockTrackMode'
|
|
| 'lockTrackTailLength'
|
|
| 'lockTrackColor'
|
|
| 'lockTrackStyle'
|
|
| 'lockGpsMarkerVisible'
|
|
| 'lockGpsMarkerStyle'
|
|
| 'lockGpsMarkerSize'
|
|
| 'lockGpsMarkerColor'
|
|
| 'lockSideButtonPlacement'
|
|
| 'lockAutoRotate'
|
|
| 'lockCompassTuning'
|
|
| 'lockScaleRulerVisible'
|
|
| 'lockScaleRulerAnchor'
|
|
| 'lockNorthReference'
|
|
| 'lockHeartRateDevice'
|
|
|
|
export type StoredUserSettings = {
|
|
animationLevel?: AnimationLevel
|
|
trackDisplayMode?: TrackDisplayMode
|
|
trackTailLength?: TrackTailLengthPreset
|
|
trackColorPreset?: TrackColorPreset
|
|
trackStyleProfile?: TrackStyleProfile
|
|
gpsMarkerVisible?: boolean
|
|
gpsMarkerStyle?: GpsMarkerStyleId
|
|
gpsMarkerSize?: GpsMarkerSizePreset
|
|
gpsMarkerColorPreset?: GpsMarkerColorPreset
|
|
autoRotateEnabled?: boolean
|
|
compassTuningProfile?: CompassTuningProfile
|
|
northReferenceMode?: UserNorthReferenceMode
|
|
sideButtonPlacement?: SideButtonPlacement
|
|
showCenterScaleRuler?: boolean
|
|
centerScaleRulerAnchorMode?: CenterScaleRulerAnchorMode
|
|
}
|
|
|
|
export interface SystemSettingsConfig {
|
|
values: Partial<StoredUserSettings>
|
|
locks: Partial<Record<SettingLockKey, boolean>>
|
|
}
|
|
|
|
export type ResolvedSystemSettingsState = {
|
|
values: Required<StoredUserSettings>
|
|
locks: Record<SettingLockKey, boolean>
|
|
}
|
|
|
|
export const USER_SETTINGS_STORAGE_KEY = 'cmr_user_settings_v1'
|
|
|
|
export const DEFAULT_STORED_USER_SETTINGS: Required<StoredUserSettings> = {
|
|
animationLevel: 'standard',
|
|
trackDisplayMode: 'full',
|
|
trackTailLength: 'medium',
|
|
trackColorPreset: 'mint',
|
|
trackStyleProfile: 'neon',
|
|
gpsMarkerVisible: true,
|
|
gpsMarkerStyle: 'beacon',
|
|
gpsMarkerSize: 'medium',
|
|
gpsMarkerColorPreset: 'cyan',
|
|
autoRotateEnabled: true,
|
|
compassTuningProfile: 'balanced',
|
|
northReferenceMode: 'magnetic',
|
|
sideButtonPlacement: 'left',
|
|
showCenterScaleRuler: false,
|
|
centerScaleRulerAnchorMode: 'screen-center',
|
|
}
|
|
|
|
export const DEFAULT_SETTING_LOCKS: Record<SettingLockKey, boolean> = {
|
|
lockAnimationLevel: false,
|
|
lockTrackMode: false,
|
|
lockTrackTailLength: false,
|
|
lockTrackColor: false,
|
|
lockTrackStyle: false,
|
|
lockGpsMarkerVisible: false,
|
|
lockGpsMarkerStyle: false,
|
|
lockGpsMarkerSize: false,
|
|
lockGpsMarkerColor: false,
|
|
lockSideButtonPlacement: false,
|
|
lockAutoRotate: false,
|
|
lockCompassTuning: false,
|
|
lockScaleRulerVisible: false,
|
|
lockScaleRulerAnchor: false,
|
|
lockNorthReference: false,
|
|
lockHeartRateDevice: false,
|
|
}
|
|
|
|
export const SETTING_LOCK_VALUE_MAP: Record<SettingLockKey, keyof StoredUserSettings | null> = {
|
|
lockAnimationLevel: 'animationLevel',
|
|
lockTrackMode: 'trackDisplayMode',
|
|
lockTrackTailLength: 'trackTailLength',
|
|
lockTrackColor: 'trackColorPreset',
|
|
lockTrackStyle: 'trackStyleProfile',
|
|
lockGpsMarkerVisible: 'gpsMarkerVisible',
|
|
lockGpsMarkerStyle: 'gpsMarkerStyle',
|
|
lockGpsMarkerSize: 'gpsMarkerSize',
|
|
lockGpsMarkerColor: 'gpsMarkerColorPreset',
|
|
lockSideButtonPlacement: 'sideButtonPlacement',
|
|
lockAutoRotate: 'autoRotateEnabled',
|
|
lockCompassTuning: 'compassTuningProfile',
|
|
lockScaleRulerVisible: 'showCenterScaleRuler',
|
|
lockScaleRulerAnchor: 'centerScaleRulerAnchorMode',
|
|
lockNorthReference: 'northReferenceMode',
|
|
lockHeartRateDevice: null,
|
|
}
|
|
|
|
function normalizeStoredUserSettings(raw: unknown): StoredUserSettings {
|
|
if (!raw || typeof raw !== 'object') {
|
|
return {}
|
|
}
|
|
|
|
const normalized = raw as Record<string, unknown>
|
|
const settings: StoredUserSettings = {}
|
|
if (normalized.animationLevel === 'standard' || normalized.animationLevel === 'lite') {
|
|
settings.animationLevel = normalized.animationLevel
|
|
}
|
|
if (normalized.trackDisplayMode === 'none' || normalized.trackDisplayMode === 'full' || normalized.trackDisplayMode === 'tail') {
|
|
settings.trackDisplayMode = normalized.trackDisplayMode
|
|
}
|
|
if (normalized.trackTailLength === 'short' || normalized.trackTailLength === 'medium' || normalized.trackTailLength === 'long') {
|
|
settings.trackTailLength = normalized.trackTailLength
|
|
}
|
|
if (normalized.trackStyleProfile === 'classic' || normalized.trackStyleProfile === 'neon') {
|
|
settings.trackStyleProfile = normalized.trackStyleProfile
|
|
}
|
|
if (typeof normalized.gpsMarkerVisible === 'boolean') {
|
|
settings.gpsMarkerVisible = normalized.gpsMarkerVisible
|
|
}
|
|
if (
|
|
normalized.gpsMarkerStyle === 'dot'
|
|
|| normalized.gpsMarkerStyle === 'beacon'
|
|
|| normalized.gpsMarkerStyle === 'disc'
|
|
|| normalized.gpsMarkerStyle === 'badge'
|
|
) {
|
|
settings.gpsMarkerStyle = normalized.gpsMarkerStyle
|
|
}
|
|
if (normalized.gpsMarkerSize === 'small' || normalized.gpsMarkerSize === 'medium' || normalized.gpsMarkerSize === 'large') {
|
|
settings.gpsMarkerSize = normalized.gpsMarkerSize
|
|
}
|
|
if (
|
|
normalized.gpsMarkerColorPreset === 'mint'
|
|
|| normalized.gpsMarkerColorPreset === 'cyan'
|
|
|| normalized.gpsMarkerColorPreset === 'sky'
|
|
|| normalized.gpsMarkerColorPreset === 'blue'
|
|
|| normalized.gpsMarkerColorPreset === 'violet'
|
|
|| normalized.gpsMarkerColorPreset === 'pink'
|
|
|| normalized.gpsMarkerColorPreset === 'orange'
|
|
|| normalized.gpsMarkerColorPreset === 'yellow'
|
|
) {
|
|
settings.gpsMarkerColorPreset = normalized.gpsMarkerColorPreset
|
|
}
|
|
if (
|
|
normalized.trackColorPreset === 'mint'
|
|
|| normalized.trackColorPreset === 'cyan'
|
|
|| normalized.trackColorPreset === 'sky'
|
|
|| normalized.trackColorPreset === 'blue'
|
|
|| normalized.trackColorPreset === 'violet'
|
|
|| normalized.trackColorPreset === 'pink'
|
|
|| normalized.trackColorPreset === 'orange'
|
|
|| normalized.trackColorPreset === 'yellow'
|
|
) {
|
|
settings.trackColorPreset = normalized.trackColorPreset
|
|
}
|
|
if (normalized.northReferenceMode === 'magnetic' || normalized.northReferenceMode === 'true') {
|
|
settings.northReferenceMode = normalized.northReferenceMode
|
|
}
|
|
if (typeof normalized.autoRotateEnabled === 'boolean') {
|
|
settings.autoRotateEnabled = normalized.autoRotateEnabled
|
|
}
|
|
if (normalized.compassTuningProfile === 'smooth' || normalized.compassTuningProfile === 'balanced' || normalized.compassTuningProfile === 'responsive') {
|
|
settings.compassTuningProfile = normalized.compassTuningProfile
|
|
}
|
|
if (normalized.sideButtonPlacement === 'left' || normalized.sideButtonPlacement === 'right') {
|
|
settings.sideButtonPlacement = normalized.sideButtonPlacement
|
|
}
|
|
if (typeof normalized.showCenterScaleRuler === 'boolean') {
|
|
settings.showCenterScaleRuler = normalized.showCenterScaleRuler
|
|
}
|
|
if (normalized.centerScaleRulerAnchorMode === 'screen-center' || normalized.centerScaleRulerAnchorMode === 'compass-center') {
|
|
settings.centerScaleRulerAnchorMode = normalized.centerScaleRulerAnchorMode
|
|
}
|
|
|
|
return settings
|
|
}
|
|
|
|
export function loadStoredUserSettings(storageKey = USER_SETTINGS_STORAGE_KEY): StoredUserSettings {
|
|
try {
|
|
return normalizeStoredUserSettings(wx.getStorageSync(storageKey))
|
|
} catch {
|
|
return {}
|
|
}
|
|
}
|
|
|
|
export function persistStoredUserSettings(
|
|
settings: StoredUserSettings,
|
|
storageKey = USER_SETTINGS_STORAGE_KEY,
|
|
): void {
|
|
try {
|
|
wx.setStorageSync(storageKey, settings)
|
|
} catch {}
|
|
}
|
|
|
|
export function mergeStoredUserSettings(
|
|
current: StoredUserSettings,
|
|
patch: Partial<StoredUserSettings>,
|
|
): StoredUserSettings {
|
|
return {
|
|
...current,
|
|
...patch,
|
|
}
|
|
}
|
|
|
|
export function buildInitialSystemSettingsState(
|
|
stored: StoredUserSettings,
|
|
config?: Partial<SystemSettingsConfig>,
|
|
): ResolvedSystemSettingsState {
|
|
const values = {
|
|
...DEFAULT_STORED_USER_SETTINGS,
|
|
...(config && config.values ? config.values : {}),
|
|
}
|
|
const locks = {
|
|
...DEFAULT_SETTING_LOCKS,
|
|
...(config && config.locks ? config.locks : {}),
|
|
}
|
|
|
|
const resolvedValues: Required<StoredUserSettings> = {
|
|
...values,
|
|
}
|
|
|
|
for (const [lockKey, isLocked] of Object.entries(locks) as Array<[SettingLockKey, boolean]>) {
|
|
const valueKey = SETTING_LOCK_VALUE_MAP[lockKey]
|
|
if (!valueKey) {
|
|
continue
|
|
}
|
|
if (!isLocked && stored[valueKey] !== undefined) {
|
|
;(resolvedValues as Record<string, unknown>)[valueKey] = stored[valueKey]
|
|
}
|
|
}
|
|
|
|
for (const [key, value] of Object.entries(stored) as Array<[keyof StoredUserSettings, StoredUserSettings[keyof StoredUserSettings]]>) {
|
|
const matchingLockKey = (Object.keys(SETTING_LOCK_VALUE_MAP) as SettingLockKey[])
|
|
.find((lockKey) => SETTING_LOCK_VALUE_MAP[lockKey] === key)
|
|
if (matchingLockKey && locks[matchingLockKey]) {
|
|
continue
|
|
}
|
|
if (value !== undefined) {
|
|
;(resolvedValues as Record<string, unknown>)[key] = value
|
|
}
|
|
}
|
|
|
|
return {
|
|
values: resolvedValues,
|
|
locks,
|
|
}
|
|
}
|
|
|
|
export function buildRuntimeSettingLocks(
|
|
locks: Partial<Record<SettingLockKey, boolean>> | undefined,
|
|
runtimeActive: boolean,
|
|
): Partial<Record<SettingLockKey, boolean>> {
|
|
const sourceLocks = locks || {}
|
|
if (runtimeActive) {
|
|
return { ...sourceLocks }
|
|
}
|
|
|
|
const unlocked: Partial<Record<SettingLockKey, boolean>> = {}
|
|
for (const key of Object.keys(sourceLocks) as SettingLockKey[]) {
|
|
unlocked[key] = false
|
|
}
|
|
return unlocked
|
|
}
|
|
|
|
export function resolveSystemSettingsState(
|
|
config?: Partial<SystemSettingsConfig>,
|
|
storageKey = USER_SETTINGS_STORAGE_KEY,
|
|
runtimeActive = false,
|
|
): ResolvedSystemSettingsState {
|
|
return buildInitialSystemSettingsState(
|
|
loadStoredUserSettings(storageKey),
|
|
{
|
|
values: config && config.values ? config.values : {},
|
|
locks: buildRuntimeSettingLocks(config && config.locks ? config.locks : {}, runtimeActive),
|
|
},
|
|
)
|
|
}
|