feat: add preferencesStore to manager preferences

This commit is contained in:
tiny-craft 2023-07-11 18:06:44 +08:00
parent 16c096702c
commit dd88c617f9
6 changed files with 217 additions and 99 deletions

View File

@ -0,0 +1,51 @@
package services
import (
"sync"
storage2 "tinyrdm/backend/storage"
"tinyrdm/backend/types"
)
type preferencesService struct {
pref *storage2.PreferencesStorage
}
var preferences *preferencesService
var oncePreferences sync.Once
func Preferences() *preferencesService {
if preferences == nil {
oncePreferences.Do(func() {
preferences = &preferencesService{
pref: storage2.NewPreferences(),
}
})
}
return preferences
}
func (p *preferencesService) GetPreferences() (resp types.JSResp) {
resp.Data = p.pref.GetPreferences()
resp.Success = true
return
}
func (p *preferencesService) SetPreferences(values map[string]any) (resp types.JSResp) {
err := p.pref.SetPreferencesN(values)
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{
"pref": defaultPref,
}
resp.Success = true
return
}

View File

@ -24,8 +24,8 @@ func (p *PreferencesStorage) DefaultPreferences() map[string]any {
"language": "en", "language": "en",
"font": "", "font": "",
"font_size": 14, "font_size": 14,
"use_proxy": false, "use_sys_proxy": false,
"use_proxy_http": false, "use_sys_proxy_http": false,
"check_update": true, "check_update": true,
}, },
"editor": map[string]any{ "editor": map[string]any{
@ -42,19 +42,41 @@ func (p *PreferencesStorage) getPreferences() (ret map[string]any) {
return return
} }
if err := yaml.Unmarshal(b, &ret); err != nil { if err = yaml.Unmarshal(b, &ret); err != nil {
ret = p.DefaultPreferences() ret = p.DefaultPreferences()
return return
} }
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 map[string]any) {
p.mutex.Lock() p.mutex.Lock()
defer p.mutex.Unlock() defer p.mutex.Unlock()
return p.getPreferences() pref := p.getPreferences()
ret = p.flatPreferences(pref, "")
return
} }
func (p *PreferencesStorage) setPreferences(pf map[string]any, key string, value any) error { func (p *PreferencesStorage) setPreferences(pf map[string]any, key string, value any) error {
@ -120,5 +142,5 @@ func (p *PreferencesStorage) SetPreferencesN(values map[string]any) error {
func (p *PreferencesStorage) RestoreDefault() map[string]any { func (p *PreferencesStorage) RestoreDefault() map[string]any {
pf := p.DefaultPreferences() pf := p.DefaultPreferences()
p.savePreferences(pf) p.savePreferences(pf)
return pf return p.flatPreferences(pf, "")
} }

View File

@ -1,15 +1,15 @@
<script setup> <script setup>
import ContentPane from './components/content/ContentPane.vue' import ContentPane from './components/content/ContentPane.vue'
import BrowserPane from './components/sidebar/BrowserPane.vue' import BrowserPane from './components/sidebar/BrowserPane.vue'
import { computed, nextTick, onMounted, provide, reactive, ref } from 'vue' import { computed, nextTick, onMounted, reactive } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { GetPreferences } from '../wailsjs/go/storage/PreferencesStorage.js'
import { get } from 'lodash' import { get } from 'lodash'
import { useThemeVars } from 'naive-ui' import { useThemeVars } from 'naive-ui'
import NavMenu from './components/sidebar/NavMenu.vue' import NavMenu from './components/sidebar/NavMenu.vue'
import ConnectionPane from './components/sidebar/ConnectionPane.vue' import ConnectionPane from './components/sidebar/ConnectionPane.vue'
import ContentServerPane from './components/content/ContentServerPane.vue' import ContentServerPane from './components/content/ContentServerPane.vue'
import useTabStore from './stores/tab.js' import useTabStore from './stores/tab.js'
import usePreferencesStore from './stores/preferences.js'
const themeVars = useThemeVars() const themeVars = useThemeVars()
@ -20,21 +20,22 @@ const data = reactive({
}) })
const tabStore = useTabStore() const tabStore = useTabStore()
const preferences = ref({}) // const preferences = ref({})
provide('preferences', preferences) // provide('preferences', preferences)
const i18n = useI18n() const i18n = useI18n()
onMounted(async () => { onMounted(async () => {
preferences.value = await GetPreferences() const prefStore = usePreferencesStore()
await prefStore.loadPreferences()
await nextTick(() => { await nextTick(() => {
i18n.locale.value = get(preferences.value, 'general.language', 'en') i18n.locale.value = get(prefStore.general, 'language', 'en')
}) })
}) })
// TODO: apply font size to all elements // TODO: apply font size to all elements
const getFontSize = computed(() => { // const getFontSize = computed(() => {
return get(preferences.value, 'general.font_size', 'en') // return get(prefStore.general, 'fontSize', 'en')
}) // })
const handleResize = (evt) => { const handleResize = (evt) => {
if (data.resizing) { if (data.resizing) {

View File

@ -1,14 +1,11 @@
<script setup> <script setup>
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { GetPreferences, RestoreDefault, SetPreferencesN } from '../../../wailsjs/go/storage/PreferencesStorage.js'
import { lang } from '../../langs/index'
import useDialog from '../../stores/dialog' import useDialog from '../../stores/dialog'
import usePreferencesStore from '../../stores/preferences.js'
import { useMessage } from 'naive-ui'
const langOption = Object.entries(lang).map(([key, value]) => ({ const prefStore = usePreferencesStore()
value: key,
label: `${value['lang_name']}`,
}))
const fontOption = [ const fontOption = [
{ {
@ -17,91 +14,45 @@ const fontOption = [
}, },
] ]
const generalForm = reactive({
language: langOption[0].value,
font: '',
fontSize: 14,
useSystemProxy: false,
useSystemProxyHttp: false,
checkUpdate: false,
})
const editorForm = reactive({
font: '',
fontSize: 14,
})
const prevPreferences = ref({}) const prevPreferences = ref({})
const tab = ref('general') const tab = ref('general')
const formLabelWidth = '80px' const formLabelWidth = '80px'
const dialogStore = useDialog() const dialogStore = useDialog()
const i18n = useI18n() const i18n = useI18n()
const message = useMessage()
const applyPreferences = (pf) => {
const { general = {}, editor = {} } = pf
generalForm.language = general['language']
generalForm.font = general['font']
generalForm.fontSize = general['font_size'] || 14
generalForm.useSystemProxy = general['use_system_proxy'] === true
generalForm.useSystemProxyHttp = general['use_system_proxy_http'] === true
generalForm.checkUpdate = general['check_update'] === true
editorForm.font = editor['font']
editorForm.fontSize = editor['font_size'] || 14
}
watch( watch(
() => dialogStore.preferencesDialogVisible, () => dialogStore.preferencesDialogVisible,
(visible) => { (visible) => {
if (visible) { if (visible) {
GetPreferences() prefStore.loadPreferences()
.then((pf) => {
// load preferences from local
applyPreferences(pf)
prevPreferences.value = pf
})
.catch((e) => {
console.log(e)
})
} }
} }
) )
const onSavePreferences = async () => { const onSavePreferences = async () => {
const pf = { const success = await prefStore.savePreferences()
'general.language': generalForm.language, if (success) {
'general.font': generalForm.font, message.success(i18n.t('handle_succ'))
'general.font_size': generalForm.fontSize,
'general.use_system_proxy': generalForm.useSystemProxy,
'general.use_system_proxy_http': generalForm.useSystemProxyHttp,
'general.check_update': generalForm.checkUpdate,
'editor.font': editorForm.font,
'editor.font_size': editorForm.fontSize,
}
await SetPreferencesN(pf)
dialogStore.closePreferencesDialog() dialogStore.closePreferencesDialog()
} }
}
// Watch language and dynamically switch // Watch language and dynamically switch
watch( watch(
() => generalForm.language, () => prefStore.general.language,
(lang) => (i18n.locale.value = lang) (lang) => (i18n.locale.value = lang)
) )
watch( watch(
() => generalForm.font, () => prefStore.general.font,
(font) => {} (font) => {}
) )
const onRestoreDefaults = async () => {
const pf = await RestoreDefault()
applyPreferences(pf)
}
const onClose = () => { const onClose = () => {
dialogStore.closePreferencesDialog() dialogStore.closePreferencesDialog()
// restore to old preferences // restore to old preferences
applyPreferences(prevPreferences.value) prefStore.restorePreferences()
} }
</script> </script>
@ -120,33 +71,37 @@ const onClose = () => {
<n-tab-pane :tab="$t('general')" display-directive="show" name="general"> <n-tab-pane :tab="$t('general')" display-directive="show" name="general">
<n-form <n-form
:label-width="formLabelWidth" :label-width="formLabelWidth"
:model="generalForm" :model="prefStore.general"
:show-require-mark="false" :show-require-mark="false"
label-align="right" label-align="right"
label-placement="left" label-placement="left"
> >
<n-form-item :label="$t('language')" required> <n-form-item :label="$t('language')" required>
<n-select v-model:value="generalForm.language" :options="langOption" filterable /> <n-select
v-model:value="prefStore.general.language"
:options="prefStore.langOption"
filterable
/>
</n-form-item> </n-form-item>
<n-form-item :label="$t('font')" required> <n-form-item :label="$t('font')" required>
<n-select v-model:value="generalForm.font" :options="fontOption" filterable /> <n-select v-model:value="prefStore.general.font" :options="fontOption" filterable />
</n-form-item> </n-form-item>
<n-form-item :label="$t('font_size')"> <n-form-item :label="$t('font_size')">
<n-input-number v-model:value="generalForm.fontSize" :max="65535" :min="1" /> <n-input-number v-model:value="prefStore.general.fontSize" :max="65535" :min="1" />
</n-form-item> </n-form-item>
<n-form-item :label="$t('proxy')"> <n-form-item :label="$t('proxy')">
<n-space> <n-space>
<n-checkbox v-model:checked="generalForm.useSystemProxy"> <n-checkbox v-model:checked="prefStore.general.useSysProxy">
{{ $t('use_system_proxy') }} {{ $t('use_system_proxy') }}
</n-checkbox> </n-checkbox>
<n-checkbox v-model:checked="generalForm.useSystemProxyHttp"> <n-checkbox v-model:checked="prefStore.general.useSysProxyHttp">
{{ $t('use_system_proxy_http') }} {{ $t('use_system_proxy_http') }}
</n-checkbox> </n-checkbox>
</n-space> </n-space>
</n-form-item> </n-form-item>
<n-form-item :label="$t('update')"> <n-form-item :label="$t('update')">
<n-checkbox v-model:checked="generalForm.checkUpdate" <n-checkbox v-model:checked="prefStore.general.checkUpdate">
>{{ $t('auto_check_update') }} {{ $t('auto_check_update') }}
</n-checkbox> </n-checkbox>
</n-form-item> </n-form-item>
</n-form> </n-form>
@ -155,16 +110,16 @@ const onClose = () => {
<n-tab-pane :tab="$t('editor')" display-directive="show" name="editor"> <n-tab-pane :tab="$t('editor')" display-directive="show" name="editor">
<n-form <n-form
:label-width="formLabelWidth" :label-width="formLabelWidth"
:model="editorForm" :model="prefStore.editor"
:show-require-mark="false" :show-require-mark="false"
label-align="right" label-align="right"
label-placement="left" label-placement="left"
> >
<n-form-item :label="$t('font')" :label-width="formLabelWidth" required> <n-form-item :label="$t('font')" :label-width="formLabelWidth" required>
<n-select v-model="editorForm.font" :options="fontOption" filterable /> <n-select v-model="prefStore.editor.font" :options="fontOption" filterable />
</n-form-item> </n-form-item>
<n-form-item :label="$t('font_size')" :label-width="formLabelWidth"> <n-form-item :label="$t('font_size')" :label-width="formLabelWidth">
<n-input-number v-model="editorForm.fontSize" :max="65535" :min="1" /> <n-input-number v-model="prefStore.editor.fontSize" :max="65535" :min="1" />
</n-form-item> </n-form-item>
</n-form> </n-form>
</n-tab-pane> </n-tab-pane>
@ -172,7 +127,7 @@ const onClose = () => {
<template #action> <template #action>
<div class="flex-item-expand"> <div class="flex-item-expand">
<n-button @click="onRestoreDefaults">{{ $t('restore_defaults') }}</n-button> <n-button @click="prefStore.restorePreferences">{{ $t('restore_defaults') }}</n-button>
</div> </div>
<div class="flex-item n-dialog__action"> <div class="flex-item n-dialog__action">
<n-button @click="onClose">{{ $t('cancel') }}</n-button> <n-button @click="onClose">{{ $t('cancel') }}</n-button>

View File

@ -0,0 +1,93 @@
import { defineStore } from 'pinia'
import { lang } from '../langs/index.js'
import { camelCase, isObject, map, set, snakeCase, split } from 'lodash'
import { GetPreferences, RestorePreferences, SetPreferences } from '../../wailsjs/go/services/preferencesService.js'
const usePreferencesStore = defineStore('preferences', {
state: () => ({
general: {
language: 'en',
font: '',
fontSize: 14,
useSysProxy: false,
useSysProxyHttp: false,
checkUpdate: false,
},
editor: {
font: '',
fontSize: 14,
},
}),
getters: {
getSeparator() {
return ':'
},
/**
* all available language
* @returns {{label: string, value: string}[]}
*/
langOption() {
return Object.entries(lang).map(([key, value]) => ({
value: key,
label: `${value['lang_name']}`,
}))
},
},
actions: {
_applyPreferences(data) {
for (const key in data) {
const keys = map(split(key, '.'), camelCase)
set(this, keys, data[key])
}
},
/**
* load preferences from local
* @returns {Promise<void>}
*/
async loadPreferences() {
const { success, data } = await GetPreferences()
if (success) {
this._applyPreferences(data)
}
},
/**
* save preferences to local
* @returns {Promise<boolean>}
*/
async savePreferences() {
const obj2Map = (prefix, obj) => {
const result = {}
for (const key in obj) {
if (isObject(obj[key])) {
// TODO: perform sub object
} else {
result[`${prefix}.${snakeCase(key)}`] = obj[key]
}
}
return result
}
const pf = Object.assign({}, obj2Map('general', this.general), obj2Map('editor', this.editor))
const { success, msg } = await SetPreferences(pf)
return success === true
},
/**
* restore preferences to default
* @returns {Promise<boolean>}
*/
async restorePreferences() {
const { success, data } = await RestorePreferences()
if (success === true) {
const { pref } = data
this._applyPreferences(pref)
return true
}
return false
},
},
})
export default usePreferencesStore

12
main.go
View File

@ -3,13 +3,11 @@ package main
import ( import (
"context" "context"
"embed" "embed"
"github.com/wailsapp/wails/v2/pkg/options/mac"
"tinyrdm/backend/services"
"tinyrdm/backend/storage"
"github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver" "github.com/wailsapp/wails/v2/pkg/options/assetserver"
"github.com/wailsapp/wails/v2/pkg/options/mac"
"tinyrdm/backend/services"
) )
//go:embed all:frontend/dist //go:embed all:frontend/dist
@ -18,9 +16,8 @@ var assets embed.FS
func main() { func main() {
// Create an instance of the app structure // Create an instance of the app structure
app := NewApp() app := NewApp()
preferences := storage.NewPreferences()
//connections := storage.NewConnections()
connSvc := services.Connection() connSvc := services.Connection()
prefSvc := services.Preferences()
// Create application with options // Create application with options
err := wails.Run(&options.App{ err := wails.Run(&options.App{
@ -42,9 +39,8 @@ func main() {
}, },
Bind: []interface{}{ Bind: []interface{}{
app, app,
preferences,
//connections,
connSvc, connSvc,
prefSvc,
}, },
Mac: &mac.Options{ Mac: &mac.Options{
TitleBar: &mac.TitleBar{ TitleBar: &mac.TitleBar{