refactor: use swr
parent
32079a50e7
commit
bd569c1c80
|
|
@ -0,0 +1,12 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['.eslintrc.cjs'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
}
|
||||||
|
|
@ -12,28 +12,32 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^1.5.3",
|
"@tauri-apps/api": "^1.5.3",
|
||||||
"classnames": "^2.5.1",
|
"clsx": "^2.1.0",
|
||||||
"daisyui": "^4.6.1",
|
"daisyui": "^4.6.1",
|
||||||
"i18next": "^23.8.1",
|
"i18next": "^23.8.1",
|
||||||
"i18next-browser-languagedetector": "^7.2.0",
|
"i18next-browser-languagedetector": "^7.2.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-error-boundary": "^4.0.12",
|
||||||
"react-i18next": "^14.0.1",
|
"react-i18next": "^14.0.1",
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"react-use": "^17.5.0"
|
"react-use": "^17.5.0",
|
||||||
|
"swr": "^2.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.5.9",
|
"@tauri-apps/cli": "^1.5.9",
|
||||||
"@types/node": "^20.11.10",
|
"@types/node": "^20.11.10",
|
||||||
"@types/react": "^18.0.15",
|
"@types/react": "^18.0.15",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.20.0",
|
||||||
"@typescript-eslint/parser": "^6.20.0",
|
"@typescript-eslint/parser": "^6.20.0",
|
||||||
"@typescript-eslint/typescript-estree": "^6.20.0",
|
"@typescript-eslint/typescript-estree": "^6.20.0",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.5",
|
||||||
"i18next-http-backend": "^2.4.2",
|
"i18next-http-backend": "^2.4.2",
|
||||||
"postcss": "^8.4.33",
|
"postcss": "^8.4.33",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
|
|
@ -41,22 +45,5 @@
|
||||||
"vite": "^5.0.12",
|
"vite": "^5.0.12",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-tsconfig-paths": "^4.3.1"
|
"vite-tsconfig-paths": "^4.3.1"
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": "react-app"
|
|
||||||
},
|
|
||||||
"pnpm": {
|
|
||||||
"packageExtensions": {
|
|
||||||
"eslint-plugin-flowtype": {
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@babel/plugin-syntax-flow": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@babel/plugin-transform-react-jsx": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2706
gui/pnpm-lock.yaml
2706
gui/pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -459,12 +459,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.1.26"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
|
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -494,7 +494,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn 2.0.23",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -505,7 +505,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.23",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1128,9 +1128,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "html5ever"
|
name = "html5ever"
|
||||||
version = "0.25.2"
|
version = "0.26.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148"
|
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"mac",
|
"mac",
|
||||||
|
|
@ -1260,9 +1260,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "infer"
|
name = "infer"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a898e4b7951673fce96614ce5751d13c40fc5674bc2d759288e46c3ab62598b3"
|
checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfb",
|
"cfb",
|
||||||
]
|
]
|
||||||
|
|
@ -1352,9 +1352,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "json-patch"
|
name = "json-patch"
|
||||||
version = "1.0.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f54898088ccb91df1b492cc80029a6fdf1c48ca0db7c6822a8babad69c94658"
|
checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
@ -1363,13 +1363,14 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kuchiki"
|
name = "kuchikiki"
|
||||||
version = "0.8.1"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358"
|
checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cssparser",
|
"cssparser",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
|
"indexmap 1.9.2",
|
||||||
"matches",
|
"matches",
|
||||||
"selectors",
|
"selectors",
|
||||||
]
|
]
|
||||||
|
|
@ -1413,12 +1414,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.17"
|
version = "0.4.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "loom"
|
name = "loom"
|
||||||
|
|
@ -1452,13 +1450,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "markup5ever"
|
name = "markup5ever"
|
||||||
version = "0.10.1"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
|
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"phf 0.8.0",
|
"phf 0.10.1",
|
||||||
"phf_codegen",
|
"phf_codegen 0.10.0",
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"string_cache_codegen",
|
"string_cache_codegen",
|
||||||
"tendril",
|
"tendril",
|
||||||
|
|
@ -1779,9 +1777,17 @@ version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
|
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_macros 0.10.0",
|
|
||||||
"phf_shared 0.10.0",
|
"phf_shared 0.10.0",
|
||||||
"proc-macro-hack",
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
|
dependencies = [
|
||||||
|
"phf_macros 0.11.2",
|
||||||
|
"phf_shared 0.11.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1794,6 +1800,16 @@ dependencies = [
|
||||||
"phf_shared 0.8.0",
|
"phf_shared 0.8.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_codegen"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator 0.10.0",
|
||||||
|
"phf_shared 0.10.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_generator"
|
name = "phf_generator"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
|
@ -1814,6 +1830,16 @@ dependencies = [
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_generator"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared 0.11.2",
|
||||||
|
"rand 0.8.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_macros"
|
name = "phf_macros"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
|
@ -1830,16 +1856,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_macros"
|
name = "phf_macros"
|
||||||
version = "0.10.0"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
|
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_generator 0.10.0",
|
"phf_generator 0.11.2",
|
||||||
"phf_shared 0.10.0",
|
"phf_shared 0.11.2",
|
||||||
"proc-macro-hack",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1861,10 +1886,19 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "phf_shared"
|
||||||
version = "0.2.9"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
|
|
@ -1958,9 +1992,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.63"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
@ -1976,9 +2010,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.29"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
@ -2220,7 +2254,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"matches",
|
"matches",
|
||||||
"phf 0.8.0",
|
"phf 0.8.0",
|
||||||
"phf_codegen",
|
"phf_codegen 0.8.0",
|
||||||
"precomputed-hash",
|
"precomputed-hash",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
|
@ -2256,29 +2290,29 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.166"
|
version = "1.0.196"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8"
|
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.166"
|
version = "1.0.196"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6"
|
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.23",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.99"
|
version = "1.0.113"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
|
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 1.0.5",
|
"itoa 1.0.5",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
|
@ -2330,7 +2364,7 @@ dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.23",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2504,9 +2538,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.23"
|
version = "2.0.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
|
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -2610,9 +2644,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri"
|
name = "tauri"
|
||||||
version = "1.4.1"
|
version = "1.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e"
|
checksum = "fd27c04b9543776a972c86ccf70660b517ecabbeced9fb58d8b961a13ad129af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cocoa",
|
"cocoa",
|
||||||
|
|
@ -2659,12 +2693,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-build"
|
name = "tauri-build"
|
||||||
version = "1.4.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d2edd6a259b5591c8efdeb9d5702cb53515b82a6affebd55c7fd6d3a27b7d1b"
|
checksum = "e9914a4715e0b75d9f387a285c7e26b5bbfeb1249ad9f842675a82481565c532"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
|
"dirs-next",
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"json-patch",
|
"json-patch",
|
||||||
"semver 1.0.16",
|
"semver 1.0.16",
|
||||||
|
|
@ -2672,13 +2707,14 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"tauri-winres",
|
"tauri-winres",
|
||||||
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-codegen"
|
name = "tauri-codegen"
|
||||||
version = "1.4.0"
|
version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a"
|
checksum = "a1554c5857f65dbc377cefb6b97c8ac77b1cb2a90d30d3448114d5d6b48a77fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.0",
|
"base64 0.21.0",
|
||||||
"brotli",
|
"brotli",
|
||||||
|
|
@ -2702,9 +2738,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-macros"
|
name = "tauri-macros"
|
||||||
version = "1.4.0"
|
version = "1.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8eb12a2454e747896929338d93b0642144bb51e0dddbb36e579035731f0d76b7"
|
checksum = "277abf361a3a6993ec16bcbb179de0d6518009b851090a01adfea12ac89fa875"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|
@ -2716,9 +2752,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "0.14.0"
|
version = "0.14.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "108683199cb18f96d2d4134187bb789964143c845d2d154848dda209191fd769"
|
checksum = "cf2d0652aa2891ff3e9caa2401405257ea29ab8372cce01f186a5825f1bd0e76"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
|
|
@ -2737,9 +2773,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime-wry"
|
name = "tauri-runtime-wry"
|
||||||
version = "0.14.0"
|
version = "0.14.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b7aa256a1407a3a091b5d843eccc1a5042289baf0a43d1179d9f0fcfea37c1b"
|
checksum = "6cae61fbc731f690a4899681c9052dde6d05b159b44563ace8186fc1bfb7d158"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"gtk",
|
"gtk",
|
||||||
|
|
@ -2757,9 +2793,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "1.4.0"
|
version = "1.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03fc02bb6072bb397e1d473c6f76c953cda48b4a2d0cce605df284aa74a12e84"
|
checksum = "ece74810b1d3d44f29f732a7ae09a63183d63949bbdd59c61f8ed2a1b70150db"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brotli",
|
"brotli",
|
||||||
"ctor",
|
"ctor",
|
||||||
|
|
@ -2769,9 +2805,10 @@ dependencies = [
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"infer",
|
"infer",
|
||||||
"json-patch",
|
"json-patch",
|
||||||
"kuchiki",
|
"kuchikiki",
|
||||||
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"phf 0.10.1",
|
"phf 0.11.2",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"semver 1.0.16",
|
"semver 1.0.16",
|
||||||
|
|
@ -2781,7 +2818,7 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
"url",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"windows 0.39.0",
|
"windows-version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2841,7 +2878,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.23",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2898,11 +2935,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.29.1"
|
version = "1.35.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
|
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
|
@ -3177,7 +3213,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.23",
|
"syn 2.0.48",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -3199,7 +3235,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.23",
|
"syn 2.0.48",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
@ -3438,12 +3474,36 @@ dependencies = [
|
||||||
"windows_x86_64_msvc 0.48.0",
|
"windows_x86_64_msvc 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.52.0",
|
||||||
|
"windows_aarch64_msvc 0.52.0",
|
||||||
|
"windows_i686_gnu 0.52.0",
|
||||||
|
"windows_i686_msvc 0.52.0",
|
||||||
|
"windows_x86_64_gnu 0.52.0",
|
||||||
|
"windows_x86_64_gnullvm 0.52.0",
|
||||||
|
"windows_x86_64_msvc 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-tokens"
|
name = "windows-tokens"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597"
|
checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-version"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
|
|
@ -3456,6 +3516,12 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
|
@ -3474,6 +3540,12 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
|
@ -3492,6 +3564,12 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
|
@ -3510,6 +3588,12 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
|
@ -3528,6 +3612,12 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
|
|
@ -3540,6 +3630,12 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
|
@ -3558,6 +3654,12 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
|
@ -3579,9 +3681,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wry"
|
name = "wry"
|
||||||
version = "0.24.3"
|
version = "0.24.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "33748f35413c8a98d45f7a08832d848c0c5915501803d1faade5a4ebcd258cea"
|
checksum = "6ad85d0e067359e409fcb88903c3eac817c392e5d638258abfb3da5ad8ba6fc4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.1",
|
"base64 0.13.1",
|
||||||
"block",
|
"block",
|
||||||
|
|
@ -3595,7 +3697,7 @@ dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"http",
|
"http",
|
||||||
"kuchiki",
|
"kuchikiki",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"objc",
|
"objc",
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1.4.0", features = [] }
|
tauri-build = { version = "1.5.1", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "1.4.1", features = [
|
tauri = { version = "1.5.4", features = [
|
||||||
"fs-all",
|
"fs-all",
|
||||||
"path-all",
|
"path-all",
|
||||||
"process-relaunch",
|
"process-relaunch",
|
||||||
|
|
@ -22,9 +22,9 @@ tauri = { version = "1.4.1", features = [
|
||||||
"shell-sidecar",
|
"shell-sidecar",
|
||||||
"window-all",
|
"window-all",
|
||||||
] }
|
] }
|
||||||
serde = { version = "1.0.164", features = ["derive"] }
|
serde = { version = "1.0.196", features = ["derive"] }
|
||||||
serde_json = "1.0.97"
|
serde_json = "1.0.113"
|
||||||
tokio = { version = "1.28.2", features = ["time"] }
|
tokio = { version = "1.35.1", features = ["time"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,20 @@ import { Home } from "pages/Home";
|
||||||
import { Settings } from "pages/Settings";
|
import { Settings } from "pages/Settings";
|
||||||
import { Guide } from 'pages/Guide';
|
import { Guide } from 'pages/Guide';
|
||||||
import { useShowWindow } from 'hooks/useShowWindow';
|
import { useShowWindow } from 'hooks/useShowWindow';
|
||||||
|
import { AppContextProvider } from 'context/app';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
useShowWindow();
|
useShowWindow();
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<AppContextProvider>
|
||||||
<Route path='/' element={<Layout />}>
|
<Routes>
|
||||||
<Route index element={<Home />} />
|
<Route path='/' element={<Layout />}>
|
||||||
<Route path='/settings' element={<Settings />} />
|
<Route index element={<Home />} />
|
||||||
<Route path='/guide' element={<Guide />} />
|
<Route path='/settings' element={<Settings />} />
|
||||||
</Route>
|
<Route path='/guide' element={<Guide />} />
|
||||||
</Routes>
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
</AppContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AiOutlineWarning } from 'react-icons/ai'
|
import { AiOutlineWarning } from 'react-icons/ai'
|
||||||
|
import { FallbackProps } from 'react-error-boundary'
|
||||||
|
|
||||||
type ErrorContentProps = {
|
type ErrorContentProps = {
|
||||||
error: any
|
error: unknown
|
||||||
retry?: () => void
|
retry?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,3 +25,8 @@ export const ErrorContent: React.FC<ErrorContentProps> = ({ error, retry }) => {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const FallbackComponent: React.FC<FallbackProps> = ({ error, resetErrorBoundary }) => {
|
||||||
|
console.error('FallbackComponent', error)
|
||||||
|
return <ErrorContent error={error} retry={resetErrorBoundary} />
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke } from '@tauri-apps/api';
|
||||||
import classNames from 'classnames';
|
import clsx from 'clsx';
|
||||||
import { usePromise } from 'hooks/usePromise';
|
import { useService, useServiceMutation } from 'services/useService';
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { getConfig, getProfile, setProfile } from 'services/config';
|
|
||||||
import { ensureTokenValid } from 'services/s3si';
|
import { ensureTokenValid } from 'services/s3si';
|
||||||
import { composeLoadable } from 'utils/composeLoadable';
|
|
||||||
import { ErrorContent } from './ErrorContent';
|
import { ErrorContent } from './ErrorContent';
|
||||||
|
|
||||||
type OpenSplatnetProps = {
|
type OpenSplatnetProps = {
|
||||||
|
|
@ -12,30 +10,29 @@ type OpenSplatnetProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OpenSplatnet: React.FC<OpenSplatnetProps> = ({ children }) => {
|
export const OpenSplatnet: React.FC<OpenSplatnetProps> = ({ children }) => {
|
||||||
let { loading, error, retry, result } = composeLoadable({
|
const profileResult = useService('profile', 0)
|
||||||
config: usePromise(getConfig),
|
const { trigger: setProfile } = useServiceMutation('profile', 0)
|
||||||
profile: usePromise(() => getProfile(0)),
|
|
||||||
});
|
|
||||||
const [doing, setDoing] = useState(false);
|
const [doing, setDoing] = useState(false);
|
||||||
const [err, setError] = useState<any>();
|
const [err, setError] = useState<unknown>();
|
||||||
|
|
||||||
const onClick = async () => {
|
const onClick = async () => {
|
||||||
setDoing(true);
|
setDoing(true);
|
||||||
try {
|
try {
|
||||||
if (!result) {
|
if (!profileResult.data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const state = result.profile.state;
|
const state = profileResult.data.state;
|
||||||
const newState = await ensureTokenValid(state);
|
const newState = await ensureTokenValid(state);
|
||||||
await setProfile(0, {
|
await setProfile({
|
||||||
...result.profile,
|
...profileResult.data,
|
||||||
state: newState,
|
state: newState,
|
||||||
});
|
});
|
||||||
retry?.();
|
|
||||||
const gtoken = newState.loginState?.gToken;
|
const gtoken = newState.loginState?.gToken;
|
||||||
await invoke('open_splatnet', {
|
await invoke('open_splatnet', {
|
||||||
gtoken,
|
gtoken,
|
||||||
lang: result.profile.state.userLang,
|
lang: profileResult.data.state.userLang,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setError(e);
|
setError(e);
|
||||||
|
|
@ -45,18 +42,18 @@ export const OpenSplatnet: React.FC<OpenSplatnetProps> = ({ children }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if (error || err) {
|
if (err) {
|
||||||
return <>
|
return <>
|
||||||
<ErrorContent error={error || err} retry={retry} />
|
<ErrorContent error={err} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
const btnLoading = loading || doing;
|
const btnLoading = profileResult.isLoading || doing;
|
||||||
return <>
|
return <>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className={classNames('btn w-full', {
|
className={clsx('btn w-full', {
|
||||||
'btn-disabled': !result?.profile.state.loginState?.sessionToken,
|
'btn-disabled': !profileResult.data?.state?.loginState?.sessionToken,
|
||||||
})}
|
})}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={btnLoading}
|
disabled={btnLoading}
|
||||||
|
|
|
||||||
|
|
@ -1,72 +1,39 @@
|
||||||
import classNames from 'classnames';
|
import clsx from 'clsx';
|
||||||
import { usePromise } from 'hooks/usePromise';
|
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { canExport, getProfile, setProfile } from 'services/config';
|
import { useLog } from 'services/s3si';
|
||||||
import { addLog, run, useLog } from 'services/s3si';
|
|
||||||
import { Checkbox } from './Checkbox';
|
import { Checkbox } from './Checkbox';
|
||||||
import { Loading } from './Loading';
|
import { Loading } from './Loading';
|
||||||
|
import { useService } from 'services/useService';
|
||||||
|
import { useAppContext } from 'context/app'
|
||||||
|
|
||||||
type RunPanelProps = {
|
type RunPanelProps = Record<string, never>
|
||||||
}
|
|
||||||
|
|
||||||
export const RunPanel: React.FC<RunPanelProps> = () => {
|
export const RunPanel: React.FC<RunPanelProps> = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { result } = usePromise(() => getProfile(0));
|
const { data: result } = useService('profile', 0)
|
||||||
const [exportBattle, setExportBattle] = useState(true);
|
const [exportBattle, setExportBattle] = useState(true);
|
||||||
const [exportCoop, setExportCoop] = useState(true);
|
const [exportCoop, setExportCoop] = useState(true);
|
||||||
const [loading, setLoading] = useState(false);
|
const { exports } = useAppContext()
|
||||||
|
const disabled = !exports
|
||||||
|
const isExporting = exports?.isExporting ?? false
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return <Loading />
|
return <Loading />
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClick = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
addLog({
|
|
||||||
level: 'log',
|
|
||||||
msg: ['Export started at', new Date().toLocaleString()],
|
|
||||||
})
|
|
||||||
const { state } = result;
|
|
||||||
const newState = await run(state, {
|
|
||||||
exporter: "stat.ink",
|
|
||||||
monitor: false,
|
|
||||||
withSummary: false,
|
|
||||||
skipMode: exportBattle === false ? 'vs' : exportCoop === false ? 'coop' : undefined,
|
|
||||||
});
|
|
||||||
await setProfile(0, {
|
|
||||||
...result,
|
|
||||||
state: newState,
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
addLog({
|
|
||||||
level: 'error',
|
|
||||||
msg: [e],
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
addLog({
|
|
||||||
level: 'log',
|
|
||||||
msg: ['Export ended at', new Date().toLocaleString()],
|
|
||||||
})
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const disabled = !canExport(result);
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className="tooltip" data-tip={disabled ? t('请先在设置中完成Nintendo Account登录和stat.ink的API密钥') : undefined}>
|
<div className="tooltip" data-tip={disabled ? t('请先在设置中完成Nintendo Account登录和stat.ink的API密钥') : undefined}>
|
||||||
<Checkbox disabled={disabled || loading} value={exportBattle} onChange={setExportBattle}>{t('导出对战数据')}</Checkbox>
|
<Checkbox disabled={disabled || isExporting} value={exportBattle} onChange={setExportBattle}>{t('导出对战数据')}</Checkbox>
|
||||||
<Checkbox disabled={disabled || loading} value={exportCoop} onChange={setExportCoop}>{t('导出打工数据')}</Checkbox>
|
<Checkbox disabled={disabled || isExporting} value={exportCoop} onChange={setExportCoop}>{t('导出打工数据')}</Checkbox>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
onClick={onClick}
|
onClick={() => exports?.trigger({ exportBattle, exportCoop })}
|
||||||
className={classNames('btn btn-primary w-full', {
|
className={clsx('btn btn-primary w-full', {
|
||||||
'btn-disabled': disabled || (!exportBattle && !exportCoop),
|
'btn-disabled': disabled || (!exportBattle && !exportCoop),
|
||||||
})}
|
})}
|
||||||
disabled={loading}
|
disabled={isExporting}
|
||||||
>{loading ? <span className='loading' /> : t('导出')}</button>
|
>{isExporting ? <span className='loading' /> : t('导出')}</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { ReactNode, createContext, useContext } from 'react'
|
||||||
|
import { canExport } from 'services/config';
|
||||||
|
import { addLog, run } from 'services/s3si';
|
||||||
|
import { useService, useServiceMutation } from 'services/useService';
|
||||||
|
import useSWRMutation from 'swr/mutation';
|
||||||
|
|
||||||
|
export type ExportArgs = {
|
||||||
|
exportBattle: boolean,
|
||||||
|
exportCoop: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
const APP_CONTEXT = createContext<{
|
||||||
|
exports?: {
|
||||||
|
isExporting: boolean
|
||||||
|
trigger: (args: ExportArgs) => Promise<void>
|
||||||
|
}
|
||||||
|
}>({})
|
||||||
|
|
||||||
|
export const useAppContext = () => {
|
||||||
|
return useContext(APP_CONTEXT)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppContextProvider: React.FC<{ children?: ReactNode }> = ({ children }) => {
|
||||||
|
const { data: result } = useService('profile', 0)
|
||||||
|
const { trigger: setProfile } = useServiceMutation('profile', 0)
|
||||||
|
const { trigger: doExport, isMutating } = useSWRMutation<
|
||||||
|
unknown,
|
||||||
|
Error,
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
exportBattle: boolean,
|
||||||
|
exportCoop: boolean,
|
||||||
|
}
|
||||||
|
>('export', async (_, { arg: {
|
||||||
|
exportBattle, exportCoop,
|
||||||
|
} }) => {
|
||||||
|
try {
|
||||||
|
if (!result) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addLog({
|
||||||
|
level: 'log',
|
||||||
|
msg: ['Export started at', new Date().toLocaleString()],
|
||||||
|
})
|
||||||
|
const { state } = result;
|
||||||
|
const newState = await run(state, {
|
||||||
|
exporter: "stat.ink",
|
||||||
|
monitor: false,
|
||||||
|
withSummary: false,
|
||||||
|
skipMode: exportBattle === false ? 'vs' : exportCoop === false ? 'coop' : undefined,
|
||||||
|
});
|
||||||
|
await setProfile({
|
||||||
|
...result,
|
||||||
|
state: newState,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
addLog({
|
||||||
|
level: 'error',
|
||||||
|
msg: [e],
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
addLog({
|
||||||
|
level: 'log',
|
||||||
|
msg: ['Export ended at', new Date().toLocaleString()],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
return <APP_CONTEXT.Provider value={{
|
||||||
|
exports: result && canExport(result) ? {
|
||||||
|
isExporting: isMutating,
|
||||||
|
trigger: async (args: ExportArgs) => {
|
||||||
|
await doExport(args)
|
||||||
|
},
|
||||||
|
} : undefined,
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</APP_CONTEXT.Provider>
|
||||||
|
}
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A hook that returns a promise and its state.
|
|
||||||
*
|
|
||||||
* @param factory A function that returns a promise.
|
|
||||||
* @returns An object containing the promise's state and result.
|
|
||||||
* @example
|
|
||||||
* const { loading, result, error } = usePromise(() => fetch('https://example.com')
|
|
||||||
* .then(response => response.text())
|
|
||||||
* );
|
|
||||||
* if (loading) {
|
|
||||||
* return <p>Loading...</p>;
|
|
||||||
* }
|
|
||||||
* if (error) {
|
|
||||||
* return <p>Error: {error.message}</p>;
|
|
||||||
* }
|
|
||||||
* return <p>Result: {result}</p>;
|
|
||||||
*/
|
|
||||||
export function usePromise<T>(factory: () => Promise<T>) {
|
|
||||||
const init = () => {
|
|
||||||
const promise = factory();
|
|
||||||
if (!promise || typeof promise.then !== "function") {
|
|
||||||
throw new Error("The factory function must return a promise.");
|
|
||||||
}
|
|
||||||
return promise
|
|
||||||
.then(r => {
|
|
||||||
setResult(r);
|
|
||||||
setLoading(false);
|
|
||||||
return r;
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
setError(e);
|
|
||||||
setLoading(false);
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [result, setResult] = useState<T | undefined>(undefined);
|
|
||||||
const [error, setError] = useState<any | undefined>(undefined);
|
|
||||||
const [promise, setPromise] = useState(init);
|
|
||||||
const retry = () => {
|
|
||||||
setLoading(true);
|
|
||||||
setResult(undefined);
|
|
||||||
setError(undefined);
|
|
||||||
setPromise(init);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { loading, result, error, promise, retry };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A hook that returns a promise and its state.
|
|
||||||
*/
|
|
||||||
export function usePromiseLazy<T, Args extends any[]>(factory: (...args: Args) => Promise<T>) {
|
|
||||||
const init = (promise: Promise<T>) => {
|
|
||||||
if (!promise || typeof promise.then !== "function") {
|
|
||||||
throw new Error("The factory function must return a promise.");
|
|
||||||
}
|
|
||||||
return promise
|
|
||||||
.then(r => {
|
|
||||||
setResult(r);
|
|
||||||
setLoading(false);
|
|
||||||
return r;
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
setError(e);
|
|
||||||
setLoading(false);
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [result, setResult] = useState<T | undefined>(undefined);
|
|
||||||
const [error, setError] = useState<any | undefined>(undefined);
|
|
||||||
const [promise, setPromise] = useState<Promise<T> | undefined>(undefined);
|
|
||||||
const execute = (...args: Args) => {
|
|
||||||
setLoading(true);
|
|
||||||
setResult(undefined);
|
|
||||||
setError(undefined);
|
|
||||||
setPromise(init(factory(...args)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return [execute, { loading, result, error, promise }] as const;
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* eslint-disable @typescript-eslint/ban-types */
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
type Maybe<T> = T | null | undefined;
|
type Maybe<T> = T | null | undefined;
|
||||||
type KeyOf<T extends Record<string, any>, K = keyof T> = K extends string ? (T[K] extends Function ? never : K) : never;
|
type KeyOf<T extends Record<string, any>, K = keyof T> = K extends string ? (T[K] extends Function ? never : K) : never;
|
||||||
type DotField<T extends Maybe<Record<string, any>>, K = KeyOf<NonNullable<T>>> = K extends string
|
type DotField<T extends Maybe<Record<string, any>>, K = KeyOf<NonNullable<T>>> = K extends string
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export class JSONRPCClient<S extends Service> {
|
||||||
protected transport: Transport;
|
protected transport: Transport;
|
||||||
protected requestMap: Map<
|
protected requestMap: Map<
|
||||||
ID,
|
ID,
|
||||||
(result: RPCResult<any, ResponseError>) => void
|
(result: RPCResult<unknown, ResponseError>) => void
|
||||||
> = new Map();
|
> = new Map();
|
||||||
protected fatal: unknown = undefined;
|
protected fatal: unknown = undefined;
|
||||||
protected task: Promise<void>;
|
protected task: Promise<void>;
|
||||||
|
|
@ -55,6 +55,7 @@ export class JSONRPCClient<S extends Service> {
|
||||||
// receive response from server
|
// receive response from server
|
||||||
protected async run() {
|
protected async run() {
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
const data = await this.transport.recv();
|
const data = await this.transport.recv();
|
||||||
if (data === undefined) {
|
if (data === undefined) {
|
||||||
|
|
@ -111,7 +112,7 @@ export class JSONRPCClient<S extends Service> {
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
rej(new JSONRPCError(result.error));
|
rej(new JSONRPCError(result.error));
|
||||||
} else {
|
} else {
|
||||||
res(result.result);
|
res(result.result as R);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -120,6 +121,7 @@ export class JSONRPCClient<S extends Service> {
|
||||||
getProxy(): S {
|
getProxy(): S {
|
||||||
const proxy = new Proxy({}, {
|
const proxy = new Proxy({}, {
|
||||||
get: (_, method: string) => {
|
get: (_, method: string) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
return (...params: unknown[]) => this.call(method, ...params as any);
|
return (...params: unknown[]) => this.call(method, ...params as any);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import classNames from 'classnames';
|
import clsx from 'clsx';
|
||||||
import { Header } from 'components/Header';
|
import { Header } from 'components/Header';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
@ -32,14 +32,14 @@ const Steps: React.FC<{ steps: Step[], className?: string }> = ({ className, ste
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => setStep(s => s - 1)}
|
onClick={() => setStep(s => s - 1)}
|
||||||
className={classNames('btn', {
|
className={clsx('btn', {
|
||||||
'btn-disabled': !hasPrev || !state.prev,
|
'btn-disabled': !hasPrev || !state.prev,
|
||||||
})}
|
})}
|
||||||
>{t('上一步')}</button>
|
>{t('上一步')}</button>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => setStep(s => s + 1)}
|
onClick={() => setStep(s => s + 1)}
|
||||||
className={classNames('btn', {
|
className={clsx('btn', {
|
||||||
'btn-disabled': !hasNext || !state.next,
|
'btn-disabled': !hasNext || !state.next,
|
||||||
})}
|
})}
|
||||||
>{t('下一步')}</button>
|
>{t('下一步')}</button>
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,32 @@
|
||||||
import { OpenSplatnet } from 'components/OpenSplatnet';
|
import { OpenSplatnet } from 'components/OpenSplatnet';
|
||||||
import { LogPanel, RunPanel } from 'components/RunPanel';
|
import { LogPanel, RunPanel } from 'components/RunPanel';
|
||||||
import { STAT_INK } from 'constant';
|
import { STAT_INK } from 'constant';
|
||||||
import React from 'react'
|
import React, { Suspense } from 'react'
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from 'react-router-dom';
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
|
import { FallbackComponent } from 'components/ErrorContent';
|
||||||
|
import { Loading } from 'components/Loading';
|
||||||
|
|
||||||
export const Home: React.FC = () => {
|
export const Home: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return <div className='flex p-2 w-full h-full gap-2'>
|
return <ErrorBoundary FallbackComponent={FallbackComponent}>
|
||||||
<div className='max-w-full h-full md:max-w-sm flex-auto'>
|
<Suspense fallback={<Loading />}>
|
||||||
<div className='flex flex-col gap-2 h-full'>
|
<div className='flex p-2 w-full h-full gap-2'>
|
||||||
<LogPanel className='sm:hidden flex-auto' />
|
<div className='max-w-full h-full md:max-w-sm flex-auto'>
|
||||||
<RunPanel />
|
<div className='flex flex-col gap-2 h-full'>
|
||||||
<Link to='/settings' className='btn'>{t('设置')}</Link>
|
<LogPanel className='sm:hidden flex-auto' />
|
||||||
<div className='flex gap-2 flex-auto-all'>
|
<RunPanel />
|
||||||
<OpenSplatnet>{t('打开鱿鱼圈3')}</OpenSplatnet>
|
<Link to='/settings' className='btn'>{t('设置')}</Link>
|
||||||
<a className='btn w-full' href={STAT_INK} target='_blank' rel='noreferrer'>{t('前往 stat.ink')}</a>
|
<div className='flex gap-2 flex-auto-all'>
|
||||||
|
<OpenSplatnet>{t('打开鱿鱼圈3')}</OpenSplatnet>
|
||||||
|
<a className='btn w-full' href={STAT_INK} target='_blank' rel='noreferrer'>{t('前往 stat.ink')}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<LogPanel className='hidden sm:block flex-1' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Suspense>
|
||||||
<LogPanel className='hidden sm:block flex-1' />
|
</ErrorBoundary>
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
import { ErrorContent } from 'components/ErrorContent';
|
import { ErrorContent, FallbackComponent } from 'components/ErrorContent';
|
||||||
import { Loading } from 'components/Loading';
|
import { Loading } from 'components/Loading';
|
||||||
import { usePromise, usePromiseLazy } from 'hooks/usePromise';
|
import React, { Suspense, useState } from 'react'
|
||||||
import React, { useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Config, getConfig, getProfile, Profile, setConfig, setProfile } from 'services/config';
|
import { Config, Profile } from 'services/config';
|
||||||
import { composeLoadable } from 'utils/composeLoadable';
|
import clsx from 'clsx';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useLogin } from 'services/s3si';
|
import { useLogin } from 'services/s3si';
|
||||||
import { STAT_INK } from 'constant';
|
import { STAT_INK } from 'constant';
|
||||||
import { Header } from 'components/Header';
|
import { Header } from 'components/Header';
|
||||||
import { useSubField } from 'hooks/useSubField';
|
import { useSubField } from 'hooks/useSubField';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import useSWRMutation from 'swr/mutation'
|
||||||
|
import { useService, useServiceMutation } from 'services/useService';
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
|
|
||||||
const STAT_INK_KEY_LENGTH = 43;
|
const STAT_INK_KEY_LENGTH = 43;
|
||||||
|
|
||||||
|
|
@ -57,6 +58,8 @@ const Form: React.FC<{
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const [value, setValue] = useState(oldValue);
|
const [value, setValue] = useState(oldValue);
|
||||||
const { subField } = useSubField({ value, onChange: setValue });
|
const { subField } = useSubField({ value, onChange: setValue });
|
||||||
|
const { trigger: setProfile } = useServiceMutation('profile', 0)
|
||||||
|
const { trigger: setConfig } = useServiceMutation('config')
|
||||||
|
|
||||||
const changed = JSON.stringify(value) !== JSON.stringify(oldValue);
|
const changed = JSON.stringify(value) !== JSON.stringify(oldValue);
|
||||||
|
|
||||||
|
|
@ -64,12 +67,12 @@ const Form: React.FC<{
|
||||||
const statInkApiKey = subField('profile.state.statInkApiKey')
|
const statInkApiKey = subField('profile.state.statInkApiKey')
|
||||||
const splatnet3Lang = subField('profile.state.userLang')
|
const splatnet3Lang = subField('profile.state.userLang')
|
||||||
|
|
||||||
const [onSave, { loading, error }] = usePromiseLazy(async () => {
|
const { trigger: onSave, isMutating: loading, error } = useSWRMutation('saveSettings', async () => {
|
||||||
await setProfile(0, value.profile);
|
await setProfile(value.profile);
|
||||||
await setConfig(value.config);
|
await setConfig(value.config);
|
||||||
onSaved?.();
|
onSaved?.();
|
||||||
})
|
})
|
||||||
const [onLogin, loginState] = usePromiseLazy(async () => {
|
const loginState = useSWRMutation('login', async () => {
|
||||||
const result = await login();
|
const result = await login();
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -86,11 +89,11 @@ const Form: React.FC<{
|
||||||
<span className="label-text">{t('Nintendo Account 会话令牌')}</span>
|
<span className="label-text">{t('Nintendo Account 会话令牌')}</span>
|
||||||
<span className="label-text-alt"><button
|
<span className="label-text-alt"><button
|
||||||
type='button'
|
type='button'
|
||||||
className={classNames('link', {
|
className={clsx('link', {
|
||||||
loading: loginState.loading,
|
loading: loginState.isMutating,
|
||||||
})}
|
})}
|
||||||
onClick={onLogin}
|
onClick={() => loginState.trigger()}
|
||||||
disabled={loginState.loading}
|
disabled={loginState.isMutating}
|
||||||
>{t('网页登录')}</button></span>
|
>{t('网页登录')}</button></span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -114,7 +117,7 @@ const Form: React.FC<{
|
||||||
</label>
|
</label>
|
||||||
<div className='tooltip' data-tip={statInkKeyError ? t('密钥的长度应该为{{length}}, 请检查', { length: STAT_INK_KEY_LENGTH }) : null}>
|
<div className='tooltip' data-tip={statInkKeyError ? t('密钥的长度应该为{{length}}, 请检查', { length: STAT_INK_KEY_LENGTH }) : null}>
|
||||||
<input
|
<input
|
||||||
className={classNames("input input-bordered w-full", {
|
className={clsx("input input-bordered w-full", {
|
||||||
'input-error': statInkKeyError,
|
'input-error': statInkKeyError,
|
||||||
})}
|
})}
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -149,19 +152,19 @@ const Form: React.FC<{
|
||||||
</div>
|
</div>
|
||||||
<ErrorContent error={error} />
|
<ErrorContent error={error} />
|
||||||
<div className='flex gap-4 max-w-md justify-between flex-auto-all'>
|
<div className='flex gap-4 max-w-md justify-between flex-auto-all'>
|
||||||
<div className="tooltip" data-tip={changed ? undefined : t('没有更改')}>
|
<div className='tooltip' data-tip={changed ? undefined : t('没有更改')}>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className={classNames('btn btn-primary w-full', {
|
className={clsx('btn btn-primary w-full', {
|
||||||
loading,
|
loading,
|
||||||
})}
|
})}
|
||||||
onClick={onSave}
|
onClick={() => onSave()}
|
||||||
disabled={!changed || statInkKeyError}
|
disabled={!changed || statInkKeyError}
|
||||||
>{t('保存')}</button>
|
>{t('保存')}</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className={classNames('btn', {
|
className={clsx('btn', {
|
||||||
loading,
|
loading,
|
||||||
})}
|
})}
|
||||||
onClick={() => setValue(oldValue)}
|
onClick={() => setValue(oldValue)}
|
||||||
|
|
@ -170,26 +173,28 @@ const Form: React.FC<{
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Settings: React.FC = () => {
|
const SettingsLoader: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
let { loading, error, retry, result } = composeLoadable({
|
const { data: config } = useService('config')
|
||||||
config: usePromise(getConfig),
|
const { data: profile } = useService('profile', 0)
|
||||||
profile: usePromise(() => getProfile(0)),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (loading) {
|
if (!config || !profile) {
|
||||||
return <Page>
|
return <>
|
||||||
<div className='h-full flex items-center justify-center'><Loading /></div>
|
Error
|
||||||
</Page>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
return <>
|
||||||
return <Page>
|
<Form oldValue={{ config, profile }} onSaved={() => navigate(-1)} />
|
||||||
<ErrorContent error={error} retry={retry} />
|
</>
|
||||||
</Page>
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
export const Settings: React.FC = () => {
|
||||||
return <Page>
|
return <Page>
|
||||||
{result && <Form oldValue={result} onSaved={() => navigate(-1)} />}
|
<ErrorBoundary FallbackComponent={FallbackComponent}>
|
||||||
|
<Suspense fallback={<div className='h-full flex items-center justify-center'><Loading /></div>}>
|
||||||
|
<SettingsLoader />
|
||||||
|
</Suspense>
|
||||||
|
</ErrorBoundary>
|
||||||
</Page>
|
</Page>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { fs } from "@tauri-apps/api"
|
import { fs } from "@tauri-apps/api"
|
||||||
import { appConfigDir, join } from '@tauri-apps/api/path'
|
import { appConfigDir, join } from '@tauri-apps/api/path'
|
||||||
import { State } from '../../../src/state';
|
import type { State } from '../../../src/state';
|
||||||
|
|
||||||
const configFile = appConfigDir().then(c => join(c, 'config.json'));
|
const configFile = appConfigDir().then(c => join(c, 'config.json'));
|
||||||
const profileDir = appConfigDir().then(c => join(c, 'profile'));
|
const profileDir = appConfigDir().then(c => join(c, 'profile'));
|
||||||
|
|
@ -9,8 +9,7 @@ export type Profile = {
|
||||||
state: State,
|
state: State,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Config = {
|
export type Config = Record<string, never>
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: import from state.ts.
|
// TODO: import from state.ts.
|
||||||
const DEFAULT_STATE: State = {
|
const DEFAULT_STATE: State = {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ const client = new JSONRPCClient<S3SIService>({
|
||||||
const LOG_SUB = new Set<(logs: Log[]) => void>();
|
const LOG_SUB = new Set<(logs: Log[]) => void>();
|
||||||
|
|
||||||
async function getLogs() {
|
async function getLogs() {
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
const r = await client.getLogs()
|
const r = await client.getLogs()
|
||||||
|
|
||||||
|
|
@ -57,7 +58,7 @@ export const useLog = () => {
|
||||||
return useContext(LOG_CONTEXT);
|
return useContext(LOG_CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMsg(i: any) {
|
function renderMsg(i: unknown) {
|
||||||
if (i instanceof Error) {
|
if (i instanceof Error) {
|
||||||
return i.message
|
return i.message
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import useSWR, { Key, SWRResponse } from 'swr'
|
||||||
|
import useSWRMutation, { SWRMutationResponse } from 'swr/mutation'
|
||||||
|
import { getConfig, getProfile, setConfig, setProfile } from './config'
|
||||||
|
|
||||||
|
const SERVICES = {
|
||||||
|
profile: {
|
||||||
|
fetcher: getProfile,
|
||||||
|
updater: setProfile,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
fetcher: getConfig,
|
||||||
|
updater: setConfig,
|
||||||
|
},
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type Services = keyof typeof SERVICES
|
||||||
|
|
||||||
|
export const useService = <S extends Services>(service: S, ...args: Parameters<(typeof SERVICES)[S]['fetcher']>): SWRResponse<
|
||||||
|
Awaited<ReturnType<(typeof SERVICES)[S]['fetcher']>>
|
||||||
|
> => {
|
||||||
|
// @ts-expect-error TypeScript can not infer type here
|
||||||
|
return useSWR(['service', service, ...args], () => SERVICES[service].fetcher(...args), { suspense: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
type RemoveLastParamters<T extends (...args: any) => any> = T extends (...args: [...infer P, any]) => any ? P : never;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
type LastParamter<T extends (...args: any) => any> = T extends (...args: [...infer _, infer P]) => any ? P : never;
|
||||||
|
export const useServiceMutation = <S extends Services>(service: S, ...args: RemoveLastParamters<(typeof SERVICES)[S]['updater']>): SWRMutationResponse<
|
||||||
|
Awaited<ReturnType<(typeof SERVICES)[S]['updater']>>,
|
||||||
|
Error,
|
||||||
|
Key,
|
||||||
|
LastParamter<(typeof SERVICES)[S]['updater']>
|
||||||
|
> => {
|
||||||
|
// @ts-expect-error TypeScript can not infer type here
|
||||||
|
return useSWRMutation(['service', service, ...args], (_, { arg }) => SERVICES[service].updater(...args, arg))
|
||||||
|
}
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
export type Loadable<T> = {
|
|
||||||
loading: boolean;
|
|
||||||
result?: T;
|
|
||||||
error?: any;
|
|
||||||
retry?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function composeLoadable<T extends Record<string, Loadable<any>>>(map: T): Loadable<{
|
|
||||||
[P in keyof T]: T[P] extends Loadable<infer R> ? R : never
|
|
||||||
}> {
|
|
||||||
const values = Object.values(map)
|
|
||||||
|
|
||||||
const loading = values.some(v => v.loading);
|
|
||||||
const error = values.find(v => v.error)?.error;
|
|
||||||
const result = loading || error ? undefined : Object.fromEntries(Object.entries(map).map(([k, v]) => [k, v.result])) as any;
|
|
||||||
const retry = values.some(i => !!i.retry) ? () => Object.values(map).forEach(v => v.retry?.()) : undefined;
|
|
||||||
|
|
||||||
return { loading, result, error, retry };
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export { IPC } from "./stdio.ts";
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
/// <reference lib="deno.ns" />
|
|
||||||
|
|
||||||
import { io, writeAll } from "../../deps.ts";
|
|
||||||
import type { ExtractType } from "./types.ts";
|
|
||||||
|
|
||||||
export class IPC<T extends { type: string }> {
|
|
||||||
lines: AsyncIterableIterator<string>;
|
|
||||||
writer: Deno.Writer;
|
|
||||||
constructor({ reader, writer }: {
|
|
||||||
reader: Deno.Reader;
|
|
||||||
writer: Deno.Writer;
|
|
||||||
}) {
|
|
||||||
this.lines = io.readLines(reader);
|
|
||||||
this.writer = writer;
|
|
||||||
}
|
|
||||||
async recvType<K extends T["type"]>(
|
|
||||||
type: K,
|
|
||||||
): Promise<ExtractType<T, K>> {
|
|
||||||
const data = await this.recv();
|
|
||||||
if (data.type !== type) {
|
|
||||||
throw new Error(`Unexpected type: ${data.type}`);
|
|
||||||
}
|
|
||||||
return data as ExtractType<T, K>;
|
|
||||||
}
|
|
||||||
async recv(): Promise<T> {
|
|
||||||
const result = await this.lines.next();
|
|
||||||
|
|
||||||
if (!result.done) {
|
|
||||||
return JSON.parse(result.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("EOF");
|
|
||||||
}
|
|
||||||
async send(data: T) {
|
|
||||||
await writeAll(
|
|
||||||
this.writer,
|
|
||||||
new TextEncoder().encode(JSON.stringify(data) + "\n"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
export type Command = {
|
|
||||||
type: "hello";
|
|
||||||
data: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ExtractType<T extends { type: string }, K extends T["type"]> =
|
|
||||||
Extract<
|
|
||||||
T,
|
|
||||||
{ type: K }
|
|
||||||
>;
|
|
||||||
|
|
@ -3,13 +3,13 @@ import { Transport } from "./types.ts";
|
||||||
|
|
||||||
export class DenoIO implements Transport {
|
export class DenoIO implements Transport {
|
||||||
lines: AsyncIterableIterator<string>;
|
lines: AsyncIterableIterator<string>;
|
||||||
writer: WritableStream<Uint8Array>;
|
writer: WritableStreamDefaultWriter<Uint8Array>;
|
||||||
constructor({ reader, writer }: {
|
constructor({ reader, writer }: {
|
||||||
reader: ReadableStream<Uint8Array>;
|
reader: ReadableStream<Uint8Array>;
|
||||||
writer: WritableStream<Uint8Array>;
|
writer: WritableStream<Uint8Array>;
|
||||||
}) {
|
}) {
|
||||||
this.lines = readLines(reader);
|
this.lines = readLines(reader);
|
||||||
this.writer = writer;
|
this.writer = writer.getWriter();
|
||||||
}
|
}
|
||||||
async recv(): Promise<string | undefined> {
|
async recv(): Promise<string | undefined> {
|
||||||
const result = await this.lines.next();
|
const result = await this.lines.next();
|
||||||
|
|
@ -21,13 +21,8 @@ export class DenoIO implements Transport {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
async send(data: string) {
|
async send(data: string) {
|
||||||
const writer = this.writer.getWriter();
|
await this.writer.ready;
|
||||||
try {
|
await this.writer.write(new TextEncoder().encode(data + "\n"));
|
||||||
await writer.ready;
|
|
||||||
await writer.write(new TextEncoder().encode(data + "\n"));
|
|
||||||
} finally {
|
|
||||||
writer.releaseLock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
async close() {
|
async close() {
|
||||||
await this.writer.close();
|
await this.writer.close();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue