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
}
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
}

View File

@ -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)
}
}
if subNode, ok := node.(map[string]any); ok {
subNode[keyPath[len(keyPath)-1]] = value
}
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
}
}
}
func (p *PreferencesStorage) savePreferences(pf map[string]any) error {
b, err := yaml.Marshal(&pf)
return fmt.Errorf("invalid key path(%s)", key)
}
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
}

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 { 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<boolean>}
*/
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<void>}
*/
async resetToLastPreferences() {