File size: 5,557 Bytes
00a912e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | import { create } from 'zustand'
import { storage, SETTING_DEFAULTS } from '@/lib/storage'
// 重新导出供 UI 层使用
export { SETTING_DEFAULTS }
// ---------------------------------------------------------------------------
// 类型定义
// ---------------------------------------------------------------------------
/** 默认值表的 key 类型 */
type SettingKey = keyof typeof SETTING_DEFAULTS
/** 从默认值表的 key 推导出 value 类型 */
type SettingValue<K extends SettingKey> = (typeof SETTING_DEFAULTS)[K]
/** 每个 resettable key 在 store 中生成的四个字段名 */
type ResettableFields<K extends string, V> = {
[P in K]: V
} & {
[P in `${K}Default`]: V
} & {
[P in `set${Capitalize<K>}`]: (v: V) => void
} & {
[P in `reset${Capitalize<K>}`]: () => void
}
/** 合并所有 resettable key 的字段为完整 SettingsStore 类型 */
type SettingsStore = ResettableFields<'playbackTempoSyncEnabled', boolean> &
ResettableFields<'playbackHardSeekSyncEnabled', boolean> &
ResettableFields<'syncPacketIntervalSeconds', number> &
ResettableFields<'ttmlEnabled', boolean> &
ResettableFields<'ttmlDbUrl', string> &
ResettableFields<'lyricAlignAnchor', 'top' | 'center' | 'bottom'> &
ResettableFields<'lyricAlignPosition', number> &
ResettableFields<'lyricEnableSpring', boolean> &
ResettableFields<'lyricEnableBlur', boolean> &
ResettableFields<'lyricEnableScale', boolean> &
ResettableFields<'lyricFontWeight', number> &
ResettableFields<'lyricFontSize', number> &
ResettableFields<'lyricTranslationFontSize', number> &
ResettableFields<'lyricRomanFontSize', number> & {
lyricOffsets: Record<string, number>
setLyricOffset: (key: string, offsetMs: number) => void
clearLyricOffset: (key: string) => void
} & ResettableFields<'bgFps', number> &
ResettableFields<'bgFlowSpeed', number> &
ResettableFields<'bgRenderScale', number>
// ---------------------------------------------------------------------------
// Store 实现
// ---------------------------------------------------------------------------
/** 首字母大写 */
function capitalize(s: string): string {
return s.charAt(0).toUpperCase() + s.slice(1)
}
export const useSettingsStore = create<SettingsStore>((set) => {
/**
* resettable 工厂:为每个 setting key 生成 4 个 store 字段
* key → 当前值
* keyDefault → 默认值(只读)
* setKey → setter(持久化 + 更新 store)
* resetKey → 恢复默认值
*/
function resettable<K extends SettingKey>(
key: K,
load: () => SettingValue<K>,
persist: (v: SettingValue<K>) => void,
): Record<string, unknown> {
const defaultValue = SETTING_DEFAULTS[key]
const initial = load()
const cap = capitalize(key)
const setValue = (v: SettingValue<K>) => {
persist(v)
set({ [key]: v } as Partial<SettingsStore>)
}
return {
[key]: initial,
[`${key}Default`]: defaultValue,
[`set${cap}`]: setValue,
[`reset${cap}`]: () => setValue(defaultValue),
}
}
return {
...resettable('playbackTempoSyncEnabled', storage.getPlaybackTempoSyncEnabled, storage.setPlaybackTempoSyncEnabled),
...resettable(
'playbackHardSeekSyncEnabled',
storage.getPlaybackHardSeekSyncEnabled,
storage.setPlaybackHardSeekSyncEnabled,
),
...resettable(
'syncPacketIntervalSeconds',
storage.getSyncPacketIntervalSeconds,
storage.setSyncPacketIntervalSeconds,
),
...resettable('ttmlEnabled', storage.getTtmlEnabled, storage.setTtmlEnabled),
...resettable('ttmlDbUrl', storage.getTtmlDbUrl, storage.setTtmlDbUrl),
...resettable('lyricAlignAnchor', storage.getLyricAlignAnchor, storage.setLyricAlignAnchor),
...resettable('lyricAlignPosition', storage.getLyricAlignPosition, storage.setLyricAlignPosition),
...resettable('lyricEnableSpring', storage.getLyricEnableSpring, storage.setLyricEnableSpring),
...resettable('lyricEnableBlur', storage.getLyricEnableBlur, storage.setLyricEnableBlur),
...resettable('lyricEnableScale', storage.getLyricEnableScale, storage.setLyricEnableScale),
...resettable('lyricFontWeight', storage.getLyricFontWeight, storage.setLyricFontWeight),
...resettable('lyricFontSize', storage.getLyricFontSize, storage.setLyricFontSize),
...resettable('lyricTranslationFontSize', storage.getLyricTranslationFontSize, storage.setLyricTranslationFontSize),
...resettable('lyricRomanFontSize', storage.getLyricRomanFontSize, storage.setLyricRomanFontSize),
lyricOffsets: storage.getLyricOffsets(),
setLyricOffset: (key, offsetMs) => {
const value = Math.round(Math.max(-10_000, Math.min(10_000, offsetMs)))
set((state) => {
const lyricOffsets = { ...state.lyricOffsets }
if (value === 0) delete lyricOffsets[key]
else lyricOffsets[key] = value
storage.setLyricOffsets(lyricOffsets)
return { lyricOffsets }
})
},
clearLyricOffset: (key) => {
set((state) => {
if (!(key in state.lyricOffsets)) return state
const lyricOffsets = { ...state.lyricOffsets }
delete lyricOffsets[key]
storage.setLyricOffsets(lyricOffsets)
return { lyricOffsets }
})
},
...resettable('bgFps', storage.getBgFps, storage.setBgFps),
...resettable('bgFlowSpeed', storage.getBgFlowSpeed, storage.setBgFlowSpeed),
...resettable('bgRenderScale', storage.getBgRenderScale, storage.setBgRenderScale),
} as SettingsStore
})
|