refactor: define preferences data with go struct

This commit is contained in:
tiny-craft 2023-10-04 22:21:35 +08:00
parent ffc50a7fcc
commit 66ac7194e2
4 changed files with 115 additions and 130 deletions

View File

@ -39,8 +39,8 @@ func (p *preferencesService) GetPreferences() (resp types.JSResp) {
return return
} }
func (p *preferencesService) SetPreferences(values map[string]any) (resp types.JSResp) { func (p *preferencesService) SetPreferences(pf types.Preferences) (resp types.JSResp) {
err := p.pref.SetPreferencesN(values) err := p.pref.SetPreferences(&pf)
if err != nil { if err != nil {
resp.Msg = err.Error() resp.Msg = err.Error()
return return
@ -50,6 +50,16 @@ func (p *preferencesService) SetPreferences(values map[string]any) (resp types.J
return 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) { func (p *preferencesService) RestorePreferences() (resp types.JSResp) {
defaultPref := p.pref.RestoreDefault() defaultPref := p.pref.RestoreDefault()
resp.Data = map[string]any{ resp.Data = map[string]any{
@ -103,21 +113,20 @@ func (p *preferencesService) GetAppVersion() (resp types.JSResp) {
} }
func (p *preferencesService) SaveWindowSize(width, height int) { func (p *preferencesService) SaveWindowSize(width, height int) {
p.SetPreferences(map[string]any{ p.UpdatePreferences(map[string]any{
"behavior.window_width": width, "behavior.windowWidth": width,
"behavior.window_height": height, "behavior.windowHeight": height,
}) })
} }
func (p *preferencesService) GetWindowSize() (width, height int) { func (p *preferencesService) GetWindowSize() (width, height int) {
data := p.pref.GetPreferences() data := p.pref.GetPreferences()
w, h := data["behavior.window_width"], data["behavior.window_height"] width, height = data.Behavior.WindowWidth, data.Behavior.WindowHeight
var ok bool if width <= 0 {
if width, ok = w.(int); !ok { width = consts.DEFAULT_WINDOW_WIDTH
return consts.DEFAULT_WINDOW_WIDTH, consts.DEFAULT_WINDOW_HEIGHT
} }
if height, ok = h.(int); !ok { if height <= 0 {
return consts.DEFAULT_WINDOW_WIDTH, consts.DEFAULT_WINDOW_HEIGHT height = consts.DEFAULT_WINDOW_HEIGHT
} }
return return
} }

View File

@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"log" "log"
"reflect"
"strings" "strings"
"sync" "sync"
"tinyrdm/backend/consts" "tinyrdm/backend/types"
) )
type PreferencesStorage struct { type PreferencesStorage struct {
@ -20,30 +21,11 @@ func NewPreferences() *PreferencesStorage {
} }
} }
func (p *PreferencesStorage) DefaultPreferences() map[string]any { func (p *PreferencesStorage) DefaultPreferences() types.Preferences {
return map[string]any{ return types.NewPreferences()
"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) getPreferences() (ret map[string]any) { func (p *PreferencesStorage) getPreferences() (ret types.Preferences) {
b, err := p.storage.Load() b, err := p.storage.Load()
if err != nil { if err != nil {
ret = p.DefaultPreferences() ret = p.DefaultPreferences()
@ -57,79 +39,43 @@ func (p *PreferencesStorage) getPreferences() (ret map[string]any) {
return 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 // GetPreferences Get preferences from local
func (p *PreferencesStorage) GetPreferences() (ret map[string]any) { func (p *PreferencesStorage) GetPreferences() (ret types.Preferences) {
p.mutex.Lock() p.mutex.Lock()
defer p.mutex.Unlock() defer p.mutex.Unlock()
pref := p.getPreferences() ret = p.getPreferences()
ret = p.flatPreferences(pref, "")
return return
} }
func (p *PreferencesStorage) Value(keys ...string) any { func (p *PreferencesStorage) setPreferences(pf *types.Preferences, key string, value any) error {
kv := p.getPreferences() parts := strings.Split(key, ".")
var ok bool if len(parts) > 0 {
var a any var reflectValue reflect.Value
total := len(keys) if reflect.TypeOf(pf).Kind() == reflect.Ptr {
for i, key := range keys { reflectValue = reflect.ValueOf(pf).Elem()
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]
} else { } 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 { return fmt.Errorf("invalid key path(%s)", key)
subNode[keyPath[len(keyPath)-1]] = value
}
return nil
} }
func (p *PreferencesStorage) savePreferences(pf map[string]any) error { func (p *PreferencesStorage) savePreferences(pf *types.Preferences) error {
b, err := yaml.Marshal(&pf) b, err := yaml.Marshal(pf)
if err != nil { if err != nil {
return err return err
} }
@ -140,37 +86,35 @@ func (p *PreferencesStorage) savePreferences(pf map[string]any) error {
return nil return nil
} }
// SetPreferences assign value to key path, the key path use "." to indicate multiple level // SetPreferences replace preferences
func (p *PreferencesStorage) SetPreferences(key string, value any) error { func (p *PreferencesStorage) SetPreferences(pf *types.Preferences) error {
p.mutex.Lock() p.mutex.Lock()
defer p.mutex.Unlock() defer p.mutex.Unlock()
pf := p.getPreferences()
if err := p.setPreferences(pf, key, value); err != nil {
return err
}
return p.savePreferences(pf) return p.savePreferences(pf)
} }
// SetPreferencesN set multiple key path and value // UpdatePreferences update values by key paths, the key path use "." to indicate multiple level
func (p *PreferencesStorage) SetPreferencesN(values map[string]any) error { func (p *PreferencesStorage) UpdatePreferences(values map[string]any) error {
p.mutex.Lock() p.mutex.Lock()
defer p.mutex.Unlock() defer p.mutex.Unlock()
pf := p.getPreferences() pf := p.getPreferences()
for path, v := range values { 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 return err
} }
} }
log.Println("after save", pf) 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() pf := p.DefaultPreferences()
p.savePreferences(pf) p.savePreferences(&pf)
return p.flatPreferences(pf, "") return pf
} }

View File

@ -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"`
}

View File

@ -1,6 +1,6 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { lang } from '@/langs/index.js' 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 { import {
CheckForUpdate, CheckForUpdate,
GetFontList, GetFontList,
@ -165,8 +165,7 @@ const usePreferencesStore = defineStore('preferences', {
actions: { actions: {
_applyPreferences(data) { _applyPreferences(data) {
for (const key in data) { for (const key in data) {
const keys = map(split(key, '.'), camelCase) set(this, key, data[key])
set(this, keys, data[key])
} }
}, },
@ -203,30 +202,13 @@ const usePreferencesStore = defineStore('preferences', {
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
async savePreferences() { async savePreferences() {
const obj2Map = (prefix, obj) => { const pf = pick(this, ['behavior', 'general', 'editor'])
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 { success, msg } = await SetPreferences(pf) const { success, msg } = await SetPreferences(pf)
return success === true return success === true
}, },
/** /**
* reset to last loaded preferences * reset to last-loaded preferences
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async resetToLastPreferences() { async resetToLastPreferences() {