diff --git a/backend/services/preferences_service.go b/backend/services/preferences_service.go index 7e06569..7604b58 100644 --- a/backend/services/preferences_service.go +++ b/backend/services/preferences_service.go @@ -39,8 +39,8 @@ func (p *preferencesService) GetPreferences() (resp types.JSResp) { return } -func (p *preferencesService) SetPreferences(values map[string]any) (resp types.JSResp) { - err := p.pref.SetPreferencesN(values) +func (p *preferencesService) SetPreferences(pf types.Preferences) (resp types.JSResp) { + err := p.pref.SetPreferences(&pf) if err != nil { resp.Msg = err.Error() return @@ -50,6 +50,16 @@ func (p *preferencesService) SetPreferences(values map[string]any) (resp types.J return } +func (p *preferencesService) UpdatePreferences(value map[string]any) (resp types.JSResp) { + err := p.pref.UpdatePreferences(value) + if err != nil { + resp.Msg = err.Error() + return + } + resp.Success = true + return +} + func (p *preferencesService) RestorePreferences() (resp types.JSResp) { defaultPref := p.pref.RestoreDefault() resp.Data = map[string]any{ @@ -103,21 +113,20 @@ func (p *preferencesService) GetAppVersion() (resp types.JSResp) { } func (p *preferencesService) SaveWindowSize(width, height int) { - p.SetPreferences(map[string]any{ - "behavior.window_width": width, - "behavior.window_height": height, + p.UpdatePreferences(map[string]any{ + "behavior.windowWidth": width, + "behavior.windowHeight": height, }) } func (p *preferencesService) GetWindowSize() (width, height int) { data := p.pref.GetPreferences() - w, h := data["behavior.window_width"], data["behavior.window_height"] - var ok bool - if width, ok = w.(int); !ok { - return consts.DEFAULT_WINDOW_WIDTH, consts.DEFAULT_WINDOW_HEIGHT + width, height = data.Behavior.WindowWidth, data.Behavior.WindowHeight + if width <= 0 { + width = consts.DEFAULT_WINDOW_WIDTH } - if height, ok = h.(int); !ok { - return consts.DEFAULT_WINDOW_WIDTH, consts.DEFAULT_WINDOW_HEIGHT + if height <= 0 { + height = consts.DEFAULT_WINDOW_HEIGHT } return } diff --git a/backend/storage/preferences.go b/backend/storage/preferences.go index 1db7143..3435db0 100644 --- a/backend/storage/preferences.go +++ b/backend/storage/preferences.go @@ -4,9 +4,10 @@ import ( "fmt" "gopkg.in/yaml.v3" "log" + "reflect" "strings" "sync" - "tinyrdm/backend/consts" + "tinyrdm/backend/types" ) type PreferencesStorage struct { @@ -20,30 +21,11 @@ func NewPreferences() *PreferencesStorage { } } -func (p *PreferencesStorage) DefaultPreferences() map[string]any { - return map[string]any{ - "behavior": map[string]any{ - "aside_width": consts.DEFAULT_ASIDE_WIDTH, - "window_width": consts.DEFAULT_WINDOW_WIDTH, - "window_height": consts.DEFAULT_WINDOW_HEIGHT, - }, - "general": map[string]any{ - "language": "auto", - "font": "", - "font_size": consts.DEFAULT_FONT_SIZE, - "use_sys_proxy": false, - "use_sys_proxy_http": false, - "check_update": true, - "skip_version": "", - }, - "editor": map[string]any{ - "font": "", - "font_size": 14, - }, - } +func (p *PreferencesStorage) DefaultPreferences() types.Preferences { + return types.NewPreferences() } -func (p *PreferencesStorage) getPreferences() (ret map[string]any) { +func (p *PreferencesStorage) getPreferences() (ret types.Preferences) { b, err := p.storage.Load() if err != nil { ret = p.DefaultPreferences() @@ -57,79 +39,43 @@ func (p *PreferencesStorage) getPreferences() (ret map[string]any) { return } -func (p *PreferencesStorage) flatPreferences(data map[string]any, prefix string) map[string]any { - flattened := make(map[string]any) - for key, value := range data { - newKey := key - if prefix != "" { - newKey = prefix + "." + key - } - - if nested, ok := value.(map[string]any); ok { - nestedFlattened := p.flatPreferences(nested, newKey) - for k, v := range nestedFlattened { - flattened[k] = v - } - } else { - flattened[newKey] = value - } - } - return flattened -} - // GetPreferences Get preferences from local -func (p *PreferencesStorage) GetPreferences() (ret map[string]any) { +func (p *PreferencesStorage) GetPreferences() (ret types.Preferences) { p.mutex.Lock() defer p.mutex.Unlock() - pref := p.getPreferences() - ret = p.flatPreferences(pref, "") + ret = p.getPreferences() return } -func (p *PreferencesStorage) Value(keys ...string) any { - kv := p.getPreferences() - var ok bool - var a any - total := len(keys) - for i, key := range keys { - if a, ok = kv[key]; !ok { - return nil - } - if i == total-1 { - // last key, return value - return a - } - if kv, ok = a.(map[string]any); !ok { - return nil - } - } - return nil -} - -func (p *PreferencesStorage) setPreferences(pf map[string]any, key string, value any) error { - keyPath := strings.Split(key, ".") - if len(keyPath) <= 0 { - return fmt.Errorf("invalid key path(%s)", key) - } - var node any = pf - for _, k := range keyPath[:len(keyPath)-1] { - if subNode, ok := node.(map[string]any); ok { - node = subNode[k] +func (p *PreferencesStorage) setPreferences(pf *types.Preferences, key string, value any) error { + parts := strings.Split(key, ".") + if len(parts) > 0 { + var reflectValue reflect.Value + if reflect.TypeOf(pf).Kind() == reflect.Ptr { + reflectValue = reflect.ValueOf(pf).Elem() } else { - return fmt.Errorf("invalid key path(%s)", key) + reflectValue = reflect.ValueOf(pf) + } + for i, part := range parts { + part = strings.ToUpper(part[:1]) + part[1:] + reflectValue = reflectValue.FieldByName(part) + if reflectValue.IsValid() { + if i == len(parts)-1 { + reflectValue.Set(reflect.ValueOf(value)) + return nil + } + } else { + break + } } } - if subNode, ok := node.(map[string]any); ok { - subNode[keyPath[len(keyPath)-1]] = value - } - - return nil + return fmt.Errorf("invalid key path(%s)", key) } -func (p *PreferencesStorage) savePreferences(pf map[string]any) error { - b, err := yaml.Marshal(&pf) +func (p *PreferencesStorage) savePreferences(pf *types.Preferences) error { + b, err := yaml.Marshal(pf) if err != nil { return err } @@ -140,37 +86,35 @@ func (p *PreferencesStorage) savePreferences(pf map[string]any) error { return nil } -// SetPreferences assign value to key path, the key path use "." to indicate multiple level -func (p *PreferencesStorage) SetPreferences(key string, value any) error { +// SetPreferences replace preferences +func (p *PreferencesStorage) SetPreferences(pf *types.Preferences) error { p.mutex.Lock() defer p.mutex.Unlock() - pf := p.getPreferences() - if err := p.setPreferences(pf, key, value); err != nil { - return err - } return p.savePreferences(pf) } -// SetPreferencesN set multiple key path and value -func (p *PreferencesStorage) SetPreferencesN(values map[string]any) error { +// UpdatePreferences update values by key paths, the key path use "." to indicate multiple level +func (p *PreferencesStorage) UpdatePreferences(values map[string]any) error { p.mutex.Lock() defer p.mutex.Unlock() pf := p.getPreferences() for path, v := range values { - log.Println("path", path, v) - if err := p.setPreferences(pf, path, v); err != nil { + if err := p.setPreferences(&pf, path, v); err != nil { return err } } log.Println("after save", pf) - return p.savePreferences(pf) + return p.savePreferences(&pf) } -func (p *PreferencesStorage) RestoreDefault() map[string]any { +func (p *PreferencesStorage) RestoreDefault() types.Preferences { + p.mutex.Lock() + defer p.mutex.Unlock() + pf := p.DefaultPreferences() - p.savePreferences(pf) - return p.flatPreferences(pf, "") + p.savePreferences(&pf) + return pf } diff --git a/backend/types/preferences.go b/backend/types/preferences.go new file mode 100644 index 0000000..fd4b956 --- /dev/null +++ b/backend/types/preferences.go @@ -0,0 +1,50 @@ +package types + +import "tinyrdm/backend/consts" + +type Preferences struct { + Behavior PreferencesBehavior `json:"behavior" yaml:"behavior"` + General PreferencesGeneral `json:"general" yaml:"general"` + Editor PreferencesEditor `json:"editor" yaml:"editor"` +} + +func NewPreferences() Preferences { + return Preferences{ + Behavior: PreferencesBehavior{ + AsideWidth: consts.DEFAULT_ASIDE_WIDTH, + WindowWidth: consts.DEFAULT_WINDOW_WIDTH, + WindowHeight: consts.DEFAULT_WINDOW_HEIGHT, + }, + General: PreferencesGeneral{ + Theme: "auto", + Language: "auto", + FontSize: consts.DEFAULT_FONT_SIZE, + CheckUpdate: true, + }, + Editor: PreferencesEditor{ + FontSize: consts.DEFAULT_FONT_SIZE, + }, + } +} + +type PreferencesBehavior struct { + AsideWidth int `json:"asideWidth" yaml:"aside_width"` + WindowWidth int `json:"windowWidth" yaml:"window_width"` + WindowHeight int `json:"windowHeight" yaml:"window_height"` +} + +type PreferencesGeneral struct { + Theme string `json:"theme" yaml:"theme"` + Language string `json:"language" yaml:"language"` + Font string `json:"font" yaml:"font,omitempty"` + FontSize int `json:"fontSize" yaml:"font_size"` + UseSysProxy bool `json:"useSysProxy" yaml:"use_sys_proxy,omitempty"` + UseSysProxyHttp bool `json:"useSysProxyHttp" yaml:"use_sys_proxy_http,omitempty"` + CheckUpdate bool `json:"checkUpdate" yaml:"check_update"` + SkipVersion string `json:"skipVersion" yaml:"skip_version,omitempty"` +} + +type PreferencesEditor struct { + Font string `json:"font" yaml:"font,omitempty"` + FontSize int `json:"fontSize" yaml:"font_size"` +} diff --git a/frontend/src/stores/preferences.js b/frontend/src/stores/preferences.js index 199a2ea..dd316de 100644 --- a/frontend/src/stores/preferences.js +++ b/frontend/src/stores/preferences.js @@ -1,6 +1,6 @@ import { defineStore } from 'pinia' import { lang } from '@/langs/index.js' -import { camelCase, clone, find, get, isEmpty, isObject, map, set, snakeCase, split } from 'lodash' +import { clone, find, get, isEmpty, map, pick, set, split } from 'lodash' import { CheckForUpdate, GetFontList, @@ -165,8 +165,7 @@ const usePreferencesStore = defineStore('preferences', { actions: { _applyPreferences(data) { for (const key in data) { - const keys = map(split(key, '.'), camelCase) - set(this, keys, data[key]) + set(this, key, data[key]) } }, @@ -203,30 +202,13 @@ const usePreferencesStore = defineStore('preferences', { * @returns {Promise} */ async savePreferences() { - const obj2Map = (prefix, obj) => { - const result = {} - for (const key in obj) { - if (isObject(obj[key])) { - const subResult = obj2Map(`${prefix}.${snakeCase(key)}`, obj[key]) - Object.assign(result, subResult) - } else { - result[`${prefix}.${snakeCase(key)}`] = obj[key] - } - } - return result - } - const pf = Object.assign( - {}, - obj2Map('behavior', this.behavior), - obj2Map('general', this.general), - obj2Map('editor', this.editor), - ) + const pf = pick(this, ['behavior', 'general', 'editor']) const { success, msg } = await SetPreferences(pf) return success === true }, /** - * reset to last loaded preferences + * reset to last-loaded preferences * @returns {Promise} */ async resetToLastPreferences() {