Fully implement settings

This commit is contained in:
2024-08-09 22:19:50 +02:00
parent 7b6108d0dd
commit 8119775084
16 changed files with 235 additions and 22 deletions

8
app.go
View File

@@ -55,6 +55,10 @@ func (a *App) GetLastPer100(name string) WailsPer100 {
func (a *App) GetSettings() settings {
return Settings
}
func (a *App) SetSetting(key string, value any) settings {
return settingsService.Set(key, value)
func (a *App) SetSetting(key string, value int64) WailsGenericAck {
_, err := settingsService.Set(key, value)
if err != nil {
return WailsGenericAck{Success: false, Error: err.Error()}
}
return WailsGenericAck{Success: true}
}

View File

@@ -21,7 +21,11 @@
"tailwindcss": "^3.4.3",
"tslib": "^2.4.0",
"typescript": "^4.6.4",
"vite": "^3.0.7"
"vite": "^3.0.7",
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2",
"@fortawesome/free-regular-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2"
},
"dependencies": {
"autoprefixer": "^10.4.20",

View File

@@ -1 +1 @@
0539f67989f30cc4f97afd5c6035bcad
bd9b801d4541f25052f0d4cb72b7d95a

View File

@@ -18,6 +18,18 @@ importers:
specifier: ^3.1.5
version: 3.1.5(chart.js@4.4.3)(svelte@3.59.2)
devDependencies:
'@fortawesome/fontawesome-svg-core':
specifier: ^6.5.2
version: 6.6.0
'@fortawesome/free-brands-svg-icons':
specifier: ^6.5.2
version: 6.6.0
'@fortawesome/free-regular-svg-icons':
specifier: ^6.5.2
version: 6.6.0
'@fortawesome/free-solid-svg-icons':
specifier: ^6.5.2
version: 6.6.0
'@sveltejs/vite-plugin-svelte':
specifier: ^1.0.1
version: 1.4.0(svelte@3.59.2)(vite@3.2.10(sass@1.77.8))
@@ -73,6 +85,26 @@ packages:
cpu: [loong64]
os: [linux]
'@fortawesome/fontawesome-common-types@6.6.0':
resolution: {integrity: sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==}
engines: {node: '>=6'}
'@fortawesome/fontawesome-svg-core@6.6.0':
resolution: {integrity: sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==}
engines: {node: '>=6'}
'@fortawesome/free-brands-svg-icons@6.6.0':
resolution: {integrity: sha512-1MPD8lMNW/earme4OQi1IFHtmHUwAKgghXlNwWi9GO7QkTfD+IIaYpIai4m2YJEzqfEji3jFHX1DZI5pbY/biQ==}
engines: {node: '>=6'}
'@fortawesome/free-regular-svg-icons@6.6.0':
resolution: {integrity: sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==}
engines: {node: '>=6'}
'@fortawesome/free-solid-svg-icons@6.6.0':
resolution: {integrity: sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==}
engines: {node: '>=6'}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
@@ -947,6 +979,24 @@ snapshots:
'@esbuild/linux-loong64@0.15.18':
optional: true
'@fortawesome/fontawesome-common-types@6.6.0': {}
'@fortawesome/fontawesome-svg-core@6.6.0':
dependencies:
'@fortawesome/fontawesome-common-types': 6.6.0
'@fortawesome/free-brands-svg-icons@6.6.0':
dependencies:
'@fortawesome/fontawesome-common-types': 6.6.0
'@fortawesome/free-regular-svg-icons@6.6.0':
dependencies:
'@fortawesome/fontawesome-common-types': 6.6.0
'@fortawesome/free-solid-svg-icons@6.6.0':
dependencies:
'@fortawesome/fontawesome-common-types': 6.6.0
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2

View File

@@ -70,6 +70,7 @@
scope="row"
>
</th>
<!-- svelte-ignore a11y-autofocus -->
<td
bind:innerText={name}
class:border-[3px]={!name}

View File

@@ -58,7 +58,7 @@
</script>
<template>
<div class="relative flex flex-col flex-grow h-[95vh]" data-vaul-drawer-wrapper id="page">
<div class="relative flex flex-col flex-grow h-[93vh] select-none" data-vaul-drawer-wrapper id="page">
<div class="relative overflow-auto h-full shadow-md sm:rounded-lg">
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
<thead class="text-xs text-gray-700 uppercase dark:text-gray-400">

View File

@@ -1,5 +1,9 @@
<script lang="ts">
import { link, location } from "svelte-spa-router";
import { faGear } from "@fortawesome/free-solid-svg-icons";
import Fa from "svelte-fa";
import Settings from "./Settings/Settings.svelte";
Fa;
type Link = {
label: string;
@@ -38,6 +42,8 @@
};
});
}
let showModal = true;
</script>
<header
@@ -75,4 +81,12 @@
{/each}
</nav>
</div>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="absolute right-0 pt-4 pb-4 pr-8 pl-8 cursor-pointer" on:click={() => (showModal = true)}>
<button>
<Fa icon={faGear} scale={2} />
</button>
</div>
</header>
<Settings bind:showModal />

View File

@@ -0,0 +1,50 @@
<script lang="ts">
export let showModal: boolean;
let dialog: HTMLDialogElement;
$: if (dialog && showModal) dialog.showModal();
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<dialog
bind:this={dialog}
class="min-w-[40vw] rounded-lg border-0 p-0 bg-gray-800 text-white"
on:close={() => (showModal = false)}
on:click|self={() => dialog.close()}
>
<div class="p-6" on:click|stopPropagation>
<slot name="header" />
<hr class="my-2 border-gray-600" />
<slot />
<hr class="my-2 border-gray-600" />
</div>
</dialog>
<style>
dialog::backdrop {
background: rgba(0, 0, 0, 0.7);
}
dialog[open] {
animation: zoom 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes zoom {
from {
transform: scale(0.95);
}
to {
transform: scale(1);
}
}
dialog[open]::backdrop {
animation: fade 0.2s ease-out;
}
@keyframes fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>

View File

@@ -0,0 +1,43 @@
<script lang="ts">
import { settingsStore } from "$lib/store/SettingsStore";
import { SetSetting } from "$wails/main/App";
import { toast } from "svelte-sonner";
export let key: string;
export let setting: number;
let editSetting = String(setting);
async function updateSetting() {
const numSetting = parseInt(editSetting);
if (isNaN(numSetting)) {
console.log("Invalid setting, must be number");
editSetting = String(setting);
return;
}
const res = await SetSetting(key, numSetting);
if (!res.success) {
toast.error(`Failed to set setting with error ${res.error}`);
editSetting = String(setting);
return
}
setting = numSetting;
settingsStore.update((store) => {
// @ts-ignore
store[key] = numSetting;
return store;
});
toast.success(`Successfully set setting ${key} to ${numSetting}`);
}
</script>
<template>
<div class="flex items-center">
<span class="w-[30vw] capitalize">{key}</span>
<span
contenteditable="true"
on:focusout={updateSetting}
class="w-[10vw] ml-2 p-1 border rounded bg-slate-900"
bind:innerText={editSetting}
></span>
</div>
</template>

View File

@@ -0,0 +1,17 @@
<script lang="ts">
import { settingsStore } from "$lib/store/SettingsStore";
import Modal from "../Modal.svelte";
import Setting from "./Setting.svelte";
export let showModal: boolean = false;
</script>
<Modal bind:showModal>
<h2 slot="header" class="select-none text-2xl font-bold">Settings</h2>
<div class="flex flex-2 flex-col gap-4 text-left text-xl">
{#each Object.keys($settingsStore) as key}
<Setting key={key} setting={$settingsStore[key]} />
{/each}
</div>
</Modal>

View File

@@ -2,6 +2,7 @@ import { type Writable, writable } from "svelte/store";
import { main } from "$wails/models";
import { GetFood } from "$wails/main/App";
import { toast } from "svelte-sonner";
import { settingsStore } from "./SettingsStore";
async function createStore(): Promise<Writable<main.Food[]>> {
let foods: main.Food[] = [];
@@ -29,4 +30,10 @@ async function createStore(): Promise<Writable<main.Food[]>> {
};
}
export const foodStore = await createStore();
const foodStore = await createStore();
settingsStore.subscribe((settings) => {
// @ts-ignore
foodStore.refresh();
});
export {foodStore}

View File

@@ -10,6 +10,6 @@ export function GetLastPer100(arg1:string):Promise<main.WailsPer100>;
export function GetSettings():Promise<main.settings>;
export function SetSetting(arg1:string,arg2:any):Promise<main.settings>;
export function SetSetting(arg1:string,arg2:number):Promise<main.WailsGenericAck>;
export function UpdateFood(arg1:main.Food):Promise<main.WailsFood1>;

View File

@@ -92,6 +92,20 @@ export namespace main {
return a;
}
}
export class WailsGenericAck {
success: boolean;
error?: string;
static createFrom(source: any = {}) {
return new WailsGenericAck(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.success = source["success"];
this.error = source["error"];
}
}
export class WailsPer100 {
data: number;
success: boolean;

View File

@@ -51,7 +51,7 @@ func main() {
os.Exit(1)
}
settingsService := SettingsService{db: &db}
settingsService = &SettingsService{db: &db}
err = settingsService.LoadSettings()
if err != nil {
Error.Printf("%++v", err)

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"log"
"reflect"
"strings"
)
type (
@@ -60,24 +61,27 @@ func (s *SettingsService) LoadSettings() error {
}
return nil
}
func (s *SettingsService) Set(key string, value any) settings {
func (s *SettingsService) Set(key string, value int64) (settings, error) {
if s == nil || s.db == nil || !s.db.Ready {
return Settings, fmt.Errorf("cannot set setting, db is nil or is not ready")
}
key = strings.ToLower(key)
fields := reflect.ValueOf(&Settings).Elem()
for i := 0; i < fields.NumField(); i++ {
field := fields.Type().Field(i)
if field.Name == key {
fields.Field(i).SetInt(value.(int64))
fieldName := strings.ToLower(field.Name)
if fieldName == key {
fields.Field(i).SetInt(value)
// This could cause desync between client and server
// But we can just refresh, I think it's fine
go func() {
_, err := s.db.writeConn.Exec("UPDATE settings SET value = ? WHERE key = ?", value, key)
if err != nil {
Error.Printf("error updating setting for key %s: %v", key)
Error.Printf("error updating setting for key %s: %v", key, err)
return Settings, err
}
}()
return Settings
return Settings, nil
}
}
return Settings
return Settings, fmt.Errorf("key %s not found", key)
}

View File

@@ -31,6 +31,11 @@ type (
Error string `json:"error,omitempty"`
}
WailsGenericAck struct {
Success bool `json:"success"`
Error string `json:"error,omitempty"`
}
Weight struct {
rowid int
date time.Time