From 8495fb7e69ef6ac4835185def5772c159ea06351 Mon Sep 17 00:00:00 2001 From: Wk Date: Fri, 13 Dec 2024 05:18:21 +0100 Subject: [PATCH] feat: use taurpc for IPC management (type safe) Close #33 --- .changeset/great-plums-tap.md | 5 + package.json | 19 +- pnpm-lock.yaml | 118 +-- src-tauri/Cargo.lock | 705 +++++++++++++++--- src-tauri/Cargo.toml | 28 +- src-tauri/capabilities/main.json | 5 +- src-tauri/src/almanax.rs | 204 ++--- src-tauri/src/api.rs | 126 ++-- src-tauri/src/conf.rs | 235 +++--- src-tauri/src/guides.rs | 282 +++---- src-tauri/src/id.rs | 6 - src-tauri/src/image.rs | 46 +- src-tauri/src/json.rs | 19 +- src-tauri/src/lib.rs | 90 +-- src-tauri/src/main.rs | 3 +- src-tauri/src/quest.rs | 33 +- src-tauri/src/security.rs | 17 +- src-tauri/src/update.rs | 73 +- src/components/almanax-frame.tsx | 3 +- src/components/download-image.tsx | 120 +-- src/components/error-component.tsx | 10 +- src/components/guide-card.tsx | 2 +- src/components/guide-frame.tsx | 7 +- src/ipc/almanax.ts | 7 +- src/ipc/bindings.ts | 87 +++ src/ipc/conf.ts | 22 +- src/ipc/guide_from_server.ts | 7 +- src/ipc/guides.ts | 49 +- src/ipc/guides_from_server.ts | 11 +- src/ipc/id.ts | 4 +- src/ipc/ipc.ts | 7 + src/ipc/is_app_old_version.ts | 7 +- src/ipc/security.ts | 24 +- src/ipc/update.ts | 8 +- src/lib/conf.ts | 5 + src/lib/guide.ts | 16 +- src/lib/profile.ts | 3 +- src/lib/progress.ts | 8 +- src/locales/en/messages.po | 64 +- src/locales/es/messages.po | 64 +- src/locales/fr/messages.po | 64 +- src/locales/pt/messages.po | 64 +- src/main.tsx | 3 +- .../download-guide-from-server.mutation.ts | 2 +- src/mutations/set-conf.mutation.ts | 48 +- .../toggle-guide-checkbox.mutation.ts | 2 +- src/queries/almanax.query.ts | 4 +- src/queries/conf.query.ts | 4 +- src/queries/guides-from-server.query.ts | 2 +- src/routes/_app/dofusdb/hunt.tsx | 3 +- src/routes/_app/dofusdb/map.tsx | 3 +- src/routes/_app/downloads/$status.tsx | 16 +- src/routes/_app/guides/index.tsx | 6 +- src/routes/_app/settings.tsx | 6 +- src/routes/app-old-version.tsx | 14 +- src/types/almanax.ts | 10 - src/types/conf.ts | 34 - src/types/download.ts | 38 - src/types/guide.ts | 29 - src/types/profile.ts | 21 - src/types/status.ts | 5 - src/types/step.ts | 11 - 62 files changed, 1782 insertions(+), 1156 deletions(-) create mode 100644 .changeset/great-plums-tap.md delete mode 100644 src-tauri/src/id.rs create mode 100644 src/ipc/bindings.ts create mode 100644 src/ipc/ipc.ts create mode 100644 src/lib/conf.ts delete mode 100644 src/types/almanax.ts delete mode 100644 src/types/conf.ts delete mode 100644 src/types/download.ts delete mode 100644 src/types/guide.ts delete mode 100644 src/types/profile.ts delete mode 100644 src/types/status.ts delete mode 100644 src/types/step.ts diff --git a/.changeset/great-plums-tap.md b/.changeset/great-plums-tap.md new file mode 100644 index 0000000..c41d8d7 --- /dev/null +++ b/.changeset/great-plums-tap.md @@ -0,0 +1,5 @@ +--- +"ganymede-app": patch +--- + +Certains liens étaient marqués masqués par erreur. Ceci est désormais corrigé. diff --git a/package.json b/package.json index c50b360..4d0c54f 100644 --- a/package.json +++ b/package.json @@ -28,15 +28,15 @@ "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-query": "^5.55.4", "@tanstack/react-router": "^1.57.8", - "@tauri-apps/api": "2.0.3", - "@tauri-apps/plugin-clipboard-manager": "2.0.0", - "@tauri-apps/plugin-global-shortcut": "^2.0.0", - "@tauri-apps/plugin-http": "2.0.1", - "@tauri-apps/plugin-log": "~2", - "@tauri-apps/plugin-os": "~2", - "@tauri-apps/plugin-shell": "2.0.1", - "@tauri-apps/plugin-updater": "~2", - "@tauri-apps/plugin-window-state": "2.0.0", + "@tauri-apps/api": "^2.1.1", + "@tauri-apps/plugin-clipboard-manager": "^2.2.0", + "@tauri-apps/plugin-global-shortcut": "^2.2.0", + "@tauri-apps/plugin-http": "^2.2.0", + "@tauri-apps/plugin-log": "^2.2.0", + "@tauri-apps/plugin-opener": "^2.2.1", + "@tauri-apps/plugin-os": "^2.2.0", + "@tauri-apps/plugin-updater": "^2.3.0", + "@tauri-apps/plugin-window-state": "^2.2.0", "@uidotdev/usehooks": "^2.4.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", @@ -51,6 +51,7 @@ "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", "tauri-plugin-sentry-api": "^0.2.0", + "taurpc": "^1.6.0", "zod": "^3.23.8" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e40f11a..5f1e2c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,32 +60,32 @@ importers: specifier: ^1.57.8 version: 1.57.8(@tanstack/router-generator@1.57.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tauri-apps/api': - specifier: 2.0.3 - version: 2.0.3 + specifier: ^2.1.1 + version: 2.1.1 '@tauri-apps/plugin-clipboard-manager': - specifier: 2.0.0 - version: 2.0.0 + specifier: ^2.2.0 + version: 2.2.0 '@tauri-apps/plugin-global-shortcut': - specifier: ^2.0.0 - version: 2.0.0 + specifier: ^2.2.0 + version: 2.2.0 '@tauri-apps/plugin-http': - specifier: 2.0.1 - version: 2.0.1 + specifier: ^2.2.0 + version: 2.2.0 '@tauri-apps/plugin-log': - specifier: ~2 - version: 2.0.0 + specifier: ^2.2.0 + version: 2.2.0 + '@tauri-apps/plugin-opener': + specifier: ^2.2.1 + version: 2.2.1 '@tauri-apps/plugin-os': - specifier: ~2 - version: 2.0.0 - '@tauri-apps/plugin-shell': - specifier: 2.0.1 - version: 2.0.1 + specifier: ^2.2.0 + version: 2.2.0 '@tauri-apps/plugin-updater': - specifier: ~2 - version: 2.0.0 + specifier: ^2.3.0 + version: 2.3.0 '@tauri-apps/plugin-window-state': - specifier: 2.0.0 - version: 2.0.0 + specifier: ^2.2.0 + version: 2.2.0 '@uidotdev/usehooks': specifier: ^2.4.1 version: 2.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -128,6 +128,9 @@ importers: tauri-plugin-sentry-api: specifier: ^0.2.0 version: 0.2.0 + taurpc: + specifier: ^1.6.0 + version: 1.6.0 zod: specifier: ^3.23.8 version: 3.23.8 @@ -1582,8 +1585,8 @@ packages: resolution: {integrity: sha512-fN5u+9HsSfhRaXGOdD2kPGbqDgyX+nkm1XF/4d/LNuM4WiNfvHjjRAqWQYBhQsg1aF9nsTPmSW+tmy+Yn5T5+A==} engines: {node: '>= 18', npm: '>= 6.6.0', yarn: '>= 1.19.1'} - '@tauri-apps/api@2.0.3': - resolution: {integrity: sha512-840qk6n8rbXBXMA5/aAgTYsg5JAubKO0nXw5wf7IzGnUuYKGbB4oFBIZtXOIWy+E0kNTDI3qhq5iqsoMJfwp8g==} + '@tauri-apps/api@2.1.1': + resolution: {integrity: sha512-fzUfFFKo4lknXGJq8qrCidkUcKcH2UHhfaaCNt4GzgzGaW2iS26uFOg4tS3H4P8D6ZEeUxtiD5z0nwFF0UN30A==} '@tauri-apps/cli-darwin-arm64@2.0.4': resolution: {integrity: sha512-siH7rOHobb16rPbc11k64p1mxIpiRCkWmzs2qmL5IX21Gx9K5onI3Tk67Oqpf2uNupbYzItrOttaDT4NHFC7tw==} @@ -1650,29 +1653,29 @@ packages: engines: {node: '>= 10'} hasBin: true - '@tauri-apps/plugin-clipboard-manager@2.0.0': - resolution: {integrity: sha512-V1sXmbjnwfXt/r48RJMwfUmDMSaP/8/YbH4CLNxt+/sf1eHlIP8PRFdFDQwLN0cNQKu2rqQVbG/Wc/Ps6cDUhw==} + '@tauri-apps/plugin-clipboard-manager@2.2.0': + resolution: {integrity: sha512-sIBrW/HioKq2vqomwwcU/Y8ygAv3DlS32yKPBX5XijCc0IyQKiDxYpGqmvE9DC5Y0lNJ/G53dfS961B31wjJ1g==} - '@tauri-apps/plugin-global-shortcut@2.0.0': - resolution: {integrity: sha512-pnB4CUwFVjg4twtBSxoLJ4uLFTYxsvOdC1zIbG581pYzhYatOl6mjB+ijD5SSXgiS/jNoqMcfkOF9PWAisurew==} + '@tauri-apps/plugin-global-shortcut@2.2.0': + resolution: {integrity: sha512-clI9Bg/BcxWXNDK+ij601o1qC2WxMEy8ovhGgEW5Ai17oPy0KK8uwzmc59KiVnOYKpBWHCUPqBxG+KBNUFXgzw==} - '@tauri-apps/plugin-http@2.0.1': - resolution: {integrity: sha512-j6IA3pVBybSCwPpsihpX4z8bs6PluuGtr06ahL/xy4D8HunNBTmRmadJrFOQi0gOAbaig4MkQ15nzNLBLy8R1A==} + '@tauri-apps/plugin-http@2.2.0': + resolution: {integrity: sha512-ZY6sIHhgu8hcu6BkkegoiOEbvOsQFSVcK8J7l+g9RNHrkhl5uzpNIytR4R/H50fj7gyG80DJvrXDx/LBo7Easw==} - '@tauri-apps/plugin-log@2.0.0': - resolution: {integrity: sha512-C+NII9vzswqnOQE8k7oRtnaw0z5TZsMmnirRhXkCKDEhQQH9841Us/PC1WHtGiAaJ8za1A1JB2xXndEq/47X/w==} + '@tauri-apps/plugin-log@2.2.0': + resolution: {integrity: sha512-g6CsQAR1lsm5ABSZZxpM/iCn86GrMDTTlhj7GPkZkYBRSm3+WczfOAl7SV7HDn77tOKCzhZffwI5uHfRoHutrw==} - '@tauri-apps/plugin-os@2.0.0': - resolution: {integrity: sha512-M7hG/nNyQYTJxVG/UhTKhp9mpXriwWzrs9mqDreB8mIgqA3ek5nHLdwRZJWhkKjZrnDT4v9CpA9BhYeplTlAiA==} + '@tauri-apps/plugin-opener@2.2.1': + resolution: {integrity: sha512-zloo4xzBqeh363xNA+xYt+7+/cu/lPYuG5PRtxjWAaSyfMqFo6IINdizkDBYylUewLiplXb5+S65GLVkeXTHPg==} - '@tauri-apps/plugin-shell@2.0.1': - resolution: {integrity: sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==} + '@tauri-apps/plugin-os@2.2.0': + resolution: {integrity: sha512-HszbCdbisMlu5QhCNAN8YIWyz2v33abAWha6+uvV2CKX8P5VSct/y+kEe22JeyqrxCnWlQ3DRx7s49Byg7/0EA==} - '@tauri-apps/plugin-updater@2.0.0': - resolution: {integrity: sha512-N0cl71g7RPr7zK2Fe5aoIwzw14NcdLcz7XMGFWZVjprsqgDRWoxbnUkknyCQMZthjhGkppCd/wN2MIsUz+eAhQ==} + '@tauri-apps/plugin-updater@2.3.0': + resolution: {integrity: sha512-qdzyZEUN69FZQ/nRx51fBub10tT6wffJl3DLVo9q922Gvw8Wk++rZhoD9eethPlZYbog/7RGgT8JkrfLh5BKAg==} - '@tauri-apps/plugin-window-state@2.0.0': - resolution: {integrity: sha512-O82iRlrh1BLgBI8CTc+NMTPxQhQo8II5admKq9mLvH45Us5i4Zcr74At6eM46nOflFd7R8bZsVNGy+PxOEqUmQ==} + '@tauri-apps/plugin-window-state@2.2.0': + resolution: {integrity: sha512-PFZ/vkZ6UPaRyuggEn8jWc/xwpiEw3Id8i6bin54zUR3vHY0MOK+ovvpvp6SEHKryCJbZMigYJz0OUT2eZ4YmQ==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -2885,6 +2888,9 @@ packages: tauri-plugin-sentry-api@0.2.0: resolution: {integrity: sha512-HnIbCVnq7jjrj8aRyzP5hfU8GwfSIkl65BH7MHj0Vbx1X4fqrKNc2lzzP/wjjrJ1hsXJCg5bRKF26HshPmgw8g==} + taurpc@1.6.0: + resolution: {integrity: sha512-RXHzIirFSfuMRzRu5sEOyhJgnZmNsT8a0pTqJlT0OUvlZwFOpijUdTpuhEf3RyiC18LYwX7PwPicWUsJ0RKClA==} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -4414,7 +4420,7 @@ snapshots: '@tauri-apps/api@2.0.0-beta.8': {} - '@tauri-apps/api@2.0.3': {} + '@tauri-apps/api@2.1.1': {} '@tauri-apps/cli-darwin-arm64@2.0.4': optional: true @@ -4459,37 +4465,37 @@ snapshots: '@tauri-apps/cli-win32-ia32-msvc': 2.0.4 '@tauri-apps/cli-win32-x64-msvc': 2.0.4 - '@tauri-apps/plugin-clipboard-manager@2.0.0': + '@tauri-apps/plugin-clipboard-manager@2.2.0': dependencies: - '@tauri-apps/api': 2.0.3 + '@tauri-apps/api': 2.1.1 - '@tauri-apps/plugin-global-shortcut@2.0.0': + '@tauri-apps/plugin-global-shortcut@2.2.0': dependencies: - '@tauri-apps/api': 2.0.3 + '@tauri-apps/api': 2.1.1 - '@tauri-apps/plugin-http@2.0.1': + '@tauri-apps/plugin-http@2.2.0': dependencies: - '@tauri-apps/api': 2.0.3 + '@tauri-apps/api': 2.1.1 - '@tauri-apps/plugin-log@2.0.0': + '@tauri-apps/plugin-log@2.2.0': dependencies: - '@tauri-apps/api': 2.0.3 + '@tauri-apps/api': 2.1.1 - '@tauri-apps/plugin-os@2.0.0': + '@tauri-apps/plugin-opener@2.2.1': dependencies: - '@tauri-apps/api': 2.0.3 + '@tauri-apps/api': 2.1.1 - '@tauri-apps/plugin-shell@2.0.1': + '@tauri-apps/plugin-os@2.2.0': dependencies: - '@tauri-apps/api': 2.0.3 + '@tauri-apps/api': 2.1.1 - '@tauri-apps/plugin-updater@2.0.0': + '@tauri-apps/plugin-updater@2.3.0': dependencies: - '@tauri-apps/api': 2.0.3 + '@tauri-apps/api': 2.1.1 - '@tauri-apps/plugin-window-state@2.0.0': + '@tauri-apps/plugin-window-state@2.2.0': dependencies: - '@tauri-apps/api': 2.0.3 + '@tauri-apps/api': 2.1.1 '@types/babel__core@7.20.5': dependencies: @@ -5736,6 +5742,10 @@ snapshots: '@tauri-apps/api': 2.0.0-beta.8 tslib: 2.7.0 + taurpc@1.6.0: + dependencies: + '@tauri-apps/api': 2.1.1 + term-size@2.2.1: {} thenify-all@1.6.0: diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index eb359ab..211a0dd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + [[package]] name = "addr2line" version = "0.24.1" @@ -129,6 +135,149 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "atk" version = "0.18.0" @@ -251,6 +400,19 @@ dependencies = [ "objc2", ] +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "borsh" version = "1.3.0" @@ -485,9 +647,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -829,6 +991,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_arbitrary" version = "1.4.1" @@ -960,6 +1133,12 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "embed-resource" version = "2.4.3" @@ -989,6 +1168,33 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "env_filter" version = "0.1.2" @@ -1031,6 +1237,27 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.2.0" @@ -1205,6 +1432,19 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-lite" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.30" @@ -1266,18 +1506,22 @@ dependencies = [ "serde", "serde_json", "serde_path_to_error", + "specta", + "specta-util", "tauri", "tauri-build", "tauri-plugin-clipboard-manager", "tauri-plugin-global-shortcut", "tauri-plugin-http", "tauri-plugin-log", + "tauri-plugin-opener", "tauri-plugin-os", "tauri-plugin-sentry", - "tauri-plugin-shell", "tauri-plugin-updater", "tauri-plugin-window-state", - "thiserror 2.0.3", + "taurpc", + "thiserror 2.0.6", + "tokio", "uuid", ] @@ -1960,6 +2204,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -2276,7 +2529,7 @@ dependencies = [ "memmap2", "memoffset", "minidump-common", - "nix", + "nix 0.28.0", "procfs-core", "scroll", "tempfile", @@ -2423,6 +2676,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nix" version = "0.28.0" @@ -2800,6 +3065,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "os_info" version = "3.8.2" @@ -2811,16 +3086,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "os_pipe" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "pango" version = "0.18.3" @@ -2846,6 +3111,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -2869,6 +3140,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pathdiff" version = "0.2.1" @@ -3047,6 +3324,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -3768,9 +4056,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] @@ -3896,9 +4184,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] @@ -3916,9 +4204,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -3938,9 +4226,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa 1.0.11", "memchr", @@ -4053,10 +4341,10 @@ dependencies = [ ] [[package]] -name = "sha2" -version = "0.10.8" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -4064,13 +4352,14 @@ dependencies = [ ] [[package]] -name = "shared_child" -version = "1.0.1" +name = "sha2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "libc", - "windows-sys 0.59.0", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] @@ -4079,6 +4368,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -4181,6 +4479,62 @@ dependencies = [ "system-deps", ] +[[package]] +name = "specta" +version = "2.0.0-rc.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ccbb212565d2dc177bc15ecb7b039d66c4490da892436a4eee5b394d620c9bc" +dependencies = [ + "paste", + "specta-macros", + "thiserror 1.0.63", +] + +[[package]] +name = "specta-macros" +version = "2.0.0-rc.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68999d29816965eb9e5201f60aec02a76512139811661a7e8e653abc810b8f72" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "specta-serde" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12260cbb21abb2e83a0375b1521867910e3aed8a7afa782206150ce552cd2e5a" +dependencies = [ + "specta", + "thiserror 1.0.63", +] + +[[package]] +name = "specta-typescript" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4472229365ceb6395487e3a60d921ad8e21f9ad06eaecc396f098902c9adc" +dependencies = [ + "specta", + "specta-serde", + "thiserror 1.0.63", +] + +[[package]] +name = "specta-util" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8113d65b58a6de3184b01d6df9e50b6d4bbe7f724251f576d84f23228824456" +dependencies = [ + "ctor", + "serde", + "specta", + "specta-macros", +] + [[package]] name = "spin" version = "0.9.8" @@ -4193,6 +4547,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "string_cache" version = "0.8.7" @@ -4440,7 +4800,7 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror 2.0.3", + "thiserror 2.0.6", "tokio", "tray-icon", "url", @@ -4493,7 +4853,7 @@ dependencies = [ "sha2", "syn 2.0.87", "tauri-utils", - "thiserror 2.0.3", + "thiserror 2.0.6", "time", "url", "uuid", @@ -4533,9 +4893,9 @@ dependencies = [ [[package]] name = "tauri-plugin-clipboard-manager" -version = "2.0.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a66feaa0fb7fce8e5073323d11ca381c9da7ac06f458e42b9ff77364b76a360" +checksum = "5be2c6f5d82396c1a86d5b16052cc97976a82e92244bf074dd6e2f6272d8619d" dependencies = [ "arboard", "log", @@ -4543,14 +4903,14 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 1.0.63", + "thiserror 2.0.6", ] [[package]] name = "tauri-plugin-fs" -version = "2.0.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba7d46e86db8c830d143ef90ab5a453328365b0cc834c24edea4267b16aba0" +checksum = "a1a1edf18000f02903a7c2e5997fb89aca455ecbc0acc15c6535afbb883be223" dependencies = [ "anyhow", "dunce", @@ -4562,16 +4922,18 @@ dependencies = [ "serde_repr", "tauri", "tauri-plugin", - "thiserror 1.0.63", + "tauri-utils", + "thiserror 2.0.6", + "toml 0.8.2", "url", "uuid", ] [[package]] name = "tauri-plugin-global-shortcut" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c15fb7f5e4c80a73ce97217dcff27e423f496178cbcb87e13b4efe99eebb550" +checksum = "00f646a09511e8d283267dcdaa08c2ef27c4116bf271d9114849d9ca215606c3" dependencies = [ "global-hotkey", "log", @@ -4579,14 +4941,14 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 1.0.63", + "thiserror 2.0.6", ] [[package]] name = "tauri-plugin-http" -version = "2.0.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c752aee1b00ec3c4d4f440095995d9bd2c640b478f2067d1fba388900b82eb96" +checksum = "e62a9bde54d6a0218b63f5a248f02056ad4316ba6ad81dfb9e4f73715df5deb1" dependencies = [ "data-url", "http", @@ -4598,7 +4960,7 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-plugin-fs", - "thiserror 1.0.63", + "thiserror 2.0.6", "tokio", "url", "urlpattern", @@ -4606,9 +4968,9 @@ dependencies = [ [[package]] name = "tauri-plugin-log" -version = "2.0.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aa13d15daf90230ba26d5a9b4a4612975fa64ce17290cb7f6e0f89bb6997d82" +checksum = "eddd784c138c08a43954bc3e735402e6b2b2ee8d8c254a7391f4e77c01273dd5" dependencies = [ "android_logger", "byte-unit", @@ -4622,15 +4984,37 @@ dependencies = [ "swift-rs", "tauri", "tauri-plugin", - "thiserror 1.0.63", + "thiserror 2.0.6", "time", ] +[[package]] +name = "tauri-plugin-opener" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c57f68e88f81a299463b4d1eaf214f7b1be012e6a415b3088eda46a38bb9cef" +dependencies = [ + "dunce", + "glob", + "objc2-app-kit", + "objc2-foundation", + "open", + "schemars", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.6", + "url", + "windows 0.58.0", + "zbus", +] + [[package]] name = "tauri-plugin-os" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc5f23a86f37687c7f4fecfdc706b279087bc44f7a46702f7307ff1551ee03a" +checksum = "dda2d571a9baf0664c1f2088db227e3072f9028602fafa885deade7547c3b738" dependencies = [ "gethostname 0.5.0", "log", @@ -4641,7 +5025,7 @@ dependencies = [ "sys-locale", "tauri", "tauri-plugin", - "thiserror 1.0.63", + "thiserror 2.0.6", ] [[package]] @@ -4660,32 +5044,11 @@ dependencies = [ "thiserror 1.0.63", ] -[[package]] -name = "tauri-plugin-shell" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad7880c5586b6b2104be451e3d7fc0f3800c84bda69e9ba81c828f87cb34267" -dependencies = [ - "encoding_rs", - "log", - "open", - "os_pipe", - "regex", - "schemars", - "serde", - "serde_json", - "shared_child", - "tauri", - "tauri-plugin", - "thiserror 1.0.63", - "tokio", -] - [[package]] name = "tauri-plugin-updater" -version = "2.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd3d2fe0f02bf52eebb5a9d23b987fffac6684646ab6fd683d706dafb18da87" +checksum = "b7351014c140906bcfff59d96e04b1170c8f602557f40eb37f7de356d4e7067b" dependencies = [ "base64 0.22.1", "dirs", @@ -4703,7 +5066,7 @@ dependencies = [ "tauri", "tauri-plugin", "tempfile", - "thiserror 1.0.63", + "thiserror 2.0.6", "time", "tokio", "url", @@ -4713,9 +5076,9 @@ dependencies = [ [[package]] name = "tauri-plugin-window-state" -version = "2.0.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683c8764751fbbcebf3a594bcee24cf84c62773fa0080d1b40fc80698472421e" +checksum = "234dd891cc7960fa28f93ea911f3e0d9ce8375ebf9ff303831bdd7a3443d5714" dependencies = [ "bitflags 2.6.0", "log", @@ -4723,7 +5086,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 1.0.63", + "thiserror 2.0.6", ] [[package]] @@ -4740,7 +5103,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "thiserror 2.0.3", + "thiserror 2.0.6", "url", "windows 0.58.0", ] @@ -4800,7 +5163,7 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror 2.0.3", + "thiserror 2.0.6", "toml 0.8.2", "url", "urlpattern", @@ -4818,6 +5181,38 @@ dependencies = [ "toml 0.7.8", ] +[[package]] +name = "taurpc" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60976c95a019386ee2fec74182430b377f7104f9b089de4990a6a48bb2f3890c" +dependencies = [ + "itertools", + "serde", + "serde_json", + "specta", + "specta-macros", + "specta-serde", + "specta-typescript", + "specta-util", + "tauri", + "taurpc-macros", + "tokio", +] + +[[package]] +name = "taurpc-macros" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4bd7bb6c49ae057b476a512813a5e2d20a74c042f8a16e17a6972ea44639cac" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.87", +] + [[package]] name = "tempfile" version = "3.13.0" @@ -4859,11 +5254,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.6", ] [[package]] @@ -4879,9 +5274,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" dependencies = [ "proc-macro2", "quote", @@ -4949,15 +5344,17 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -5101,9 +5498,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -5171,6 +5580,17 @@ dependencies = [ "libc", ] +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + [[package]] name = "uname" version = "0.1.1" @@ -6063,6 +6483,80 @@ dependencies = [ "rustix", ] +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.27.1", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -6104,3 +6598,40 @@ dependencies = [ "memchr", "thiserror 1.0.63", ] + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c87ee57..434b9d0 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -12,31 +12,35 @@ name = "ganymede_app_lib" crate-type = ["lib", "cdylib", "staticlib"] [build-dependencies] -tauri-build = { version = "2.0.2", features = [] } +tauri-build = { version = "2.0.3", features = [] } [dependencies] tauri = { version = "2.1.1", features = ["macos-private-api"] } -tauri-plugin-shell = "2.0.2" serde = { version = "1", features = ["derive"] } serde_json = "1" uuid = { version = "1.11.0", features = ["v6", "fast-rng"] } -tauri-plugin-http = "2.0.3" +tauri-plugin-http = "2.2.0" serde_path_to_error = "0.1" -tauri-plugin-clipboard-manager = "2.0.2" -tauri-plugin-global-shortcut = "2.0.1" +tauri-plugin-clipboard-manager = "2.2.0" +tauri-plugin-global-shortcut = "2.2.0" glob = "0.3.1" -chrono = "0.4.38" -semver = "1.0.23" -tauri-plugin-os = "2" +chrono = "0.4.39" +semver = "1.0.24" +tauri-plugin-os = "2.2.0" tauri-plugin-sentry = "0.2" -thiserror = "2.0.3" -tauri-plugin-log = "2" +thiserror = "2.0.6" +tauri-plugin-log = "2.2.0" log = "^0.4" +taurpc = "0.3.2" +specta = { version = "=2.0.0-rc.20", features = ["derive"] } +specta-util = { version = "0.0.7", features = ["export"] } +tokio = { version = "1.42.0", features = ["full"] } +tauri-plugin-opener = "2.2.1" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] -tauri-plugin-window-state = "2.0.2" +tauri-plugin-window-state = "2.2.0" machine-uid = "0.5.3" -tauri-plugin-updater = "2" +tauri-plugin-updater = "2.3.0" [profile.release] debug = true diff --git a/src-tauri/capabilities/main.json b/src-tauri/capabilities/main.json index c34d080..4d94b9b 100644 --- a/src-tauri/capabilities/main.json +++ b/src-tauri/capabilities/main.json @@ -15,7 +15,6 @@ "core:window:allow-toggle-maximize", "core:window:allow-close", "core:window:allow-start-dragging", - "shell:allow-open", "window-state:default", { "identifier": "http:default", @@ -32,6 +31,8 @@ "core:webview:allow-internal-toggle-devtools", "os:default", "sentry:default", - "log:default" + "log:default", + "opener:allow-open-path", + "opener:allow-open-url" ] } diff --git a/src-tauri/src/almanax.rs b/src-tauri/src/almanax.rs index 054c3fa..70f4e43 100644 --- a/src-tauri/src/almanax.rs +++ b/src-tauri/src/almanax.rs @@ -6,50 +6,59 @@ use serde::{Deserialize, Serialize}; use tauri::AppHandle; use tauri_plugin_http::reqwest; -use crate::conf::{Conf, Lang}; +use crate::conf::{Conf, ConfLang}; const REWARD_REDUCED_SCALE: f32 = 0.7; const REWARD_SCALE_CAP: f32 = 1.5; const PLAYER_LEVEL: u32 = 200; +#[derive(Debug, Serialize, thiserror::Error)] pub enum Error { - DofusDbAlmanaxMalformed(crate::json::Error), + #[error("failed to get almanax data from DofusDb")] + DofusDbAlmanaxMalformed(#[from] crate::json::Error), + #[error("failed to get item data from DofusDb")] DofusDbItemMalformed(crate::json::Error), - RequestAlmanax(reqwest::Error), - RequestAlmanaxContent(reqwest::Error), - RequestItem(reqwest::Error), - RequestItemContent(reqwest::Error), - Conf(crate::conf::Error), - Quest(crate::quest::Error), + #[error("failed to request almanax data: {0}")] + RequestAlmanax(String), + #[error("failed to request almanax content: {0}")] + RequestAlmanaxContent(String), + #[error("failed to request item data: {0}")] + RequestItem(String), + #[error("failed to request item content: {0}")] + RequestItemContent(String), + #[error("failed to get conf")] + Conf(#[from] crate::conf::Error), + #[error("failed to get quest")] + Quest(#[from] crate::quest::Error), } -impl Into for Error { - fn into(self) -> tauri::ipc::InvokeError { - match self { - Error::DofusDbAlmanaxMalformed(err) => tauri::ipc::InvokeError::from(format!( - "DofusDbAlmanaxMalformed({})", - err.to_string() - )), - Error::DofusDbItemMalformed(err) => { - tauri::ipc::InvokeError::from(format!("DofusDbItemMalformed({})", err.to_string())) - } - Error::RequestAlmanax(err) => { - tauri::ipc::InvokeError::from(format!("RequestAlmanax({})", err.to_string())) - } - Error::RequestAlmanaxContent(err) => { - tauri::ipc::InvokeError::from(format!("RequestAlmanaxContent({})", err.to_string())) - } - Error::RequestItem(err) => { - tauri::ipc::InvokeError::from(format!("RequestItem({})", err.to_string())) - } - Error::RequestItemContent(err) => { - tauri::ipc::InvokeError::from(format!("RequestItemContent({})", err.to_string())) - } - Error::Conf(err) => err.into(), - Error::Quest(err) => err.into(), - } - } -} +// impl Into for Error { +// fn into(self) -> tauri::ipc::InvokeError { +// match self { +// Error::DofusDbAlmanaxMalformed(err) => tauri::ipc::InvokeError::from(format!( +// "DofusDbAlmanaxMalformed({})", +// err.to_string() +// )), +// Error::DofusDbItemMalformed(err) => { +// tauri::ipc::InvokeError::from(format!("DofusDbItemMalformed({})", err.to_string())) +// } +// Error::RequestAlmanax(err) => { +// tauri::ipc::InvokeError::from(format!("RequestAlmanax({})", err.to_string())) +// } +// Error::RequestAlmanaxContent(err) => { +// tauri::ipc::InvokeError::from(format!("RequestAlmanaxContent({})", err.to_string())) +// } +// Error::RequestItem(err) => { +// tauri::ipc::InvokeError::from(format!("RequestItem({})", err.to_string())) +// } +// Error::RequestItemContent(err) => { +// tauri::ipc::InvokeError::from(format!("RequestItemContent({})", err.to_string())) +// } +// Error::Conf(err) => err.into(), +// Error::Quest(err) => err.into(), +// } +// } +// } #[derive(Serialize, Deserialize, Debug)] pub struct AlmanaxName { @@ -74,17 +83,18 @@ pub struct Almanax { } impl Almanax { - pub fn description(&self, lang: Lang) -> &str { + pub fn description(&self, lang: ConfLang) -> &str { match lang { - Lang::En => self.desc.en.as_str(), - Lang::Es => self.desc.es.as_str(), - Lang::Fr => self.desc.fr.as_str(), - Lang::Pt => self.desc.pt.as_str(), + ConfLang::En => self.desc.en.as_str(), + ConfLang::Es => self.desc.es.as_str(), + ConfLang::Fr => self.desc.fr.as_str(), + ConfLang::Pt => self.desc.pt.as_str(), } } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug)] +#[taurpc::ipc_type] pub struct AlmanaxReward { pub name: String, pub quantity: u32, @@ -160,9 +170,12 @@ pub async fn get_almanax_data() -> Result { DOFUSDB_API, month, day, year )) .await - .map_err(Error::RequestAlmanax)?; + .map_err(|err| Error::RequestAlmanax(err.to_string()))?; - let text = res.text().await.map_err(Error::RequestAlmanaxContent)?; + let text = res + .text() + .await + .map_err(|err| Error::RequestAlmanaxContent(err.to_string()))?; let almanax = crate::json::from_str::(text.as_str()).map_err(Error::DofusDbAlmanaxMalformed)?; @@ -173,58 +186,71 @@ pub async fn get_almanax_data() -> Result { pub async fn get_item_data(item_id: u32) -> Result { let res = reqwest::get(format!("{}/items/{}", DOFUSDB_API, item_id)) .await - .map_err(Error::RequestItem)?; + .map_err(|err| Error::RequestItem(err.to_string()))?; - let text = res.text().await.map_err(Error::RequestItemContent)?; + let text = res + .text() + .await + .map_err(|err| Error::RequestItemContent(err.to_string()))?; let item = crate::json::from_str::(text.as_str()).map_err(Error::DofusDbItemMalformed)?; Ok(item) } -#[tauri::command] -pub async fn get_almanax(app: AppHandle) -> Result { - let almanax = get_almanax_data().await?; - let quest = get_quest_data(almanax.id).await.map_err(Error::Quest)?; - let item_id = quest.data[0].steps[0].objectives[0].need.generated.items[0]; - let quantity = quest.data[0].steps[0].objectives[0] - .need - .generated - .quantities[0]; - let item = get_item_data(item_id).await?; - let conf = Conf::get(&app).map_err(Error::Conf)?; - - let name = match conf.lang { - crate::conf::Lang::En => item.name.en, - crate::conf::Lang::Es => item.name.es, - crate::conf::Lang::Fr => item.name.fr, - crate::conf::Lang::Pt => item.name.pt, - }; +#[taurpc::procedures(path = "almanax", export_to = "../src/ipc/bindings.ts")] +pub trait AlmanaxApi { + async fn get(app_handle: AppHandle) -> Result; +} - let experience = get_experience_reward( - PLAYER_LEVEL, - quest.optimal_level(), - quest.experience_ratio(), - quest.duration(), - ); - - let kamas = get_kamas_reward( - PLAYER_LEVEL, - quest.level_max(), - quest.optimal_level(), - quest.kamas_ratio(), - quest.duration(), - quest.kamas_scale_with_player_level(), - ); - - let bonus = almanax.description(conf.lang); - - Ok(AlmanaxReward { - name, - quantity, - kamas, - experience, - bonus: bonus.to_string(), - img: item.img, - }) +#[derive(Clone)] +pub struct AlmanaxApiImpl; + +#[taurpc::resolvers] +impl AlmanaxApi for AlmanaxApiImpl { + async fn get(self, app: AppHandle) -> Result { + let almanax = get_almanax_data().await?; + let quest = get_quest_data(almanax.id).await.map_err(Error::Quest)?; + let item_id = quest.data[0].steps[0].objectives[0].need.generated.items[0]; + let quantity = quest.data[0].steps[0].objectives[0] + .need + .generated + .quantities[0]; + let item = get_item_data(item_id).await?; + let conf = Conf::get(&app).map_err(Error::Conf)?; + + let name = match conf.lang { + crate::conf::ConfLang::En => item.name.en, + crate::conf::ConfLang::Es => item.name.es, + crate::conf::ConfLang::Fr => item.name.fr, + crate::conf::ConfLang::Pt => item.name.pt, + }; + + let experience = get_experience_reward( + PLAYER_LEVEL, + quest.optimal_level(), + quest.experience_ratio(), + quest.duration(), + ); + + let kamas = get_kamas_reward( + PLAYER_LEVEL, + quest.level_max(), + quest.optimal_level(), + quest.kamas_ratio(), + quest.duration(), + quest.kamas_scale_with_player_level(), + ); + + let bonus = almanax.description(conf.lang); + + Ok(AlmanaxReward { + name, + quantity, + kamas, + experience, + bonus: bonus.to_string(), + img: item.img, + }) + } } diff --git a/src-tauri/src/api.rs b/src-tauri/src/api.rs index 22b2162..3934b35 100644 --- a/src-tauri/src/api.rs +++ b/src-tauri/src/api.rs @@ -1,12 +1,9 @@ use log::debug; use serde::{Deserialize, Serialize}; -use tauri::{AppHandle, Runtime}; -use tauri_plugin_http::reqwest; +use tauri::AppHandle; #[cfg(not(dev))] use log::info; -#[cfg(not(dev))] -use serde::Serialize; pub const DOFUSDB_API: &str = "https://api.dofusdb.fr"; #[cfg(not(dev))] @@ -16,12 +13,16 @@ pub const GANYMEDE_API_V2: &str = "https://ganymede-dofus.com/api/v2"; const GITHUB_API: &str = "https://api.github.com/repos/GanymedeTeam/ganymede-app"; #[allow(dead_code)] -#[derive(Debug)] +#[derive(Debug, Serialize, thiserror::Error)] pub enum Error { + #[error("failed to get os")] OsNotFound, - BuildClientBuilder(reqwest::Error), - RequestDownloaded(reqwest::Error), - DownloadedCount(reqwest::StatusCode, String), + #[error("failed to build client builder: {0}")] + BuildClientBuilder(String), + #[error("failed to request downloaded: {0}")] + RequestDownloaded(String), + #[error("failed to increment downloaded count: {0} - {1}")] + DownloadedCount(String, String), } #[cfg(not(dev))] @@ -67,19 +68,19 @@ pub async fn increment_app_download_count( let res = tauri_plugin_http::reqwest::ClientBuilder::new() .user_agent("GANYMEDE_TAURI_APP") .build() - .map_err(Error::BuildClientBuilder)? + .map_err(|err| Error::BuildClientBuilder(err.to_string()))? .post(format!("{}/downloaded", GANYMEDE_API)) .header("Content-Type", "application/json") .body(body) .send() .await - .map_err(Error::RequestDownloaded)?; + .map_err(|err| Error::RequestDownloaded(err.to_string()))?; if res.status().is_success() { Ok(res) } else { Err(Error::DownloadedCount( - res.status(), + res.status().to_string(), res.text().await.expect("[Api] failed to get response text"), )) } @@ -90,29 +91,17 @@ struct AppRelease { tag_name: String, } +#[derive(Debug, Serialize, thiserror::Error)] pub enum AppVersionError { - GitHub(reqwest::Error), - JsonMalformed(reqwest::Error), - SemverParse(semver::Error), + #[error("failed to get latest release from GitHub: {0}")] + GitHub(String), + #[error("failed to parse GitHub release json: {0}")] + JsonMalformed(String), + #[error("failed to parse semver: {0}")] + SemverParse(String), } -impl Into for AppVersionError { - fn into(self) -> tauri::ipc::InvokeError { - match self { - AppVersionError::GitHub(err) => { - tauri::ipc::InvokeError::from(format!("GitHub({})", err.to_string())) - } - AppVersionError::JsonMalformed(err) => { - tauri::ipc::InvokeError::from(format!("JsonMalformed({})", err.to_string())) - } - AppVersionError::SemverParse(err) => { - tauri::ipc::InvokeError::from(format!("SemverParse({})", err.to_string())) - } - } - } -} - -#[derive(Serialize)] +#[taurpc::ipc_type] pub struct IsOld { from: String, to: String, @@ -120,37 +109,48 @@ pub struct IsOld { is_old: bool, } -#[tauri::command] -pub async fn is_app_version_old(app: AppHandle) -> Result { - let version = app.package_info().version.to_string(); - - let client = tauri_plugin_http::reqwest::ClientBuilder::new() - .user_agent("GANYMEDE_TAURI_APP") - .build() - .unwrap(); - - let res = client - .get(format!("{}/releases/latest", GITHUB_API)) - .send() - .await - .map_err(AppVersionError::GitHub)? - .json::() - .await - .map_err(AppVersionError::JsonMalformed)?; - - let release_version = semver::VersionReq::parse(format!("<{}", res.tag_name).as_str()) - .map_err(AppVersionError::SemverParse)?; - - let version = semver::Version::parse(&version).unwrap(); - - debug!( - "[Api] version from package: {:?} - release_version from GitHub: {:?}", - version, release_version - ); +#[taurpc::procedures(export_to = "../src/ipc/bindings.ts")] +pub trait Api { + #[taurpc(alias = "isAppVersionOld")] + async fn is_app_version_old(app_handle: AppHandle) -> Result; +} - Ok(IsOld { - from: version.to_string(), - to: res.tag_name, - is_old: release_version.matches(&version), - }) +#[derive(Clone)] +pub struct ApiImpl; + +#[taurpc::resolvers] +impl Api for ApiImpl { + async fn is_app_version_old(self, app: AppHandle) -> Result { + let version = app.package_info().version.to_string(); + + let client = tauri_plugin_http::reqwest::ClientBuilder::new() + .user_agent("GANYMEDE_TAURI_APP") + .build() + .unwrap(); + + let res = client + .get(format!("{}/releases/latest", GITHUB_API)) + .send() + .await + .map_err(|err| AppVersionError::GitHub(err.to_string()))? + .json::() + .await + .map_err(|err| AppVersionError::JsonMalformed(err.to_string()))?; + + let release_version = semver::VersionReq::parse(format!("<{}", res.tag_name).as_str()) + .map_err(|err| AppVersionError::SemverParse(err.to_string()))?; + + let version = semver::Version::parse(&version).unwrap(); + + debug!( + "[Api] version from package: {:?} - release_version from GitHub: {:?}", + version, release_version + ); + + Ok(IsOld { + from: version.to_string(), + to: res.tag_name, + is_old: release_version.matches(&version), + }) + } } diff --git a/src-tauri/src/conf.rs b/src-tauri/src/conf.rs index 3a47d9d..5f6dd07 100644 --- a/src-tauri/src/conf.rs +++ b/src-tauri/src/conf.rs @@ -1,72 +1,79 @@ use crate::tauri_api_ext::ConfPathExt; -use log::info; +use log::{debug, info}; use serde::{Deserialize, Serialize}; use std::borrow::BorrowMut; use std::collections::HashMap; use std::fs; -use tauri::ipc::InvokeError; use tauri::{AppHandle, Manager, Window, Wry}; -#[derive(Debug)] +#[derive(Debug, Serialize, thiserror::Error)] pub enum Error { - Malformed(crate::json::Error), - CreateConfDir(std::io::Error), - ConfDir(tauri::Error), - SerializeConf(serde_json::Error), - UnhandledIo(std::io::Error), - SaveConf(std::io::Error), + #[error("failed to get conf, file is malformed")] + Malformed(#[from] crate::json::Error), + #[error("failed to create conf dir: {0}")] + CreateConfDir(String), + #[error("failed to get conf dir: {0}")] + ConfDir(String), + #[error("failed to serialize conf")] + SerializeConf(crate::json::Error), + #[error("unhandled io error: {0}")] + UnhandledIo(String), + #[error("failed to save conf: {0}")] + SaveConf(String), + #[error("failed to get profile in use")] GetProfileInUse, + #[error("failed to reset conf: {0}")] ResetConf(Box), } -impl Into for Error { - fn into(self) -> InvokeError { - use Error::*; - - let message = match self { - Malformed(err) => format!("Malformed({})", err.to_string()), - CreateConfDir(err) => format!("CreateConfDir({})", err.to_string()), - ConfDir(err) => format!("ConfDir({})", err.to_string()), - SerializeConf(err) => format!("SerializeConf({})", err.to_string()), - UnhandledIo(err) => format!("UnhandledIo({})", err.to_string()), - SaveConf(err) => format!("SaveConf({})", err.to_string()), - GetProfileInUse => "GetProfileInUse".to_string(), - ResetConf(err) => return (*err).into(), - }; - - InvokeError::from(message) - } -} - -#[derive(Serialize, Deserialize, Debug, Clone)] +// impl Into for Error { +// fn into(self) -> InvokeError { +// use Error::*; + +// let message = match self { +// Malformed(err) => format!("Malformed({})", err.to_string()), +// CreateConfDir(err) => format!("CreateConfDir({})", err.to_string()), +// ConfDir(err) => format!("ConfDir({})", err.to_string()), +// SerializeConf(err) => format!("SerializeConf({})", err.to_string()), +// UnhandledIo(err) => format!("UnhandledIo({})", err.to_string()), +// SaveConf(err) => format!("SaveConf({})", err.to_string()), +// GetProfileInUse => "GetProfileInUse".to_string(), +// ResetConf(err) => return (*err).into(), +// }; + +// InvokeError::from(message) +// } +// } + +#[derive(Serialize, Deserialize, Debug, Clone, taurpc::specta::Type)] pub struct Profile { pub id: String, pub name: String, pub progresses: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Step { - pub checkboxes: Vec, +#[derive(Serialize, Deserialize, Debug, Clone, taurpc::specta::Type)] +pub struct ConfStep { + pub checkboxes: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, taurpc::specta::Type)] #[serde(rename_all = "camelCase")] pub struct Progress { pub id: u32, // guide id - pub current_step: usize, - pub steps: HashMap, + pub current_step: u32, + pub steps: HashMap, } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum Lang { +#[derive(Debug, Clone, Serialize, Deserialize, taurpc::specta::Type)] +pub enum ConfLang { En, Fr, Es, Pt, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, taurpc::specta::Type)] pub enum FontSize { ExtraSmall, Small, @@ -75,25 +82,26 @@ pub enum FontSize { ExtraLarge, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, taurpc::specta::Type)] pub struct AutoPilot { pub name: String, pub position: String, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, taurpc::specta::Type)] pub struct Note { pub name: String, pub text: String, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Debug)] +#[taurpc::ipc_type] #[serde(rename_all = "camelCase")] pub struct Conf { pub auto_travel_copy: bool, pub show_done_guides: bool, #[serde(default)] - pub lang: Lang, + pub lang: ConfLang, #[serde(default)] pub font_size: FontSize, pub profiles: Vec, @@ -104,7 +112,7 @@ pub struct Conf { } impl Progress { - pub fn add_or_update_step(&mut self, step: Step, step_index: usize) { + pub fn add_or_update_step(&mut self, step: ConfStep, step_index: u32) { match self.steps.get(&step_index) { Some(s) => { self.steps.insert(step_index, s.clone()); @@ -140,8 +148,8 @@ impl Profile { } } -impl Step { - pub fn toggle_checkbox(&mut self, checkbox_index: usize) { +impl ConfStep { + pub fn toggle_checkbox(&mut self, checkbox_index: u32) { match self.checkboxes.iter().position(|&i| i == checkbox_index) { Some(index) => { self.checkboxes.remove(index); @@ -164,7 +172,7 @@ impl Conf { match file { Err(err) => match err.kind() { std::io::ErrorKind::NotFound => Ok(Conf::default()), - _ => Err(Error::UnhandledIo(err)), + _ => Err(Error::UnhandledIo(err.to_string())), }, Ok(file) => Ok(crate::json::from_str::(file.as_str()).map_err(Error::Malformed)?), } @@ -176,9 +184,9 @@ impl Conf { self.normalize(); - let json = serde_json::to_string_pretty(self).map_err(Error::SerializeConf)?; + let json = crate::json::serialize_pretty(self).map_err(Error::SerializeConf)?; - fs::write(conf_path, json).map_err(Error::SaveConf) + fs::write(conf_path, json).map_err(|err| Error::SaveConf(err.to_string())) } pub fn get_profile_in_use_mut(&mut self) -> Result<&mut Profile, Error> { @@ -193,15 +201,15 @@ impl Conf { } } -impl Default for Step { +impl Default for ConfStep { fn default() -> Self { - Step { checkboxes: vec![] } + ConfStep { checkboxes: vec![] } } } -impl Default for Lang { +impl Default for ConfLang { fn default() -> Self { - Lang::Fr + ConfLang::Fr } } @@ -219,7 +227,7 @@ impl Default for Conf { Conf { auto_travel_copy: true, show_done_guides: true, - lang: Lang::default(), + lang: ConfLang::default(), font_size: FontSize::default(), profiles: vec![default_profile], profile_in_use: default_profile_id, @@ -243,10 +251,12 @@ impl Default for Profile { /// Ensure that the conf file exists, if not, create it with default values pub fn ensure(app: &AppHandle) -> Result<(), Error> { let resolver = app.path(); - let conf_dir = resolver.app_config_dir().map_err(Error::ConfDir)?; + let conf_dir = resolver + .app_config_dir() + .map_err(|err| Error::ConfDir(err.to_string()))?; if !conf_dir.exists() { - fs::create_dir_all(conf_dir).map_err(Error::CreateConfDir)?; + fs::create_dir_all(conf_dir).map_err(|err| Error::CreateConfDir(err.to_string()))?; } let conf_path = resolver.app_conf_file(); @@ -264,63 +274,84 @@ pub fn ensure(app: &AppHandle) -> Result<(), Error> { Ok(()) } -#[tauri::command] -pub fn get_conf(app: AppHandle) -> Result { - Conf::get(&app) +#[taurpc::procedures(path = "conf", export_to = "../src/ipc/bindings.ts")] +pub trait ConfApi { + async fn get(app_handle: AppHandle) -> Result; + async fn set(conf: Conf, app_handle: AppHandle) -> Result<(), Error>; + #[taurpc(alias = "toggleGuideCheckbox")] + async fn toggle_guide_checkbox( + app_handle: AppHandle, + guide_id: u32, + step_index: u32, + checkbox_index: u32, + ) -> Result; + async fn reset(app_handle: AppHandle, window: Window) -> Result<(), Error>; } -#[tauri::command] -pub fn set_conf(conf: Conf, app: AppHandle) -> Result<(), Error> { - conf.clone().borrow_mut().save(&app) -} +#[derive(Clone)] +pub struct ConfApiImpl; -#[tauri::command] -pub fn toggle_guide_checkbox( - app: AppHandle, - guide_id: u32, - step_index: usize, - checkbox_index: usize, -) -> Result { - let conf = &mut Conf::get(&app)?; - let profile = conf.get_profile_in_use_mut()?; - let progress = profile.get_progress_mut(guide_id); - - let step = match progress.steps.get_mut(&step_index) { - Some(step) => { - step.toggle_checkbox(checkbox_index); - - step.clone() - } - None => { - let mut step = Step::default(); - step.toggle_checkbox(checkbox_index); +#[taurpc::resolvers] +impl ConfApi for ConfApiImpl { + async fn get(self, app: AppHandle) -> Result { + Conf::get(&app) + } - step - } - }; + async fn set(self, conf: Conf, app: AppHandle) -> Result<(), Error> { + conf.clone().borrow_mut().save(&app) + } - progress.add_or_update_step(step, step_index); + async fn toggle_guide_checkbox( + self, + app: AppHandle, + guide_id: u32, + step_index: u32, + checkbox_index: u32, + ) -> Result { + debug!( + "[Conf] toggle_guide_checkbox: guide_id: {}, step_index: {}, checkbox_index: {}", + guide_id, step_index, checkbox_index + ); + let conf = &mut Conf::get(&app)?; + let profile = conf.get_profile_in_use_mut()?; + let progress = profile.get_progress_mut(guide_id); + + let step = match progress.steps.get_mut(&step_index) { + Some(step) => { + step.toggle_checkbox(checkbox_index); + + step.clone() + } + None => { + let mut step = ConfStep::default(); + step.toggle_checkbox(checkbox_index); - conf.save(&app)?; + step + } + }; - Ok(checkbox_index) -} + progress.add_or_update_step(step, step_index); -#[tauri::command] -pub fn reset_conf(app: AppHandle, window: Window) -> Result<(), Error> { - Conf::default() - .save(&app) - .map_err(|e| Error::ResetConf(Box::new(e)))?; + conf.save(&app)?; - let mut webview = window - .get_webview_window("main") - .expect("[Conf] main webview should exist"); + Ok(checkbox_index) + } - let url = webview.url().unwrap(); + async fn reset(self, app: AppHandle, window: Window) -> Result<(), Error> { + Conf::default() + .save(&app) + .map_err(|e| Error::ResetConf(Box::new(e)))?; - webview - .navigate(url) - .expect("[Conf] failed to reload webview"); + let mut webview = window + .get_webview_window("main") + .expect("[Conf] main webview should exist"); - Ok(()) + let url = webview.url().unwrap(); + + webview + .navigate(url) + .expect("[Conf] failed to reload webview"); + + Ok(()) + } } diff --git a/src-tauri/src/guides.rs b/src-tauri/src/guides.rs index 854434a..8ffa943 100644 --- a/src-tauri/src/guides.rs +++ b/src-tauri/src/guides.rs @@ -1,72 +1,48 @@ use crate::api::GANYMEDE_API_V2; use crate::tauri_api_ext::GuidesPathExt; -use log::info; +use log::{debug, info}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use std::{fmt, fs, vec}; -use tauri::ipc::InvokeError; -use tauri::{AppHandle, Manager, Window, Wry}; +use tauri::{AppHandle, Manager}; use tauri_plugin_http::reqwest; -use tauri_plugin_shell::ShellExt; +use tauri_plugin_opener::OpenerExt; pub const DEFAULT_GUIDE_ID: u32 = 1074; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Serialize, thiserror::Error)] pub enum Error { #[error("cannot parse glob pattern: {0}")] - Pattern(glob::PatternError), + Pattern(String), #[error("cannot read the guides directory glob: {0}")] - ReadGuidesDirGlob(glob::GlobError), + ReadGuidesDirGlob(String), #[error("cannot read a guide file: {0}")] - ReadGuideFile(std::io::Error), - #[error("malformed guide: {0}")] - GuideMalformed(crate::json::Error), - #[error("cannot serialize guide: {0}")] - SerializeGuide(serde_json::Error), + ReadGuideFile(String), + #[error("malformed guide")] + GuideMalformed(#[from] crate::json::Error), + #[error("cannot serialize guide")] + SerializeGuide(crate::json::Error), #[error("cannot create the guides directory: {0}")] - CreateGuidesDir(std::io::Error), + CreateGuidesDir(String), #[error("cannot write a guide file: {0}")] - WriteGuideFile(std::io::Error), + WriteGuideFile(String), #[error("cannot request a guide from server: {0}")] - RequestGuide(reqwest::Error), + RequestGuide(String), #[error("cannot get the content of a guide request: {0}")] - RequestGuideContent(reqwest::Error), + RequestGuideContent(String), #[error("cannot request guides from server: {0}")] - RequestGuides(reqwest::Error), + RequestGuides(String), #[error("cannot get the content of a guides request: {0}")] - RequestGuidesContent(reqwest::Error), - #[error("malformed guide with steps: {0}")] + RequestGuidesContent(String), + #[error("malformed guide with steps")] GuideWithStepsMalformed(crate::json::Error), - #[error("malformed guides: {0}")] + #[error("malformed guides")] GuidesMalformed(crate::json::Error), #[error("cannot read the guides directory: {0}")] - ReadGuidesDir(std::io::Error), + ReadGuidesDir(String), } -impl Into for Error { - fn into(self) -> InvokeError { - use Error::*; - - InvokeError::from(match self { - Pattern(err) => format!("Pattern({})", err.to_string()), - ReadGuidesDirGlob(err) => format!("ReadGuidesDirGlob({})", err.to_string()), - ReadGuideFile(err) => format!("ReadGuideFile({})", err.to_string()), - GuideMalformed(err) => format!("GuideMalformed({})", err.to_string()), - SerializeGuide(err) => format!("SerializeGuide({})", err.to_string()), - CreateGuidesDir(err) => format!("CreateGuidesDir({})", err.to_string()), - WriteGuideFile(err) => format!("WriteGuideFile({})", err.to_string()), - RequestGuide(err) => format!("RequestGuide({})", err.to_string()), - RequestGuideContent(err) => format!("RequestGuideContent({})", err.to_string()), - RequestGuides(err) => format!("RequestGuides({})", err.to_string()), - RequestGuidesContent(err) => format!("RequestGuidesContent({})", err.to_string()), - GuideWithStepsMalformed(err) => format!("GuideWithStepsMalformed({})", err.to_string()), - GuidesMalformed(err) => format!("GuidesMalformed({})", err.to_string()), - ReadGuidesDir(err) => format!("ReadGuidesDir({})", err.to_string()), - }) - } -} - -#[derive(Serialize, Deserialize, Clone)] +#[taurpc::ipc_type] pub struct User { pub id: u32, pub name: String, @@ -74,16 +50,16 @@ pub struct User { pub is_certified: u8, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, taurpc::specta::Type)] #[serde(rename_all = "camelCase")] -pub enum Lang { +pub enum GuideLang { En, Fr, Es, Pt, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, taurpc::specta::Type)] #[serde(rename_all = "camelCase")] pub enum Status { Draft, @@ -93,8 +69,8 @@ pub enum Status { Gp, } -#[derive(Serialize, Deserialize, Clone)] -pub struct Step { +#[taurpc::ipc_type] +pub struct GuideStep { pub name: Option, pub map: Option, pub pos_x: i32, @@ -102,7 +78,7 @@ pub struct Step { pub web_text: String, } -#[derive(Serialize, Deserialize, Clone)] +#[taurpc::ipc_type] pub struct Guide { pub id: u32, pub name: String, @@ -113,7 +89,7 @@ pub struct Guide { pub created_at: String, pub deleted_at: Option, pub updated_at: Option, - pub lang: Lang, + pub lang: GuideLang, pub order: u32, pub user: User, pub user_id: u32, @@ -121,7 +97,7 @@ pub struct Guide { pub web_description: Option, } -#[derive(Serialize, Deserialize, Clone)] +#[taurpc::ipc_type] pub struct GuideWithSteps { pub id: u32, pub name: String, @@ -132,28 +108,29 @@ pub struct GuideWithSteps { pub downloads: Option, pub deleted_at: Option, pub updated_at: Option, - pub lang: Lang, + pub lang: GuideLang, pub order: u32, pub user: User, pub web_description: Option, - pub steps: Vec, + pub steps: Vec, #[serde(skip_deserializing, serialize_with = "crate::json::serialize_path")] pub folder: Option, } -#[derive(Serialize, Deserialize, Clone, Default)] +#[derive(Default)] +#[taurpc::ipc_type] #[serde(rename_all = "camelCase")] pub struct Guides { pub guides: Vec, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, taurpc::specta::Type)] #[serde(rename_all = "camelCase")] pub struct Folder { pub name: String, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, taurpc::specta::Type)] #[serde(tag = "type", rename_all = "camelCase")] pub enum GuidesOrFolder { Guide(GuideWithSteps), @@ -175,8 +152,10 @@ impl GuidesOrFolder { let mut result = vec![]; - for entry in fs::read_dir(guide_folder).map_err(Error::ReadGuidesDir)? { - let entry = entry.map_err(Error::ReadGuidesDir)?; + for entry in + fs::read_dir(guide_folder).map_err(|err| Error::ReadGuidesDir(err.to_string()))? + { + let entry = entry.map_err(|err| Error::ReadGuidesDir(err.to_string()))?; let path = entry.path(); let file_name = path.file_name().unwrap().to_str().unwrap().to_string(); @@ -187,7 +166,8 @@ impl GuidesOrFolder { if let Some(ext) = extension { if ext == "json" { - let file = fs::read_to_string(&path).map_err(Error::ReadGuideFile)?; + let file = fs::read_to_string(&path) + .map_err(|err| Error::ReadGuideFile(err.to_string()))?; let mut guide = crate::json::from_str::(file.as_str()) .map_err(Error::GuideMalformed)?; @@ -233,15 +213,15 @@ impl Guides { }; let files = glob::glob_with(path_buf.join("**/*.json").to_str().unwrap(), options) - .map_err(Error::Pattern)?; + .map_err(|err| Error::Pattern(err.to_string()))?; let mut guides = vec![]; for entry in files { - let file_path = entry.map_err(Error::ReadGuidesDirGlob)?; + let file_path = entry.map_err(|err| Error::ReadGuidesDirGlob(err.to_string()))?; - let file = - fs::read_to_string(file_path.to_str().unwrap()).map_err(Error::ReadGuideFile)?; + let file = fs::read_to_string(file_path.to_str().unwrap()) + .map_err(|err| Error::ReadGuideFile(err.to_string()))?; let mut guide = crate::json::from_str::(file.as_str()) .map_err(Error::GuideMalformed)?; @@ -262,20 +242,25 @@ impl Guides { Ok(Guides { guides }) } - fn from_handle(app: &AppHandle) -> Result { - let guides_dir = &app.path().app_guides_dir(); + fn from_handle(app: &AppHandle, folder: String) -> Result { + let mut guides_dir = app.path().app_guides_dir(); - Guides::from_path(guides_dir) + if folder != "" { + guides_dir = guides_dir.join(folder); + } + + Guides::from_path(&guides_dir) } fn write(&self, app: &AppHandle) -> Result<(), Error> { let guides_dir = &app.path().app_guides_dir(); for guide in &self.guides { - let json = serde_json::to_string_pretty(guide).map_err(Error::SerializeGuide)?; + let json = crate::json::serialize_pretty(guide).map_err(Error::SerializeGuide)?; if !guides_dir.exists() { - fs::create_dir_all(guides_dir).map_err(Error::CreateGuidesDir)?; + fs::create_dir_all(guides_dir) + .map_err(|err| Error::CreateGuidesDir(err.to_string()))?; } let file = guide @@ -286,7 +271,8 @@ impl Guides { println!("Writing guide to {:?}", file); - fs::write(file.as_path(), json).map_err(Error::WriteGuideFile)?; + fs::write(file.as_path(), json) + .map_err(|err| Error::WriteGuideFile(err.to_string()))?; } Ok(()) @@ -307,13 +293,101 @@ impl Guides { } } +#[taurpc::procedures(path = "guides", export_to = "../src/ipc/bindings.ts")] +pub trait GuidesApi { + #[taurpc(alias = "getFlatGuides")] + async fn get_flat_guides( + app_handle: AppHandle, + folder: String, + ) -> Result, Error>; + #[taurpc(alias = "getGuides")] + async fn get_guides( + app_handle: AppHandle, + folder: Option, + ) -> Result, Error>; + #[taurpc(alias = "getGuideFromServer")] + async fn get_guide_from_server(guide_id: u32) -> Result; + #[taurpc(alias = "getGuidesFromServer")] + async fn get_guides_from_server(status: Status) -> Result, Error>; + #[taurpc(alias = "downloadGuideFromServer")] + async fn download_guide_from_server( + app_handle: AppHandle, + guide_id: u32, + folder: String, + ) -> Result; + #[taurpc(alias = "openGuidesFolder")] + async fn open_guides_folder(app_handle: AppHandle) -> Result<(), tauri_plugin_opener::Error>; +} + +#[derive(Clone)] +pub struct GuidesApiImpl; + +#[taurpc::resolvers] +impl GuidesApi for GuidesApiImpl { + async fn get_flat_guides( + self, + app: AppHandle, + folder: String, + ) -> Result, Error> { + let guides = Guides::from_handle(&app, folder)?; + + Ok(guides.guides.into_iter().collect()) + } + + async fn get_guides( + self, + app: AppHandle, + folder: Option, + ) -> Result, Error> { + GuidesOrFolder::from_handle(&app, folder) + } + + async fn get_guide_from_server(self, guide_id: u32) -> Result { + get_guide_from_server(guide_id).await + } + + async fn get_guides_from_server(self, status: Status) -> Result, Error> { + info!("[Guides] get_guides_from_server"); + + let res = reqwest::get(format!("{}/guides?status={}", GANYMEDE_API_V2, status)) + .await + .map_err(|err| Error::RequestGuides(err.to_string()))?; + + let text = res + .text() + .await + .map_err(|err| Error::RequestGuidesContent(err.to_string()))?; + + crate::json::from_str::>(text.as_str()).map_err(Error::GuidesMalformed) + } + + async fn download_guide_from_server( + self, + app: AppHandle, + guide_id: u32, + folder: String, + ) -> Result { + info!("[Guides] download_guide_from_server"); + + Ok(download_guide_by_id(&app, guide_id, folder).await?) + } + + async fn open_guides_folder(self, app: AppHandle) -> Result<(), tauri_plugin_opener::Error> { + let resolver = app.app_handle().path(); + let guides_dir = resolver.app_guides_dir(); + + app.opener() + .open_path(guides_dir.to_str().unwrap().to_string(), None::) + } +} + pub fn ensure(app: &AppHandle) -> Result<(), Error> { let guides_dir = &app.path().app_guides_dir(); info!("Log dir: {:?}", &app.path().app_log_dir().unwrap()); if !guides_dir.exists() { - fs::create_dir_all(guides_dir).map_err(Error::CreateGuidesDir)?; + fs::create_dir_all(guides_dir).map_err(|err| Error::CreateGuidesDir(err.to_string()))?; } Ok(()) @@ -323,26 +397,16 @@ pub async fn download_default_guide(app: &AppHandle) -> Result { download_guide_by_id(app, DEFAULT_GUIDE_ID, "".into()).await } -#[tauri::command] -pub fn get_flat_guides(app: AppHandle) -> Result, Error> { - let guides = Guides::from_handle(&app)?; - - Ok(guides.guides.into_iter().collect()) -} - -#[tauri::command] -pub fn get_guides(app: AppHandle, folder: Option) -> Result, Error> { - GuidesOrFolder::from_handle(&app, folder) -} - -#[tauri::command] pub async fn get_guide_from_server(guide_id: u32) -> Result { info!("[Guides] get_guide_from_server: {}", guide_id); let res = reqwest::get(format!("{}/guides/{}", GANYMEDE_API_V2, guide_id)) .await - .map_err(Error::RequestGuide)?; - let text = res.text().await.map_err(Error::RequestGuideContent)?; + .map_err(|err| Error::RequestGuide(err.to_string()))?; + let text = res + .text() + .await + .map_err(|err| Error::RequestGuideContent(err.to_string()))?; let mut guide = crate::json::from_str::(text.as_str()) .map_err(Error::GuideWithStepsMalformed)?; @@ -352,41 +416,6 @@ pub async fn get_guide_from_server(guide_id: u32) -> Result Result, Error> { - info!("[Guides] get_guides_from_server"); - - let res = reqwest::get(format!("{}/guides?status={}", GANYMEDE_API_V2, status)) - .await - .map_err(Error::RequestGuides)?; - - let text = res.text().await.map_err(Error::RequestGuidesContent)?; - - crate::json::from_str::>(text.as_str()).map_err(Error::GuidesMalformed) -} - -#[tauri::command] -pub async fn download_guide_from_server( - guide_id: u32, - folder: String, - app: AppHandle, -) -> Result { - info!("[Guides] download_guide_from_server"); - - Ok(download_guide_by_id(&app, guide_id, folder).await?) -} - -#[tauri::command] -pub fn open_guides_folder(window: Window) -> Result<(), tauri_plugin_shell::Error> { - let resolver = window.path(); - let guides_dir = resolver.app_guides_dir(); - - window - .app_handle() - .shell() - .open(guides_dir.to_str().unwrap().to_string(), None) -} - async fn download_guide_by_id( app: &AppHandle, guide_id: u32, @@ -396,13 +425,14 @@ async fn download_guide_by_id( guide.folder = Some(app.path().app_guides_dir().join(folder.clone())); - println!("folder {:?} + {}", guide.folder, folder); + debug!( + "[Guides] download_guide_by_id: {} in {:?} (via {})", + guide_id, guide.folder, folder + ); - let mut guides = Guides::from_handle(app)?; + let mut guides = Guides::from_handle(app, folder)?; let guides_ref = &mut guides; - // implement add_or_replace, this fn should add the guide if it doesn't exist, or replace it if it does - // it should write the file guide in the correct folder ignoring status guides_ref.add_or_replace(app, guide)?; Ok(guides) diff --git a/src-tauri/src/id.rs b/src-tauri/src/id.rs deleted file mode 100644 index ed08339..0000000 --- a/src-tauri/src/id.rs +++ /dev/null @@ -1,6 +0,0 @@ -use uuid::Uuid; - -#[tauri::command] -pub fn new_id() -> String { - Uuid::new_v4().to_string() -} diff --git a/src-tauri/src/image.rs b/src-tauri/src/image.rs index 3e3f1c0..ee49658 100644 --- a/src-tauri/src/image.rs +++ b/src-tauri/src/image.rs @@ -1,30 +1,32 @@ +use serde::Serialize; use tauri_plugin_http::reqwest; +#[derive(Debug, Serialize, thiserror::Error)] pub enum Error { - RequestImage(reqwest::Error), - ConvertToBytes(reqwest::Error), + #[error("RequestImage({0})")] + RequestImage(String), + #[error("ConvertToBytes({0})")] + ConvertToBytes(String), } -impl Into for Error { - fn into(self) -> tauri::ipc::InvokeError { - match self { - Error::RequestImage(err) => { - tauri::ipc::InvokeError::from(format!("RequestImage({})", err.to_string())) - } - Error::ConvertToBytes(err) => { - tauri::ipc::InvokeError::from(format!("ConvertToBytes({})", err.to_string())) - } - } - } +#[taurpc::procedures(path = "image", export_to = "../src/ipc/bindings.ts")] +pub trait ImageApi { + #[taurpc(alias = "fetchImage")] + async fn fetch_image(url: String) -> Result, Error>; } -#[tauri::command] -pub async fn fetch_image(url: String) -> Result, Error> { - reqwest::get(url) - .await - .map_err(Error::RequestImage)? - .bytes() - .await - .map(|bytes| bytes.to_vec()) - .map_err(Error::ConvertToBytes) +#[derive(Clone)] +pub struct ImageApiImpl; + +#[taurpc::resolvers] +impl ImageApi for ImageApiImpl { + async fn fetch_image(self, url: String) -> Result, Error> { + reqwest::get(url) + .await + .map_err(|err| Error::RequestImage(err.to_string()))? + .bytes() + .await + .map(|bytes| bytes.to_vec()) + .map_err(|err| Error::ConvertToBytes(err.to_string())) + } } diff --git a/src-tauri/src/json.rs b/src-tauri/src/json.rs index e730f06..336c385 100644 --- a/src-tauri/src/json.rs +++ b/src-tauri/src/json.rs @@ -1,15 +1,28 @@ use std::path::PathBuf; -use serde::{Deserialize, Serializer}; +use serde::{Deserialize, Serialize, Serializer}; -pub type Error = serde_path_to_error::Error; +#[derive(Debug, Serialize, thiserror::Error)] +pub enum Error { + #[error("failed to parse JSON: {0}")] + Json(String), + #[error("failed to serialize JSON: {0}")] + Serialize(String), +} pub fn from_str<'de, T>(text: &'de str) -> Result where T: Deserialize<'de>, { let des = &mut serde_json::Deserializer::from_str(text); - serde_path_to_error::deserialize::<_, T>(des) + serde_path_to_error::deserialize::<_, T>(des).map_err(|err| Error::Json(err.to_string())) +} + +pub fn serialize_pretty(value: &T) -> Result +where + T: Serialize, +{ + serde_json::to_string_pretty(value).map_err(|err| Error::Serialize(err.to_string())) } pub fn serialize_path(path: &Option, serializer: S) -> Result diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e3b0d60..d330e98 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,22 +1,19 @@ -use crate::almanax::get_almanax; -use crate::api::is_app_version_old; -use crate::conf::{get_conf, reset_conf, set_conf, toggle_guide_checkbox}; +use crate::almanax::{AlmanaxApi, AlmanaxApiImpl}; +use crate::api::{Api, ApiImpl}; +use crate::conf::{ConfApi, ConfApiImpl}; use crate::first_start::FirstStartExt; -use crate::guides::{ - download_default_guide, download_guide_from_server, get_flat_guides, get_guide_from_server, - get_guides, get_guides_from_server, open_guides_folder, -}; -use crate::id::new_id; -use crate::image::fetch_image; -use crate::security::get_white_list; +use crate::guides::{download_default_guide, GuidesApi, GuidesApiImpl}; +use crate::image::{ImageApi, ImageApiImpl}; +use crate::security::{SecurityApi, SecurityApiImpl}; use crate::shortcut::handle_shortcuts; -use crate::update::start_update; +use crate::update::{UpdateApi, UpdateApiImpl}; use log::{error, info, LevelFilter}; -use tauri::Wry; +use tauri::AppHandle; use tauri_plugin_log::{Target, TargetKind}; +use tauri_plugin_opener::OpenerExt; #[cfg(not(dev))] use tauri_plugin_sentry::{minidump, sentry}; -use tauri_plugin_shell::ShellExt; +use taurpc::Router; mod almanax; mod api; @@ -24,7 +21,6 @@ mod conf; mod event; mod first_start; mod guides; -mod id; mod image; mod item; mod json; @@ -34,14 +30,6 @@ mod shortcut; mod tauri_api_ext; mod update; -#[tauri::command] -async fn open_in_shell( - app: tauri::AppHandle, - href: String, -) -> Result<(), tauri_plugin_shell::Error> { - app.shell().open(href, None) -} - #[cfg(debug_assertions)] const LOG_TARGETS: [Target; 2] = [ Target::new(TargetKind::Stdout), @@ -51,9 +39,33 @@ const LOG_TARGETS: [Target; 2] = [ #[cfg(not(debug_assertions))] const LOG_TARGETS: [Target; 2] = [ Target::new(TargetKind::Stdout), - Target::new(TargetKind::LogDir), + Target::new(TargetKind::LogDir { file_name: None }), ]; +#[taurpc::procedures(path = "base", export_to = "../src/ipc/bindings.ts")] +trait BaseApi { + #[taurpc(alias = "newId")] + async fn new_id() -> String; + #[taurpc(alias = "openUrl")] + async fn open_url(app_handle: AppHandle, url: String) -> Result<(), String>; +} + +#[derive(Clone)] +struct BaseApiImpl; + +#[taurpc::resolvers] +impl BaseApi for BaseApiImpl { + async fn new_id(self) -> String { + uuid::Uuid::new_v4().to_string() + } + + async fn open_url(self, app: AppHandle, url: String) -> Result<(), String> { + app.opener() + .open_url(url, None::) + .map_err(|err| err.to_string()) + } +} + // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { @@ -76,13 +88,13 @@ pub fn run() { }; let app = tauri::Builder::default() + .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_log::Builder::new().build()) .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_window_state::Builder::new().build()) - .plugin(tauri_plugin_shell::init()) .plugin( tauri_plugin_log::Builder::new() .clear_targets() @@ -97,6 +109,16 @@ pub fn run() { #[cfg(not(dev))] let app = app.plugin(tauri_plugin_sentry::init_with_no_injection(&sentry_client)); + let router = Router::new() + .merge(BaseApiImpl.into_handler()) + .merge(AlmanaxApiImpl.into_handler()) + .merge(GuidesApiImpl.into_handler()) + .merge(ApiImpl.into_handler()) + .merge(SecurityApiImpl.into_handler()) + .merge(ImageApiImpl.into_handler()) + .merge(UpdateApiImpl.into_handler()) + .merge(ConfApiImpl.into_handler()); + app.setup(|app| { if let Err(err) = crate::conf::ensure(app.handle()) { error!("[Lib] failed to ensure conf: {:?}", err); @@ -162,25 +184,7 @@ pub fn run() { Ok(()) }) - .invoke_handler(tauri::generate_handler![ - get_conf, - set_conf, - new_id, - get_guides_from_server, - get_guides, - download_guide_from_server, - get_almanax, - open_guides_folder, - toggle_guide_checkbox, - open_in_shell, - fetch_image, - get_guide_from_server, - is_app_version_old, - start_update, - reset_conf, - get_white_list, - get_flat_guides - ]) + .invoke_handler(router.into_handler()) .run(tauri::generate_context!()) .expect("[Lib] error while running tauri application"); } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 0fd7ebb..b3ee54e 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,6 +1,7 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -fn main() { +#[tokio::main] +async fn main() { ganymede_app_lib::run() } diff --git a/src-tauri/src/quest.rs b/src-tauri/src/quest.rs index 49b1cc1..6d21c9e 100644 --- a/src-tauri/src/quest.rs +++ b/src-tauri/src/quest.rs @@ -3,26 +3,14 @@ use tauri_plugin_http::reqwest; use crate::api::DOFUSDB_API; +#[derive(Debug, Serialize, thiserror::Error)] pub enum Error { - RequestQuest(reqwest::Error), - RequestQuestContent(reqwest::Error), - DofusDbQuestMalformed(crate::json::Error), -} - -impl Into for Error { - fn into(self) -> tauri::ipc::InvokeError { - match self { - Error::RequestQuest(err) => { - tauri::ipc::InvokeError::from(format!("RequestQuest({})", err.to_string())) - } - Error::RequestQuestContent(err) => { - tauri::ipc::InvokeError::from(format!("RequestQuestContent({})", err.to_string())) - } - Error::DofusDbQuestMalformed(err) => { - tauri::ipc::InvokeError::from(format!("DofusDbQuestMalformed({})", err.to_string())) - } - } - } + #[error("failed to request quest data: {0}")] + RequestQuest(String), + #[error("failed to request quest content: {0}")] + RequestQuestContent(String), + #[error("failed to parse quest data")] + DofusDbQuestMalformed(#[from] crate::json::Error), } #[derive(Serialize, Deserialize, Debug)] @@ -116,9 +104,12 @@ pub async fn get_quest_data(id: u32) -> Result { DOFUSDB_API, id )) .await - .map_err(Error::RequestQuest)?; + .map_err(|err| Error::RequestQuest(err.to_string()))?; - let text = res.text().await.map_err(Error::RequestQuestContent)?; + let text = res + .text() + .await + .map_err(|err| Error::RequestQuestContent(err.to_string()))?; crate::json::from_str::(text.as_str()).map_err(Error::DofusDbQuestMalformed) } diff --git a/src-tauri/src/security.rs b/src-tauri/src/security.rs index 9735e20..955c335 100644 --- a/src-tauri/src/security.rs +++ b/src-tauri/src/security.rs @@ -29,7 +29,18 @@ const VALID_LINKS: [&'static str; 28] = [ "https://gamosaurus.com", ]; -#[tauri::command] -pub fn get_white_list<'a>() -> Result, ()> { - Ok(VALID_LINKS.to_vec()) +#[taurpc::procedures(path = "security", export_to = "../src/ipc/bindings.ts")] +pub trait SecurityApi { + #[taurpc(alias = "getWhiteList")] + async fn get_white_list() -> Result, ()>; +} + +#[derive(Clone)] +pub struct SecurityApiImpl; + +#[taurpc::resolvers] +impl SecurityApi for SecurityApiImpl { + async fn get_white_list(self) -> Result, ()> { + Ok(VALID_LINKS.to_vec().iter().map(|s| s.to_string()).collect()) + } } diff --git a/src-tauri/src/update.rs b/src-tauri/src/update.rs index 3186a35..599562c 100644 --- a/src-tauri/src/update.rs +++ b/src-tauri/src/update.rs @@ -3,36 +3,47 @@ use log::info; use tauri::{AppHandle, Emitter}; use tauri_plugin_updater::UpdaterExt; -#[tauri::command] -pub async fn start_update(app: AppHandle) -> tauri_plugin_updater::Result<()> { - if let Some(update) = app.updater()?.check().await? { - app.emit(Event::UpdateStarted.into(), 0) - .expect("[Update] failed to emit update in progress event"); - - let mut downloaded = 0; - - // alternatively we could also call update.download() and update.install() separately - update - .download_and_install( - |chunk_length, content_length| { - downloaded += chunk_length; - app.emit(Event::UpdateInProgress.into(), (downloaded, content_length)) - .expect("[Update] failed to emit update in progress event"); - info!("[Update] downloaded {downloaded} from {content_length:?}"); - }, - || { - app.emit(Event::UpdateFinished.into(), 0) - .expect("[Update] failed to emit update finished event"); - info!("[Update] download finished"); - }, - ) - .await?; - - info!("[Update] update installed"); - - // not required, but we can restart the app after the update - app.restart(); - } +#[taurpc::procedures(path = "update", export_to = "../src/ipc/bindings.ts")] +pub trait UpdateApi { + #[taurpc(alias = "startUpdate")] + async fn start_update(app_handle: AppHandle) -> tauri_plugin_updater::Result<()>; +} + +#[derive(Clone)] +pub struct UpdateApiImpl; + +#[taurpc::resolvers] +impl UpdateApi for UpdateApiImpl { + async fn start_update(self, app: AppHandle) -> tauri_plugin_updater::Result<()> { + if let Some(update) = app.updater()?.check().await? { + app.emit(Event::UpdateStarted.into(), 0) + .expect("[Update] failed to emit update in progress event"); + + let mut downloaded = 0; - Ok(()) + // alternatively we could also call update.download() and update.install() separately + update + .download_and_install( + |chunk_length, content_length| { + downloaded += chunk_length; + app.emit(Event::UpdateInProgress.into(), (downloaded, content_length)) + .expect("[Update] failed to emit update in progress event"); + info!("[Update] downloaded {downloaded} from {content_length:?}"); + }, + || { + app.emit(Event::UpdateFinished.into(), 0) + .expect("[Update] failed to emit update finished event"); + info!("[Update] download finished"); + }, + ) + .await?; + + info!("[Update] update installed"); + + // not required, but we can restart the app after the update + app.restart(); + } + + Ok(()) + } } diff --git a/src/components/almanax-frame.tsx b/src/components/almanax-frame.tsx index 1b3880e..b45f9f5 100644 --- a/src/components/almanax-frame.tsx +++ b/src/components/almanax-frame.tsx @@ -1,5 +1,6 @@ import kamasIcon from '@/assets/kamas.webp' import xpIcon from '@/assets/xp.webp' +import { getLang } from '@/lib/conf.ts' import { almanaxQuery } from '@/queries/almanax.query' import { confQuery } from '@/queries/conf.query' import { useQuery, useSuspenseQuery } from '@tanstack/react-query' @@ -8,7 +9,7 @@ import { DownloadImage } from './download-image' export function AlmanaxFrame() { const conf = useSuspenseQuery(confQuery) - const almanax = useQuery(almanaxQuery(conf.data.lang)) + const almanax = useQuery(almanaxQuery(getLang(conf.data.lang))) return (
diff --git a/src/components/download-image.tsx b/src/components/download-image.tsx index e71bd96..2601eca 100644 --- a/src/components/download-image.tsx +++ b/src/components/download-image.tsx @@ -1,60 +1,60 @@ -import { cn } from '@/lib/utils' -import { useQuery } from '@tanstack/react-query' -import { invoke } from '@tauri-apps/api/core' -import { error } from '@tauri-apps/plugin-log' -import { fromPromise } from 'neverthrow' -import { ComponentProps } from 'react' -import { GenericLoader } from './generic-loader' - -class FetchImageError extends Error {} - -export function DownloadImage({ - src, - loaderClassName, - ...props -}: Omit, 'srcset'> & { - loaderClassName?: string -}) { - const enabled = !!src && src.startsWith('http') && (src.includes('dofusdb.fr') || src.includes('ganymede-dofus.com')) - const image = useQuery({ - queryKey: ['image', src], - queryFn: async () => { - // should not happen with enabled: !!src - if (!src) throw new Error('No image source provided') - - const data = await fromPromise( - invoke('fetch_image', { url: src }), - (err) => new FetchImageError('Failed to load image', { cause: err }), - ) - - if (data.isErr()) throw data.error - - const binary = String.fromCharCode(...new Uint8Array(data.value)) - - return btoa(binary) - }, - enabled, - staleTime: Infinity, - retry: 2, - }) - - if (!enabled) return - - if (image.isLoading) { - return ( - - - - ) - } - - if (image.isError) { - error(`Failed to load image: ${src}. ${image.error.message}`).then(() => { - error(image.error.stack ?? 'No stack trace') - }) - - return - } - - return -} +import { taurpc } from '@/ipc/ipc.ts' +import { cn } from '@/lib/utils' +import { useQuery } from '@tanstack/react-query' +import { error } from '@tauri-apps/plugin-log' +import { fromPromise } from 'neverthrow' +import { ComponentProps } from 'react' +import { GenericLoader } from './generic-loader' + +class FetchImageError extends Error {} + +export function DownloadImage({ + src, + loaderClassName, + ...props +}: Omit, 'srcset'> & { + loaderClassName?: string +}) { + const enabled = !!src && src.startsWith('http') && (src.includes('dofusdb.fr') || src.includes('ganymede-dofus.com')) + const image = useQuery({ + queryKey: ['image', src], + queryFn: async () => { + // should not happen with enabled: !!src + if (!src) throw new Error('No image source provided') + + const data = await fromPromise( + taurpc.image.fetchImage(src), + (err) => new FetchImageError('Failed to load image', { cause: err }), + ) + + if (data.isErr()) throw data.error + + const binary = String.fromCharCode(...new Uint8Array(data.value)) + + return btoa(binary) + }, + enabled, + staleTime: Infinity, + retry: 2, + }) + + if (!enabled) return + + if (image.isLoading) { + return ( + + + + ) + } + + if (image.isError) { + error(`Failed to load image: ${src}. ${image.error.message}`).then(() => { + error(image.error.stack ?? 'No stack trace') + }) + + return + } + + return +} diff --git a/src/components/error-component.tsx b/src/components/error-component.tsx index d7c8228..23f2af8 100644 --- a/src/components/error-component.tsx +++ b/src/components/error-component.tsx @@ -1,22 +1,22 @@ +import { ConfLang } from '@/ipc/bindings.ts' +import { GetConfError } from '@/ipc/conf' +import { useResetConf } from '@/mutations/reset-conf.mutation' import { confQuery } from '@/queries/conf.query' -import { Lang } from '@/types/conf' import { Trans } from '@lingui/macro' import { useQuery } from '@tanstack/react-query' import { ErrorComponentProps, useLocation } from '@tanstack/react-router' import { TriangleAlertIcon } from 'lucide-react' import { useState } from 'react' -import { GetConfError } from '@/ipc/conf' import { PageScrollableContent } from './page-scrollable-content' import { SelectLangLabel, SelectLangSelect } from './select-lang' import { Alert, AlertDescription, AlertTitle } from './ui/alert' import { Button } from './ui/button' -import { useResetConf } from '@/mutations/reset-conf.mutation' export function ErrorComponent({ error, reset, info }: ErrorComponentProps) { const location = useLocation() const isMacOs = navigator.userAgent.toLowerCase().includes('mac os x') const conf = useQuery(confQuery) - const [lang, setLang] = useState('Fr') + const [lang, setLang] = useState('Fr') const resetConf = useResetConf() const onClickResetConf = () => { @@ -32,7 +32,7 @@ export function ErrorComponent({ error, reset, info }: ErrorComponentProps) { id="select-lang" value={lang} onValueChange={(value) => { - setLang(value as Lang) + setLang(value as ConfLang) }} /> diff --git a/src/components/guide-card.tsx b/src/components/guide-card.tsx index 124accd..118795a 100644 --- a/src/components/guide-card.tsx +++ b/src/components/guide-card.tsx @@ -1,9 +1,9 @@ import { Button } from '@/components/ui/button.tsx' +import { Guide } from '@/ipc/bindings.ts' import { getGuideById, isGuideNew } from '@/lib/guide.ts' import { useDownloadGuideFromServer } from '@/mutations/download-guide-from-server.mutation.ts' import { guidesFromServerQuery } from '@/queries/guides-from-server.query.ts' import { guidesQuery } from '@/queries/guides.query.ts' -import { Guide } from '@/types/guide.ts' import { t } from '@lingui/macro' import { useSuspenseQuery } from '@tanstack/react-query' import { CircleAlertIcon, CircleCheckIcon, ImportIcon, LoaderIcon } from 'lucide-react' diff --git a/src/components/guide-frame.tsx b/src/components/guide-frame.tsx index 0f79a58..e05cad3 100644 --- a/src/components/guide-frame.tsx +++ b/src/components/guide-frame.tsx @@ -50,7 +50,12 @@ export function GuideFrame({ replace: (domNode) => { // #region positions if (domNode.type === 'text') { - if (domNode.data.includes('https://') || domNode.data.includes('http://')) { + const href = domNode.data + const isHrefHttp = href !== '' && href.startsWith('http') + const url = isHrefHttp ? new URL(href) : undefined + const isValid = url !== undefined ? whiteList.data.includes(`${url.protocol}//${url.hostname}`) : false + + if (isHrefHttp && !isValid) { return lien masqué } diff --git a/src/ipc/almanax.ts b/src/ipc/almanax.ts index 0b4ba6e..6845f9f 100644 --- a/src/ipc/almanax.ts +++ b/src/ipc/almanax.ts @@ -1,5 +1,4 @@ -import { AlmanaxZod } from '@/types/almanax.ts' -import { invoke } from '@tauri-apps/api/core' +import { taurpc } from '@/ipc/ipc.ts' import { fromPromise } from 'neverthrow' export class GetAlmanaxError extends Error { @@ -9,7 +8,5 @@ export class GetAlmanaxError extends Error { } export function getAlmanax() { - return fromPromise(invoke('get_almanax'), GetAlmanaxError.from).map((response) => { - return AlmanaxZod.parseAsync(response) - }) + return fromPromise(taurpc.almanax.get(), GetAlmanaxError.from) } diff --git a/src/ipc/bindings.ts b/src/ipc/bindings.ts new file mode 100644 index 0000000..a13280c --- /dev/null +++ b/src/ipc/bindings.ts @@ -0,0 +1,87 @@ +// This file has been generated by Specta. DO NOT EDIT. + +export type AlmanaxReward = { name: string; quantity: number; kamas: number; experience: number; bonus: string; img: string | null } + +export type AutoPilot = { name: string; position: string } + +export type Conf = { autoTravelCopy: boolean; showDoneGuides: boolean; lang?: ConfLang; fontSize?: FontSize; profiles: Profile[]; profileInUse: string; autoPilots: AutoPilot[]; notes: Note[]; opacity: number } + +export type ConfLang = "En" | "Fr" | "Es" | "Pt" + +export type ConfStep = { checkboxes: number[] } + +export type Folder = { name: string } + +export type FontSize = "ExtraSmall" | "Small" | "Normal" | "Large" | "ExtraLarge" + +export type Guide = { id: number; name: string; status: Status; likes: number; dislikes: number; downloads: number | null; created_at: string; deleted_at: string | null; updated_at: string | null; lang: GuideLang; order: number; user: User; user_id: number; description: string | null; web_description: string | null } + +export type GuideLang = "en" | "fr" | "es" | "pt" + +export type GuideStep = { name: string | null; map: string | null; pos_x: number; pos_y: number; web_text: string } + +export type GuideWithSteps = { id: number; name: string; description: string | null; status: Status; likes: number; dislikes: number; downloads: number | null; deleted_at: string | null; updated_at: string | null; lang: GuideLang; order: number; user: User; web_description: string | null; steps: GuideStep[] } + +export type Guides = { guides: GuideWithSteps[] } + +export type GuidesOrFolder = ({ type: "guide" } & GuideWithSteps) | ({ type: "folder" } & Folder) + +export type IsOld = { from: string; to: string; isOld: boolean } + +export type Note = { name: string; text: string } + +export type Profile = { id: string; name: string; progresses: Progress[] } + +export type Progress = { id: number; currentStep: number; steps: { [key in number]: ConfStep } } + +export type Status = "draft" | "public" | "private" | "certified" | "gp" + +export type TauRpcAlmanaxApiInputTypes = { proc_name: "get"; input_type: null } + +export type TauRpcAlmanaxApiOutputTypes = { proc_name: "get"; output_type: AlmanaxReward } + +export type TauRpcApiInputTypes = { proc_name: "isAppVersionOld"; input_type: null } + +export type TauRpcApiOutputTypes = { proc_name: "isAppVersionOld"; output_type: IsOld } + +export type TauRpcBaseApiInputTypes = { proc_name: "newId"; input_type: null } | { proc_name: "openUrl"; input_type: { __taurpc_type: string } } + +export type TauRpcBaseApiOutputTypes = { proc_name: "newId"; output_type: string } | { proc_name: "openUrl"; output_type: null } + +export type TauRpcConfApiInputTypes = { proc_name: "get"; input_type: null } | { proc_name: "set"; input_type: { __taurpc_type: Conf } } | { proc_name: "toggleGuideCheckbox"; input_type: [number, number, number] } | { proc_name: "reset"; input_type: null } + +export type TauRpcConfApiOutputTypes = { proc_name: "get"; output_type: Conf } | { proc_name: "set"; output_type: null } | { proc_name: "toggleGuideCheckbox"; output_type: number } | { proc_name: "reset"; output_type: null } + +export type TauRpcGuidesApiInputTypes = { proc_name: "getFlatGuides"; input_type: { __taurpc_type: string } } | { proc_name: "getGuides"; input_type: { __taurpc_type: string | null } } | { proc_name: "getGuideFromServer"; input_type: { __taurpc_type: number } } | { proc_name: "getGuidesFromServer"; input_type: { __taurpc_type: Status } } | { proc_name: "downloadGuideFromServer"; input_type: [number, string] } | { proc_name: "openGuidesFolder"; input_type: null } + +export type TauRpcGuidesApiOutputTypes = { proc_name: "getFlatGuides"; output_type: GuideWithSteps[] } | { proc_name: "getGuides"; output_type: GuidesOrFolder[] } | { proc_name: "getGuideFromServer"; output_type: GuideWithSteps } | { proc_name: "getGuidesFromServer"; output_type: Guide[] } | { proc_name: "downloadGuideFromServer"; output_type: Guides } | { proc_name: "openGuidesFolder"; output_type: null } + +export type TauRpcImageApiInputTypes = { proc_name: "fetchImage"; input_type: { __taurpc_type: string } } + +export type TauRpcImageApiOutputTypes = { proc_name: "fetchImage"; output_type: number[] } + +export type TauRpcSecurityApiInputTypes = { proc_name: "getWhiteList"; input_type: null } + +export type TauRpcSecurityApiOutputTypes = { proc_name: "getWhiteList"; output_type: string[] } + +export type TauRpcUpdateApiInputTypes = { proc_name: "startUpdate"; input_type: null } + +export type TauRpcUpdateApiOutputTypes = { proc_name: "startUpdate"; output_type: null } + +export type User = { id: number; name: string; is_admin: number; is_certified: number } + +const ARGS_MAP = {'almanax':'{"get":[]}', 'update':'{"startUpdate":[]}', 'conf':'{"toggleGuideCheckbox":["guide_id","step_index","checkbox_index"],"set":["conf"],"get":[],"reset":[]}', 'guides':'{"openGuidesFolder":[],"getFlatGuides":["folder"],"getGuideFromServer":["guide_id"],"getGuides":["folder"],"downloadGuideFromServer":["guide_id","folder"],"getGuidesFromServer":["status"]}', 'base':'{"newId":[],"openUrl":["url"]}', '':'{"isAppVersionOld":[]}', 'image':'{"fetchImage":["url"]}', 'security':'{"getWhiteList":[]}'} +import { createTauRPCProxy as createProxy } from "taurpc" + +export const createTauRPCProxy = () => createProxy(ARGS_MAP) + +type Router = { + 'base': [TauRpcBaseApiInputTypes, TauRpcBaseApiOutputTypes], + 'almanax': [TauRpcAlmanaxApiInputTypes, TauRpcAlmanaxApiOutputTypes], + 'guides': [TauRpcGuidesApiInputTypes, TauRpcGuidesApiOutputTypes], + '': [TauRpcApiInputTypes, TauRpcApiOutputTypes], + 'security': [TauRpcSecurityApiInputTypes, TauRpcSecurityApiOutputTypes], + 'image': [TauRpcImageApiInputTypes, TauRpcImageApiOutputTypes], + 'update': [TauRpcUpdateApiInputTypes, TauRpcUpdateApiOutputTypes], + 'conf': [TauRpcConfApiInputTypes, TauRpcConfApiOutputTypes], +} \ No newline at end of file diff --git a/src/ipc/conf.ts b/src/ipc/conf.ts index ba2aae1..be355e1 100644 --- a/src/ipc/conf.ts +++ b/src/ipc/conf.ts @@ -1,5 +1,5 @@ -import { Conf, ConfZod } from '@/types/conf.ts' -import { invoke } from '@tauri-apps/api/core' +import { Conf } from '@/ipc/bindings.ts' +import { taurpc } from '@/ipc/ipc.ts' import { fromPromise } from 'neverthrow' export class GetConfError extends Error { @@ -20,16 +20,24 @@ export class ResetConfError extends Error { } } +export class ToggleGuideCheckboxError extends Error { + static from(error: unknown) { + return new ToggleGuideCheckboxError('Failed to toggle checkbox guide', { cause: error }) + } +} + export function getConf() { - return fromPromise(invoke('get_conf'), GetConfError.from).map((response) => { - return ConfZod.parseAsync(response) - }) + return fromPromise(taurpc.conf.get(), GetConfError.from) } export async function setConf(conf: Conf) { - return fromPromise(invoke('set_conf', { conf }), SetConfError.from) + return fromPromise(taurpc.conf.set(conf), SetConfError.from) } export async function resetConf() { - return fromPromise(invoke('reset_conf'), ResetConfError.from) + return fromPromise(taurpc.conf.reset(), ResetConfError.from) +} + +export function toggleGuideCheckbox(guideId: number, checkboxIndex: number, stepIndex: number) { + return fromPromise(taurpc.conf.toggleGuideCheckbox(guideId, stepIndex, checkboxIndex), ToggleGuideCheckboxError.from) } diff --git a/src/ipc/guide_from_server.ts b/src/ipc/guide_from_server.ts index ae4d34f..3e61f80 100644 --- a/src/ipc/guide_from_server.ts +++ b/src/ipc/guide_from_server.ts @@ -1,5 +1,4 @@ -import { GuideWithStepsZod } from '@/types/download' -import { invoke } from '@tauri-apps/api/core' +import { taurpc } from '@/ipc/ipc.ts' import { fromPromise } from 'neverthrow' export class GetGuideFromServerError extends Error { @@ -9,7 +8,5 @@ export class GetGuideFromServerError extends Error { } export function getGuideFromServer(guideId: number) { - return fromPromise(invoke('get_guide_from_server', { guideId }), GetGuideFromServerError.from).map((res) => { - return GuideWithStepsZod.parseAsync(res) - }) + return fromPromise(taurpc.guides.getGuideFromServer(guideId), GetGuideFromServerError.from) } diff --git a/src/ipc/guides.ts b/src/ipc/guides.ts index 864003c..c624e81 100644 --- a/src/ipc/guides.ts +++ b/src/ipc/guides.ts @@ -1,13 +1,26 @@ -import { GuideInFolder, GuidesZod } from '@/types/download.ts' -import { invoke } from '@tauri-apps/api/core' -import { fromPromise } from 'neverthrow' +import { GuideWithStepsWithFolder, taurpc } from '@/ipc/ipc.ts' +import { error } from '@tauri-apps/plugin-log' +import { ResultAsync, fromPromise } from 'neverthrow' export class GetGuidesError extends Error { static from(error: unknown) { + console.log(error) return new GetGuidesError('Failed to get guides', { cause: error }) } } +export class GetFlatGuidesError extends Error { + static from(err: unknown) { + error('Failed to get flat guides').then(() => { + return error(JSON.stringify(err, undefined, 2)) + }) + + return new GetFlatGuidesError('Failed to get flat guides', { + cause: !(err instanceof Error) ? new Error(JSON.stringify(err, undefined, 2)) : err, + }) + } +} + export class DownloadGuideFromServerError extends Error { static from(error: unknown) { return new DownloadGuideFromServerError('Failed to download guide', { cause: error }) @@ -20,12 +33,6 @@ export class OpenGuidesFolderError extends Error { } } -export class ToggleGuideCheckboxError extends Error { - static from(error: unknown) { - return new ToggleGuideCheckboxError('Failed to toggle checkbox guide', { cause: error }) - } -} - export class OpenGuidesLinkError extends Error { static from(error: unknown) { return new OpenGuidesLinkError('Failed to open guide link', { cause: error }) @@ -33,32 +40,24 @@ export class OpenGuidesLinkError extends Error { } export function getGuides(folder?: string) { - return fromPromise(invoke('get_guides', { folder }), GetGuidesError.from).map((response) => { - return GuideInFolder.parseAsync(response) - }) + return fromPromise(taurpc.guides.getGuides(folder ?? null), GetGuidesError.from) } export function getFlatGuides(folder: string) { - return fromPromise(invoke('get_flat_guides', { folder }), GetGuidesError.from).map((response) => { - return GuidesZod.parseAsync(response) - }) + return fromPromise(taurpc.guides.getFlatGuides(folder), GetFlatGuidesError.from) as ResultAsync< + GuideWithStepsWithFolder[], + GetFlatGuidesError + > } export async function downloadGuideFromServer(guideId: number, folder: string) { - return fromPromise(invoke('download_guide_from_server', { guideId, folder }), DownloadGuideFromServerError.from) + return fromPromise(taurpc.guides.downloadGuideFromServer(guideId, folder), DownloadGuideFromServerError.from) } export async function openGuidesFolder() { - return fromPromise(invoke('open_guides_folder'), OpenGuidesFolderError.from) -} - -export async function toggleGuideCheckbox(guideId: number, checkboxIndex: number, stepIndex: number) { - return fromPromise( - invoke('toggle_guide_checkbox', { guideId, checkboxIndex, stepIndex }), - ToggleGuideCheckboxError.from, - ) + return fromPromise(taurpc.guides.openGuidesFolder(), OpenGuidesFolderError.from) } export async function openGuideLink(href: string) { - return fromPromise(invoke('open_in_shell', { href }), OpenGuidesLinkError.from) + return fromPromise(taurpc.base.openUrl(href), OpenGuidesLinkError.from) } diff --git a/src/ipc/guides_from_server.ts b/src/ipc/guides_from_server.ts index a6a57e4..8459b53 100644 --- a/src/ipc/guides_from_server.ts +++ b/src/ipc/guides_from_server.ts @@ -1,7 +1,6 @@ -import { GuideZod } from '@/types/guide.ts' -import { invoke } from '@tauri-apps/api/core' +import { Status } from '@/ipc/bindings.ts' +import { taurpc } from '@/ipc/ipc.ts' import { fromPromise } from 'neverthrow' -import { z } from 'zod' export class GetGuidesFromServerError extends Error { static from(error: unknown) { @@ -9,8 +8,6 @@ export class GetGuidesFromServerError extends Error { } } -export function getGuidesFromServer(status: string) { - return fromPromise(invoke('get_guides_from_server', { status }), GetGuidesFromServerError.from).map((res) => { - return z.array(GuideZod).parseAsync(res) - }) +export function getGuidesFromServer(status: Status) { + return fromPromise(taurpc.guides.getGuidesFromServer(status), GetGuidesFromServerError.from) } diff --git a/src/ipc/id.ts b/src/ipc/id.ts index d47d1c0..22f80c0 100644 --- a/src/ipc/id.ts +++ b/src/ipc/id.ts @@ -1,6 +1,6 @@ -import { invoke } from '@tauri-apps/api/core' +import { taurpc } from '@/ipc/ipc.ts' import { fromPromise } from 'neverthrow' export function newId() { - return fromPromise(invoke('new_id'), (error) => new Error('Failed to generate new id', { cause: error })) + return fromPromise(taurpc.base.newId(), (error) => new Error('Failed to generate new id', { cause: error })) } diff --git a/src/ipc/ipc.ts b/src/ipc/ipc.ts new file mode 100644 index 0000000..1a1a81f --- /dev/null +++ b/src/ipc/ipc.ts @@ -0,0 +1,7 @@ +import { GuideWithSteps, createTauRPCProxy } from '@/ipc/bindings.ts' + +export const taurpc = await createTauRPCProxy() + +// specta-rs doesn't seem to resolve fields with "skip_deserializing" attribute, +// so we need to manually define the folder field here. +export type GuideWithStepsWithFolder = GuideWithSteps & { folder?: string } diff --git a/src/ipc/is_app_old_version.ts b/src/ipc/is_app_old_version.ts index e74699c..4a4bb59 100644 --- a/src/ipc/is_app_old_version.ts +++ b/src/ipc/is_app_old_version.ts @@ -1,4 +1,4 @@ -import { invoke } from '@tauri-apps/api/core' +import { taurpc } from '@/ipc/ipc.ts' import { fromPromise } from 'neverthrow' class GetIsAppOldVersionError extends Error { @@ -8,8 +8,5 @@ class GetIsAppOldVersionError extends Error { } export function isAppOldVersion() { - return fromPromise( - invoke<{ from: string; to: string; isOld: boolean }>('is_app_version_old'), - GetIsAppOldVersionError.from, - ) + return fromPromise(taurpc.isAppVersionOld(), GetIsAppOldVersionError.from) } diff --git a/src/ipc/security.ts b/src/ipc/security.ts index 097b4df..c5c864e 100644 --- a/src/ipc/security.ts +++ b/src/ipc/security.ts @@ -1,12 +1,12 @@ -import { invoke } from '@tauri-apps/api/core' -import { fromPromise } from 'neverthrow' - -class CannotGetWhiteListError extends Error { - static from(err: unknown) { - return new CannotGetWhiteListError('cannot get white list', { cause: err }) - } -} - -export function getWhiteList() { - return fromPromise(invoke('get_white_list'), CannotGetWhiteListError.from) -} +import { taurpc } from '@/ipc/ipc.ts' +import { fromPromise } from 'neverthrow' + +class CannotGetWhiteListError extends Error { + static from(err: unknown) { + return new CannotGetWhiteListError('cannot get white list', { cause: err }) + } +} + +export function getWhiteList() { + return fromPromise(taurpc.security.getWhiteList(), CannotGetWhiteListError.from) +} diff --git a/src/ipc/update.ts b/src/ipc/update.ts index 5280778..f746154 100644 --- a/src/ipc/update.ts +++ b/src/ipc/update.ts @@ -1,5 +1,5 @@ -import { invoke } from '@tauri-apps/api/core' -import { fromPromise, ResultAsync } from 'neverthrow' +import { taurpc } from '@/ipc/ipc.ts' +import { fromPromise } from 'neverthrow' class StartUpdateError extends Error { static from(err: unknown) { @@ -7,6 +7,6 @@ class StartUpdateError extends Error { } } -export function startUpdate(): ResultAsync { - return fromPromise(invoke('start_update'), StartUpdateError.from) +export function startUpdate() { + return fromPromise(taurpc.update.startUpdate(), StartUpdateError.from) } diff --git a/src/lib/conf.ts b/src/lib/conf.ts new file mode 100644 index 0000000..269b36c --- /dev/null +++ b/src/lib/conf.ts @@ -0,0 +1,5 @@ +import { ConfLang } from '@/ipc/bindings.ts' + +export function getLang(lang?: ConfLang): ConfLang { + return lang ?? 'Fr' +} diff --git a/src/lib/guide.ts b/src/lib/guide.ts index 2b9fa3a..435d4e0 100644 --- a/src/lib/guide.ts +++ b/src/lib/guide.ts @@ -1,5 +1,5 @@ -import { GuideWithSteps } from '@/types/download.ts' -import { Guide } from '@/types/guide.ts' +import { Guide, GuideWithSteps } from '@/ipc/bindings.ts' +import { GuideWithStepsWithFolder } from '@/ipc/ipc.ts' /** * Check if a guide is new compared to another guide. @@ -8,14 +8,20 @@ import { Guide } from '@/types/guide.ts' * @param guide * @param otherGuide */ -export function isGuideNew(guide?: Guide | GuideWithSteps, otherGuide?: Guide | GuideWithSteps): boolean { +export function isGuideNew( + guide?: Guide | GuideWithSteps | GuideWithStepsWithFolder, + otherGuide?: Guide | GuideWithSteps | GuideWithStepsWithFolder, +): boolean { if (!guide?.updated_at || !otherGuide?.updated_at) { return false } - return +guide.updated_at !== +otherGuide.updated_at + return new Date(guide.updated_at).getTime() !== new Date(otherGuide.updated_at).getTime() } -export function getGuideById(guides: T[], id: number): T | undefined { +export function getGuideById( + guides: T[], + id: number, +): T | undefined { return guides.find((guide) => guide.id === id) } diff --git a/src/lib/profile.ts b/src/lib/profile.ts index b808271..d0fe61d 100644 --- a/src/lib/profile.ts +++ b/src/lib/profile.ts @@ -1,5 +1,4 @@ -import { Conf } from '@/types/conf' -import { Profile } from '@/types/profile.ts' +import { Conf, Profile } from '@/ipc/bindings.ts' export function getProfile(conf: Conf) { return conf.profiles.find((profile) => profile.id === conf.profileInUse) ?? conf.profiles[0] diff --git a/src/lib/progress.ts b/src/lib/progress.ts index 3159631..896477a 100644 --- a/src/lib/progress.ts +++ b/src/lib/progress.ts @@ -1,6 +1,6 @@ -import { GuideProgress, Profile } from '@/types/profile' +import { ConfStep, Profile, Progress } from '@/ipc/bindings.ts' -export function newProgress(guideId: number): GuideProgress { +export function newProgress(guideId: number): Progress { return { id: guideId, currentStep: 0, @@ -8,10 +8,10 @@ export function newProgress(guideId: number): GuideProgress { } } -export function getProgress(profile: Profile, guideId: number): GuideProgress | undefined { +export function getProgress(profile: Profile, guideId: number): Progress | undefined { return profile.progresses.find((p) => p.id === guideId) } -export function getStep(progress: GuideProgress, stepIndex: number): GuideProgress['steps'][number] | undefined { +export function getStep(progress: Progress, stepIndex: number): ConfStep | undefined { return progress.steps[stepIndex] } diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 095a1e8..c0d5e0e 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -13,7 +13,7 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" -#: src/routes/app-old-version.tsx:84 +#: src/routes/app-old-version.tsx:86 msgid "<0>Vous trouverez les notes de version sur notre Discord <1>#changelog" msgstr "<0>You can find the release notes on our Discord <1>#changelog" @@ -29,7 +29,7 @@ msgstr "Display completed guides" msgid "Ajouter" msgstr "Add" -#: src/routes/_app/downloads/$status.tsx:140 +#: src/routes/_app/downloads/$status.tsx:141 msgid "Attention" msgstr "Warning" @@ -37,11 +37,11 @@ msgstr "Warning" msgid "Attention !" msgstr "Warning!" -#: src/routes/_app/downloads/$status.tsx:160 +#: src/routes/_app/downloads/$status.tsx:161 msgid "Aucun guides trouvé" msgstr "No guides found" -#: src/routes/_app/downloads/$status.tsx:176 +#: src/routes/_app/downloads/$status.tsx:177 msgid "Aucun guides trouvé avec {term}" msgstr "No guides found with {term}" @@ -80,40 +80,40 @@ msgstr "This tool is designed to assist you throughout your adventure on <0>Dofu msgid "Chasse au trésor" msgstr "Hunt" -#: src/components/guide-frame.tsx:250 +#: src/components/guide-frame.tsx:255 msgid "Cliquez pour copier le nom de l'objet. Cmd+clic pour ouvrir sur dofusdb" msgstr "Click to copy the item's name. Cmd+click to open on dofusdb" -#: src/components/guide-frame.tsx:251 +#: src/components/guide-frame.tsx:256 msgid "Cliquez pour copier le nom de l'objet. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Click to copy the item's name. Ctrl+click to open on dofusdb" -#: src/components/guide-frame.tsx:258 +#: src/components/guide-frame.tsx:263 msgid "Cliquez pour copier le nom de la quête. Cmd+clic pour ouvrir sur dofusdb" msgstr "Click to copy the quest's name. Cmd+click to open on dofusdb" -#: src/components/guide-frame.tsx:259 +#: src/components/guide-frame.tsx:264 msgid "Cliquez pour copier le nom de la quête. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Click to copy the quest's name. Ctrl+click to open on dofusdb" -#: src/components/guide-frame.tsx:246 +#: src/components/guide-frame.tsx:251 msgid "Cliquez pour copier le nom du donjon. Cmd+clic pour ouvrir sur dofusdb" msgstr "Click to copy the dungeon's name. Cmd+click to open on dofusdb" -#: src/components/guide-frame.tsx:247 +#: src/components/guide-frame.tsx:252 msgid "Cliquez pour copier le nom du donjon. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Click to copy the dungeon's name. Ctrl+click to open on dofusdb" -#: src/components/guide-frame.tsx:254 +#: src/components/guide-frame.tsx:259 msgid "Cliquez pour copier le nom du monstre. Cmd+clic pour ouvrir sur dofusdb" msgstr "Click to copy the monster's name. Cmd+click to open on dofusdb" -#: src/components/guide-frame.tsx:255 +#: src/components/guide-frame.tsx:260 msgid "Cliquez pour copier le nom du monstre. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Click to copy the monster's name. Ctrl+click to open on dofusdb" -#: src/components/guide-frame.tsx:293 -#: src/components/guide-frame.tsx:327 +#: src/components/guide-frame.tsx:298 +#: src/components/guide-frame.tsx:332 msgid "Cliquez pour ouvrir dans le navigateur" msgstr "Click to open in browser" @@ -159,7 +159,7 @@ msgstr "Create a new note" msgid "Créez vos guides via le site officiel et téléchargez ceux des autres !" msgstr "Create your guides on the official website and download those made by others!" -#: src/routes/_app/downloads/$status.tsx:197 +#: src/routes/_app/downloads/$status.tsx:212 msgid "de <0>{0}" msgstr "from <0>{0}" @@ -192,42 +192,42 @@ msgid "Grande" msgstr "Large" #: src/components/title-bar.tsx:49 -#: src/routes/_app/downloads/$status.tsx:94 +#: src/routes/_app/downloads/$status.tsx:93 #: src/routes/_app/guides/index.tsx:38 #: src/routes/_app/guides/index.tsx:135 msgid "Guides" msgstr "Guides" -#: src/routes/_app/downloads/$status.tsx:92 +#: src/routes/_app/downloads/$status.tsx:91 #: src/routes/_app/downloads/index.tsx:51 msgid "Guides certifiés" msgstr "Certified guides" -#: src/routes/_app/downloads/$status.tsx:88 +#: src/routes/_app/downloads/$status.tsx:87 #: src/routes/_app/downloads/index.tsx:57 msgid "Guides draft" msgstr "Draft guides" -#: src/routes/_app/downloads/$status.tsx:90 +#: src/routes/_app/downloads/$status.tsx:89 #: src/routes/_app/downloads/index.tsx:48 msgid "Guides principaux" msgstr "Main guides" -#: src/routes/_app/downloads/$status.tsx:86 +#: src/routes/_app/downloads/$status.tsx:85 msgid "Guides privés" msgstr "Private guides" -#: src/routes/_app/downloads/$status.tsx:84 +#: src/routes/_app/downloads/$status.tsx:83 #: src/routes/_app/downloads/index.tsx:54 msgid "Guides publics" msgstr "Publics guides" -#: src/routes/_app/downloads/$status.tsx:188 +#: src/routes/_app/downloads/$status.tsx:189 #: src/routes/_app/guides/index.tsx:201 msgid "id <0>{0}" msgstr "id <0>{0}" -#: src/routes/app-old-version.tsx:115 +#: src/routes/app-old-version.tsx:117 msgid "Il semblerait que la mise à jour ait eu un problème." msgstr "It seems that the update encountered an issue." @@ -236,7 +236,7 @@ msgstr "It seems that the update encountered an issue." msgid "Informations supplémentaires : {0}" msgstr "More info: {0}" -#: src/routes/_app/downloads/$status.tsx:151 +#: src/routes/_app/downloads/$status.tsx:152 msgid "J'ai compris" msgstr "I understand" @@ -248,11 +248,11 @@ msgstr "I understand" msgid "Langue" msgstr "Language" -#: src/routes/_app/downloads/$status.tsx:143 +#: src/routes/_app/downloads/$status.tsx:144 msgid "Les guides de cette section n'ont pas été vérifiés manuellement par l'équipe Ganymède. <0/> Bien que les liens vers les sites soient sécurisés, veuillez vérifier attentivement les guides <1>sur notre site avant de les télécharger." msgstr "Guides in this section have not been manually verified by the Ganymede team. <0/> Although the links to the sites are secure, please carefully check the guides <1>on our site before downloading them." -#: src/components/guide-frame.tsx:54 +#: src/components/guide-frame.tsx:59 msgid "lien masqué" msgstr "hidden link" @@ -260,11 +260,11 @@ msgstr "hidden link" msgid "Mettre à jour le guide" msgstr "Update the guide" -#: src/routes/app-old-version.tsx:97 +#: src/routes/app-old-version.tsx:99 msgid "Mise à jour en cours." msgstr "Update in progress." -#: src/routes/app-old-version.tsx:103 +#: src/routes/app-old-version.tsx:105 msgid "Mise à jour terminée. L'application va redémarrer." msgstr "Update completed. The application will restart." @@ -329,7 +329,7 @@ msgstr "Profiles" msgid "Rafraichir le dossier des guides téléchargés" msgstr "Refresh downloaded guides folder" -#: src/routes/_app/downloads/$status.tsx:171 +#: src/routes/_app/downloads/$status.tsx:172 #: src/routes/_app/guides/index.tsx:171 msgid "Rechercher un guide" msgstr "Search a guide" @@ -367,7 +367,7 @@ msgstr "Guides's font size" msgid "Téléchargements" msgstr "Downloads" -#: src/routes/app-old-version.tsx:114 +#: src/routes/app-old-version.tsx:116 msgid "Télécharger la version {toVersion}" msgstr "Download version {toVersion}" @@ -399,7 +399,7 @@ msgstr "An error occurred while retrieving the configuration." msgid "Votre progression dans les guides sera réinitialisée. Les guides téléchargés seront toujours présents." msgstr "Your progress in guides will be reset. The downloaded guides will still be available." -#: src/routes/app-old-version.tsx:63 +#: src/routes/app-old-version.tsx:65 msgid "Vous ne disposez pas de la dernière version de <0>Ganymède." msgstr "You do not have the latest version of <0>Ganymede." @@ -407,6 +407,6 @@ msgstr "You do not have the latest version of <0>Ganymede." msgid "Vous pouvez créer, utiliser et partager des guides vous permettant d'optimiser votre aventure." msgstr "You can create, use, and share guides to optimize your adventure." -#: src/routes/app-old-version.tsx:70 +#: src/routes/app-old-version.tsx:72 msgid "Vous utilisez actuellement la version <0>{fromVersion}." msgstr "You are currently using version <0>{fromVersion}." diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index bcf6a5e..7a00911 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -13,7 +13,7 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" -#: src/routes/app-old-version.tsx:84 +#: src/routes/app-old-version.tsx:86 msgid "<0>Vous trouverez les notes de version sur notre Discord <1>#changelog" msgstr "<0>Encontrarás las notas de versión en nuestro Discord <1>#changelog" @@ -29,7 +29,7 @@ msgstr "Mostrar las guías terminadas" msgid "Ajouter" msgstr "Añadir" -#: src/routes/_app/downloads/$status.tsx:140 +#: src/routes/_app/downloads/$status.tsx:141 msgid "Attention" msgstr "Atención" @@ -37,11 +37,11 @@ msgstr "Atención" msgid "Attention !" msgstr "¡Atención!" -#: src/routes/_app/downloads/$status.tsx:160 +#: src/routes/_app/downloads/$status.tsx:161 msgid "Aucun guides trouvé" msgstr "No se encontraron guías" -#: src/routes/_app/downloads/$status.tsx:176 +#: src/routes/_app/downloads/$status.tsx:177 msgid "Aucun guides trouvé avec {term}" msgstr "No se encontraron guías con {term}" @@ -80,40 +80,40 @@ msgstr "¡Esta herramienta tiene como objetivo asistirte durante toda tu aventur msgid "Chasse au trésor" msgstr "Búsqueda del tesoro" -#: src/components/guide-frame.tsx:250 +#: src/components/guide-frame.tsx:255 msgid "Cliquez pour copier le nom de l'objet. Cmd+clic pour ouvrir sur dofusdb" msgstr "Haz clic para copiar el nombre del objeto. Cmd+clic para abrir en dofusdb" -#: src/components/guide-frame.tsx:251 +#: src/components/guide-frame.tsx:256 msgid "Cliquez pour copier le nom de l'objet. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Haz clic para copiar el nombre del objeto. Ctrl+clic para abrir en dofusdb" -#: src/components/guide-frame.tsx:258 +#: src/components/guide-frame.tsx:263 msgid "Cliquez pour copier le nom de la quête. Cmd+clic pour ouvrir sur dofusdb" msgstr "Haz clic para copiar el nombre de la misión. Cmd+clic para abrir en dofusdb" -#: src/components/guide-frame.tsx:259 +#: src/components/guide-frame.tsx:264 msgid "Cliquez pour copier le nom de la quête. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Haz clic para copiar el nombre de la misión. Ctrl+clic para abrir en dofusdb" -#: src/components/guide-frame.tsx:246 +#: src/components/guide-frame.tsx:251 msgid "Cliquez pour copier le nom du donjon. Cmd+clic pour ouvrir sur dofusdb" msgstr "Haz clic para copiar el nombre del calabozo. Cmd+clic para abrir en dofusdb" -#: src/components/guide-frame.tsx:247 +#: src/components/guide-frame.tsx:252 msgid "Cliquez pour copier le nom du donjon. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Haz clic para copiar el nombre del calabozo. Ctrl+clic para abrir en dofusdb" -#: src/components/guide-frame.tsx:254 +#: src/components/guide-frame.tsx:259 msgid "Cliquez pour copier le nom du monstre. Cmd+clic pour ouvrir sur dofusdb" msgstr "Haz clic para copiar el nombre del monstruo. Cmd+clic para abrir en dofusdb" -#: src/components/guide-frame.tsx:255 +#: src/components/guide-frame.tsx:260 msgid "Cliquez pour copier le nom du monstre. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Haz clic para copiar el nombre del monstruo. Ctrl+clic para abrir en dofusdb" -#: src/components/guide-frame.tsx:293 -#: src/components/guide-frame.tsx:327 +#: src/components/guide-frame.tsx:298 +#: src/components/guide-frame.tsx:332 msgid "Cliquez pour ouvrir dans le navigateur" msgstr "Haz clic para abrir en el navegador" @@ -159,7 +159,7 @@ msgstr "Crear una nueva nota" msgid "Créez vos guides via le site officiel et téléchargez ceux des autres !" msgstr "¡Crea tus guías en el sitio oficial y descarga las de otros!" -#: src/routes/_app/downloads/$status.tsx:197 +#: src/routes/_app/downloads/$status.tsx:212 msgid "de <0>{0}" msgstr "de <0>{0}" @@ -192,42 +192,42 @@ msgid "Grande" msgstr "Grande" #: src/components/title-bar.tsx:49 -#: src/routes/_app/downloads/$status.tsx:94 +#: src/routes/_app/downloads/$status.tsx:93 #: src/routes/_app/guides/index.tsx:38 #: src/routes/_app/guides/index.tsx:135 msgid "Guides" msgstr "Guías" -#: src/routes/_app/downloads/$status.tsx:92 +#: src/routes/_app/downloads/$status.tsx:91 #: src/routes/_app/downloads/index.tsx:51 msgid "Guides certifiés" msgstr "Guías certificadas" -#: src/routes/_app/downloads/$status.tsx:88 +#: src/routes/_app/downloads/$status.tsx:87 #: src/routes/_app/downloads/index.tsx:57 msgid "Guides draft" msgstr "Borradores de guías" -#: src/routes/_app/downloads/$status.tsx:90 +#: src/routes/_app/downloads/$status.tsx:89 #: src/routes/_app/downloads/index.tsx:48 msgid "Guides principaux" msgstr "Guías principales" -#: src/routes/_app/downloads/$status.tsx:86 +#: src/routes/_app/downloads/$status.tsx:85 msgid "Guides privés" msgstr "Guías privadas" -#: src/routes/_app/downloads/$status.tsx:84 +#: src/routes/_app/downloads/$status.tsx:83 #: src/routes/_app/downloads/index.tsx:54 msgid "Guides publics" msgstr "Guías públicas" -#: src/routes/_app/downloads/$status.tsx:188 +#: src/routes/_app/downloads/$status.tsx:189 #: src/routes/_app/guides/index.tsx:201 msgid "id <0>{0}" msgstr "id <0>{0}" -#: src/routes/app-old-version.tsx:115 +#: src/routes/app-old-version.tsx:117 msgid "Il semblerait que la mise à jour ait eu un problème." msgstr "Parece que la actualización ha tenido un problema." @@ -236,7 +236,7 @@ msgstr "Parece que la actualización ha tenido un problema." msgid "Informations supplémentaires : {0}" msgstr "Información adicional: {0}" -#: src/routes/_app/downloads/$status.tsx:151 +#: src/routes/_app/downloads/$status.tsx:152 msgid "J'ai compris" msgstr "Entendido" @@ -248,11 +248,11 @@ msgstr "Entendido" msgid "Langue" msgstr "Idioma" -#: src/routes/_app/downloads/$status.tsx:143 +#: src/routes/_app/downloads/$status.tsx:144 msgid "Les guides de cette section n'ont pas été vérifiés manuellement par l'équipe Ganymède. <0/> Bien que les liens vers les sites soient sécurisés, veuillez vérifier attentivement les guides <1>sur notre site avant de les télécharger." msgstr "Las guías de esta sección no han sido verificadas manualmente por el equipo de Ganymede. <0/> Aunque los enlaces a los sitios son seguros, por favor, verifica cuidadosamente las guías <1>en nuestro sitio antes de descargarlas." -#: src/components/guide-frame.tsx:54 +#: src/components/guide-frame.tsx:59 msgid "lien masqué" msgstr "enlace oculto" @@ -260,11 +260,11 @@ msgstr "enlace oculto" msgid "Mettre à jour le guide" msgstr "Actualizar la guía" -#: src/routes/app-old-version.tsx:97 +#: src/routes/app-old-version.tsx:99 msgid "Mise à jour en cours." msgstr "Actualización en curso." -#: src/routes/app-old-version.tsx:103 +#: src/routes/app-old-version.tsx:105 msgid "Mise à jour terminée. L'application va redémarrer." msgstr "Actualización completada. La aplicación se reiniciará." @@ -329,7 +329,7 @@ msgstr "Perfiles" msgid "Rafraichir le dossier des guides téléchargés" msgstr "Actualizar la carpeta de guías descargadas" -#: src/routes/_app/downloads/$status.tsx:171 +#: src/routes/_app/downloads/$status.tsx:172 #: src/routes/_app/guides/index.tsx:171 msgid "Rechercher un guide" msgstr "Buscar una guía" @@ -367,7 +367,7 @@ msgstr "Tamaño del texto de las guías" msgid "Téléchargements" msgstr "Descargas" -#: src/routes/app-old-version.tsx:114 +#: src/routes/app-old-version.tsx:116 msgid "Télécharger la version {toVersion}" msgstr "Descargar la versión {toVersion}" @@ -399,7 +399,7 @@ msgstr "Ha ocurrido un error al recuperar la configuración." msgid "Votre progression dans les guides sera réinitialisée. Les guides téléchargés seront toujours présents." msgstr "Tu progreso en las guías será restablecido. Las guías descargadas seguirán disponibles." -#: src/routes/app-old-version.tsx:63 +#: src/routes/app-old-version.tsx:65 msgid "Vous ne disposez pas de la dernière version de <0>Ganymède." msgstr "No tienes la última versión de <0>Ganymède." @@ -407,6 +407,6 @@ msgstr "No tienes la última versión de <0>Ganymède." msgid "Vous pouvez créer, utiliser et partager des guides vous permettant d'optimiser votre aventure." msgstr "Puedes crear, usar y compartir guías que te permitirán optimizar tu aventura." -#: src/routes/app-old-version.tsx:70 +#: src/routes/app-old-version.tsx:72 msgid "Vous utilisez actuellement la version <0>{fromVersion}." msgstr "Actualmente estás usando la versión <0>{fromVersion}." diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index 12cdf7e..1bbfbd2 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -13,7 +13,7 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" -#: src/routes/app-old-version.tsx:84 +#: src/routes/app-old-version.tsx:86 msgid "<0>Vous trouverez les notes de version sur notre Discord <1>#changelog" msgstr "<0>Vous trouverez les notes de version sur notre Discord <1>#changelog" @@ -29,7 +29,7 @@ msgstr "Afficher les guides terminés" msgid "Ajouter" msgstr "Ajouter" -#: src/routes/_app/downloads/$status.tsx:140 +#: src/routes/_app/downloads/$status.tsx:141 msgid "Attention" msgstr "Attention" @@ -37,11 +37,11 @@ msgstr "Attention" msgid "Attention !" msgstr "Attention !" -#: src/routes/_app/downloads/$status.tsx:160 +#: src/routes/_app/downloads/$status.tsx:161 msgid "Aucun guides trouvé" msgstr "Aucun guides trouvé" -#: src/routes/_app/downloads/$status.tsx:176 +#: src/routes/_app/downloads/$status.tsx:177 msgid "Aucun guides trouvé avec {term}" msgstr "Aucun guides trouvé avec {term}" @@ -80,40 +80,40 @@ msgstr "Cet outil a pour but de vous assister tout au long de votre aventure sur msgid "Chasse au trésor" msgstr "Chasse au trésor" -#: src/components/guide-frame.tsx:250 +#: src/components/guide-frame.tsx:255 msgid "Cliquez pour copier le nom de l'objet. Cmd+clic pour ouvrir sur dofusdb" msgstr "Cliquez pour copier le nom de l'objet. Cmd+clic pour ouvrir sur dofusdb" -#: src/components/guide-frame.tsx:251 +#: src/components/guide-frame.tsx:256 msgid "Cliquez pour copier le nom de l'objet. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Cliquez pour copier le nom de l'objet. Ctrl+clic pour ouvrir sur dofusdb" -#: src/components/guide-frame.tsx:258 +#: src/components/guide-frame.tsx:263 msgid "Cliquez pour copier le nom de la quête. Cmd+clic pour ouvrir sur dofusdb" msgstr "Cliquez pour copier le nom de la quête. Cmd+clic pour ouvrir sur dofusdb" -#: src/components/guide-frame.tsx:259 +#: src/components/guide-frame.tsx:264 msgid "Cliquez pour copier le nom de la quête. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Cliquez pour copier le nom de la quête. Ctrl+clic pour ouvrir sur dofusdb" -#: src/components/guide-frame.tsx:246 +#: src/components/guide-frame.tsx:251 msgid "Cliquez pour copier le nom du donjon. Cmd+clic pour ouvrir sur dofusdb" msgstr "Cliquez pour copier le nom du donjon. Cmd+clic pour ouvrir sur dofusdb" -#: src/components/guide-frame.tsx:247 +#: src/components/guide-frame.tsx:252 msgid "Cliquez pour copier le nom du donjon. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Cliquez pour copier le nom du donjon. Ctrl+clic pour ouvrir sur dofusdb" -#: src/components/guide-frame.tsx:254 +#: src/components/guide-frame.tsx:259 msgid "Cliquez pour copier le nom du monstre. Cmd+clic pour ouvrir sur dofusdb" msgstr "Cliquez pour copier le nom du monstre. Cmd+clic pour ouvrir sur dofusdb" -#: src/components/guide-frame.tsx:255 +#: src/components/guide-frame.tsx:260 msgid "Cliquez pour copier le nom du monstre. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Cliquez pour copier le nom du monstre. Ctrl+clic pour ouvrir sur dofusdb" -#: src/components/guide-frame.tsx:293 -#: src/components/guide-frame.tsx:327 +#: src/components/guide-frame.tsx:298 +#: src/components/guide-frame.tsx:332 msgid "Cliquez pour ouvrir dans le navigateur" msgstr "Cliquez pour ouvrir dans le navigateur" @@ -159,7 +159,7 @@ msgstr "Créer une nouvelle note" msgid "Créez vos guides via le site officiel et téléchargez ceux des autres !" msgstr "Créez vos guides via le site officiel et téléchargez ceux des autres !" -#: src/routes/_app/downloads/$status.tsx:197 +#: src/routes/_app/downloads/$status.tsx:212 msgid "de <0>{0}" msgstr "de <0>{0}" @@ -192,42 +192,42 @@ msgid "Grande" msgstr "Grande" #: src/components/title-bar.tsx:49 -#: src/routes/_app/downloads/$status.tsx:94 +#: src/routes/_app/downloads/$status.tsx:93 #: src/routes/_app/guides/index.tsx:38 #: src/routes/_app/guides/index.tsx:135 msgid "Guides" msgstr "Guides" -#: src/routes/_app/downloads/$status.tsx:92 +#: src/routes/_app/downloads/$status.tsx:91 #: src/routes/_app/downloads/index.tsx:51 msgid "Guides certifiés" msgstr "Guides certifiés" -#: src/routes/_app/downloads/$status.tsx:88 +#: src/routes/_app/downloads/$status.tsx:87 #: src/routes/_app/downloads/index.tsx:57 msgid "Guides draft" msgstr "Guides draft" -#: src/routes/_app/downloads/$status.tsx:90 +#: src/routes/_app/downloads/$status.tsx:89 #: src/routes/_app/downloads/index.tsx:48 msgid "Guides principaux" msgstr "Guides principaux" -#: src/routes/_app/downloads/$status.tsx:86 +#: src/routes/_app/downloads/$status.tsx:85 msgid "Guides privés" msgstr "Guides privés" -#: src/routes/_app/downloads/$status.tsx:84 +#: src/routes/_app/downloads/$status.tsx:83 #: src/routes/_app/downloads/index.tsx:54 msgid "Guides publics" msgstr "Guides publics" -#: src/routes/_app/downloads/$status.tsx:188 +#: src/routes/_app/downloads/$status.tsx:189 #: src/routes/_app/guides/index.tsx:201 msgid "id <0>{0}" msgstr "id <0>{0}" -#: src/routes/app-old-version.tsx:115 +#: src/routes/app-old-version.tsx:117 msgid "Il semblerait que la mise à jour ait eu un problème." msgstr "Il semblerait que la mise à jour ait eu un problème." @@ -236,7 +236,7 @@ msgstr "Il semblerait que la mise à jour ait eu un problème." msgid "Informations supplémentaires : {0}" msgstr "Informations supplémentaires : {0}" -#: src/routes/_app/downloads/$status.tsx:151 +#: src/routes/_app/downloads/$status.tsx:152 msgid "J'ai compris" msgstr "J'ai compris" @@ -248,11 +248,11 @@ msgstr "J'ai compris" msgid "Langue" msgstr "Langue" -#: src/routes/_app/downloads/$status.tsx:143 +#: src/routes/_app/downloads/$status.tsx:144 msgid "Les guides de cette section n'ont pas été vérifiés manuellement par l'équipe Ganymède. <0/> Bien que les liens vers les sites soient sécurisés, veuillez vérifier attentivement les guides <1>sur notre site avant de les télécharger." msgstr "Les guides de cette section n'ont pas été vérifiés manuellement par l'équipe Ganymède. <0/> Bien que les liens vers les sites soient sécurisés, veuillez vérifier attentivement les guides <1>sur notre site avant de les télécharger." -#: src/components/guide-frame.tsx:54 +#: src/components/guide-frame.tsx:59 msgid "lien masqué" msgstr "lien masqué" @@ -260,11 +260,11 @@ msgstr "lien masqué" msgid "Mettre à jour le guide" msgstr "Mettre à jour le guide" -#: src/routes/app-old-version.tsx:97 +#: src/routes/app-old-version.tsx:99 msgid "Mise à jour en cours." msgstr "Mise à jour en cours." -#: src/routes/app-old-version.tsx:103 +#: src/routes/app-old-version.tsx:105 msgid "Mise à jour terminée. L'application va redémarrer." msgstr "Mise à jour terminée. L'application va redémarrer." @@ -329,7 +329,7 @@ msgstr "Profils" msgid "Rafraichir le dossier des guides téléchargés" msgstr "Rafraichir le dossier des guides téléchargés" -#: src/routes/_app/downloads/$status.tsx:171 +#: src/routes/_app/downloads/$status.tsx:172 #: src/routes/_app/guides/index.tsx:171 msgid "Rechercher un guide" msgstr "Rechercher un guide" @@ -367,7 +367,7 @@ msgstr "Taille de texte des guides" msgid "Téléchargements" msgstr "Téléchargements" -#: src/routes/app-old-version.tsx:114 +#: src/routes/app-old-version.tsx:116 msgid "Télécharger la version {toVersion}" msgstr "Télécharger la version {toVersion}" @@ -399,7 +399,7 @@ msgstr "Une erreur est survenue lors de la récupération de la configuration." msgid "Votre progression dans les guides sera réinitialisée. Les guides téléchargés seront toujours présents." msgstr "Votre progression dans les guides sera réinitialisée. Les guides téléchargés seront toujours présents." -#: src/routes/app-old-version.tsx:63 +#: src/routes/app-old-version.tsx:65 msgid "Vous ne disposez pas de la dernière version de <0>Ganymède." msgstr "Vous ne disposez pas de la dernière version de <0>Ganymède." @@ -407,6 +407,6 @@ msgstr "Vous ne disposez pas de la dernière version de <0>Ganymède." msgid "Vous pouvez créer, utiliser et partager des guides vous permettant d'optimiser votre aventure." msgstr "Vous pouvez créer, utiliser et partager des guides vous permettant d'optimiser votre aventure." -#: src/routes/app-old-version.tsx:70 +#: src/routes/app-old-version.tsx:72 msgid "Vous utilisez actuellement la version <0>{fromVersion}." msgstr "Vous utilisez actuellement la version <0>{fromVersion}." diff --git a/src/locales/pt/messages.po b/src/locales/pt/messages.po index 07e9ebb..7067698 100644 --- a/src/locales/pt/messages.po +++ b/src/locales/pt/messages.po @@ -13,7 +13,7 @@ msgstr "" "Language-Team: \n" "Plural-Forms: \n" -#: src/routes/app-old-version.tsx:84 +#: src/routes/app-old-version.tsx:86 msgid "<0>Vous trouverez les notes de version sur notre Discord <1>#changelog" msgstr "<0>Você encontrará as notas de versão em nosso Discord <1>#changelog" @@ -29,7 +29,7 @@ msgstr "Exibir os guias concluídos" msgid "Ajouter" msgstr "Adicionar" -#: src/routes/_app/downloads/$status.tsx:140 +#: src/routes/_app/downloads/$status.tsx:141 msgid "Attention" msgstr "Atenção" @@ -37,11 +37,11 @@ msgstr "Atenção" msgid "Attention !" msgstr "Atenção!" -#: src/routes/_app/downloads/$status.tsx:160 +#: src/routes/_app/downloads/$status.tsx:161 msgid "Aucun guides trouvé" msgstr "Nenhum guia encontrado" -#: src/routes/_app/downloads/$status.tsx:176 +#: src/routes/_app/downloads/$status.tsx:177 msgid "Aucun guides trouvé avec {term}" msgstr "Nenhum guia encontrado com {term}" @@ -80,40 +80,40 @@ msgstr "Esta ferramenta foi projetada para ajudá-lo ao longo de sua aventura em msgid "Chasse au trésor" msgstr "Caça ao tesouro" -#: src/components/guide-frame.tsx:250 +#: src/components/guide-frame.tsx:255 msgid "Cliquez pour copier le nom de l'objet. Cmd+clic pour ouvrir sur dofusdb" msgstr "Clique para copiar o nome do item. Cmd+clique para abrir no dofusdb" -#: src/components/guide-frame.tsx:251 +#: src/components/guide-frame.tsx:256 msgid "Cliquez pour copier le nom de l'objet. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Clique para copiar o nome do item. Ctrl+clique para abrir no dofusdb" -#: src/components/guide-frame.tsx:258 +#: src/components/guide-frame.tsx:263 msgid "Cliquez pour copier le nom de la quête. Cmd+clic pour ouvrir sur dofusdb" msgstr "Clique para copiar o nome da missão. Cmd+clique para abrir no dofusdb" -#: src/components/guide-frame.tsx:259 +#: src/components/guide-frame.tsx:264 msgid "Cliquez pour copier le nom de la quête. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Clique para copiar o nome da missão. Ctrl+clique para abrir no dofusdb" -#: src/components/guide-frame.tsx:246 +#: src/components/guide-frame.tsx:251 msgid "Cliquez pour copier le nom du donjon. Cmd+clic pour ouvrir sur dofusdb" msgstr "Clique para copiar o nome do calabouço. Cmd+clique para abrir no dofusdb" -#: src/components/guide-frame.tsx:247 +#: src/components/guide-frame.tsx:252 msgid "Cliquez pour copier le nom du donjon. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Clique para copiar o nome do calabouço. Ctrl+clique para abrir no dofusdb" -#: src/components/guide-frame.tsx:254 +#: src/components/guide-frame.tsx:259 msgid "Cliquez pour copier le nom du monstre. Cmd+clic pour ouvrir sur dofusdb" msgstr "Clique para copiar o nome do monstro. Cmd+clique para abrir no dofusdb" -#: src/components/guide-frame.tsx:255 +#: src/components/guide-frame.tsx:260 msgid "Cliquez pour copier le nom du monstre. Ctrl+clic pour ouvrir sur dofusdb" msgstr "Clique para copiar o nome do monstro. Ctrl+clique para abrir no dofusdb" -#: src/components/guide-frame.tsx:293 -#: src/components/guide-frame.tsx:327 +#: src/components/guide-frame.tsx:298 +#: src/components/guide-frame.tsx:332 msgid "Cliquez pour ouvrir dans le navigateur" msgstr "Clique para abrir no navegador" @@ -159,7 +159,7 @@ msgstr "Criar uma nova nota" msgid "Créez vos guides via le site officiel et téléchargez ceux des autres !" msgstr "Crie seus guias no site oficial e baixe os de outros usuários!" -#: src/routes/_app/downloads/$status.tsx:197 +#: src/routes/_app/downloads/$status.tsx:212 msgid "de <0>{0}" msgstr "de <0>{0}" @@ -192,42 +192,42 @@ msgid "Grande" msgstr "Grande" #: src/components/title-bar.tsx:49 -#: src/routes/_app/downloads/$status.tsx:94 +#: src/routes/_app/downloads/$status.tsx:93 #: src/routes/_app/guides/index.tsx:38 #: src/routes/_app/guides/index.tsx:135 msgid "Guides" msgstr "Guias" -#: src/routes/_app/downloads/$status.tsx:92 +#: src/routes/_app/downloads/$status.tsx:91 #: src/routes/_app/downloads/index.tsx:51 msgid "Guides certifiés" msgstr "Guias certificados" -#: src/routes/_app/downloads/$status.tsx:88 +#: src/routes/_app/downloads/$status.tsx:87 #: src/routes/_app/downloads/index.tsx:57 msgid "Guides draft" msgstr "Guias rascunho" -#: src/routes/_app/downloads/$status.tsx:90 +#: src/routes/_app/downloads/$status.tsx:89 #: src/routes/_app/downloads/index.tsx:48 msgid "Guides principaux" msgstr "Guias principais" -#: src/routes/_app/downloads/$status.tsx:86 +#: src/routes/_app/downloads/$status.tsx:85 msgid "Guides privés" msgstr "Guias privados" -#: src/routes/_app/downloads/$status.tsx:84 +#: src/routes/_app/downloads/$status.tsx:83 #: src/routes/_app/downloads/index.tsx:54 msgid "Guides publics" msgstr "Guias públicos" -#: src/routes/_app/downloads/$status.tsx:188 +#: src/routes/_app/downloads/$status.tsx:189 #: src/routes/_app/guides/index.tsx:201 msgid "id <0>{0}" msgstr "id <0>{0}" -#: src/routes/app-old-version.tsx:115 +#: src/routes/app-old-version.tsx:117 msgid "Il semblerait que la mise à jour ait eu un problème." msgstr "Parece que houve um problema com a atualização." @@ -236,7 +236,7 @@ msgstr "Parece que houve um problema com a atualização." msgid "Informations supplémentaires : {0}" msgstr "Informações adicionais: {0}" -#: src/routes/_app/downloads/$status.tsx:151 +#: src/routes/_app/downloads/$status.tsx:152 msgid "J'ai compris" msgstr "Entendi" @@ -248,11 +248,11 @@ msgstr "Entendi" msgid "Langue" msgstr "Idioma" -#: src/routes/_app/downloads/$status.tsx:143 +#: src/routes/_app/downloads/$status.tsx:144 msgid "Les guides de cette section n'ont pas été vérifiés manuellement par l'équipe Ganymède. <0/> Bien que les liens vers les sites soient sécurisés, veuillez vérifier attentivement les guides <1>sur notre site avant de les télécharger." msgstr "Os guias desta seção não foram verificados manualmente pela equipe Ganimedes. <0/> Embora os links para os sites sejam seguros, verifique cuidadosamente os guias <1>em nosso site antes de baixá-los." -#: src/components/guide-frame.tsx:54 +#: src/components/guide-frame.tsx:59 msgid "lien masqué" msgstr "link oculto" @@ -260,11 +260,11 @@ msgstr "link oculto" msgid "Mettre à jour le guide" msgstr "Atualizar o guia" -#: src/routes/app-old-version.tsx:97 +#: src/routes/app-old-version.tsx:99 msgid "Mise à jour en cours." msgstr "Atualização em curso." -#: src/routes/app-old-version.tsx:103 +#: src/routes/app-old-version.tsx:105 msgid "Mise à jour terminée. L'application va redémarrer." msgstr "Atualização concluída. A aplicação vai reiniciar." @@ -329,7 +329,7 @@ msgstr "Perfis" msgid "Rafraichir le dossier des guides téléchargés" msgstr "Atualizar a pasta dos guias baixados" -#: src/routes/_app/downloads/$status.tsx:171 +#: src/routes/_app/downloads/$status.tsx:172 #: src/routes/_app/guides/index.tsx:171 msgid "Rechercher un guide" msgstr "Pesquisar um guia" @@ -367,7 +367,7 @@ msgstr "Tamanho do texto dos guias" msgid "Téléchargements" msgstr "Downloads" -#: src/routes/app-old-version.tsx:114 +#: src/routes/app-old-version.tsx:116 msgid "Télécharger la version {toVersion}" msgstr "Baixar a versão {toVersion}" @@ -399,7 +399,7 @@ msgstr "Ocorreu um erro ao recuperar a configuração." msgid "Votre progression dans les guides sera réinitialisée. Les guides téléchargés seront toujours présents." msgstr "Seu progresso nos guias será redefinido. Os guias baixados ainda estarão disponíveis." -#: src/routes/app-old-version.tsx:63 +#: src/routes/app-old-version.tsx:65 msgid "Vous ne disposez pas de la dernière version de <0>Ganymède." msgstr "Não tem a versão mais recente do <0>Ganymède." @@ -407,6 +407,6 @@ msgstr "Não tem a versão mais recente do <0>Ganymède." msgid "Vous pouvez créer, utiliser et partager des guides vous permettant d'optimiser votre aventure." msgstr "Você pode criar, usar e compartilhar guias que ajudam a otimizar sua aventura." -#: src/routes/app-old-version.tsx:70 +#: src/routes/app-old-version.tsx:72 msgid "Vous utilisez actuellement la version <0>{fromVersion}." msgstr "Você está usando a versão <0>{fromVersion}." diff --git a/src/main.tsx b/src/main.tsx index 2a6c829..d292148 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -7,6 +7,7 @@ import ReactDOM from 'react-dom/client' import { ErrorComponent } from './components/error-component.tsx' import { dynamicActiveLocale } from './i18n.ts' import './main.css' +import { getLang } from '@/lib/conf.ts' import { sentry, setupSentry } from '@/lib/sentry.ts' import { whiteListQuery } from '@/queries/white_list.query.ts' import { attachConsole, error } from '@tauri-apps/plugin-log' @@ -63,7 +64,7 @@ queryClient .then(async (conf) => { window.document.documentElement.style.setProperty('--opacity', `${conf.opacity.toFixed(2)}`) - await dynamicActiveLocale(conf.lang.toLowerCase()) + await dynamicActiveLocale(getLang(conf.lang).toLowerCase()) }) .catch((err) => { error(err) diff --git a/src/mutations/download-guide-from-server.mutation.ts b/src/mutations/download-guide-from-server.mutation.ts index 1ea6451..c6cb999 100644 --- a/src/mutations/download-guide-from-server.mutation.ts +++ b/src/mutations/download-guide-from-server.mutation.ts @@ -1,6 +1,6 @@ +import { Guide } from '@/ipc/bindings.ts' import { downloadGuideFromServer } from '@/ipc/guides.ts' import { guidesQuery } from '@/queries/guides.query.ts' -import { Guide } from '@/types/guide.ts' import { useMutation, useQueryClient } from '@tanstack/react-query' export function useDownloadGuideFromServer() { diff --git a/src/mutations/set-conf.mutation.ts b/src/mutations/set-conf.mutation.ts index 7ccc6d8..6775938 100644 --- a/src/mutations/set-conf.mutation.ts +++ b/src/mutations/set-conf.mutation.ts @@ -1,24 +1,24 @@ -import { setConf } from '@/ipc/conf.ts' -import { confQuery } from '@/queries/conf.query.ts' -import { Conf } from '@/types/conf.ts' -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { debug } from '@tauri-apps/plugin-log' - -export function useSetConf() { - const queryClient = useQueryClient() - - return useMutation({ - mutationFn: async (conf: Conf) => { - await debug(`set the conf: ${JSON.stringify(conf, undefined, 2)}`) - - const result = await setConf(conf) - - if (result.isErr()) { - throw result.error - } - }, - onSuccess: async () => { - await queryClient.invalidateQueries(confQuery) - }, - }) -} +import { Conf } from '@/ipc/bindings.ts' +import { setConf } from '@/ipc/conf.ts' +import { confQuery } from '@/queries/conf.query.ts' +import { useMutation, useQueryClient } from '@tanstack/react-query' +import { debug } from '@tauri-apps/plugin-log' + +export function useSetConf() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (conf: Conf) => { + await debug(`set the conf: ${JSON.stringify(conf, undefined, 2)}`) + + const result = await setConf(conf) + + if (result.isErr()) { + throw result.error + } + }, + onSuccess: async () => { + await queryClient.invalidateQueries(confQuery) + }, + }) +} diff --git a/src/mutations/toggle-guide-checkbox.mutation.ts b/src/mutations/toggle-guide-checkbox.mutation.ts index 14f431e..4a3b4fb 100644 --- a/src/mutations/toggle-guide-checkbox.mutation.ts +++ b/src/mutations/toggle-guide-checkbox.mutation.ts @@ -1,4 +1,4 @@ -import { toggleGuideCheckbox } from '@/ipc/guides.ts' +import { toggleGuideCheckbox } from '@/ipc/conf.ts' import { getProfile } from '@/lib/profile' import { getProgress, getStep, newProgress } from '@/lib/progress' import { confQuery } from '@/queries/conf.query' diff --git a/src/queries/almanax.query.ts b/src/queries/almanax.query.ts index ac50588..08428b1 100644 --- a/src/queries/almanax.query.ts +++ b/src/queries/almanax.query.ts @@ -1,8 +1,8 @@ import { getAlmanax } from '@/ipc/almanax.ts' -import { Lang } from '@/types/conf' +import { ConfLang } from '@/ipc/bindings.ts' import { queryOptions } from '@tanstack/react-query' -export const almanaxQuery = (lang: Lang) => { +export const almanaxQuery = (lang: ConfLang) => { return queryOptions({ queryKey: ['almanax', lang], queryFn: async () => { diff --git a/src/queries/conf.query.ts b/src/queries/conf.query.ts index ab2f892..62b2311 100644 --- a/src/queries/conf.query.ts +++ b/src/queries/conf.query.ts @@ -1,5 +1,5 @@ -import { getConf, GetConfError } from '@/ipc/conf.ts' -import { Conf } from '@/types/conf' +import { Conf } from '@/ipc/bindings.ts' +import { GetConfError, getConf } from '@/ipc/conf.ts' import { queryOptions } from '@tanstack/react-query' export const confQuery = queryOptions({ diff --git a/src/queries/guides-from-server.query.ts b/src/queries/guides-from-server.query.ts index f42bd66..d82e2d2 100644 --- a/src/queries/guides-from-server.query.ts +++ b/src/queries/guides-from-server.query.ts @@ -1,5 +1,5 @@ +import { Status } from '@/ipc/bindings.ts' import { getGuidesFromServer } from '@/ipc/guides_from_server.ts' -import { Status } from '@/types/status.ts' import { queryOptions } from '@tanstack/react-query' export const itemsPerPage = 20 diff --git a/src/routes/_app/dofusdb/hunt.tsx b/src/routes/_app/dofusdb/hunt.tsx index 025b0fc..3478742 100644 --- a/src/routes/_app/dofusdb/hunt.tsx +++ b/src/routes/_app/dofusdb/hunt.tsx @@ -1,3 +1,4 @@ +import { getLang } from '@/lib/conf.ts' import { confQuery } from '@/queries/conf.query.ts' import { useSuspenseQuery } from '@tanstack/react-query' import { createFileRoute } from '@tanstack/react-router' @@ -5,7 +6,7 @@ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/_app/dofusdb/hunt')({ component: () => { const conf = useSuspenseQuery(confQuery) - const lang = conf.data?.lang.toLowerCase() ?? 'fr' + const lang = getLang(conf.data.lang).toLowerCase() return (