@@ -16,168 +16,167 @@ import {
import { defaultPreferences } from './config' ;
import { updateCSSVariables } from './update-css-variables' ;
const STORAGE_KEY = 'preferences' ;
const STORAGE_KEY_LOCALE = ` ${ STORAGE_KEY } -locale ` ;
const STORAGE_KEY_THEME = ` ${ STORAGE_KEY } -theme ` ;
const STORAGE_KEYS = {
MAIN : 'preferences' ,
LOCALE : 'preferences-locale' ,
THEME : 'preferences-theme' ,
} as const ;
class PreferenceManager {
private cache : null | StorageManager = null ;
// private flattenedState: Flatten<Preferences>;
private cache : StorageManager ;
private debouncedSave : ( preference : Preferences ) = > void ;
private initialPreferences : Preferences = defaultPreferences ;
private isInitialized : boolean = false ;
private savePreferences : ( preference : Preferences ) = > void ;
private state : Preferences = reactive < Preferences > ( {
. . . this . loadPreferences ( ) ,
} ) ;
private isInitialized = false ;
private state : Preferences ;
constructor ( ) {
this . cache = new StorageManager ( ) ;
// 避免频繁的操作缓存
this . savePreferences = useDebounceFn (
( preference : Preferences ) = > this . _savePreferences ( preference ) ,
this . state = reactive < Preferences > (
this . loadFromCache ( ) || { . . . defaultPreferences } ,
) ;
this . debouncedSave = useDebounceFn (
( preference ) = > this . saveToCache ( preference ) ,
150 ,
) ;
}
clearCache() {
[ STORAGE_KEY , STORAGE_KEY_LOCALE , STORAGE_KEY_THEME ] . forEach ( ( key ) = > {
this . cache ? . removeItem ( key ) ;
} ) ;
}
public getInitialPreferences() {
return this . initialPreferences ;
}
public getPreferences() {
return readonly ( this . state ) ;
}
/**
* 清除所有缓存的偏好设置
*/
clearCache = ( ) = > {
Object . values ( STORAGE_KEYS ) . forEach ( ( key ) = > this . cache . removeItem ( key ) ) ;
} ;
/**
* 覆盖 偏好设置
* overrides 要覆盖的偏好设置
* namespace 命名空间
* 获取初始化 偏好设置
*/
public async init Preferences( { namespace , overrides } : InitialOptions ) {
// 是否初始化过
getInitial Preferences = ( ) = > {
return this . initialPreferences ;
} ;
/**
* 获取当前偏好设置(只读)
*/
getPreferences = ( ) = > {
return readonly ( this . state ) ;
} ;
/**
* 初始化偏好设置
* @param namespace - 命名空间,用于隔离不同应用的配置
* @param overrides - 要覆盖的偏好设置
*/
initPreferences = async ( { namespace , overrides } : InitialOptions ) = > {
// 防止重复初始化
if ( this . isInitialized ) {
return ;
}
// 初始化存储管理器
// 使用命名空间初始化存储管理器
this . cache = new StorageManager ( { prefix : namespace } ) ;
// 合并初始偏好设置
this . initialPreferences = merge ( { } , overrides , defaultPreferences ) ;
// 加载并合并当前存储的偏好设置
// 加载缓存的偏好设置并与初始配置合并
const cachedPreferences = this . loadFromCache ( ) || { } ;
const mergedPreference = merge (
{ } ,
// overrides,
this . loadCachedPreferences ( ) || { } ,
cachedPreferences ,
this . initialPreferences ,
) ;
// 更新偏好设置
this . updatePreferences ( mergedPreference ) ;
// 设置监听器
this . setupWatcher ( ) ;
// 初始化平台标识
this . initPlatform ( ) ;
// 标记为已初始化
this . isInitialized = true ;
}
} ;
/**
* 重置偏好设置
* 偏好设置将被重置为初始值,并从 localStorage 中移除。
*
* @example
* 假设 initialPreferences 为 { theme: 'light', language: 'en' }
* 当前 state 为 { theme: 'dark', language: 'fr' }
* this.resetPreferences();
* 调用后, state 将被重置为 { theme: 'light', language: 'en' }
* 并且 localStorage 中的对应项将被移除
* 重置偏好设置到初始状态
*/
resetPreferences() {
resetPreferences = ( ) = > {
// 将状态重置为初始偏好设置
Object . assign ( this . state , this . initialPreferences ) ;
// 保存重置后的偏好设置
this . savePreferences ( this . state ) ;
// 从存储中移除偏好设置项
[ STORAGE_KEY , STORAGE_KEY_THEME , STORAGE_KEY_LOCALE ] . forEach ( ( key ) = > {
this . cache ? . removeItem ( key ) ;
} ) ;
this . updatePreferences ( this . state ) ;
}
// 保存偏好设置至缓存
this . saveToCache ( this . state ) ;
// 直接触发 UI 更新
this . handleUpdates ( this . state ) ;
} ;
/**
* 更新偏好设置
* @param updates - 要更新的偏好设置
*/
public updatePreferences ( updates : DeepPartial < Preferences > ) {
updatePreferences = ( updates : DeepPartial < Preferences > ) = > {
// 深度合并更新内容和当前状态
const mergedState = merge ( { } , updates , markRaw ( this . state ) ) ;
Object . assign ( this . state , mergedState ) ;
// 根据更新的键 值执行相应的操作
// 根据更新的值执行更新
this . handleUpdates ( updates ) ;
this . savePreferences ( this . state ) ;
}
// 保存到缓存
this . debouncedSave ( this . state ) ;
} ;
/**
* 保存偏好设置
* @param {Preferences} preference - 需要保存 的偏好设置
*/
private _savePreferences ( preference : Preferences ) {
this . cache ? . setItem ( STORAGE_KEY , preference ) ;
this . cache ? . setItem ( STORAGE_KEY_LOCALE , preference . app . locale ) ;
this . cache ? . setItem ( STORAGE_KEY_THEME , preference . theme . mode ) ;
}
/**
* 处理更新的键值
* 根据更新的键值执行相应的操作。
* @param {DeepPartial<Preferences>} updates - 部分更新的偏好设置
* 处理更新
* @param updates - 更新 的偏好设置
*/
private handleUpdates ( updates : DeepPartial < Preferences > ) {
const themeUpdates = updates . theme || { } ;
const appUpdates = updates . app || { } ;
const { theme, app } = updates ;
if (
( themeUpdates && Object . keys ( themeUpdates ) . length > 0 ) ||
Reflect . has ( themeUpdates , 'fontSize' )
theme &&
( Object . keys ( theme ) . length > 0 || Reflect . has ( theme , 'fontSize' ) )
) {
updateCSSVariables ( this . state ) ;
}
if (
Reflect . has ( appUpdates , 'colorGrayMode' ) ||
Reflect . has ( appUpdates , 'colorWeakMode' )
app &&
( Reflect . has ( app, 'colorGrayMode' ) || Reflect . has ( app , 'colorWeakMode' ) )
) {
this . updateColorMode ( this . state ) ;
}
}
/**
* 初始化平台标识
*/
private initPlatform() {
const dom = document . documentElement ;
dom . dataset . platform = isMacOs ( ) ? 'macOs' : 'window' ;
document . documentElement . dataset . platform = isMacOs ( ) ? 'macOs' : 'window' ;
}
/**
* 从缓存中 加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。
* 从缓存加载偏好设置
* @returns 缓存的偏好设置,如果不存在则返回 null
*/
private loadCached Preferences() {
return this . cache ? . getItem < Preferences > ( STORAGE_KEY ) ;
private loadFrom Cache( ) : null | Preferences {
return this . cache . getItem < Preferences > ( STORAGE_KEYS . MAIN ) ;
}
/**
* 加载 偏好设置
* @returns {P references} 加载 的偏好设置
* 保存 偏好设置到缓存
* @param p reference - 要保存 的偏好设置
*/
private loadPreferences ( ) : Preferences {
return this . loadCachedPreferences ( ) || { . . . defaultP references } ;
private saveToCache ( preference : Preferences) {
this . cache . setItem ( STORAGE_KEYS . MAIN , p reference) ;
this . cache . setItem ( STORAGE_KEYS . LOCALE , preference . app . locale ) ;
this . cache . setItem ( STORAGE_KEYS . THEME , preference . theme . mode ) ;
}
/**
* 监听状态和系统偏好设置的变化。
* 监听状态和系统偏好设置的变化
*/
private setupWatcher() {
if ( this . isInitialized ) {
@@ -187,6 +186,7 @@ class PreferenceManager {
// 监听断点,判断是否移动端
const breakpoints = useBreakpoints ( breakpointsTailwind ) ;
const isMobile = breakpoints . smaller ( 'md' ) ;
watch (
( ) = > isMobile . value ,
( val ) = > {
@@ -201,12 +201,13 @@ class PreferenceManager {
window
. matchMedia ( '(prefers-color-scheme: dark)' )
. addEventListener ( 'change' , ( { matches : isDark } ) = > {
// 如果偏好设置中主题模式为auto, 则 跟随系统更新
// 仅在自动模式下 跟随系统主题
if ( this . state . theme . mode === 'auto' ) {
// 先应用实际的主题
this . updatePreferences ( {
theme : { mode : isDark ? 'dark' : 'light' } ,
} ) ;
// 恢复为auto模式
// 再 恢复为 auto 模式,保持跟随系统的状态
this . updatePreferences ( {
theme : { mode : 'auto' } ,
} ) ;
@@ -216,19 +217,17 @@ class PreferenceManager {
/**
* 更新页面颜色模式(灰色、色弱)
* @param preference
* @param preference - 偏好设置
*/
private updateColorMode ( preference : Preferences ) {
if ( preference . app ) {
const { colorGrayMode , colorWeakMode } = preference . app ;
const dom = document . documentElement ;
const COLOR_WEAK = 'invert-mode' ;
const COLOR_GRAY = 'grayscale-mode' ;
dom . classList . toggle ( COLOR_WEAK , colorWeakMode ) ;
dom . classList . toggle ( COLOR_GRAY , colorGrayMode ) ;
}
const { colorGrayMode , colorWeakMode } = preference . app ;
const dom = document . documentElement ;
dom . classList . toggle ( 'invert-mode' , colorWeakMode ) ;
dom . classList . toggle ( 'grayscale-mode' , colorGrayMode ) ;
}
}
const preferencesManager = new PreferenceManager ( ) ;
export { PreferenceManager , preferencesManager } ;