From 4a3e268174787601051e05557df8888088318bff Mon Sep 17 00:00:00 2001
From: rai <96561881+r4ai@users.noreply.github.com>
Date: Sun, 2 Apr 2023 18:58:15 +0900
Subject: [PATCH] Feature: export import settings on desktop app (#15)
* refactor: move updateStores function from main to settings
* feature: export import config
* chore: add technology stack on README.md
* refactor
---
.vscode/settings.json | 1 +
README.md | 29 +++-
src-tauri/Cargo.lock | 170 +++++++++++++++++++++
src-tauri/Cargo.toml | 2 +-
src-tauri/tauri.conf.json | 12 +-
src/App.svelte | 33 +---
src/lib/nav/Navbar.svelte | 4 +
src/lib/nav/Toast.svelte | 98 ++++++++++++
src/lib/nav/menu/ExportConfigButton.svelte | 46 ++++++
src/lib/nav/menu/ImportConfigButton.svelte | 61 ++++++++
src/lib/settings.ts | 62 ++++++--
11 files changed, 475 insertions(+), 43 deletions(-)
create mode 100644 src/lib/nav/Toast.svelte
create mode 100644 src/lib/nav/menu/ExportConfigButton.svelte
create mode 100644 src/lib/nav/menu/ImportConfigButton.svelte
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b5a69bb..856595b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -38,6 +38,7 @@
"APPCONFIG",
"fatsine",
"headphonejames",
+ "Heroicons",
"imgs",
"Neuromodulation",
"testid",
diff --git a/README.md b/README.md
index b2d8434..6b5a689 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ This app is available as:
- Desktop app:
- Windows
- - MacOS
+ - macOS
- Linux
- Web app: https://r4ai.github.io/tinnitus-reducer-acrn/
@@ -49,7 +49,32 @@ This app is heavily inspired by following softwares:
I do not have a Linux machine and therefore cannot test the Linux version of the app. If you find any bugs, please open an issue and let me know. Even on other operating systems, if you find any problems, please let me know.
-If you have any ideas you would like to see implemented in this app, feel free to open a new issue and let me know.
+If you have any ideas you would like to see implemented in this app, feel free to open a new issue and let me know. **Any kinds of contributions are welcome!!**
+
+### Commands
+
+```bash
+# Install dependencies
+$ pnpm i
+
+# Run the dev app
+$ pnpm tauri dev
+
+# Build the app
+$ pnpm tauri build
+```
+
+### Technology stack
+
+- Framework: [Tauri](https://tauri.studio/)
+- Frontend:
+ - Language: TypeScript
+ - Framework: Svelte
+ - Audio: [tone.js](https://tonejs.github.io/)
+ - UI: TailwindCSS, DaisyUI, svelte-range-slider-pips
+ - Icons: Fluent Emoji, Heroicons
+- Backend:
+ - Language: Rust
## License
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 7e55ba2..ff55bdb 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -132,6 +132,12 @@ dependencies = [
"serde",
]
+[[package]]
+name = "bumpalo"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
+
[[package]]
name = "bytemuck"
version = "1.13.1"
@@ -1198,6 +1204,15 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+[[package]]
+name = "js-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+dependencies = [
+ "wasm-bindgen",
+]
+
[[package]]
name = "json-patch"
version = "0.2.7"
@@ -1471,6 +1486,17 @@ dependencies = [
"objc_exception",
]
+[[package]]
+name = "objc-foundation"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
+dependencies = [
+ "block",
+ "objc",
+ "objc_id",
+]
+
[[package]]
name = "objc_exception"
version = "0.1.2"
@@ -1942,6 +1968,30 @@ version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+[[package]]
+name = "rfd"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea"
+dependencies = [
+ "block",
+ "dispatch",
+ "glib-sys",
+ "gobject-sys",
+ "gtk-sys",
+ "js-sys",
+ "lazy_static",
+ "log",
+ "objc",
+ "objc-foundation",
+ "objc_id",
+ "raw-window-handle",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "windows 0.37.0",
+]
+
[[package]]
name = "rustc_version"
version = "0.4.0"
@@ -2399,6 +2449,7 @@ dependencies = [
"rand 0.8.5",
"raw-window-handle",
"regex",
+ "rfd",
"semver",
"serde",
"serde_json",
@@ -2904,6 +2955,82 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
+
+[[package]]
+name = "web-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "webkit2gtk"
version = "0.18.2"
@@ -3032,6 +3159,19 @@ dependencies = [
"windows-sys 0.42.0",
]
+[[package]]
+name = "windows"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
+dependencies = [
+ "windows_aarch64_msvc 0.37.0",
+ "windows_i686_gnu 0.37.0",
+ "windows_i686_msvc 0.37.0",
+ "windows_x86_64_gnu 0.37.0",
+ "windows_x86_64_msvc 0.37.0",
+]
+
[[package]]
name = "windows"
version = "0.39.0"
@@ -3132,6 +3272,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.39.0"
@@ -3144,6 +3290,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+[[package]]
+name = "windows_i686_gnu"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
+
[[package]]
name = "windows_i686_gnu"
version = "0.39.0"
@@ -3156,6 +3308,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
+
[[package]]
name = "windows_i686_msvc"
version = "0.39.0"
@@ -3168,6 +3326,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
+
[[package]]
name = "windows_x86_64_gnu"
version = "0.39.0"
@@ -3186,6 +3350,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.39.0"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 9cd63c7..65df037 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -13,7 +13,7 @@ edition = "2021"
tauri-build = { version = "1.2", features = [] }
[dependencies]
-tauri = { version = "1.2", features = ["fs-create-dir", "fs-read-dir", "fs-read-file", "fs-write-file", "os-all", "path-all", "shell-open", "window-close", "window-hide", "window-maximize", "window-minimize", "window-show", "window-start-dragging", "window-unmaximize", "window-unminimize"] }
+tauri = { version = "1.2", features = ["dialog-open", "dialog-save", "fs-create-dir", "fs-read-dir", "fs-read-file", "fs-write-file", "os-all", "path-all", "shell-open", "window-close", "window-hide", "window-maximize", "window-minimize", "window-show", "window-start-dragging", "window-unmaximize", "window-unminimize"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.9.19"
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index c85df66..158d516 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -8,7 +8,7 @@
},
"package": {
"productName": "tinnitus-reducer-ACRN",
- "version": "0.2.0"
+ "version": "0.2.1"
},
"tauri": {
"allowlist": {
@@ -23,7 +23,7 @@
"readDir": true,
"readFile": true,
"writeFile": true,
- "scope": ["$APPCONFIG", "$APPCONFIG/*"]
+ "scope": ["**"]
},
"path": {
"all": true
@@ -41,6 +41,14 @@
},
"os": {
"all": true
+ },
+ "dialog": {
+ "all": false,
+ "ask": false,
+ "confirm": false,
+ "message": false,
+ "open": true,
+ "save": true
}
},
"bundle": {
diff --git a/src/App.svelte b/src/App.svelte
index 5edb2d9..2c13e7b 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -3,19 +3,7 @@
import { onDestroy, onMount } from "svelte";
import Oscillator from "./lib/Oscillator.svelte";
import VolumeController from "./lib/VolumeController.svelte";
- import {
- bpm,
- frequency,
- mode,
- subscribeStores,
- volume,
- theme,
- pan,
- clientWidth,
- loopRepeat,
- restLength,
- duration,
- } from "./lib/stores";
+ import { mode, subscribeStores, theme, clientWidth } from "./lib/stores";
import FrequencyController from "./lib/FrequencyController.svelte";
import PlayController from "./lib/PlayController.svelte";
import ModeController from "./lib/ModeController.svelte";
@@ -25,12 +13,13 @@
loadSettings,
loadSettingsFromLocalStorage,
subscribeLazySaveSettings,
+ updateStores,
} from "./lib/settings";
import type { Unsubscriber } from "svelte/store";
import Navbar from "./lib/nav/Navbar.svelte";
import ConfigPanel from "./lib/config_panel/ConfigPanel.svelte";
import { isTauri } from "./lib/utils";
- import type { SettingsScheme } from "./lib/stores";
+ import Toast from "./lib/nav/Toast.svelte";
let unsubscribeStores: Unsubscriber[] | undefined = undefined;
let unsubscribeLazySave: Unsubscriber | undefined = undefined;
@@ -40,7 +29,7 @@
// * (Tauri App)
// * Load and initialize settings
const settings = await loadSettings();
- updateStates(settings);
+ updateStores(settings);
// * Subscribe lazy save settings
unsubscribeStores = subscribeStores();
@@ -52,7 +41,7 @@
// * (Browser)
// * Load settings from local storage and initialize
const settings = loadSettingsFromLocalStorage();
- updateStates(settings);
+ updateStores(settings);
console.info("Settings initialized from local storage.");
// * Subscribe lazy save settings
@@ -65,17 +54,6 @@
}
}
- function updateStates(settings: SettingsScheme) {
- $volume = [settings.volume];
- $frequency = [settings.frequency];
- $bpm = [settings.bpm];
- $pan = [settings.pan];
- $theme = settings.theme;
- $loopRepeat = [settings.loopRepeat];
- $restLength = [settings.restLength];
- $duration = [settings.duration];
- }
-
// * Setup Tone.js
onMount(() => {
Tone.start();
@@ -127,6 +105,7 @@
{/await}
+
diff --git a/src/lib/nav/menu/ExportConfigButton.svelte b/src/lib/nav/menu/ExportConfigButton.svelte
new file mode 100644
index 0000000..c65e27e
--- /dev/null
+++ b/src/lib/nav/menu/ExportConfigButton.svelte
@@ -0,0 +1,46 @@
+
+
+{#if isTauri()}
+
+{/if}
+
+
diff --git a/src/lib/nav/menu/ImportConfigButton.svelte b/src/lib/nav/menu/ImportConfigButton.svelte
new file mode 100644
index 0000000..314550e
--- /dev/null
+++ b/src/lib/nav/menu/ImportConfigButton.svelte
@@ -0,0 +1,61 @@
+
+
+{#if isTauri()}
+
+{/if}
+
+
diff --git a/src/lib/settings.ts b/src/lib/settings.ts
index 288ca9d..2f943f3 100644
--- a/src/lib/settings.ts
+++ b/src/lib/settings.ts
@@ -4,29 +4,49 @@ import {
SAVE_DELAY_TIME,
SETTINGS_FILE_NAME,
} from "@/lib/constants";
+import { appConfigDir, basename, dirname, join } from "@tauri-apps/api/path";
import { get, writable, type Writable } from "svelte/store";
import { getAll } from "tauri-settings";
import { STATUS } from "tauri-settings/dist/fs/ensure-settings-file";
import { saveSettings as saveAll } from "tauri-settings/dist/fs/load-save";
import { match } from "ts-pattern";
-import type { SettingsScheme } from "./stores";
+import {
+ bpm,
+ duration,
+ frequency,
+ loopRepeat,
+ pan,
+ restLength,
+ theme,
+ volume,
+ type SettingsScheme,
+} from "./stores";
import { isTauri } from "./utils";
export const timer = writable(0); // 0 ~ SAVE_DELAY_TIME
export const settingsCache: Writable> = writable({});
export const isSettingsChanged = writable(false);
+export async function getDefaultSettingsPath() {
+ return await join(await appConfigDir(), `${SETTINGS_FILE_NAME}.json`);
+}
+
// TODO: Add test
-export async function loadSettings(): Promise {
+export async function loadSettings(path = ""): Promise {
console.info("Start loading settings...");
// * Get settings
const {
status,
path: filePath,
settings: oldSettings,
- } = await getAll({
- fileName: SETTINGS_FILE_NAME,
- });
+ } = path === ""
+ ? await getAll({
+ fileName: SETTINGS_FILE_NAME,
+ })
+ : await getAll({
+ fileName: `${await basename(path)}.json`,
+ dir: await dirname(path),
+ });
const newSettings: SettingsScheme = match(status)
.with(STATUS.FILE_CREATED, () => ({ ...DEFAULT_SETTINGS }))
.with(STATUS.FILE_EXISTS, () => ({
@@ -36,24 +56,33 @@ export async function loadSettings(): Promise {
.exhaustive();
// * Apply settings
- await saveAll(oldSettings, filePath, {
+ await saveAll(newSettings, filePath, {
fileName: SETTINGS_FILE_NAME,
});
- console.info("Settings loaded", oldSettings);
+ console.info("Settings loaded", newSettings);
return newSettings;
}
// TODO: Add test
-export async function saveSettings(newSettings: Partial) {
+export async function saveSettings(
+ newSettings: Partial,
+ path = ""
+) {
const { settings: oldSettings, path: filePath } =
await getAll({
fileName: SETTINGS_FILE_NAME,
});
const settings = { ...oldSettings, ...newSettings };
- await saveAll(settings, filePath, {
- fileName: SETTINGS_FILE_NAME,
- });
+ if (path === "") {
+ await saveAll(settings, filePath, {
+ fileName: SETTINGS_FILE_NAME,
+ });
+ } else {
+ await saveAll(settings, path, {
+ fileName: SETTINGS_FILE_NAME,
+ });
+ }
console.info(`Settings saved to ${filePath}`, settings);
return settings;
}
@@ -144,3 +173,14 @@ export function subscribeLazySaveSettings(
});
return unsubscribe;
}
+
+export function updateStores(settings: SettingsScheme) {
+ volume.set([settings.volume]);
+ frequency.set([settings.frequency]);
+ bpm.set([settings.bpm]);
+ pan.set([settings.pan]);
+ theme.set(settings.theme);
+ loopRepeat.set([settings.loopRepeat]);
+ restLength.set([settings.restLength]);
+ duration.set([settings.duration]);
+}