From ebf87812e9597995b99e189a4dd43c639e62492f Mon Sep 17 00:00:00 2001 From: reya Date: Tue, 5 Nov 2024 08:47:24 +0700 Subject: [PATCH] wip: refactor --- .taurignore | 2 - package.json | 40 +- pnpm-lock.yaml | 876 +++++++++--------- src-tauri/Cargo.lock | 472 ++++++++-- src-tauri/Cargo.toml | 15 +- src-tauri/capabilities/default.json | 15 +- src-tauri/resources/relays.txt | 9 +- src-tauri/rustfmt.toml | 11 - src-tauri/src/commands/account.rs | 576 ++++++------ src-tauri/src/commands/chat.rs | 145 +-- src-tauri/src/commands/relay.rs | 265 +++--- src-tauri/src/commands/tray.rs | 90 +- src-tauri/src/main.rs | 232 ++++- src-tauri/tauri.conf.json | 2 +- src/commands.ts | 4 +- src/commons.ts | 65 +- src/components/user/provider.tsx | 51 +- src/hooks/useProfile.ts | 66 ++ src/main.tsx | 55 +- src/routes.gen.ts | 21 +- .../$account/_layout/chats.$id.lazy.tsx | 26 +- src/routes/$account/_layout/chats.$id.tsx | 55 +- src/routes/$account/_layout/chats.lazy.tsx | 28 +- src/routes/__root.tsx | 30 +- tsconfig.json | 1 - 25 files changed, 1868 insertions(+), 1284 deletions(-) delete mode 100644 .taurignore delete mode 100644 src-tauri/rustfmt.toml create mode 100644 src/hooks/useProfile.ts diff --git a/.taurignore b/.taurignore deleted file mode 100644 index 1209638..0000000 --- a/.taurignore +++ /dev/null @@ -1,2 +0,0 @@ -/src -/public diff --git a/package.json b/package.json index fa1c180..e94ab45 100644 --- a/package.json +++ b/package.json @@ -15,42 +15,44 @@ "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-progress": "^1.1.0", "@radix-ui/react-scroll-area": "^1.2.0", - "@tanstack/react-query": "^5.59.8", - "@tanstack/react-router": "^1.64.0", - "@tauri-apps/api": "2.0.2", - "@tauri-apps/plugin-clipboard-manager": "2.0.0", - "@tauri-apps/plugin-dialog": "2.0.0", - "@tauri-apps/plugin-fs": "2.0.0", - "@tauri-apps/plugin-notification": "2.0.0", - "@tauri-apps/plugin-os": "2.0.0", - "@tauri-apps/plugin-process": "2.0.0", - "@tauri-apps/plugin-shell": "2.0.0", - "@tauri-apps/plugin-updater": "2.0.0", + "@tanstack/query-broadcast-client-experimental": "^5.59.17", + "@tanstack/query-persist-client-core": "^5.59.17", + "@tanstack/react-query": "^5.59.19", + "@tanstack/react-router": "^1.78.3", + "@tauri-apps/api": "^2.0.3", + "@tauri-apps/plugin-clipboard-manager": "^2.0.0", + "@tauri-apps/plugin-dialog": "^2.0.1", + "@tauri-apps/plugin-fs": "^2.0.1", + "@tauri-apps/plugin-notification": "^2.0.0", + "@tauri-apps/plugin-os": "^2.0.0", + "@tauri-apps/plugin-process": "^2.0.0", + "@tauri-apps/plugin-shell": "^2.0.1", + "@tauri-apps/plugin-store": "^2.1.0", + "@tauri-apps/plugin-updater": "^2.0.0", "dayjs": "^1.11.13", - "lru-cache": "^11.0.1", "minidenticons": "^4.2.1", - "nostr-tools": "^2.7.2", + "nostr-tools": "^2.10.1", "react": "19.0.0-rc-d025ddd3-20240722", "react-dom": "19.0.0-rc-d025ddd3-20240722", "unique-names-generator": "^4.7.1", - "virtua": "^0.35.0" + "virtua": "^0.36.2" }, "devDependencies": { "@biomejs/biome": "1.9.3", - "@tanstack/router-plugin": "^1.64.0", + "@tanstack/router-plugin": "^1.78.3", "@tauri-apps/cli": "2.0.2", "@types/react": "npm:types-react@19.0.0-rc.1", "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1", - "@vitejs/plugin-react": "^4.3.2", + "@vitejs/plugin-react": "^4.3.3", "autoprefixer": "^10.4.20", "babel-plugin-react-compiler": "0.0.0-experimental-696af53-20240625", "clsx": "^2.1.1", "postcss": "^8.4.47", "tailwind-gradient-mask-image": "^1.2.0", - "tailwind-merge": "^2.5.3", - "tailwindcss": "^3.4.13", + "tailwind-merge": "^2.5.4", + "tailwindcss": "^3.4.14", "typescript": "^5.6.3", - "vite": "^5.4.8", + "vite": "^5.4.10", "vite-tsconfig-paths": "^5.0.1" }, "overrides": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8778b51..997ff45 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,51 +23,57 @@ importers: '@radix-ui/react-scroll-area': specifier: ^1.2.0 version: 1.2.0(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722)(types-react-dom@19.0.0-rc.1)(types-react@19.0.0-rc.1) + '@tanstack/query-broadcast-client-experimental': + specifier: ^5.59.17 + version: 5.59.17 + '@tanstack/query-persist-client-core': + specifier: ^5.59.17 + version: 5.59.17 '@tanstack/react-query': - specifier: ^5.59.8 - version: 5.59.8(react@19.0.0-rc-d025ddd3-20240722) + specifier: ^5.59.19 + version: 5.59.19(react@19.0.0-rc-d025ddd3-20240722) '@tanstack/react-router': - specifier: ^1.64.0 - version: 1.64.0(@tanstack/router-generator@1.64.0)(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722) + specifier: ^1.78.3 + version: 1.78.3(@tanstack/router-generator@1.78.3)(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722) '@tauri-apps/api': - specifier: 2.0.2 - version: 2.0.2 + specifier: ^2.0.3 + version: 2.0.3 '@tauri-apps/plugin-clipboard-manager': - specifier: 2.0.0 + specifier: ^2.0.0 version: 2.0.0 '@tauri-apps/plugin-dialog': - specifier: 2.0.0 - version: 2.0.0 + specifier: ^2.0.1 + version: 2.0.1 '@tauri-apps/plugin-fs': - specifier: 2.0.0 - version: 2.0.0 + specifier: ^2.0.1 + version: 2.0.1 '@tauri-apps/plugin-notification': - specifier: 2.0.0 + specifier: ^2.0.0 version: 2.0.0 '@tauri-apps/plugin-os': - specifier: 2.0.0 + specifier: ^2.0.0 version: 2.0.0 '@tauri-apps/plugin-process': - specifier: 2.0.0 + specifier: ^2.0.0 version: 2.0.0 '@tauri-apps/plugin-shell': - specifier: 2.0.0 - version: 2.0.0 + specifier: ^2.0.1 + version: 2.0.1 + '@tauri-apps/plugin-store': + specifier: ^2.1.0 + version: 2.1.0 '@tauri-apps/plugin-updater': - specifier: 2.0.0 + specifier: ^2.0.0 version: 2.0.0 dayjs: specifier: ^1.11.13 version: 1.11.13 - lru-cache: - specifier: ^11.0.1 - version: 11.0.1 minidenticons: specifier: ^4.2.1 version: 4.2.1 nostr-tools: - specifier: ^2.7.2 - version: 2.7.2(typescript@5.6.3) + specifier: ^2.10.1 + version: 2.10.1(typescript@5.6.3) react: specifier: 19.0.0-rc-d025ddd3-20240722 version: 19.0.0-rc-d025ddd3-20240722 @@ -78,15 +84,15 @@ importers: specifier: ^4.7.1 version: 4.7.1 virtua: - specifier: ^0.35.0 - version: 0.35.0(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722) + specifier: ^0.36.2 + version: 0.36.2(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722) devDependencies: '@biomejs/biome': specifier: 1.9.3 version: 1.9.3 '@tanstack/router-plugin': - specifier: ^1.64.0 - version: 1.64.0(vite@5.4.8)(webpack-sources@3.2.3) + specifier: ^1.78.3 + version: 1.78.3(vite@5.4.10)(webpack-sources@3.2.3) '@tauri-apps/cli': specifier: 2.0.2 version: 2.0.2 @@ -97,8 +103,8 @@ importers: specifier: npm:types-react-dom@19.0.0-rc.1 version: types-react-dom@19.0.0-rc.1 '@vitejs/plugin-react': - specifier: ^4.3.2 - version: 4.3.2(vite@5.4.8) + specifier: ^4.3.3 + version: 4.3.3(vite@5.4.10) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.47) @@ -115,20 +121,20 @@ importers: specifier: ^1.2.0 version: 1.2.0 tailwind-merge: - specifier: ^2.5.3 - version: 2.5.3 + specifier: ^2.5.4 + version: 2.5.4 tailwindcss: - specifier: ^3.4.13 - version: 3.4.13 + specifier: ^3.4.14 + version: 3.4.14 typescript: specifier: ^5.6.3 version: 5.6.3 vite: - specifier: ^5.4.8 - version: 5.4.8 + specifier: ^5.4.10 + version: 5.4.10 vite-tsconfig-paths: specifier: ^5.0.1 - version: 5.0.1(typescript@5.6.3)(vite@5.4.8) + version: 5.0.1(typescript@5.6.3)(vite@5.4.10) packages: @@ -140,106 +146,102 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@babel/code-frame@7.25.7': - resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==} + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.25.7': - resolution: {integrity: sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==} + '@babel/compat-data@7.26.2': + resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==} engines: {node: '>=6.9.0'} - '@babel/core@7.25.7': - resolution: {integrity: sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==} + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} engines: {node: '>=6.9.0'} '@babel/generator@7.2.0': resolution: {integrity: sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==} - '@babel/generator@7.25.7': - resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==} + '@babel/generator@7.26.2': + resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.25.7': - resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==} + '@babel/helper-compilation-targets@7.25.9': + resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.25.7': - resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==} + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.25.7': - resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==} + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.25.7': - resolution: {integrity: sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==} + '@babel/helper-plugin-utils@7.25.9': + resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} engines: {node: '>=6.9.0'} - '@babel/helper-simple-access@7.25.7': - resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==} + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.25.7': - resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==} + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.7': - resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==} + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.25.7': - resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==} + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.25.7': - resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==} - engines: {node: '>=6.9.0'} - - '@babel/highlight@7.25.7': - resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.25.7': - resolution: {integrity: sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==} + '@babel/parser@7.26.2': + resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-jsx@7.25.7': - resolution: {integrity: sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==} + '@babel/plugin-syntax-jsx@7.25.9': + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.25.7': - resolution: {integrity: sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==} + '@babel/plugin-syntax-typescript@7.25.9': + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.25.7': - resolution: {integrity: sha512-JD9MUnLbPL0WdVK8AWC7F7tTG2OS6u/AKKnsK+NdRhUiVdnzyR1S3kKQCaRLOiaULvUiqK6Z4JQE635VgtCFeg==} + '@babel/plugin-transform-react-jsx-self@7.25.9': + resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-source@7.25.7': - resolution: {integrity: sha512-S/JXG/KrbIY06iyJPKfxr0qRxnhNOdkNXYBl/rmwgDd72cQLH9tEGkDm/yJPGvcSIUoikzfjMios9i+xT/uv9w==} + '@babel/plugin-transform-react-jsx-source@7.25.9': + resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/template@7.25.7': - resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==} + '@babel/runtime@7.23.4': + resolution: {integrity: sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.25.7': - resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==} + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} - '@babel/types@7.25.7': - resolution: {integrity: sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==} + '@babel/traverse@7.25.9': + resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.26.0': + resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} engines: {node: '>=6.9.0'} '@biomejs/biome@1.9.3': @@ -865,83 +867,93 @@ packages: '@types/react': optional: true - '@rollup/rollup-android-arm-eabi@4.24.0': - resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} + '@rollup/rollup-android-arm-eabi@4.24.3': + resolution: {integrity: sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.24.0': - resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} + '@rollup/rollup-android-arm64@4.24.3': + resolution: {integrity: sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.24.0': - resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} + '@rollup/rollup-darwin-arm64@4.24.3': + resolution: {integrity: sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.24.0': - resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} + '@rollup/rollup-darwin-x64@4.24.3': + resolution: {integrity: sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.24.0': - resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} + '@rollup/rollup-freebsd-arm64@4.24.3': + resolution: {integrity: sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.24.3': + resolution: {integrity: sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.24.3': + resolution: {integrity: sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.24.0': - resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} + '@rollup/rollup-linux-arm-musleabihf@4.24.3': + resolution: {integrity: sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.24.0': - resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} + '@rollup/rollup-linux-arm64-gnu@4.24.3': + resolution: {integrity: sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.24.0': - resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} + '@rollup/rollup-linux-arm64-musl@4.24.3': + resolution: {integrity: sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': - resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.24.3': + resolution: {integrity: sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.24.0': - resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} + '@rollup/rollup-linux-riscv64-gnu@4.24.3': + resolution: {integrity: sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.24.0': - resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} + '@rollup/rollup-linux-s390x-gnu@4.24.3': + resolution: {integrity: sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.24.0': - resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} + '@rollup/rollup-linux-x64-gnu@4.24.3': + resolution: {integrity: sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.24.0': - resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} + '@rollup/rollup-linux-x64-musl@4.24.3': + resolution: {integrity: sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.24.0': - resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} + '@rollup/rollup-win32-arm64-msvc@4.24.3': + resolution: {integrity: sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.24.0': - resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} + '@rollup/rollup-win32-ia32-msvc@4.24.3': + resolution: {integrity: sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.24.0': - resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} + '@rollup/rollup-win32-x64-msvc@4.24.3': + resolution: {integrity: sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==} cpu: [x64] os: [win32] @@ -958,37 +970,43 @@ packages: resolution: {integrity: sha512-2CqERleeqO3hkhJmyJm37tiL3LYgeOpmo8szqdjgtnnG0z7ZpvzkZz6HkfOr9Ca/ha7mhAiouSvLYuLkM37AMg==} engines: {node: '>=12'} - '@tanstack/query-core@5.59.6': - resolution: {integrity: sha512-g58YTHe4ClRrjJ50GY9fas/0zARJVozY0Hs+hcSBOmwZaeKY+to0/LX8wKnnH/EJiLYcC1sHmE11CAS3ncfZBg==} + '@tanstack/query-broadcast-client-experimental@5.59.17': + resolution: {integrity: sha512-3pVQDFFFhYcDwVqkdJh3gbo9jZKGNRsTjt5YH0GbiJRuuIwh9M4TUXaH9868z9ut+H31G4ZlsWL6QvhMyAbJTg==} - '@tanstack/react-query@5.59.8': - resolution: {integrity: sha512-jkvObpbjBL6P/PHFIjvNGG19XyhI8sjP6/Mw7CbmgT6SAps/5fZY5pxDicRwAt1YGCiEQvwrJQ6IdbZ8j5CVfw==} + '@tanstack/query-core@5.59.17': + resolution: {integrity: sha512-jWdDiif8kaqnRGHNXAa9CnudtxY5v9DUxXhodgqX2Rwzj+1UwStDHEbBd9IA5C7VYAaJ2s+BxFR6PUBs8ERorA==} + + '@tanstack/query-persist-client-core@5.59.17': + resolution: {integrity: sha512-2buT5Un3fxsdYmfdRvNS62i7z0oDkTsaydAcDhmekl3jeiU/hK+X4OhV8i+jnvjEb9u+0i3aUWm9kvzlh77TXQ==} + + '@tanstack/react-query@5.59.19': + resolution: {integrity: sha512-xLRfyFyQOFcLltKCds0LijfC6/HQJrrTTnZB8ciyn74LIkVAm++vZJ6eUVG20RmJtdP8REdy7vSOYW4M3//XLA==} peerDependencies: react: ^18 || ^19 - '@tanstack/react-router@1.64.0': - resolution: {integrity: sha512-eRYnKIyedfiTWkTMyvqjgOzs0b+YSu1QduwEXz5uo2M5U2tguPvJtXck7jSR/IFn63SVq5iROnAmYIisZBuomA==} + '@tanstack/react-router@1.78.3': + resolution: {integrity: sha512-e4Mws3QTLDs++EyP3HwYKz+yZP3LyO5LonqEXngHYM8UjF2lvn6BnNR4vCmFNZfMFXpafR7jHSh7VOdBwnCngA==} engines: {node: '>=12'} peerDependencies: - '@tanstack/router-generator': 1.64.0 + '@tanstack/router-generator': 1.78.3 react: '>=18' react-dom: '>=18' peerDependenciesMeta: '@tanstack/router-generator': optional: true - '@tanstack/react-store@0.5.5': - resolution: {integrity: sha512-1orYXGatBqXCYKuroFwV8Ll/6aDa5E3pU6RR4h7RvRk7TmxF1+zLCsWALZaeijXkySNMGmvawSbUXRypivg2XA==} + '@tanstack/react-store@0.5.6': + resolution: {integrity: sha512-SitIpS5jTj28DajjLpWbIX+YetmJL+6PRY0DKKiCGBKfYIqj3ryODQYF3jB3SNoR9ifUA/jFkqbJdBKFtWd+AQ==} peerDependencies: react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - '@tanstack/router-generator@1.64.0': - resolution: {integrity: sha512-DTSMjOUG3ipMQjvOZKJLOMV5BqahUGZAYbgTol0ccdOSa64ApKBEr8vOsG3wLbNZ+Okw97hosGmMHsOr+wsFiQ==} + '@tanstack/router-generator@1.78.3': + resolution: {integrity: sha512-8/gy2+dzXeovCYsTeQZO8XvKqU5+cZi/QEIta8p74jmS0fftsIOFb1gV33cGoz+qts5ZThizDP9k8fIqHpOSQA==} engines: {node: '>=12'} - '@tanstack/router-plugin@1.64.0': - resolution: {integrity: sha512-BQB71kVS/g6bFcabLS7LXSXyqPd2PzZuxb5GzmArxo76AU+sxlX+3SBhqnRbPeqnXNDDyMdk8SVOCzHIGKsz2Q==} + '@tanstack/router-plugin@1.78.3': + resolution: {integrity: sha512-u8xkTfITnbri0VUvI3/nHGfzX11E0lLKs4MNEyMWemDptjgj70yRr+UeU/wcg44PFhwjVFIJ2xZSnBoMtDMh3Q==} engines: {node: '>=12'} peerDependencies: '@rsbuild/core': '>=1.0.2' @@ -1009,8 +1027,8 @@ packages: resolution: {integrity: sha512-soW+gE9QTmMaqXM17r7y1p8NiQVIIECjdTaYla8BKL5Flj030m3KuxEQoiG1XgjtA0O7ayznFz2YvPcXIy3qDg==} engines: {node: '>=12'} - '@tauri-apps/api@2.0.2': - resolution: {integrity: sha512-3wSwmG+1kr6WrgAFKK5ijkNFPp8TT3FLj3YHUb5EwMO+3FxX4uWlfSWkeeBy+Kc1RsKzugtYLuuya+98Flj+3w==} + '@tauri-apps/api@2.0.3': + resolution: {integrity: sha512-840qk6n8rbXBXMA5/aAgTYsg5JAubKO0nXw5wf7IzGnUuYKGbB4oFBIZtXOIWy+E0kNTDI3qhq5iqsoMJfwp8g==} '@tauri-apps/cli-darwin-arm64@2.0.2': resolution: {integrity: sha512-B+/a8Q6wAqmB4A4HVeK0oQP5TdQGKW60ZLOI9O2ktH2HPr9ETr3XkwXPuJ2uAOuGEgtRZHBgFOIgG000vMnKlg==} @@ -1080,11 +1098,11 @@ packages: '@tauri-apps/plugin-clipboard-manager@2.0.0': resolution: {integrity: sha512-V1sXmbjnwfXt/r48RJMwfUmDMSaP/8/YbH4CLNxt+/sf1eHlIP8PRFdFDQwLN0cNQKu2rqQVbG/Wc/Ps6cDUhw==} - '@tauri-apps/plugin-dialog@2.0.0': - resolution: {integrity: sha512-ApNkejXP2jpPBSifznPPcHTXxu9/YaRW+eJ+8+nYwqp0lLUtebFHG4QhxitM43wwReHE81WAV1DQ/b+2VBftOA==} + '@tauri-apps/plugin-dialog@2.0.1': + resolution: {integrity: sha512-fnUrNr6EfvTqdls/ufusU7h6UbNFzLKvHk/zTuOiBq01R3dTODqwctZlzakdbfSp/7pNwTKvgKTAgl/NAP/Z0Q==} - '@tauri-apps/plugin-fs@2.0.0': - resolution: {integrity: sha512-BNEeQQ5aH8J5SwYuWgRszVyItsmquRuzK2QRkVj8Z0sCsLnSvJFYI3JHRzzr3ltZGq1nMPtblrlZzuKqVzRawA==} + '@tauri-apps/plugin-fs@2.0.1': + resolution: {integrity: sha512-PkeZG2WAob9Xpmr66aPvj+McDVgFjV2a7YBzYVZjiCvbGeMs6Yk09tlXhCe3EyZdT/pwWMSi8lXUace+hlsjsw==} '@tauri-apps/plugin-notification@2.0.0': resolution: {integrity: sha512-6qEDYJS7mgXZWLXA0EFL+DVCJh8sJlzSoyw6B50pxhLPVFjc5Vr5DVzl5W3mUHaYhod5wsC984eQnlCCGqxYDA==} @@ -1095,8 +1113,11 @@ packages: '@tauri-apps/plugin-process@2.0.0': resolution: {integrity: sha512-OYzi0GnkrF4NAnsHZU7U3tjSoP0PbeAlO7T1Z+vJoBUH9sFQ1NSLqWYWQyf8hcb3gVWe7P1JggjiskO+LST1ug==} - '@tauri-apps/plugin-shell@2.0.0': - resolution: {integrity: sha512-OpW2+ycgJLrEoZityWeWYk+6ZWP9VyiAfbO+N/O8VfLkqyOym8kXh7odKDfINx9RAotkSGBtQM4abyKfJDkcUg==} + '@tauri-apps/plugin-shell@2.0.1': + resolution: {integrity: sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==} + + '@tauri-apps/plugin-store@2.1.0': + resolution: {integrity: sha512-GADqrc17opUKYIAKnGHIUgEeTZ2wJGu1ZITKQ1WMuOFdv8fvXRFBAqsqPjE3opgWohbczX6e1NpwmZK1AnuWVw==} '@tauri-apps/plugin-updater@2.0.0': resolution: {integrity: sha512-N0cl71g7RPr7zK2Fe5aoIwzw14NcdLcz7XMGFWZVjprsqgDRWoxbnUkknyCQMZthjhGkppCd/wN2MIsUz+eAhQ==} @@ -1128,8 +1149,8 @@ packages: '@types/prop-types@15.7.13': resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==} - '@types/react@18.3.11': - resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==} + '@types/react@18.3.12': + resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -1137,14 +1158,14 @@ packages: '@types/yargs@13.0.12': resolution: {integrity: sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==} - '@vitejs/plugin-react@4.3.2': - resolution: {integrity: sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==} + '@vitejs/plugin-react@4.3.3': + resolution: {integrity: sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 - acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true @@ -1213,8 +1234,11 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.0: - resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==} + broadcast-channel@7.0.0: + resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==} + + browserslist@4.24.2: + resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1222,12 +1246,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001667: - resolution: {integrity: sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==} - - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + caniuse-lite@1.0.30001677: + resolution: {integrity: sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1297,8 +1317,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.35: - resolution: {integrity: sha512-hOSRInrIDm0Brzp4IHW2F/VM+638qOL2CzE0DgpnGzKW27C95IqqeqgKz/hxHGnvPxvQGpHUGD5qRVC9EZY2+A==} + electron-to-chromium@1.5.50: + resolution: {integrity: sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1320,9 +1340,8 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} @@ -1380,10 +1399,6 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1468,10 +1483,6 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.0.1: - resolution: {integrity: sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==} - engines: {node: 20 || >=22} - lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -1517,8 +1528,8 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - nostr-tools@2.7.2: - resolution: {integrity: sha512-Bq3Ug0SZFtgtL1+0wCnAe8AJtI7yx/00/a2nUug9SkhfOwlKS92Tef12iCK9FdwXw+oFZWMtRnSwcLayQso+xA==} + nostr-tools@2.10.1: + resolution: {integrity: sha512-fnAxLi92UgyMAEw4fMEhDKzH53uBJ0WkkVPTNfX0b3bspeWWn0n5tDZtKRJbvW0wAGOPmU6LYWi/IKPOsq3p2Q==} peerDependencies: typescript: '>=5.0.0' peerDependenciesMeta: @@ -1536,6 +1547,22 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} + oblivious-set@1.4.0: + resolution: {integrity: sha512-szyd0ou0T8nsAqHtprRcP3WidfsN1TnAR5yWXf2mFCEr5ek3LEOkT6EZ/92Xfs74HIdyhG5WkGxIssMU0jBaeg==} + engines: {node: '>=16'} + + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + + p-queue@6.6.2: + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} + + p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -1550,8 +1577,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -1671,6 +1698,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -1682,8 +1712,8 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.24.0: - resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} + rollup@4.24.3: + resolution: {integrity: sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1738,10 +1768,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1753,11 +1779,11 @@ packages: tailwind-gradient-mask-image@1.2.0: resolution: {integrity: sha512-tUJaGhvqbJFiVKJu6EU5n//KvGdVvY3L3VOFNqjztk13+ifAk00pcSNHBTgHfUiBGOEzDn0gFRbSmsftUV1lXA==} - tailwind-merge@2.5.3: - resolution: {integrity: sha512-d9ZolCAIzom1nf/5p4LdD5zvjmgSxY0BGgdSvmXIoMYAiPdAW/dSpP7joCDYFY7r/HkEa2qmPtkgsu0xjQeQtw==} + tailwind-merge@2.5.4: + resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==} - tailwindcss@3.4.13: - resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==} + tailwindcss@3.4.14: + resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==} engines: {node: '>=14.0.0'} hasBin: true @@ -1774,10 +1800,6 @@ packages: tiny-warning@1.0.3: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} - to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1789,8 +1811,8 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tsconfck@3.1.3: - resolution: {integrity: sha512-ulNZP1SVpRDesxeMLON/LtWM8HIgAJEIVpVVhBM6gsmvQ8+Rh+ZG7FWGvHh7Ah3pRABwVJWklWCr/BTZSv0xnQ==} + tsconfck@3.1.4: + resolution: {integrity: sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==} engines: {node: ^18 || >=20} hasBin: true peerDependencies: @@ -1799,11 +1821,11 @@ packages: typescript: optional: true - tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.19.1: - resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} + tsx@4.19.2: + resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} engines: {node: '>=18.0.0'} hasBin: true @@ -1822,8 +1844,11 @@ packages: resolution: {integrity: sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==} engines: {node: '>=8'} - unplugin@1.14.1: - resolution: {integrity: sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==} + unload@2.4.1: + resolution: {integrity: sha512-IViSAm8Z3sRBYA+9wc0fLQmU9Nrxb16rcDmIiR6Y9LJSZzI7QY5QsDhqPpKOjAn0O9/kfK1TfNEMMAGPTIraPw==} + + unplugin@1.15.0: + resolution: {integrity: sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==} engines: {node: '>=14.0.0'} peerDependencies: webpack-sources: ^3 @@ -1865,13 +1890,13 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - virtua@0.35.0: - resolution: {integrity: sha512-k7JHQFRznKNwJb0XmBGQ0Wt3iFeqbvjHcOobQbSF87KZCgV+zgYBx7wREIvlkQJ+KffUN4VabK3+5IXhXCrdpw==} + virtua@0.36.2: + resolution: {integrity: sha512-v2u2nPi0bvFMq+6cIzIdcSXtBC+tu/RVey6fL814XjZXk5Wdq1cGGfbZLV0QSZw5Wtj6j/GTh3VHlkw5NbYktg==} peerDependencies: react: '>=16.14.0' react-dom: '>=16.14.0' solid-js: '>=1.0' - svelte: '>=4.0' + svelte: '>=5.0' vue: '>=3.2' peerDependenciesMeta: react: @@ -1893,8 +1918,8 @@ packages: vite: optional: true - vite@5.4.8: - resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} + vite@5.4.10: + resolution: {integrity: sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -1947,8 +1972,8 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.5.1: - resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + yaml@2.6.0: + resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==} engines: {node: '>= 14'} hasBin: true @@ -1970,25 +1995,26 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@babel/code-frame@7.25.7': + '@babel/code-frame@7.26.2': dependencies: - '@babel/highlight': 7.25.7 - picocolors: 1.1.0 + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 - '@babel/compat-data@7.25.7': {} + '@babel/compat-data@7.26.2': {} - '@babel/core@7.25.7': + '@babel/core@7.26.0': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.25.7 - '@babel/generator': 7.25.7 - '@babel/helper-compilation-targets': 7.25.7 - '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.7) - '@babel/helpers': 7.25.7 - '@babel/parser': 7.25.7 - '@babel/template': 7.25.7 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.7 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.2 + '@babel/template': 7.25.9 + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 convert-source-map: 2.0.0 debug: 4.3.7 gensync: 1.0.0-beta.2 @@ -1999,118 +2025,107 @@ snapshots: '@babel/generator@7.2.0': dependencies: - '@babel/types': 7.25.7 + '@babel/types': 7.26.0 jsesc: 2.5.2 lodash: 4.17.21 source-map: 0.5.7 trim-right: 1.0.1 - '@babel/generator@7.25.7': + '@babel/generator@7.26.2': dependencies: - '@babel/types': 7.25.7 + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.0.2 - '@babel/helper-compilation-targets@7.25.7': + '@babel/helper-compilation-targets@7.25.9': dependencies: - '@babel/compat-data': 7.25.7 - '@babel/helper-validator-option': 7.25.7 - browserslist: 4.24.0 + '@babel/compat-data': 7.26.2 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-module-imports@7.25.7': + '@babel/helper-module-imports@7.25.9': dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.7 + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.7)': + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.7 - '@babel/helper-module-imports': 7.25.7 - '@babel/helper-simple-access': 7.25.7 - '@babel/helper-validator-identifier': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.25.7': {} + '@babel/helper-plugin-utils@7.25.9': {} - '@babel/helper-simple-access@7.25.7': + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helpers@7.26.0': dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.7 - transitivePeerDependencies: - - supports-color + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 - '@babel/helper-string-parser@7.25.7': {} - - '@babel/helper-validator-identifier@7.25.7': {} - - '@babel/helper-validator-option@7.25.7': {} - - '@babel/helpers@7.25.7': + '@babel/parser@7.26.2': dependencies: - '@babel/template': 7.25.7 - '@babel/types': 7.25.7 + '@babel/types': 7.26.0 - '@babel/highlight@7.25.7': + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/helper-validator-identifier': 7.25.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.0 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 - '@babel/parser@7.25.7': + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/types': 7.25.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-syntax-jsx@7.25.7(@babel/core@7.25.7)': + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.7 - '@babel/helper-plugin-utils': 7.25.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-syntax-typescript@7.25.7(@babel/core@7.25.7)': + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.7 - '@babel/helper-plugin-utils': 7.25.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-react-jsx-self@7.25.7(@babel/core@7.25.7)': + '@babel/runtime@7.23.4': dependencies: - '@babel/core': 7.25.7 - '@babel/helper-plugin-utils': 7.25.7 + regenerator-runtime: 0.14.1 - '@babel/plugin-transform-react-jsx-source@7.25.7(@babel/core@7.25.7)': + '@babel/template@7.25.9': dependencies: - '@babel/core': 7.25.7 - '@babel/helper-plugin-utils': 7.25.7 + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 - '@babel/template@7.25.7': + '@babel/traverse@7.25.9': dependencies: - '@babel/code-frame': 7.25.7 - '@babel/parser': 7.25.7 - '@babel/types': 7.25.7 - - '@babel/traverse@7.25.7': - dependencies: - '@babel/code-frame': 7.25.7 - '@babel/generator': 7.25.7 - '@babel/parser': 7.25.7 - '@babel/template': 7.25.7 - '@babel/types': 7.25.7 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 + '@babel/parser': 7.26.2 + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 debug: 4.3.7 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.25.7': + '@babel/types@7.26.0': dependencies: - '@babel/helper-string-parser': 7.25.7 - '@babel/helper-validator-identifier': 7.25.7 - to-fast-properties: 2.0.0 + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 '@biomejs/biome@1.9.3': optionalDependencies: @@ -2542,52 +2557,58 @@ snapshots: optionalDependencies: '@types/react': types-react@19.0.0-rc.1 - '@rollup/rollup-android-arm-eabi@4.24.0': + '@rollup/rollup-android-arm-eabi@4.24.3': optional: true - '@rollup/rollup-android-arm64@4.24.0': + '@rollup/rollup-android-arm64@4.24.3': optional: true - '@rollup/rollup-darwin-arm64@4.24.0': + '@rollup/rollup-darwin-arm64@4.24.3': optional: true - '@rollup/rollup-darwin-x64@4.24.0': + '@rollup/rollup-darwin-x64@4.24.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.24.0': + '@rollup/rollup-freebsd-arm64@4.24.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.24.0': + '@rollup/rollup-freebsd-x64@4.24.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.24.0': + '@rollup/rollup-linux-arm-gnueabihf@4.24.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.24.0': + '@rollup/rollup-linux-arm-musleabihf@4.24.3': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': + '@rollup/rollup-linux-arm64-gnu@4.24.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.24.0': + '@rollup/rollup-linux-arm64-musl@4.24.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.24.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.24.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.24.0': + '@rollup/rollup-linux-riscv64-gnu@4.24.3': optional: true - '@rollup/rollup-linux-x64-musl@4.24.0': + '@rollup/rollup-linux-s390x-gnu@4.24.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.24.0': + '@rollup/rollup-linux-x64-gnu@4.24.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.24.0': + '@rollup/rollup-linux-x64-musl@4.24.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.24.0': + '@rollup/rollup-win32-arm64-msvc@4.24.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.24.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.24.3': optional: true '@scure/base@1.1.1': {} @@ -2605,49 +2626,58 @@ snapshots: '@tanstack/history@1.61.1': {} - '@tanstack/query-core@5.59.6': {} - - '@tanstack/react-query@5.59.8(react@19.0.0-rc-d025ddd3-20240722)': + '@tanstack/query-broadcast-client-experimental@5.59.17': dependencies: - '@tanstack/query-core': 5.59.6 + '@tanstack/query-core': 5.59.17 + broadcast-channel: 7.0.0 + + '@tanstack/query-core@5.59.17': {} + + '@tanstack/query-persist-client-core@5.59.17': + dependencies: + '@tanstack/query-core': 5.59.17 + + '@tanstack/react-query@5.59.19(react@19.0.0-rc-d025ddd3-20240722)': + dependencies: + '@tanstack/query-core': 5.59.17 react: 19.0.0-rc-d025ddd3-20240722 - '@tanstack/react-router@1.64.0(@tanstack/router-generator@1.64.0)(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722)': + '@tanstack/react-router@1.78.3(@tanstack/router-generator@1.78.3)(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722)': dependencies: '@tanstack/history': 1.61.1 - '@tanstack/react-store': 0.5.5(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722) + '@tanstack/react-store': 0.5.6(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722) react: 19.0.0-rc-d025ddd3-20240722 react-dom: 19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 optionalDependencies: - '@tanstack/router-generator': 1.64.0 + '@tanstack/router-generator': 1.78.3 - '@tanstack/react-store@0.5.5(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722)': + '@tanstack/react-store@0.5.6(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722)': dependencies: '@tanstack/store': 0.5.5 react: 19.0.0-rc-d025ddd3-20240722 react-dom: 19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722) use-sync-external-store: 1.2.2(react@19.0.0-rc-d025ddd3-20240722) - '@tanstack/router-generator@1.64.0': + '@tanstack/router-generator@1.78.3': dependencies: '@tanstack/virtual-file-routes': 1.64.0 prettier: 3.3.3 - tsx: 4.19.1 + tsx: 4.19.2 zod: 3.23.8 - '@tanstack/router-plugin@1.64.0(vite@5.4.8)(webpack-sources@3.2.3)': + '@tanstack/router-plugin@1.78.3(vite@5.4.10)(webpack-sources@3.2.3)': dependencies: - '@babel/core': 7.25.7 - '@babel/generator': 7.25.7 - '@babel/parser': 7.25.7 - '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.7) - '@babel/plugin-syntax-typescript': 7.25.7(@babel/core@7.25.7) - '@babel/template': 7.25.7 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.7 - '@tanstack/router-generator': 1.64.0 + '@babel/core': 7.26.0 + '@babel/generator': 7.26.2 + '@babel/parser': 7.26.2 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@babel/template': 7.25.9 + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + '@tanstack/router-generator': 1.78.3 '@tanstack/virtual-file-routes': 1.64.0 '@types/babel__core': 7.20.5 '@types/babel__generator': 7.6.8 @@ -2655,10 +2685,10 @@ snapshots: '@types/babel__traverse': 7.20.6 babel-dead-code-elimination: 1.0.6 chokidar: 3.6.0 - unplugin: 1.14.1(webpack-sources@3.2.3) + unplugin: 1.15.0(webpack-sources@3.2.3) zod: 3.23.8 optionalDependencies: - vite: 5.4.8 + vite: 5.4.10 transitivePeerDependencies: - supports-color - webpack-sources @@ -2667,7 +2697,7 @@ snapshots: '@tanstack/virtual-file-routes@1.64.0': {} - '@tauri-apps/api@2.0.2': {} + '@tauri-apps/api@2.0.3': {} '@tauri-apps/cli-darwin-arm64@2.0.2': optional: true @@ -2714,56 +2744,60 @@ snapshots: '@tauri-apps/plugin-clipboard-manager@2.0.0': dependencies: - '@tauri-apps/api': 2.0.2 + '@tauri-apps/api': 2.0.3 - '@tauri-apps/plugin-dialog@2.0.0': + '@tauri-apps/plugin-dialog@2.0.1': dependencies: - '@tauri-apps/api': 2.0.2 + '@tauri-apps/api': 2.0.3 - '@tauri-apps/plugin-fs@2.0.0': + '@tauri-apps/plugin-fs@2.0.1': dependencies: - '@tauri-apps/api': 2.0.2 + '@tauri-apps/api': 2.0.3 '@tauri-apps/plugin-notification@2.0.0': dependencies: - '@tauri-apps/api': 2.0.2 + '@tauri-apps/api': 2.0.3 '@tauri-apps/plugin-os@2.0.0': dependencies: - '@tauri-apps/api': 2.0.2 + '@tauri-apps/api': 2.0.3 '@tauri-apps/plugin-process@2.0.0': dependencies: - '@tauri-apps/api': 2.0.2 + '@tauri-apps/api': 2.0.3 - '@tauri-apps/plugin-shell@2.0.0': + '@tauri-apps/plugin-shell@2.0.1': dependencies: - '@tauri-apps/api': 2.0.2 + '@tauri-apps/api': 2.0.3 + + '@tauri-apps/plugin-store@2.1.0': + dependencies: + '@tauri-apps/api': 2.0.3 '@tauri-apps/plugin-updater@2.0.0': dependencies: - '@tauri-apps/api': 2.0.2 + '@tauri-apps/api': 2.0.3 '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.25.7 - '@babel/types': 7.25.7 + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.25.7 + '@babel/types': 7.26.0 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.25.7 - '@babel/types': 7.25.7 + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.25.7 + '@babel/types': 7.26.0 '@types/estree@1.0.6': {} @@ -2780,7 +2814,7 @@ snapshots: '@types/prop-types@15.7.13': {} - '@types/react@18.3.11': + '@types/react@18.3.12': dependencies: '@types/prop-types': 15.7.13 csstype: 3.1.3 @@ -2791,18 +2825,18 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@vitejs/plugin-react@4.3.2(vite@5.4.8)': + '@vitejs/plugin-react@4.3.3(vite@5.4.10)': dependencies: - '@babel/core': 7.25.7 - '@babel/plugin-transform-react-jsx-self': 7.25.7(@babel/core@7.25.7) - '@babel/plugin-transform-react-jsx-source': 7.25.7(@babel/core@7.25.7) + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.8 + vite: 5.4.10 transitivePeerDependencies: - supports-color - acorn@8.12.1: {} + acorn@8.14.0: {} ansi-regex@4.1.1: {} @@ -2831,31 +2865,31 @@ snapshots: aria-hidden@1.2.4: dependencies: - tslib: 2.7.0 + tslib: 2.8.1 autoprefixer@10.4.20(postcss@8.4.47): dependencies: - browserslist: 4.24.0 - caniuse-lite: 1.0.30001667 + browserslist: 4.24.2 + caniuse-lite: 1.0.30001677 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.1.0 + picocolors: 1.1.1 postcss: 8.4.47 postcss-value-parser: 4.2.0 babel-dead-code-elimination@1.0.6: dependencies: - '@babel/core': 7.25.7 - '@babel/parser': 7.25.7 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.7 + '@babel/core': 7.26.0 + '@babel/parser': 7.26.2 + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 transitivePeerDependencies: - supports-color babel-plugin-react-compiler@0.0.0-experimental-696af53-20240625: dependencies: '@babel/generator': 7.2.0 - '@babel/types': 7.25.7 + '@babel/types': 7.26.0 chalk: 4.1.2 invariant: 2.2.4 pretty-format: 24.9.0 @@ -2874,22 +2908,23 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.0: + broadcast-channel@7.0.0: dependencies: - caniuse-lite: 1.0.30001667 - electron-to-chromium: 1.5.35 + '@babel/runtime': 7.23.4 + oblivious-set: 1.4.0 + p-queue: 6.6.2 + unload: 2.4.1 + + browserslist@4.24.2: + dependencies: + caniuse-lite: 1.0.30001677 + electron-to-chromium: 1.5.50 node-releases: 2.0.18 - update-browserslist-db: 1.1.1(browserslist@4.24.0) + update-browserslist-db: 1.1.1(browserslist@4.24.2) camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001667: {} - - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 + caniuse-lite@1.0.30001677: {} chalk@4.1.2: dependencies: @@ -2950,7 +2985,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.35: {} + electron-to-chromium@1.5.50: {} emoji-regex@8.0.0: {} @@ -3011,7 +3046,7 @@ snapshots: escalade@3.2.0: {} - escape-string-regexp@1.0.5: {} + eventemitter3@4.0.7: {} fast-glob@3.3.2: dependencies: @@ -3070,8 +3105,6 @@ snapshots: globrex@0.1.2: {} - has-flag@3.0.0: {} - has-flag@4.0.0: {} hasown@2.0.2: @@ -3132,8 +3165,6 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.0.1: {} - lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -3169,7 +3200,7 @@ snapshots: normalize-range@0.1.2: {} - nostr-tools@2.7.2(typescript@5.6.3): + nostr-tools@2.10.1(typescript@5.6.3): dependencies: '@noble/ciphers': 0.5.3 '@noble/curves': 1.2.0 @@ -3188,6 +3219,19 @@ snapshots: object-hash@3.0.0: {} + oblivious-set@1.4.0: {} + + p-finally@1.0.0: {} + + p-queue@6.6.2: + dependencies: + eventemitter3: 4.0.7 + p-timeout: 3.2.0 + + p-timeout@3.2.0: + dependencies: + p-finally: 1.0.0 + package-json-from-dist@1.0.1: {} path-key@3.1.1: {} @@ -3199,7 +3243,7 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - picocolors@1.1.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -3222,7 +3266,7 @@ snapshots: postcss-load-config@4.0.2(postcss@8.4.47): dependencies: lilconfig: 3.1.2 - yaml: 2.5.1 + yaml: 2.6.0 optionalDependencies: postcss: 8.4.47 @@ -3241,7 +3285,7 @@ snapshots: postcss@8.4.47: dependencies: nanoid: 3.3.7 - picocolors: 1.1.0 + picocolors: 1.1.1 source-map-js: 1.2.1 prettier@3.3.3: {} @@ -3268,7 +3312,7 @@ snapshots: dependencies: react: 19.0.0-rc-d025ddd3-20240722 react-style-singleton: 2.2.1(react@19.0.0-rc-d025ddd3-20240722)(types-react@19.0.0-rc.1) - tslib: 2.7.0 + tslib: 2.8.1 optionalDependencies: '@types/react': types-react@19.0.0-rc.1 @@ -3277,7 +3321,7 @@ snapshots: react: 19.0.0-rc-d025ddd3-20240722 react-remove-scroll-bar: 2.3.6(react@19.0.0-rc-d025ddd3-20240722)(types-react@19.0.0-rc.1) react-style-singleton: 2.2.1(react@19.0.0-rc-d025ddd3-20240722)(types-react@19.0.0-rc.1) - tslib: 2.7.0 + tslib: 2.8.1 use-callback-ref: 1.3.2(react@19.0.0-rc-d025ddd3-20240722)(types-react@19.0.0-rc.1) use-sidecar: 1.1.2(react@19.0.0-rc-d025ddd3-20240722)(types-react@19.0.0-rc.1) optionalDependencies: @@ -3288,7 +3332,7 @@ snapshots: get-nonce: 1.0.1 invariant: 2.2.4 react: 19.0.0-rc-d025ddd3-20240722 - tslib: 2.7.0 + tslib: 2.8.1 optionalDependencies: '@types/react': types-react@19.0.0-rc.1 @@ -3302,6 +3346,8 @@ snapshots: dependencies: picomatch: 2.3.1 + regenerator-runtime@0.14.1: {} + resolve-pkg-maps@1.0.0: {} resolve@1.22.8: @@ -3312,26 +3358,28 @@ snapshots: reusify@1.0.4: {} - rollup@4.24.0: + rollup@4.24.3: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.24.0 - '@rollup/rollup-android-arm64': 4.24.0 - '@rollup/rollup-darwin-arm64': 4.24.0 - '@rollup/rollup-darwin-x64': 4.24.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.24.0 - '@rollup/rollup-linux-arm-musleabihf': 4.24.0 - '@rollup/rollup-linux-arm64-gnu': 4.24.0 - '@rollup/rollup-linux-arm64-musl': 4.24.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0 - '@rollup/rollup-linux-riscv64-gnu': 4.24.0 - '@rollup/rollup-linux-s390x-gnu': 4.24.0 - '@rollup/rollup-linux-x64-gnu': 4.24.0 - '@rollup/rollup-linux-x64-musl': 4.24.0 - '@rollup/rollup-win32-arm64-msvc': 4.24.0 - '@rollup/rollup-win32-ia32-msvc': 4.24.0 - '@rollup/rollup-win32-x64-msvc': 4.24.0 + '@rollup/rollup-android-arm-eabi': 4.24.3 + '@rollup/rollup-android-arm64': 4.24.3 + '@rollup/rollup-darwin-arm64': 4.24.3 + '@rollup/rollup-darwin-x64': 4.24.3 + '@rollup/rollup-freebsd-arm64': 4.24.3 + '@rollup/rollup-freebsd-x64': 4.24.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.24.3 + '@rollup/rollup-linux-arm-musleabihf': 4.24.3 + '@rollup/rollup-linux-arm64-gnu': 4.24.3 + '@rollup/rollup-linux-arm64-musl': 4.24.3 + '@rollup/rollup-linux-powerpc64le-gnu': 4.24.3 + '@rollup/rollup-linux-riscv64-gnu': 4.24.3 + '@rollup/rollup-linux-s390x-gnu': 4.24.3 + '@rollup/rollup-linux-x64-gnu': 4.24.3 + '@rollup/rollup-linux-x64-musl': 4.24.3 + '@rollup/rollup-win32-arm64-msvc': 4.24.3 + '@rollup/rollup-win32-ia32-msvc': 4.24.3 + '@rollup/rollup-win32-x64-msvc': 4.24.3 fsevents: 2.3.3 run-parallel@1.2.0: @@ -3384,10 +3432,6 @@ snapshots: pirates: 4.0.6 ts-interface-checker: 0.1.13 - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3396,9 +3440,9 @@ snapshots: tailwind-gradient-mask-image@1.2.0: {} - tailwind-merge@2.5.3: {} + tailwind-merge@2.5.4: {} - tailwindcss@3.4.13: + tailwindcss@3.4.14: dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -3413,7 +3457,7 @@ snapshots: micromatch: 4.0.8 normalize-path: 3.0.0 object-hash: 3.0.0 - picocolors: 1.1.0 + picocolors: 1.1.1 postcss: 8.4.47 postcss-import: 15.1.0(postcss@8.4.47) postcss-js: 4.0.1(postcss@8.4.47) @@ -3437,8 +3481,6 @@ snapshots: tiny-warning@1.0.3: {} - to-fast-properties@2.0.0: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -3447,13 +3489,13 @@ snapshots: ts-interface-checker@0.1.13: {} - tsconfck@3.1.3(typescript@5.6.3): + tsconfck@3.1.4(typescript@5.6.3): optionalDependencies: typescript: 5.6.3 - tslib@2.7.0: {} + tslib@2.8.1: {} - tsx@4.19.1: + tsx@4.19.2: dependencies: esbuild: 0.23.1 get-tsconfig: 4.8.1 @@ -3462,7 +3504,7 @@ snapshots: types-react-dom@19.0.0-rc.1: dependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.12 types-react@19.0.0-rc.1: dependencies: @@ -3472,23 +3514,25 @@ snapshots: unique-names-generator@4.7.1: {} - unplugin@1.14.1(webpack-sources@3.2.3): + unload@2.4.1: {} + + unplugin@1.15.0(webpack-sources@3.2.3): dependencies: - acorn: 8.12.1 + acorn: 8.14.0 webpack-virtual-modules: 0.6.2 optionalDependencies: webpack-sources: 3.2.3 - update-browserslist-db@1.1.1(browserslist@4.24.0): + update-browserslist-db@1.1.1(browserslist@4.24.2): dependencies: - browserslist: 4.24.0 + browserslist: 4.24.2 escalade: 3.2.0 - picocolors: 1.1.0 + picocolors: 1.1.1 use-callback-ref@1.3.2(react@19.0.0-rc-d025ddd3-20240722)(types-react@19.0.0-rc.1): dependencies: react: 19.0.0-rc-d025ddd3-20240722 - tslib: 2.7.0 + tslib: 2.8.1 optionalDependencies: '@types/react': types-react@19.0.0-rc.1 @@ -3496,7 +3540,7 @@ snapshots: dependencies: detect-node-es: 1.1.0 react: 19.0.0-rc-d025ddd3-20240722 - tslib: 2.7.0 + tslib: 2.8.1 optionalDependencies: '@types/react': types-react@19.0.0-rc.1 @@ -3506,27 +3550,27 @@ snapshots: util-deprecate@1.0.2: {} - virtua@0.35.0(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722): + virtua@0.36.2(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722): optionalDependencies: react: 19.0.0-rc-d025ddd3-20240722 react-dom: 19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722) - vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@5.4.8): + vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@5.4.10): dependencies: debug: 4.3.7 globrex: 0.1.2 - tsconfck: 3.1.3(typescript@5.6.3) + tsconfck: 3.1.4(typescript@5.6.3) optionalDependencies: - vite: 5.4.8 + vite: 5.4.10 transitivePeerDependencies: - supports-color - typescript - vite@5.4.8: + vite@5.4.10: dependencies: esbuild: 0.21.5 postcss: 8.4.47 - rollup: 4.24.0 + rollup: 4.24.3 optionalDependencies: fsevents: 2.3.3 @@ -3553,7 +3597,7 @@ snapshots: yallist@3.1.1: {} - yaml@2.5.1: {} + yaml@2.6.0: {} zod-validation-error@2.1.0(zod@3.23.8): dependencies: diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index a5e5541..a0a309e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2,6 +2,39 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "COOP" +version = "0.2.0" +dependencies = [ + "border", + "futures", + "itertools 0.13.0", + "keyring", + "keyring-search", + "nostr-connect", + "nostr-sdk", + "serde", + "serde_json", + "specta", + "specta-typescript", + "tauri", + "tauri-build", + "tauri-plugin-clipboard-manager", + "tauri-plugin-decorum", + "tauri-plugin-dialog", + "tauri-plugin-fs", + "tauri-plugin-notification", + "tauri-plugin-os", + "tauri-plugin-prevent-default", + "tauri-plugin-process", + "tauri-plugin-shell", + "tauri-plugin-store", + "tauri-plugin-updater", + "tauri-specta", + "tokio", + "tracing-subscriber", +] + [[package]] name = "Inflector" version = "0.11.4" @@ -109,9 +142,9 @@ checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "775a8770d29db3dadcb858482cc240af7b2ffde4ac4de67d1d4955728103f0e2" dependencies = [ "derive_arbitrary", ] @@ -619,7 +652,7 @@ dependencies = [ [[package]] name = "border" version = "0.1.0" -source = "git+https://github.com/ahkohd/tauri-toolkit?branch=v2#39952d8694268694836b5d9e5fa8473474fe4c45" +source = "git+https://github.com/ahkohd/tauri-toolkit?branch=v2#7f5223abbb672664c5ce8b45f55e71f472d53c17" dependencies = [ "cocoa 0.25.0", "color", @@ -935,7 +968,7 @@ dependencies = [ [[package]] name = "color" version = "0.1.0" -source = "git+https://github.com/ahkohd/tauri-toolkit?branch=v2#39952d8694268694836b5d9e5fa8473474fe4c45" +source = "git+https://github.com/ahkohd/tauri-toolkit?branch=v2#7f5223abbb672664c5ce8b45f55e71f472d53c17" dependencies = [ "cocoa 0.25.0", "objc", @@ -973,36 +1006,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "coop" -version = "0.1.0" -dependencies = [ - "border", - "futures", - "itertools 0.13.0", - "keyring", - "keyring-search", - "nostr-connect", - "nostr-sdk", - "serde", - "serde_json", - "specta", - "specta-typescript", - "tauri", - "tauri-build", - "tauri-plugin-clipboard-manager", - "tauri-plugin-decorum", - "tauri-plugin-dialog", - "tauri-plugin-fs", - "tauri-plugin-notification", - "tauri-plugin-os", - "tauri-plugin-prevent-default", - "tauri-plugin-process", - "tauri-plugin-shell", - "tauri-plugin-updater", - "tauri-specta", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -1256,9 +1259,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "d475dfebcb4854d596b17b09f477616f80f17a550517f2b3615d8c205d5c802b" dependencies = [ "proc-macro2", "quote", @@ -2193,9 +2196,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" dependencies = [ "allocator-api2", "equivalent", @@ -2454,6 +2457,124 @@ dependencies = [ "png", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2462,12 +2583,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -2527,7 +2659,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] @@ -2760,7 +2892,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f8fe839464d4e4b37d756d7e910063696af79a7e877282cb1825e4ec5f10833" dependencies = [ "byteorder", - "linux-keyutils", "log", "security-framework 2.11.1", "security-framework 3.0.0", @@ -2770,8 +2901,7 @@ dependencies = [ [[package]] name = "keyring-search" version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fba83ff0a0efb658afeaaa6de89c7abd3ccd34333f5a36d5dae417334fcd488" +source = "git+https://github.com/reyamir/keyring-search#59d54e6a28229f09f87b9b043690ee8a1d63221e" dependencies = [ "byteorder", "lazy_static", @@ -2865,7 +2995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2918,6 +3048,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lmdb-master-sys" version = "0.2.4" @@ -2972,7 +3108,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.0", + "hashbrown 0.15.1", ] [[package]] @@ -3098,9 +3234,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b18047edf23933de40835403d4b9211ffd1dcc65c0eec569df38a1fb8aebd719" +checksum = "fdae9c00e61cc0579bcac625e8ad22104c60548a025bfc972dc83868a28e1484" dependencies = [ "crossbeam-channel", "dpi", @@ -3213,7 +3349,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "nostr" version = "0.35.0" -source = "git+https://github.com/rust-nostr/nostr#4da48df74e494f8705e4887ce31a63adeba7b47b" +source = "git+https://github.com/rust-nostr/nostr#b233dcf1a4769eca97a01a34e2b0f83e3eb96d3c" dependencies = [ "aes", "async-trait", @@ -3244,7 +3380,7 @@ dependencies = [ [[package]] name = "nostr-connect" version = "0.35.0" -source = "git+https://github.com/rust-nostr/nostr#4da48df74e494f8705e4887ce31a63adeba7b47b" +source = "git+https://github.com/rust-nostr/nostr#b233dcf1a4769eca97a01a34e2b0f83e3eb96d3c" dependencies = [ "async-trait", "async-utility", @@ -3258,7 +3394,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.35.0" -source = "git+https://github.com/rust-nostr/nostr#4da48df74e494f8705e4887ce31a63adeba7b47b" +source = "git+https://github.com/rust-nostr/nostr#b233dcf1a4769eca97a01a34e2b0f83e3eb96d3c" dependencies = [ "async-trait", "flatbuffers", @@ -3272,7 +3408,7 @@ dependencies = [ [[package]] name = "nostr-lmdb" version = "0.35.0" -source = "git+https://github.com/rust-nostr/nostr#4da48df74e494f8705e4887ce31a63adeba7b47b" +source = "git+https://github.com/rust-nostr/nostr#b233dcf1a4769eca97a01a34e2b0f83e3eb96d3c" dependencies = [ "heed", "nostr", @@ -3285,7 +3421,7 @@ dependencies = [ [[package]] name = "nostr-relay-pool" version = "0.35.0" -source = "git+https://github.com/rust-nostr/nostr#4da48df74e494f8705e4887ce31a63adeba7b47b" +source = "git+https://github.com/rust-nostr/nostr#b233dcf1a4769eca97a01a34e2b0f83e3eb96d3c" dependencies = [ "async-utility", "async-wsocket", @@ -3303,7 +3439,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.35.0" -source = "git+https://github.com/rust-nostr/nostr#4da48df74e494f8705e4887ce31a63adeba7b47b" +source = "git+https://github.com/rust-nostr/nostr#b233dcf1a4769eca97a01a34e2b0f83e3eb96d3c" dependencies = [ "async-utility", "atomic-destructor", @@ -3322,7 +3458,7 @@ dependencies = [ [[package]] name = "nostr-zapper" version = "0.35.0" -source = "git+https://github.com/rust-nostr/nostr#4da48df74e494f8705e4887ce31a63adeba7b47b" +source = "git+https://github.com/rust-nostr/nostr#b233dcf1a4769eca97a01a34e2b0f83e3eb96d3c" dependencies = [ "async-trait", "nostr", @@ -3342,6 +3478,16 @@ dependencies = [ "zbus", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num" version = "0.4.3" @@ -3456,7 +3602,7 @@ dependencies = [ [[package]] name = "nwc" version = "0.35.0" -source = "git+https://github.com/rust-nostr/nostr#4da48df74e494f8705e4887ce31a63adeba7b47b" +source = "git+https://github.com/rust-nostr/nostr#b233dcf1a4769eca97a01a34e2b0f83e3eb96d3c" dependencies = [ "async-trait", "async-utility", @@ -3794,6 +3940,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "page_size" version = "0.6.0" @@ -4638,9 +4790,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.38" +version = "0.38.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" dependencies = [ "bitflags 2.6.0", "errno", @@ -5046,6 +5198,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shared_child" version = "1.0.1" @@ -5338,6 +5499,17 @@ dependencies = [ "crossbeam-queue", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "sys-locale" version = "0.3.2" @@ -5714,6 +5886,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tauri-plugin-store" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9a580be53f04bb62422d239aa798e88522877f58a0d4a0e745f030055a51bb4" +dependencies = [ + "dunce", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror", + "tokio", +] + [[package]] name = "tauri-plugin-updater" version = "2.0.2" @@ -5906,24 +6094,34 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.67" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3c6efbfc763e64eb85c11c25320f0737cb7364c4b6336db90aa9ebe27a0bbd" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.67" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" dependencies = [ "proc-macro2", "quote", "syn 2.0.87", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tiff" version = "0.9.1" @@ -5966,6 +6164,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -5991,6 +6199,7 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -6167,6 +6376,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", ] [[package]] @@ -6280,12 +6515,6 @@ dependencies = [ "unic-common", ] -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.13" @@ -6325,9 +6554,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", "idna", @@ -6353,6 +6582,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.11.0" @@ -6374,6 +6615,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version-compare" version = "0.2.0" @@ -6704,7 +6951,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -7125,6 +7372,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wry" version = "0.46.3" @@ -7225,6 +7484,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + [[package]] name = "zbus" version = "4.0.1" @@ -7311,12 +7594,55 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "zip" version = "2.2.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ff95251..a319ed1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "coop" -version = "0.1.0" +name = "COOP" +version = "0.2.0" description = "direct message client for desktop" authors = ["npub1zfss807aer0j26mwp2la0ume0jqde3823rmu97ra6sgyyg956e0s6xw445"] repository = "https://github.com/lumehq/coop" @@ -24,6 +24,7 @@ tauri-plugin-updater = "2.0.0" tauri-plugin-process = "2.0.0" tauri-plugin-fs = "2.0.0" tauri-plugin-notification = "2.0.0" +tauri-plugin-store = "2.1.0" tauri-plugin-decorum = "1.1.0" tauri-plugin-prevent-default = "^0.4" tauri-specta = { version = "2.0.0-rc", features = ["derive", "typescript"] } @@ -33,16 +34,14 @@ nostr-connect = { git = "https://github.com/rust-nostr/nostr" } specta = "^2.0.0-rc.20" specta-typescript = "0.0.7" +tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1" itertools = "0.13.0" futures = "0.3.30" -keyring-search = "1.2.0" -keyring = { version = "3", features = [ - "apple-native", - "windows-native", - "linux-native", -] } +keyring = { version = "3", features = ["apple-native", "windows-native"] } +keyring-search = { git = "https://github.com/reyamir/keyring-search" } +tracing-subscriber = { version = "0.3.18", features = ["fmt"] } [target.'cfg(target_os = "macos")'.dependencies] border = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 553e9be..df8f24f 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -14,11 +14,6 @@ "core:resources:default", "core:menu:default", "core:tray:default", - "shell:allow-open", - "dialog:default", - "dialog:allow-open", - "dialog:allow-ask", - "dialog:allow-message", "core:window:allow-close", "core:window:allow-center", "core:window:allow-minimize", @@ -27,14 +22,18 @@ "core:window:allow-set-focus", "core:window:allow-start-dragging", "core:window:allow-toggle-maximize", + "shell:allow-open", + "dialog:default", + "dialog:allow-open", + "dialog:allow-ask", + "dialog:allow-message", "decorum:allow-show-snap-overlay", "prevent-default:default", "updater:default", - "updater:allow-check", - "updater:allow-download-and-install", "clipboard-manager:allow-write-text", "clipboard-manager:allow-read-text", "fs:allow-read-file", - "notification:default" + "notification:default", + "store:default" ] } diff --git a/src-tauri/resources/relays.txt b/src-tauri/resources/relays.txt index 00e204e..6975d47 100644 --- a/src-tauri/resources/relays.txt +++ b/src-tauri/resources/relays.txt @@ -1,4 +1,5 @@ -wss://purplepag.es/, -wss://directory.yabu.me/, -wss://user.kindpag.es/, -wss://relay.damus.io/, +wss://relay.damus.io, +wss://relay.primal.net, +wss://nostr.fmt.wiz.biz, +wss://directory.yabu.me, +wss://purplepag.es, diff --git a/src-tauri/rustfmt.toml b/src-tauri/rustfmt.toml deleted file mode 100644 index f72c407..0000000 --- a/src-tauri/rustfmt.toml +++ /dev/null @@ -1,11 +0,0 @@ -edition = "2021" -format_strings = true -imports_granularity = "Crate" -group_imports = "StdExternalCrate" -comment_width = 100 -format_code_in_doc_comments = true -wrap_comments = true -use_field_init_shorthand = true -use_small_heuristics = "Max" -hard_tabs = true -ignore = ["target/", "gen/"] diff --git a/src-tauri/src/commands/account.rs b/src-tauri/src/commands/account.rs index bee0f98..a3a847f 100644 --- a/src-tauri/src/commands/account.rs +++ b/src-tauri/src/commands/account.rs @@ -5,429 +5,371 @@ use nostr_sdk::prelude::*; use serde::{Deserialize, Serialize}; use specta::Type; use std::{collections::HashSet, str::FromStr, time::Duration}; -use tauri::{Emitter, Manager, State}; -use tauri_plugin_notification::NotificationExt; +use tauri::{Manager, State}; -use crate::Nostr; +use crate::{Nostr, SUBSCRIPTION_ID}; #[derive(Clone, Serialize)] pub struct EventPayload { - event: String, // JSON String - sender: String, + event: String, // JSON String + sender: String, } #[derive(Debug, Clone, Serialize, Deserialize, Type)] struct Account { - password: String, - nostr_connect: Option, + password: String, + nostr_connect: Option, } #[tauri::command] #[specta::specta] pub async fn get_metadata(id: String, state: State<'_, Nostr>) -> Result { - let client = &state.client; - let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?; + let client = &state.client; + let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?; - let filter = Filter::new().author(public_key).kind(Kind::Metadata).limit(1); + let filter = Filter::new() + .author(public_key) + .kind(Kind::Metadata) + .limit(1); - let events = client.database().query(vec![filter]).await.map_err(|e| e.to_string())?; + let events = client + .database() + .query(vec![filter]) + .await + .map_err(|e| e.to_string())?; - match events.first() { - Some(event) => match Metadata::from_json(&event.content) { - Ok(metadata) => Ok(metadata.as_json()), - Err(e) => Err(e.to_string()), - }, - None => Err("Metadata not found".into()), - } + match events.first() { + Some(event) => match Metadata::from_json(&event.content) { + Ok(metadata) => Ok(metadata.as_json()), + Err(e) => Err(e.to_string()), + }, + None => Err("Metadata not found".into()), + } } #[tauri::command] #[specta::specta] pub fn get_accounts() -> Vec { - let search = Search::new().expect("Unexpected."); - let results = search.by_service("Coop Secret Storage"); - let list = List::list_credentials(&results, Limit::All); - let accounts: HashSet = - list.split_whitespace().filter(|v| v.starts_with("npub1")).map(String::from).collect(); + let search = Search::new().expect("Unexpected."); + let results = search.by_service("Coop Secret Storage"); + let list = List::list_credentials(&results, Limit::All); + let accounts: HashSet = list + .split_whitespace() + .filter(|v| v.starts_with("npub1")) + .map(String::from) + .collect(); - accounts.into_iter().collect() + accounts.into_iter().collect() } #[tauri::command] #[specta::specta] pub async fn get_current_account(state: State<'_, Nostr>) -> Result { - let client = &state.client; - let signer = client.signer().await.map_err(|e| e.to_string())?; - let public_key = signer.get_public_key().await.map_err(|e| e.to_string())?; - let bech32 = public_key.to_bech32().map_err(|e| e.to_string())?; + let client = &state.client; + let signer = client.signer().await.map_err(|e| e.to_string())?; + let public_key = signer.get_public_key().await.map_err(|e| e.to_string())?; + let bech32 = public_key.to_bech32().map_err(|e| e.to_string())?; - Ok(bech32) + Ok(bech32) } #[tauri::command] #[specta::specta] pub async fn create_account( - name: String, - about: String, - picture: String, - password: String, - state: State<'_, Nostr>, + name: String, + about: String, + picture: String, + password: String, + state: State<'_, Nostr>, ) -> Result { - let client = &state.client; - let keys = Keys::generate(); - let npub = keys.public_key().to_bech32().map_err(|e| e.to_string())?; - let secret_key = keys.secret_key(); - let enc = EncryptedSecretKey::new(secret_key, password, 16, KeySecurity::Medium) - .map_err(|err| err.to_string())?; - let enc_bech32 = enc.to_bech32().map_err(|err| err.to_string())?; + let client = &state.client; + let keys = Keys::generate(); + let npub = keys.public_key().to_bech32().map_err(|e| e.to_string())?; + let secret_key = keys.secret_key(); + let enc = EncryptedSecretKey::new(secret_key, password, 16, KeySecurity::Medium) + .map_err(|err| err.to_string())?; + let enc_bech32 = enc.to_bech32().map_err(|err| err.to_string())?; - // Save account - let keyring = Entry::new("Coop Secret Storage", &npub).map_err(|e| e.to_string())?; - let account = Account { password: enc_bech32, nostr_connect: None }; - let j = serde_json::to_string(&account).map_err(|e| e.to_string())?; - let _ = keyring.set_password(&j); + // Save account + let keyring = Entry::new("Coop Secret Storage", &npub).map_err(|e| e.to_string())?; + let account = Account { + password: enc_bech32, + nostr_connect: None, + }; + let j = serde_json::to_string(&account).map_err(|e| e.to_string())?; + let _ = keyring.set_password(&j); - // Update signer - client.set_signer(keys).await; + // Update signer + client.set_signer(keys).await; - let mut metadata = - Metadata::new().display_name(name.clone()).name(name.to_lowercase()).about(about); + let mut metadata = Metadata::new() + .display_name(name.clone()) + .name(name.to_lowercase()) + .about(about); - if let Ok(url) = Url::parse(&picture) { - metadata = metadata.picture(url) - } + if let Ok(url) = Url::parse(&picture) { + metadata = metadata.picture(url) + } - match client.set_metadata(&metadata).await { - Ok(_) => Ok(npub), - Err(e) => Err(e.to_string()), - } + match client.set_metadata(&metadata).await { + Ok(_) => Ok(npub), + Err(e) => Err(e.to_string()), + } } #[tauri::command] #[specta::specta] pub async fn import_account(key: String, password: String) -> Result { - let (npub, enc_bech32) = match key.starts_with("ncryptsec") { - true => { - let enc = EncryptedSecretKey::from_bech32(key).map_err(|err| err.to_string())?; - let enc_bech32 = enc.to_bech32().map_err(|err| err.to_string())?; - let secret_key = enc.to_secret_key(password).map_err(|err| err.to_string())?; - let keys = Keys::new(secret_key); - let npub = keys.public_key().to_bech32().unwrap(); + let (npub, enc_bech32) = match key.starts_with("ncryptsec") { + true => { + let enc = EncryptedSecretKey::from_bech32(key).map_err(|err| err.to_string())?; + let enc_bech32 = enc.to_bech32().map_err(|err| err.to_string())?; + let secret_key = enc.to_secret_key(password).map_err(|err| err.to_string())?; + let keys = Keys::new(secret_key); + let npub = keys.public_key().to_bech32().unwrap(); - (npub, enc_bech32) - } - false => { - let secret_key = SecretKey::from_bech32(key).map_err(|err| err.to_string())?; - let keys = Keys::new(secret_key.clone()); - let npub = keys.public_key().to_bech32().unwrap(); + (npub, enc_bech32) + } + false => { + let secret_key = SecretKey::from_bech32(key).map_err(|err| err.to_string())?; + let keys = Keys::new(secret_key.clone()); + let npub = keys.public_key().to_bech32().unwrap(); - let enc = EncryptedSecretKey::new(&secret_key, password, 16, KeySecurity::Medium) - .map_err(|err| err.to_string())?; + let enc = EncryptedSecretKey::new(&secret_key, password, 16, KeySecurity::Medium) + .map_err(|err| err.to_string())?; - let enc_bech32 = enc.to_bech32().map_err(|err| err.to_string())?; + let enc_bech32 = enc.to_bech32().map_err(|err| err.to_string())?; - (npub, enc_bech32) - } - }; + (npub, enc_bech32) + } + }; - let keyring = Entry::new("Coop Secret Storage", &npub).map_err(|e| e.to_string())?; + let keyring = Entry::new("Coop Secret Storage", &npub).map_err(|e| e.to_string())?; - let account = Account { password: enc_bech32, nostr_connect: None }; + let account = Account { + password: enc_bech32, + nostr_connect: None, + }; - let pwd = serde_json::to_string(&account).map_err(|e| e.to_string())?; - keyring.set_password(&pwd).map_err(|e| e.to_string())?; + let pwd = serde_json::to_string(&account).map_err(|e| e.to_string())?; + keyring.set_password(&pwd).map_err(|e| e.to_string())?; - Ok(npub) + Ok(npub) } #[tauri::command] #[specta::specta] pub async fn connect_account(uri: String, state: State<'_, Nostr>) -> Result { - let client = &state.client; + let client = &state.client; - match NostrConnectURI::parse(uri.clone()) { - Ok(bunker_uri) => { - // Local user - let app_keys = Keys::generate(); - let app_secret = app_keys.secret_key().to_secret_hex(); + match NostrConnectURI::parse(uri.clone()) { + Ok(bunker_uri) => { + // Local user + let app_keys = Keys::generate(); + let app_secret = app_keys.secret_key().to_secret_hex(); - // Get remote user - let remote_user = bunker_uri.remote_signer_public_key().unwrap(); - let remote_npub = remote_user.to_bech32().unwrap(); + // Get remote user + let remote_user = bunker_uri.remote_signer_public_key().unwrap(); + let remote_npub = remote_user.to_bech32().unwrap(); - match NostrConnect::new(bunker_uri, app_keys, Duration::from_secs(120), None) { - Ok(signer) => { - let mut url = Url::parse(&uri).unwrap(); - let query: Vec<(String, String)> = url - .query_pairs() - .filter(|(name, _)| name != "secret") - .map(|(name, value)| (name.into_owned(), value.into_owned())) - .collect(); - url.query_pairs_mut().clear().extend_pairs(&query); + match NostrConnect::new(bunker_uri, app_keys, Duration::from_secs(120), None) { + Ok(signer) => { + let mut url = Url::parse(&uri).unwrap(); + let query: Vec<(String, String)> = url + .query_pairs() + .filter(|(name, _)| name != "secret") + .map(|(name, value)| (name.into_owned(), value.into_owned())) + .collect(); + url.query_pairs_mut().clear().extend_pairs(&query); - let key = format!("{}_nostrconnect", remote_npub); - let keyring = Entry::new("Coop Secret Storage", &key).unwrap(); - let account = - Account { password: app_secret, nostr_connect: Some(url.to_string()) }; - let j = serde_json::to_string(&account).map_err(|e| e.to_string())?; - let _ = keyring.set_password(&j); + let key = format!("{}_nostrconnect", remote_npub); + let keyring = Entry::new("Coop Secret Storage", &key).unwrap(); + let account = Account { + password: app_secret, + nostr_connect: Some(url.to_string()), + }; + let j = serde_json::to_string(&account).map_err(|e| e.to_string())?; + let _ = keyring.set_password(&j); - // Update signer - let _ = client.set_signer(signer).await; + // Update signer + let _ = client.set_signer(signer).await; - Ok(remote_npub) - } - Err(err) => Err(err.to_string()), - } - } - Err(err) => Err(err.to_string()), - } + Ok(remote_npub) + } + Err(err) => Err(err.to_string()), + } + } + Err(err) => Err(err.to_string()), + } } #[tauri::command] #[specta::specta] pub async fn reset_password(key: String, password: String) -> Result<(), String> { - let secret_key = SecretKey::from_bech32(key).map_err(|err| err.to_string())?; - let keys = Keys::new(secret_key.clone()); - let npub = keys.public_key().to_bech32().unwrap(); + let secret_key = SecretKey::from_bech32(key).map_err(|err| err.to_string())?; + let keys = Keys::new(secret_key.clone()); + let npub = keys.public_key().to_bech32().unwrap(); - let enc = EncryptedSecretKey::new(&secret_key, password, 16, KeySecurity::Medium) - .map_err(|err| err.to_string())?; - let enc_bech32 = enc.to_bech32().map_err(|err| err.to_string())?; + let enc = EncryptedSecretKey::new(&secret_key, password, 16, KeySecurity::Medium) + .map_err(|err| err.to_string())?; + let enc_bech32 = enc.to_bech32().map_err(|err| err.to_string())?; - let keyring = Entry::new("Coop Secret Storage", &npub).map_err(|e| e.to_string())?; - let account = Account { password: enc_bech32, nostr_connect: None }; - let j = serde_json::to_string(&account).map_err(|e| e.to_string())?; - let _ = keyring.set_password(&j); + let keyring = Entry::new("Coop Secret Storage", &npub).map_err(|e| e.to_string())?; + let account = Account { + password: enc_bech32, + nostr_connect: None, + }; + let j = serde_json::to_string(&account).map_err(|e| e.to_string())?; + let _ = keyring.set_password(&j); - Ok(()) + Ok(()) } #[tauri::command] #[specta::specta] pub fn delete_account(id: String) -> Result<(), String> { - let keyring = Entry::new("Coop Secret Storage", &id).map_err(|e| e.to_string())?; - let _ = keyring.delete_credential(); + let keyring = Entry::new("Coop Secret Storage", &id).map_err(|e| e.to_string())?; + let _ = keyring.delete_credential(); - Ok(()) + Ok(()) } #[tauri::command] #[specta::specta] pub async fn get_contact_list(state: State<'_, Nostr>) -> Result, String> { - let client = &state.client; + let client = &state.client; - match client.get_contact_list(Some(Duration::from_secs(10))).await { - Ok(contacts) => { - let list = contacts.into_iter().map(|c| c.public_key.to_hex()).collect::>(); - Ok(list) - } - Err(e) => Err(e.to_string()), - } + match client.get_contact_list(Some(Duration::from_secs(10))).await { + Ok(contacts) => { + let list = contacts + .into_iter() + .map(|c| c.public_key.to_hex()) + .collect::>(); + Ok(list) + } + Err(e) => Err(e.to_string()), + } } #[tauri::command] #[specta::specta] pub async fn login( - account: String, - password: String, - state: State<'_, Nostr>, - handle: tauri::AppHandle, + account: String, + password: String, + state: State<'_, Nostr>, + handle: tauri::AppHandle, ) -> Result { - let client = &state.client; - let keyring = Entry::new("Coop Secret Storage", &account).map_err(|e| e.to_string())?; + let client = &state.client; + let keyring = Entry::new("Coop Secret Storage", &account).map_err(|e| e.to_string())?; - let account = match keyring.get_password() { - Ok(pw) => { - let account: Account = serde_json::from_str(&pw).map_err(|e| e.to_string())?; - account - } - Err(e) => return Err(e.to_string()), - }; + let account = match keyring.get_password() { + Ok(pw) => { + let account: Account = serde_json::from_str(&pw).map_err(|e| e.to_string())?; + account + } + Err(e) => return Err(e.to_string()), + }; - let public_key = match account.nostr_connect { - None => { - let ncryptsec = - EncryptedSecretKey::from_bech32(account.password).map_err(|e| e.to_string())?; - let secret_key = ncryptsec.to_secret_key(password).map_err(|_| "Wrong password.")?; - let keys = Keys::new(secret_key); - let public_key = keys.public_key(); + let public_key = match account.nostr_connect { + None => { + let ncryptsec = + EncryptedSecretKey::from_bech32(account.password).map_err(|e| e.to_string())?; + let secret_key = ncryptsec + .to_secret_key(password) + .map_err(|_| "Wrong password.")?; + let keys = Keys::new(secret_key); + let public_key = keys.public_key(); - // Update signer - client.set_signer(keys).await; + // Update signer + client.set_signer(keys).await; - public_key - } - Some(bunker) => { - let uri = NostrConnectURI::parse(bunker).map_err(|e| e.to_string())?; - let public_key = uri.remote_signer_public_key().unwrap().clone(); - let app_keys = Keys::from_str(&account.password).map_err(|e| e.to_string())?; + public_key + } + Some(bunker) => { + let uri = NostrConnectURI::parse(bunker).map_err(|e| e.to_string())?; + let public_key = uri.remote_signer_public_key().unwrap().to_owned(); + let app_keys = Keys::from_str(&account.password).map_err(|e| e.to_string())?; - match NostrConnect::new(uri, app_keys, Duration::from_secs(120), None) { - Ok(signer) => { - // Update signer - client.set_signer(signer).await; - // Return public key - public_key - } - Err(e) => return Err(e.to_string()), - } - } - }; + match NostrConnect::new(uri, app_keys, Duration::from_secs(120), None) { + Ok(signer) => { + // Update signer + client.set_signer(signer).await; + // Return public key + public_key + } + Err(e) => return Err(e.to_string()), + } + } + }; - let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1); + let filter = Filter::new() + .kind(Kind::Custom(10050)) + .author(public_key) + .limit(1); - if let Ok(events) = - client.get_events_of(vec![inbox], EventSource::relays(Some(Duration::from_secs(3)))).await - { - if let Some(event) = events.into_iter().next() { - let urls = event - .tags - .iter() - .filter_map(|tag| { - if let Some(TagStandard::Relay(relay)) = tag.as_standardized() { - Some(relay.to_string()) - } else { - None - } - }) - .collect::>(); + let mut rx = client + .stream_events(vec![filter], Some(Duration::from_secs(3))) + .await + .map_err(|e| e.to_string())?; - for url in urls.iter() { - if let Err(e) = client.add_relay(url).await { - println!("Connect relay failed: {}", e) - } - } + while let Some(event) = rx.next().await { + let urls = event + .tags + .iter() + .filter_map(|tag| { + if let Some(TagStandard::Relay(relay)) = tag.as_standardized() { + Some(relay.to_string()) + } else { + None + } + }) + .collect::>(); - // Workaround for https://github.com/rust-nostr/nostr/issues/509 - // TODO: remove this - let _ = client - .get_events_from( - urls.clone(), - vec![Filter::new().kind(Kind::TextNote).limit(0)], - Some(Duration::from_secs(5)), - ) - .await; + for url in urls.iter() { + let _ = client.add_relay(url).await; + let _ = client.connect_relay(url).await; + } - let mut inbox_relays = state.inbox_relays.lock().await; - inbox_relays.insert(public_key, urls); - } else { - return Err("404".into()); - } - } + let mut inbox_relays = state.inbox_relays.write().await; + inbox_relays.insert(public_key, urls); + } - let sub_id = SubscriptionId::new("inbox"); - let new_message = Filter::new().kind(Kind::GiftWrap).pubkey(public_key).limit(0); + tauri::async_runtime::spawn(async move { + let state = handle.state::(); + let client = &state.client; - if client.subscription(&sub_id).await.is_some() { - // Remove old subscriotion - client.unsubscribe(sub_id.clone()).await; - // Resubscribe new message for current user - let _ = client.subscribe_with_id(sub_id.clone(), vec![new_message], None).await; - } else { - let _ = client.subscribe_with_id(sub_id.clone(), vec![new_message], None).await; - } + let inbox_relays = state.inbox_relays.read().await; + let relays = inbox_relays.get(&public_key).unwrap().to_owned(); - tauri::async_runtime::spawn(async move { - let state = handle.state::(); - let client = &state.client; + let sub_id = SubscriptionId::new(SUBSCRIPTION_ID); + let new_message = Filter::new() + .kind(Kind::GiftWrap) + .pubkey(public_key) + .limit(0); - let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key); + if let Err(e) = client + .subscribe_with_id_to(&relays, sub_id, vec![new_message], None) + .await + { + println!("Subscribe error: {}", e) + }; - // Generate a fake sig for rumor event. - // TODO: Find better way to save unsigned event to database. - let fake_sig = Signature::from_str("f9e79d141c004977192d05a86f81ec7c585179c371f7350a5412d33575a2a356433f58e405c2296ed273e2fe0aafa25b641e39cc4e1f3f261ebf55bce0cbac83").unwrap(); + let filter = Filter::new() + .kind(Kind::GiftWrap) + .pubkey(public_key) + .limit(200); - if let Ok(events) = client - .pool() - .get_events_of( - vec![filter], - Duration::from_secs(12), - FilterOptions::WaitDurationAfterEOSE(Duration::from_secs(20)), - ) - .await - { - for event in events.iter() { - if let Ok(UnwrappedGift { rumor, .. }) = client.unwrap_gift_wrap(event).await { - let rumor_clone = rumor.clone(); - let ev = Event::new( - rumor_clone.id.unwrap(), - rumor_clone.pubkey, - rumor_clone.created_at, - rumor_clone.kind, - rumor_clone.tags, - rumor_clone.content, - fake_sig, - ); + let mut rx = client + .stream_events_from(&relays, vec![filter], Some(Duration::from_secs(40))) + .await + .unwrap(); - if let Err(e) = client.database().save_event(&ev).await { - println!("Error: {}", e) - } - } - } - handle.emit("synchronized", ()).unwrap(); - } + while let Some(event) = rx.next().await { + println!("Event: {}", event.as_json()); + } - client - .handle_notifications(|notification| async { - if let RelayPoolNotification::Message { message, .. } = notification { - if let RelayMessage::Event { event, subscription_id, .. } = message { - if subscription_id == sub_id && event.kind == Kind::GiftWrap { - if let Ok(UnwrappedGift { rumor, sender }) = - client.unwrap_gift_wrap(&event).await - { - let rumor_clone = rumor.clone(); - let ev = Event::new( - rumor_clone.id.unwrap(), - rumor_clone.pubkey, - rumor_clone.created_at, - rumor_clone.kind, - rumor_clone.tags, - rumor_clone.content, - fake_sig, - ); + // handle.emit("synchronized", ()).unwrap(); + }); - // Save rumor to database to further query - if let Err(e) = client.database().save_event(&ev).await { - println!("[save event] error: {}", e) - } - - // Emit new event to frontend - if let Err(e) = handle.emit( - "event", - EventPayload { - event: rumor.as_json(), - sender: sender.to_hex(), - }, - ) { - println!("[emit] error: {}", e) - } - - if sender != public_key { - if let Some(window) = handle.get_webview_window("main") { - if !window.is_focused().unwrap() { - if let Err(e) = handle - .notification() - .builder() - .body("You have a new message") - .title("Coop") - .show() - { - println!("[notification] error: {}", e); - } - } - } - } - } - } - } else { - println!("relay message: {}", message.as_json()) - } - } - Ok(false) - }) - .await - }); - - Ok(public_key.to_hex()) + Ok(public_key.to_hex()) } diff --git a/src-tauri/src/commands/chat.rs b/src-tauri/src/commands/chat.rs index 2f51a12..2d2b691 100644 --- a/src-tauri/src/commands/chat.rs +++ b/src-tauri/src/commands/chat.rs @@ -8,89 +8,110 @@ use crate::Nostr; #[tauri::command] #[specta::specta] pub async fn get_chats(state: State<'_, Nostr>) -> Result, String> { - let client = &state.client; - let signer = client.signer().await.map_err(|e| e.to_string())?; - let public_key = signer.get_public_key().await.map_err(|e| e.to_string())?; + let client = &state.client; - let filter = Filter::new().kind(Kind::PrivateDirectMessage).pubkey(public_key); + let signer = client.signer().await.map_err(|e| e.to_string())?; + let public_key = signer.get_public_key().await.map_err(|e| e.to_string())?; - match client.database().query(vec![filter]).await { - Ok(events) => { - let ev = events - .into_iter() - .sorted_by_key(|ev| Reverse(ev.created_at)) - .filter(|ev| ev.pubkey != public_key) - .unique_by(|ev| ev.pubkey) - .map(|ev| ev.as_json()) - .collect::>(); + let filter = Filter::new() + .kind(Kind::PrivateDirectMessage) + .pubkey(public_key); + let events = client + .database() + .query(vec![filter]) + .await + .map_err(|e| e.to_string())?; - Ok(ev) - } - Err(e) => Err(e.to_string()), - } + let data = events + .into_iter() + .sorted_by_key(|ev| Reverse(ev.created_at)) + .filter(|ev| ev.pubkey != public_key) + .unique_by(|ev| ev.pubkey) + .map(|ev| ev.as_json()) + .collect::>(); + + Ok(data) } #[tauri::command] #[specta::specta] pub async fn get_chat_messages(id: String, state: State<'_, Nostr>) -> Result, String> { - let client = &state.client; - let signer = client.signer().await.map_err(|e| e.to_string())?; + let client = &state.client; + let signer = client.signer().await.map_err(|e| e.to_string())?; - let receiver = signer.get_public_key().await.map_err(|e| e.to_string())?; - let sender = PublicKey::parse(id).map_err(|e| e.to_string())?; + let receiver = signer.get_public_key().await.map_err(|e| e.to_string())?; + let sender = PublicKey::parse(id).map_err(|e| e.to_string())?; - let recv_filter = - Filter::new().kind(Kind::PrivateDirectMessage).author(sender).pubkey(receiver); - let sender_filter = - Filter::new().kind(Kind::PrivateDirectMessage).author(receiver).pubkey(sender); + let recv_filter = Filter::new() + .kind(Kind::PrivateDirectMessage) + .author(sender) + .pubkey(receiver); + let sender_filter = Filter::new() + .kind(Kind::PrivateDirectMessage) + .author(receiver) + .pubkey(sender); - match client.database().query(vec![recv_filter, sender_filter]).await { - Ok(events) => { - let ev = events.into_iter().map(|ev| ev.as_json()).collect::>(); - Ok(ev) - } - Err(e) => Err(e.to_string()), - } + match client + .database() + .query(vec![recv_filter, sender_filter]) + .await + { + Ok(events) => { + let ev = events + .into_iter() + .map(|ev| ev.as_json()) + .collect::>(); + Ok(ev) + } + Err(e) => Err(e.to_string()), + } } #[tauri::command] #[specta::specta] pub async fn send_message( - to: String, - message: String, - state: State<'_, Nostr>, + to: String, + message: String, + state: State<'_, Nostr>, ) -> Result<(), String> { - let client = &state.client; - let relays = state.inbox_relays.lock().await; + let client = &state.client; + let relays = state.inbox_relays.read().await; - let signer = client.signer().await.map_err(|e| e.to_string())?; - let public_key = signer.get_public_key().await.map_err(|e| e.to_string())?; - let receiver = PublicKey::parse(&to).map_err(|e| e.to_string())?; + let signer = client.signer().await.map_err(|e| e.to_string())?; + let public_key = signer.get_public_key().await.map_err(|e| e.to_string())?; + let receiver = PublicKey::parse(&to).map_err(|e| e.to_string())?; - // TODO: Add support reply_to - let rumor = EventBuilder::private_msg_rumor(receiver, message, None); + // TODO: Add support reply_to + let rumor = EventBuilder::private_msg_rumor(receiver, message, None); - // Get inbox relays per member - let outbox_urls = match relays.get(&receiver) { - Some(relays) => relays, - None => return Err("Receiver didn't have inbox relays to receive message.".into()), - }; + // Get inbox relays for receiver + let outbox_urls = match relays.get(&receiver) { + Some(relays) => relays, + None => return Err("Receiver didn't have inbox relays to receive message.".into()), + }; - let inbox_urls = match relays.get(&public_key) { - Some(relays) => relays, - None => return Err("Please config inbox relays to backup your message.".into()), - }; + // Get inbox relays for current user + let inbox_urls = match relays.get(&public_key) { + Some(relays) => relays, + None => return Err("Please config inbox relays to backup your message.".into()), + }; - // Send message to [receiver] - match client.gift_wrap_to(outbox_urls, &receiver, rumor.clone(), None).await { - Ok(_) => { - // Send message to [yourself] - if let Err(e) = client.gift_wrap_to(inbox_urls, &public_key, rumor, None).await { - return Err(e.to_string()); - } + // Send message to [receiver] + match client + .gift_wrap_to(outbox_urls, &receiver, rumor.clone(), None) + .await + { + Ok(_) => { + // Send message to [yourself] + if let Err(e) = client + .gift_wrap_to(inbox_urls, &public_key, rumor, None) + .await + { + return Err(e.to_string()); + } - Ok(()) - } - Err(e) => Err(e.to_string()), - } + Ok(()) + } + Err(e) => Err(e.to_string()), + } } diff --git a/src-tauri/src/commands/relay.rs b/src-tauri/src/commands/relay.rs index 3a1f4aa..92e4c7a 100644 --- a/src-tauri/src/commands/relay.rs +++ b/src-tauri/src/commands/relay.rs @@ -1,8 +1,8 @@ use nostr_sdk::prelude::*; use std::{ - fs::OpenOptions, - io::{self, BufRead, Write}, - time::Duration, + fs::OpenOptions, + io::{self, BufRead, Write}, + time::Duration, }; use tauri::{Manager, State}; @@ -11,177 +11,198 @@ use crate::Nostr; #[tauri::command] #[specta::specta] pub fn get_bootstrap_relays(app: tauri::AppHandle) -> Result, String> { - let relays_path = app - .path() - .resolve("resources/relays.txt", tauri::path::BaseDirectory::Resource) - .map_err(|e| e.to_string())?; + let relays_path = app + .path() + .resolve("resources/relays.txt", tauri::path::BaseDirectory::Resource) + .map_err(|e| e.to_string())?; - let file = std::fs::File::open(relays_path).map_err(|e| e.to_string())?; - let reader = io::BufReader::new(file); + let file = std::fs::File::open(relays_path).map_err(|e| e.to_string())?; + let reader = io::BufReader::new(file); - reader.lines().collect::, io::Error>>().map_err(|e| e.to_string()) + reader + .lines() + .collect::, io::Error>>() + .map_err(|e| e.to_string()) } #[tauri::command] #[specta::specta] pub fn set_bootstrap_relays(relays: String, app: tauri::AppHandle) -> Result<(), String> { - let relays_path = app - .path() - .resolve("resources/relays.txt", tauri::path::BaseDirectory::Resource) - .map_err(|e| e.to_string())?; - let mut file = OpenOptions::new().write(true).open(relays_path).map_err(|e| e.to_string())?; + let relays_path = app + .path() + .resolve("resources/relays.txt", tauri::path::BaseDirectory::Resource) + .map_err(|e| e.to_string())?; + let mut file = OpenOptions::new() + .write(true) + .open(relays_path) + .map_err(|e| e.to_string())?; - file.write_all(relays.as_bytes()).map_err(|e| e.to_string()) + file.write_all(relays.as_bytes()).map_err(|e| e.to_string()) } #[tauri::command] #[specta::specta] pub async fn get_inbox_relays( - user_id: String, - state: State<'_, Nostr>, + user_id: String, + state: State<'_, Nostr>, ) -> Result, String> { - let client = &state.client; - let public_key = PublicKey::parse(user_id).map_err(|e| e.to_string())?; - let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1); + let client = &state.client; + let public_key = PublicKey::parse(user_id).map_err(|e| e.to_string())?; + let filter = Filter::new() + .kind(Kind::Custom(10050)) + .author(public_key) + .limit(1); - match client.get_events_of(vec![inbox], EventSource::relays(Some(Duration::from_secs(3)))).await - { - Ok(events) => { - if let Some(event) = events.into_iter().next() { - let urls = event - .tags - .iter() - .filter_map(|tag| { - if let Some(TagStandard::Relay(relay)) = tag.as_standardized() { - Some(relay.to_string()) - } else { - None - } - }) - .collect::>(); + let mut events = Events::new(&[filter.clone()]); - Ok(urls) - } else { - Ok(Vec::new()) - } - } - Err(e) => Err(e.to_string()), - } + let mut rx = client + .stream_events(vec![filter], Some(Duration::from_secs(3))) + .await + .map_err(|e| e.to_string())?; + + while let Some(event) = rx.next().await { + events.insert(event); + } + + if let Some(event) = events.first() { + let urls = event + .tags + .iter() + .filter_map(|tag| { + if let Some(TagStandard::Relay(relay)) = tag.as_standardized() { + Some(relay.to_string()) + } else { + None + } + }) + .collect::>(); + + Ok(urls) + } else { + Ok(Vec::new()) + } } #[tauri::command] #[specta::specta] pub async fn ensure_inbox_relays( - user_id: String, - state: State<'_, Nostr>, + user_id: String, + state: State<'_, Nostr>, ) -> Result, String> { - let public_key = PublicKey::parse(user_id).map_err(|e| e.to_string())?; - let relays = state.inbox_relays.lock().await; + let public_key = PublicKey::parse(user_id).map_err(|e| e.to_string())?; + let relays = state.inbox_relays.read().await; - match relays.get(&public_key) { - Some(relays) => { - if relays.is_empty() { - Err("404".into()) - } else { - Ok(relays.to_owned()) - } - } - None => Err("404".into()), - } + match relays.get(&public_key) { + Some(relays) => { + if relays.is_empty() { + Err("404".into()) + } else { + Ok(relays.to_owned()) + } + } + None => Err("404".into()), + } } #[tauri::command] #[specta::specta] pub async fn set_inbox_relays(relays: Vec, state: State<'_, Nostr>) -> Result<(), String> { - let client = &state.client; + let client = &state.client; - let tags = relays.into_iter().map(|t| Tag::custom(TagKind::Relay, vec![t])).collect::>(); - let event = EventBuilder::new(Kind::Custom(10050), "", tags); + let tags = relays + .into_iter() + .map(|t| Tag::custom(TagKind::Relay, vec![t])) + .collect::>(); + let event = EventBuilder::new(Kind::Custom(10050), "", tags); - match client.send_event_builder(event).await { - Ok(_) => Ok(()), - Err(e) => Err(e.to_string()), - } + match client.send_event_builder(event).await { + Ok(_) => Ok(()), + Err(e) => Err(e.to_string()), + } } #[tauri::command] #[specta::specta] pub async fn connect_inbox_relays( - user_id: String, - ignore_cache: bool, - state: State<'_, Nostr>, + user_id: String, + ignore_cache: bool, + state: State<'_, Nostr>, ) -> Result, String> { - let client = &state.client; - let public_key = PublicKey::parse(&user_id).map_err(|e| e.to_string())?; + let client = &state.client; + let public_key = PublicKey::parse(&user_id).map_err(|e| e.to_string())?; - // let nip65_relays = connect_nip65_relays(public_key, client).await; - let mut inbox_relays = state.inbox_relays.lock().await; + let mut inbox_relays = state.inbox_relays.write().await; - if !ignore_cache { - if let Some(relays) = inbox_relays.get(&public_key) { - for url in relays { - if let Ok(relay) = client.relay(url).await { - if !relay.is_connected() { - if let Err(e) = client.connect_relay(url).await { - println!("Connect relay failed: {}", e) - } - } - } else if let Err(e) = client.add_relay(url).await { - println!("Connect relay failed: {}", e) - } - } - return Ok(relays.to_owned()); - }; - }; + if !ignore_cache { + if let Some(relays) = inbox_relays.get(&public_key) { + for url in relays { + if let Ok(relay) = client.relay(url).await { + if !relay.is_connected() { + if let Err(e) = client.connect_relay(url).await { + println!("Connect relay failed: {}", e) + } + } + } else if let Err(e) = client.add_relay(url).await { + println!("Connect relay failed: {}", e) + } + } + return Ok(relays.to_owned()); + }; + }; - let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1); + let filter = Filter::new() + .kind(Kind::Custom(10050)) + .author(public_key) + .limit(1); - match client.get_events_of(vec![inbox], EventSource::relays(Some(Duration::from_secs(3)))).await - { - Ok(events) => { - let mut relays = Vec::new(); + let mut events = Events::new(&[filter.clone()]); - if let Some(event) = events.into_iter().next() { - for tag in event.tags.iter() { - if let Some(TagStandard::Relay(relay)) = tag.as_standardized() { - let url = relay.to_string(); + let mut rx = client + .stream_events(vec![filter], Some(Duration::from_secs(3))) + .await + .map_err(|e| e.to_string())?; - if let Err(e) = client.add_relay(&url).await { - println!("Connect relay failed: {}", e) - }; + while let Some(event) = rx.next().await { + events.insert(event); + } - relays.push(url) - } - } + if let Some(event) = events.first() { + let mut relays = Vec::new(); - // Update state - inbox_relays.insert(public_key, relays.clone()); + for tag in event.tags.iter() { + if let Some(TagStandard::Relay(relay)) = tag.as_standardized() { + let url = relay.to_string(); + let _ = client.add_relay(&url).await; + let _ = client.connect_relay(&url).await; - // Disconnect user's nip65 relays to save bandwidth - // disconnect_nip65_relays(nip65_relays, client).await; - } + relays.push(url) + } + } - Ok(relays) - } - Err(e) => Err(e.to_string()), - } + // Update state + inbox_relays.insert(public_key, relays.clone()); + + Ok(relays) + } else { + Err("User's inbox relays not found.".to_string()) + } } #[tauri::command] #[specta::specta] pub async fn disconnect_inbox_relays( - user_id: String, - state: State<'_, Nostr>, + user_id: String, + state: State<'_, Nostr>, ) -> Result<(), String> { - let client = &state.client; - let public_key = PublicKey::parse(&user_id).map_err(|e| e.to_string())?; - let inbox_relays = state.inbox_relays.lock().await; + let client = &state.client; + let public_key = PublicKey::parse(&user_id).map_err(|e| e.to_string())?; + let inbox_relays = state.inbox_relays.read().await; - if let Some(relays) = inbox_relays.get(&public_key) { - for relay in relays { - let _ = client.disconnect_relay(relay).await; - } - } + if let Some(relays) = inbox_relays.get(&public_key) { + for relay in relays { + let _ = client.disconnect_relay(relay).await; + } + } - Ok(()) + Ok(()) } diff --git a/src-tauri/src/commands/tray.rs b/src-tauri/src/commands/tray.rs index 3b82f49..be2f7e9 100644 --- a/src-tauri/src/commands/tray.rs +++ b/src-tauri/src/commands/tray.rs @@ -5,60 +5,52 @@ use tauri::{Manager, WebviewWindowBuilder}; use tauri_plugin_decorum::WebviewWindowExt; pub fn setup_tray_icon(app: &tauri::AppHandle) -> tauri::Result<()> { - let tray = app.tray_by_id("main").expect("Error: tray icon not found."); + let tray = app.tray_by_id("main").expect("Error: tray icon not found."); - let menu = tauri::menu::MenuBuilder::new(app) - .item( - &tauri::menu::MenuItem::with_id(app, "open", "Open Coop", true, None::<&str>).unwrap(), - ) - .item(&tauri::menu::MenuItem::with_id(app, "quit", "Quit", true, None::<&str>).unwrap()) - .build() - .expect("Error: cannot create menu."); + let menu = tauri::menu::MenuBuilder::new(app) + .item( + &tauri::menu::MenuItem::with_id(app, "open", "Open Coop", true, None::<&str>).unwrap(), + ) + .item(&tauri::menu::MenuItem::with_id(app, "quit", "Quit", true, None::<&str>).unwrap()) + .build() + .expect("Error: cannot create menu."); - if tray.set_menu(Some(menu)).is_err() { - panic!("Error: cannot set menu for tray icon.") - } + if tray.set_menu(Some(menu)).is_err() { + panic!("Error: cannot set menu for tray icon.") + } - tray.on_menu_event(move |app, event| match event.id.0.as_str() { - "open" => { - if let Some(window) = app.get_webview_window("main") { - if window.is_visible().unwrap_or_default() { - let _ = window.set_focus(); - } else { - let _ = window.show(); - let _ = window.set_focus(); - }; - } else { - let config = app.config().app.windows.first().unwrap(); - let window = - WebviewWindowBuilder::from_config(app, config).unwrap().build().unwrap(); + tray.on_menu_event(move |app, event| match event.id.0.as_str() { + "open" => { + if let Some(window) = app.get_webview_window("main") { + if window.is_visible().unwrap_or_default() { + let _ = window.set_focus(); + } else { + let _ = window.show(); + let _ = window.set_focus(); + }; + } else { + let config = app.config().app.windows.first().unwrap(); + let window = WebviewWindowBuilder::from_config(app, config) + .unwrap() + .build() + .unwrap(); - // Set custom decoration - #[cfg(target_os = "windows")] - window.create_overlay_titlebar().unwrap(); + // Set custom decoration + #[cfg(target_os = "windows")] + window.create_overlay_titlebar().unwrap(); - // Set traffic light inset - #[cfg(target_os = "macos")] - window.set_traffic_lights_inset(12.0, 18.0).unwrap(); + // Set traffic light inset + #[cfg(target_os = "macos")] + window.set_traffic_lights_inset(12.0, 18.0).unwrap(); - // Workaround for reset traffic light when theme changed - #[cfg(target_os = "macos")] - let win_ = window.clone(); - #[cfg(target_os = "macos")] - window.on_window_event(move |event| { - if let tauri::WindowEvent::ThemeChanged(_) = event { - win_.set_traffic_lights_inset(12.0, 18.0).unwrap(); - } - }); + // Restore native border + #[cfg(target_os = "macos")] + window.add_border(None); + } + } + "quit" => std::process::exit(0), + _ => {} + }); - // Restore native border - #[cfg(target_os = "macos")] - window.add_border(None); - } - } - "quit" => std::process::exit(0), - _ => {} - }); - - Ok(()) + Ok(()) } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 51c6ad6..b8b7924 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,65 +3,91 @@ #[cfg(target_os = "macos")] use border::WebviewWindowExt as WebviewWindowExtAlt; -use commands::tray::setup_tray_icon; use nostr_sdk::prelude::*; +use serde::{Deserialize, Serialize}; use specta_typescript::Typescript; use std::{ - collections::HashMap, - env, fs, - io::{self, BufRead}, - str::FromStr, + collections::{HashMap, HashSet}, + env, fs, + io::{self, BufRead}, + str::FromStr, }; -use tauri::{async_runtime::Mutex, Manager}; +use tauri::{Emitter, Listener, Manager}; #[cfg(not(target_os = "linux"))] use tauri_plugin_decorum::WebviewWindowExt; use tauri_specta::{collect_commands, Builder}; +use tokio::{sync::RwLock, time::sleep, time::Duration}; -use commands::{account::*, chat::*, relay::*}; +use commands::{account::*, chat::*, relay::*, tray::*}; mod commands; pub struct Nostr { - client: Client, - inbox_relays: Mutex>>, + client: Client, + queue: RwLock>, + inbox_relays: RwLock>>, } +#[derive(Serialize, Deserialize, Debug)] +pub struct Payload { + id: String, +} + +#[derive(Clone, Serialize)] +pub struct EventPayload { + event: String, // JSON String + sender: String, +} + +pub const QUEUE_DELAY: u64 = 300; +pub const SUBSCRIPTION_ID: &str = "inbox"; + fn main() { - #[cfg(target_os = "linux")] - std::env::set_var("WEBKIT_DISABLE_COMPOSITING_MODE", "1"); + #[cfg(target_os = "linux")] + std::env::set_var("WEBKIT_DISABLE_COMPOSITING_MODE", "1"); - let builder = Builder::::new().commands(collect_commands![ - get_bootstrap_relays, - set_bootstrap_relays, - get_inbox_relays, - set_inbox_relays, - ensure_inbox_relays, - connect_inbox_relays, - disconnect_inbox_relays, - login, - create_account, - import_account, - connect_account, - delete_account, - reset_password, - get_accounts, - get_current_account, - get_metadata, - get_contact_list, - get_chats, - get_chat_messages, - send_message, - ]); + let builder = Builder::::new().commands(collect_commands![ + get_bootstrap_relays, + set_bootstrap_relays, + get_inbox_relays, + set_inbox_relays, + ensure_inbox_relays, + connect_inbox_relays, + disconnect_inbox_relays, + login, + create_account, + import_account, + connect_account, + delete_account, + reset_password, + get_accounts, + get_current_account, + get_metadata, + get_contact_list, + get_chats, + get_chat_messages, + send_message, + ]); - #[cfg(debug_assertions)] - builder - .export(Typescript::default(), "../src/commands.ts") - .expect("Failed to export typescript bindings"); + #[cfg(debug_assertions)] + builder + .export(Typescript::default(), "../src/commands.ts") + .expect("Failed to export typescript bindings"); - tauri::Builder::default() + #[cfg(debug_assertions)] + let tauri_builder = tauri::Builder::default().plugin(tauri_plugin_devtools::init()); + + #[cfg(not(debug_assertions))] + let tauri_builder = tauri::Builder::default(); + + tauri_builder .invoke_handler(builder.invoke_handler()) .setup(move |app| { let handle = app.handle(); + let handle_clone = handle.clone(); + let handle_clone_child = handle_clone.clone(); + + // Setup tray icon let _ = setup_tray_icon(handle); #[cfg(not(target_os = "linux"))] @@ -88,11 +114,10 @@ fn main() { let _ = fs::create_dir_all(&dir); // Setup database - let database = NostrLMDB::open(dir.join("nostr-lmdb")) - .expect("Error: cannot create database."); + let database = NostrLMDB::open(dir.join("nostr-lmdb")).expect("Error: cannot create database."); // Setup nostr client - let opts = Options::new().gossip(true).autoconnect(true); + let opts = Options::new().gossip(true).automatic_authentication(false).max_avg_latency(Duration::from_millis(500)); let client = ClientBuilder::default().opts(opts).database(database).build(); // Add bootstrap relays @@ -123,15 +148,128 @@ fn main() { } } + let _ = client.add_discovery_relay("wss://user.kindpag.es/").await; + + // Connect + client.connect().await; + client }); // Create global state - app.manage(Nostr { client, inbox_relays: Mutex::new(HashMap::new()) }); + app.manage(Nostr { + client, + queue: RwLock::new(HashSet::new()), + inbox_relays: RwLock::new(HashMap::new()), + }); + + // Listen for request metadata + app.listen_any("request_metadata", move |event| { + let payload = event.payload(); + let parsed_payload: Payload = serde_json::from_str(payload).expect("Parse failed"); + let handle = handle_clone.to_owned(); + + tauri::async_runtime::spawn(async move { + let state = handle.state::(); + let client = &state.client; + + if let Ok(public_key) = PublicKey::parse(parsed_payload.id) { + let mut write_queue = state.queue.write().await; + write_queue.insert(public_key); + }; + + // Wait for [QUEUE_DELAY] + sleep(Duration::from_millis(QUEUE_DELAY)).await; + + let read_queue = state.queue.read().await; + + if !read_queue.is_empty() { + let authors: HashSet = read_queue.iter().copied().collect(); + + let filter = Filter::new().authors(authors).kind(Kind::Metadata).limit(200); + + let opts = SubscribeAutoCloseOptions::default() + .filter(FilterOptions::WaitDurationAfterEOSE(Duration::from_secs(2))); + + // Drop queue, you don't need it at this time anymore + drop(read_queue); + // Clear queue + let mut write_queue = state.queue.write().await; + write_queue.clear(); + + if let Err(e) = client.subscribe(vec![filter], Some(opts)).await { + println!("Subscribe error: {}", e); + } + } + }); + }); + + // Run a thread for handle notification + tauri::async_runtime::spawn(async move { + let handle = handle_clone_child.to_owned(); + let state = handle.state::(); + let client = &state.client; + + // Generate a fake sig for rumor event. + // TODO: Find better way to save unsigned event to database. + let fake_sig = Signature::from_str("f9e79d141c004977192d05a86f81ec7c585179c371f7350a5412d33575a2a356433f58e405c2296ed273e2fe0aafa25b641e39cc4e1f3f261ebf55bce0cbac83").unwrap(); + + let _ = client + .handle_notifications(|notification| async { + #[allow(clippy::collapsible_match)] + if let RelayPoolNotification::Message { message, .. } = notification { + if let RelayMessage::Event { event, .. } = message { + if event.kind == Kind::GiftWrap { + if let Ok(UnwrappedGift { rumor, sender }) = + client.unwrap_gift_wrap(&event).await + { + let mut rumor_clone = rumor.clone(); + + // Compute event id if not exist + rumor_clone.ensure_id(); + + let ev = Event::new( + rumor_clone.id.unwrap(), // unwrap() must be fine + rumor_clone.pubkey, + rumor_clone.created_at, + rumor_clone.kind, + rumor_clone.tags, + rumor_clone.content, + fake_sig, + ); + + // Save rumor to database to further query + if let Err(e) = client.database().save_event(&ev).await { + println!("[save event] error: {}", e) + } + + // Emit new event to frontend + if let Err(e) = handle.emit( + "event", + EventPayload { + event: rumor.as_json(), + sender: sender.to_hex(), + }, + ) { + println!("[emit] error: {}", e) + } + } + } else if event.kind == Kind::Metadata { + if let Err(e) = handle.emit("metadata", event.as_json()) { + println!("Emit error: {}", e) + } + } + } + } + Ok(false) + }) + .await; + }); Ok(()) }) .plugin(prevent_default()) + .plugin(tauri_plugin_store::Builder::default().build()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_updater::Builder::new().build()) @@ -152,14 +290,14 @@ fn main() { #[cfg(debug_assertions)] fn prevent_default() -> tauri::plugin::TauriPlugin { - use tauri_plugin_prevent_default::Flags; + use tauri_plugin_prevent_default::Flags; - tauri_plugin_prevent_default::Builder::new() - .with_flags(Flags::all().difference(Flags::CONTEXT_MENU)) - .build() + tauri_plugin_prevent_default::Builder::new() + .with_flags(Flags::all().difference(Flags::CONTEXT_MENU)) + .build() } #[cfg(not(debug_assertions))] fn prevent_default() -> tauri::plugin::TauriPlugin { - tauri_plugin_prevent_default::Builder::new().build() + tauri_plugin_prevent_default::Builder::new().build() } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 7dbff96..84115f3 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,5 +1,5 @@ { - "productName": "Coop", + "productName": "COOP", "version": "0.2.0", "identifier": "su.reya.coop", "build": { diff --git a/src/commands.ts b/src/commands.ts index 2615522..33762a4 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -120,9 +120,9 @@ async getCurrentAccount() : Promise> { else return { status: "error", error: e as any }; } }, -async getMetadata(userId: string) : Promise> { +async getMetadata(id: string) : Promise> { try { - return { status: "ok", data: await TAURI_INVOKE("get_metadata", { userId }) }; + return { status: "ok", data: await TAURI_INVOKE("get_metadata", { id }) }; } catch (e) { if(e instanceof Error) throw e; else return { status: "error", error: e as any }; diff --git a/src/commons.ts b/src/commons.ts index f39ff01..b90ea75 100644 --- a/src/commons.ts +++ b/src/commons.ts @@ -1,3 +1,8 @@ +import type { + AsyncStorage, + MaybePromise, + PersistedQuery, +} from "@tanstack/query-persist-client-core"; import { ask, message, open } from "@tauri-apps/plugin-dialog"; import { readFile } from "@tauri-apps/plugin-fs"; import { @@ -5,6 +10,7 @@ import { requestPermission, } from "@tauri-apps/plugin-notification"; import { relaunch } from "@tauri-apps/plugin-process"; +import type { LazyStore } from "@tauri-apps/plugin-store"; import { check } from "@tauri-apps/plugin-updater"; import { type ClassValue, clsx } from "clsx"; import dayjs from "dayjs"; @@ -13,22 +19,6 @@ import updateLocale from "dayjs/plugin/updateLocale"; import { type NostrEvent, nip19 } from "nostr-tools"; import { twMerge } from "tailwind-merge"; -dayjs.extend(relativeTime); -dayjs.extend(updateLocale); - -dayjs.updateLocale("en", { - relativeTime: { - past: "%s", - s: "now", - m: "1m", - mm: "%dm", - h: "1h", - hh: "%dh", - d: "1d", - dd: "%dd", - }, -}); - export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } @@ -52,6 +42,22 @@ export function npub(pubkey: string, len: number) { } export function ago(time: number) { + dayjs.extend(relativeTime); + dayjs.extend(updateLocale); + + dayjs.updateLocale("en", { + relativeTime: { + past: "%s", + s: "now", + m: "1m", + mm: "%dm", + h: "1h", + hh: "%dh", + d: "1d", + dd: "%dd", + }, + }); + let formated: string; const now = dayjs(); @@ -68,6 +74,22 @@ export function ago(time: number) { } export function time(time: number) { + dayjs.extend(relativeTime); + dayjs.extend(updateLocale); + + dayjs.updateLocale("en", { + relativeTime: { + past: "%s", + s: "now", + m: "1m", + mm: "%dm", + h: "1h", + hh: "%dh", + d: "1d", + dd: "%dd", + }, + }); + const input = new Date(time * 1000); const formattedTime = input.toLocaleTimeString([], { hour: "2-digit", @@ -187,3 +209,14 @@ export async function upload() { return null; } } + +export function newQueryStorage( + store: LazyStore, +): AsyncStorage { + return { + getItem: async (key) => await store.get(key), + setItem: async (key, value) => await store.set(key, value), + removeItem: async (key) => + (await store.delete(key)) as unknown as MaybePromise, + }; +} diff --git a/src/components/user/provider.tsx b/src/components/user/provider.tsx index c81c5d5..8af73dc 100644 --- a/src/components/user/provider.tsx +++ b/src/components/user/provider.tsx @@ -1,27 +1,13 @@ -import { commands } from "@/commands"; -import { useQuery } from "@tanstack/react-query"; +import { type Metadata, useProfile } from "@/hooks/useProfile"; import { type ReactNode, createContext, useContext } from "react"; -type Metadata = { - name?: string; - display_name?: string; - about?: string; - website?: string; - picture?: string; - banner?: string; - nip05?: string; - lud06?: string; - lud16?: string; -}; - type UserContext = { pubkey: string; - isLoading: boolean; - isError: boolean; profile: Metadata | undefined; + isLoading: boolean; }; -const UserContext = createContext(null); +const UserContext = createContext(null); export function UserProvider({ pubkey, @@ -30,37 +16,10 @@ export function UserProvider({ pubkey: string; children: ReactNode; }) { - const { - isLoading, - isError, - data: profile, - } = useQuery({ - queryKey: ["profile", pubkey], - queryFn: async () => { - try { - const normalizePubkey = pubkey - .replace("nostr:", "") - .replace(/[^\w\s]/gi, ""); - - const res = await commands.getMetadata(normalizePubkey); - - if (res.status === "ok") { - return JSON.parse(res.data) as Metadata; - } else { - throw new Error(res.error); - } - } catch (e) { - throw new Error(String(e)); - } - }, - refetchOnMount: false, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - staleTime: Number.POSITIVE_INFINITY, - }); + const { isLoading, profile } = useProfile(pubkey); return ( - + {children} ); diff --git a/src/hooks/useProfile.ts b/src/hooks/useProfile.ts new file mode 100644 index 0000000..619b41d --- /dev/null +++ b/src/hooks/useProfile.ts @@ -0,0 +1,66 @@ +import { commands } from "@/commands"; +import { useQuery } from "@tanstack/react-query"; +import { getCurrentWindow } from "@tauri-apps/api/window"; +import { nip19 } from "nostr-tools"; +import { useMemo } from "react"; + +export type Metadata = { + name?: string; + display_name?: string; + about?: string; + website?: string; + picture?: string; + banner?: string; + nip05?: string; + lud06?: string; + lud16?: string; +}; + +export function useProfile(pubkey: string, data?: string) { + const hex = useMemo(() => { + try { + const normalized = pubkey.replace("nostr:", "").replace(/[^\w\s]/gi, ""); + const decoded = nip19.decode(normalized); + + switch (decoded.type) { + case "npub": + return decoded.data; + case "nprofile": + return decoded.data.pubkey; + case "naddr": + return decoded.data.pubkey; + default: + return normalized; + } + } catch { + return pubkey; + } + }, [pubkey]); + + const { isLoading, data: profile } = useQuery({ + queryKey: ["profile", hex], + queryFn: async () => { + if (data?.length) { + const metadata: Metadata = JSON.parse(data); + return metadata; + } + + const res = await commands.getMetadata(hex); + + if (res.status === "ok") { + const metadata: Metadata = JSON.parse(res.data); + return metadata; + } else { + await getCurrentWindow().emit("request_metadata", { id: hex }); + throw new Error(res.error); + } + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + enabled: !!hex, + retry: false, + }); + + return { isLoading, profile }; +} diff --git a/src/main.tsx b/src/main.tsx index 8a93b41..30769cf 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,27 +1,43 @@ +import { experimental_createPersister } from "@tanstack/query-persist-client-core"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { RouterProvider, createRouter } from "@tanstack/react-router"; import { type } from "@tauri-apps/plugin-os"; -import { LRUCache } from "lru-cache"; +import { LazyStore } from "@tauri-apps/plugin-store"; import { type ReactNode, StrictMode } from "react"; import ReactDOM from "react-dom/client"; +import { newQueryStorage } from "./commons"; + +// Global CSS import "./global.css"; -// Import the generated commands -import { commands } from "./commands"; // Import the generated route tree import { routeTree } from "./routes.gen"; +// Register the router instance for type safety +declare module "@tanstack/react-router" { + interface Register { + router: typeof router; + } +} + const platform = type(); -const queryClient = new QueryClient(); -const chatManager = new LRUCache({ - max: 3, - dispose: async (v, _) => await commands.disconnectInboxRelays(v), +const store = new LazyStore(".data", { autoSave: 300 }); +const storage = newQueryStorage(store); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 20, // 20 seconds + persister: experimental_createPersister({ + storage: storage, + maxAge: 1000 * 60 * 60 * 6, // 6 hours + }), + }, + }, }); const router = createRouter({ routeTree, context: { queryClient, - chatManager, platform, }, Wrap: ({ children }: { children: ReactNode }) => { @@ -31,20 +47,11 @@ const router = createRouter({ }, }); -// Register the router instance for type safety -declare module "@tanstack/react-router" { - interface Register { - router: typeof router; - } -} +const rootElement = document.getElementById("root"); +const root = ReactDOM.createRoot(rootElement as unknown as HTMLElement); -// Render the app -const rootElement = document.getElementById("root") as HTMLElement; -if (!rootElement.innerHTML) { - const root = ReactDOM.createRoot(rootElement); - root.render( - - - , - ); -} +root.render( + + + , +); diff --git a/src/routes.gen.ts b/src/routes.gen.ts index 968cd02..b281867 100644 --- a/src/routes.gen.ts +++ b/src/routes.gen.ts @@ -1,12 +1,12 @@ -/* prettier-ignore-start */ - /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols -// This file is auto-generated by TanStack Router +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { createFileRoute } from '@tanstack/react-router' @@ -38,26 +38,31 @@ const AccountLayoutChatsNewLazyImport = createFileRoute( // Create/Update Routes const AccountRoute = AccountImport.update({ + id: '/$account', path: '/$account', getParentRoute: () => rootRoute, } as any) const ResetLazyRoute = ResetLazyImport.update({ + id: '/reset', path: '/reset', getParentRoute: () => rootRoute, } as any).lazy(() => import('./routes/reset.lazy').then((d) => d.Route)) const NewLazyRoute = NewLazyImport.update({ + id: '/new', path: '/new', getParentRoute: () => rootRoute, } as any).lazy(() => import('./routes/new.lazy').then((d) => d.Route)) const InboxRelaysRoute = InboxRelaysImport.update({ + id: '/inbox-relays', path: '/inbox-relays', getParentRoute: () => rootRoute, } as any).lazy(() => import('./routes/inbox-relays.lazy').then((d) => d.Route)) const BootstrapRelaysRoute = BootstrapRelaysImport.update({ + id: '/bootstrap-relays', path: '/bootstrap-relays', getParentRoute: () => rootRoute, } as any).lazy(() => @@ -65,21 +70,25 @@ const BootstrapRelaysRoute = BootstrapRelaysImport.update({ ) const IndexRoute = IndexImport.update({ + id: '/', path: '/', getParentRoute: () => rootRoute, } as any).lazy(() => import('./routes/index.lazy').then((d) => d.Route)) const AuthNewRoute = AuthNewImport.update({ + id: '/auth/new', path: '/auth/new', getParentRoute: () => rootRoute, } as any) const AuthImportRoute = AuthImportImport.update({ + id: '/auth/import', path: '/auth/import', getParentRoute: () => rootRoute, } as any) const AuthConnectRoute = AuthConnectImport.update({ + id: '/auth/connect', path: '/auth/connect', getParentRoute: () => rootRoute, } as any) @@ -90,6 +99,7 @@ const AccountLayoutRoute = AccountLayoutImport.update({ } as any) const AccountLayoutChatsLazyRoute = AccountLayoutChatsLazyImport.update({ + id: '/chats', path: '/chats', getParentRoute: () => AccountLayoutRoute, } as any).lazy(() => @@ -97,6 +107,7 @@ const AccountLayoutChatsLazyRoute = AccountLayoutChatsLazyImport.update({ ) const AccountLayoutContactsRoute = AccountLayoutContactsImport.update({ + id: '/contacts', path: '/contacts', getParentRoute: () => AccountLayoutRoute, } as any).lazy(() => @@ -104,6 +115,7 @@ const AccountLayoutContactsRoute = AccountLayoutContactsImport.update({ ) const AccountLayoutChatsNewLazyRoute = AccountLayoutChatsNewLazyImport.update({ + id: '/new', path: '/new', getParentRoute: () => AccountLayoutChatsLazyRoute, } as any).lazy(() => @@ -111,6 +123,7 @@ const AccountLayoutChatsNewLazyRoute = AccountLayoutChatsNewLazyImport.update({ ) const AccountLayoutChatsIdRoute = AccountLayoutChatsIdImport.update({ + id: '/$id', path: '/$id', getParentRoute: () => AccountLayoutChatsLazyRoute, } as any).lazy(() => @@ -393,8 +406,6 @@ export const routeTree = rootRoute ._addFileChildren(rootRouteChildren) ._addFileTypes() -/* prettier-ignore-end */ - /* ROUTE_MANIFEST_START { "routes": { diff --git a/src/routes/$account/_layout/chats.$id.lazy.tsx b/src/routes/$account/_layout/chats.$id.lazy.tsx index 0f913f1..e4c0196 100644 --- a/src/routes/$account/_layout/chats.$id.lazy.tsx +++ b/src/routes/$account/_layout/chats.$id.lazy.tsx @@ -12,6 +12,7 @@ import { message } from "@tauri-apps/plugin-dialog"; import type { NostrEvent } from "nostr-tools"; import { type Dispatch, + type RefObject, type SetStateAction, useCallback, useRef, @@ -154,13 +155,12 @@ function List() { if (!group.includes(sender)) return; if (!group.some((item) => receivers.includes(item))) return; - await queryClient.setQueryData( - ["chats", id], - (prevEvents: NostrEvent[]) => { - if (!prevEvents) return [event]; - return [event, ...prevEvents]; - }, - ); + queryClient.setQueryData(["chats", id], (prevEvents: NostrEvent[]) => { + if (!prevEvents) return [event]; + return [event, ...prevEvents]; + }); + + await queryClient.invalidateQueries({ queryKey: ["chats", id] }); }); return () => { @@ -189,7 +189,7 @@ function List() { className="relative h-full py-2 [&>div]:!flex [&>div]:flex-col [&>div]:justify-end [&>div]:min-h-full" > } ref={ref} shift={true} onScroll={(offset) => { @@ -218,12 +218,12 @@ function List() { Cannot load message. Please try again later. - ) : !data.length ? ( + ) : !data?.length ? (
) : ( - data.map((item) => ( + data?.map((item) => (
{item[1] - .sort((a, b) => a.created_at - b.created_at) - .map((item, idx) => renderItem(item, idx))} + ? item[1] + .sort((a, b) => a.created_at - b.created_at) + .map((item, idx) => renderItem(item, idx)) + : null}
)) diff --git a/src/routes/$account/_layout/chats.$id.tsx b/src/routes/$account/_layout/chats.$id.tsx index d382b51..10ad6f8 100644 --- a/src/routes/$account/_layout/chats.$id.tsx +++ b/src/routes/$account/_layout/chats.$id.tsx @@ -1,34 +1,31 @@ -import { commands } from '@/commands' -import { Spinner } from '@/components/spinner' -import { createFileRoute } from '@tanstack/react-router' +import { commands } from "@/commands"; +import { Spinner } from "@/components/spinner"; +import { createFileRoute } from "@tanstack/react-router"; -export const Route = createFileRoute('/$account/_layout/chats/$id')({ - loader: async ({ params, context }) => { - const res = await commands.connectInboxRelays(params.id, false) +export const Route = createFileRoute("/$account/_layout/chats/$id")({ + loader: async ({ params }) => { + const res = await commands.connectInboxRelays(params.id, false); - if (res.status === 'ok') { - // Add id to chat manager to unsubscribe later. - context.chatManager.set(params.id, params.id) - - return res.data - } else { - return [] - } - }, - pendingComponent: Pending, - pendingMs: 200, - pendingMinMs: 100, -}) + if (res.status === "ok") { + return res.data; + } else { + return []; + } + }, + pendingComponent: Pending, + pendingMs: 200, + pendingMinMs: 100, +}); function Pending() { - return ( -
-
- - - Connection in progress. Please wait ... - -
-
- ) + return ( +
+
+ + + Connection in progress. Please wait ... + +
+
+ ); } diff --git a/src/routes/$account/_layout/chats.lazy.tsx b/src/routes/$account/_layout/chats.lazy.tsx index f8175d4..5f92128 100644 --- a/src/routes/$account/_layout/chats.lazy.tsx +++ b/src/routes/$account/_layout/chats.lazy.tsx @@ -20,7 +20,14 @@ import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager"; import { message } from "@tauri-apps/plugin-dialog"; import { open } from "@tauri-apps/plugin-shell"; import { type NostrEvent, nip19 } from "nostr-tools"; -import { useCallback, useEffect, useRef, useState, useTransition } from "react"; +import { + type RefObject, + useCallback, + useEffect, + useRef, + useState, + useTransition, +} from "react"; import { Virtualizer } from "virtua"; type EventPayload = { @@ -122,10 +129,12 @@ function ChatList() { useEffect(() => { const unlisten = listen("event", async (data) => { - const event: NostrEvent = JSON.parse(data.payload.event); - const chats: NostrEvent[] = await queryClient.getQueryData(["chats"]); + const chats: NostrEvent[] | undefined = await queryClient.getQueryData([ + "chats", + ]); if (chats) { + const event: NostrEvent = JSON.parse(data.payload.event); const index = chats.findIndex((item) => item.pubkey === event.pubkey); if (index === -1) { @@ -140,11 +149,13 @@ function ChatList() { ); } else { const newEvents = [...chats]; + newEvents[index] = { ...event, }; - await queryClient.setQueryData(["chats"], newEvents); + queryClient.setQueryData(["chats"], newEvents); + await queryClient.invalidateQueries({ queryKey: ["chats"] }); } } }); @@ -173,14 +184,14 @@ function ChatList() { ))} - ) : isSync && !data.length ? ( + ) : isSync && !data?.length ? (
No chats.
) : ( - data.map((item) => ( + data?.map((item) => ( - + } + overscan={1} + > {isLoading ? (
diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 631c607..7d5b5ad 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -1,12 +1,14 @@ import { cn } from "@/commons"; +import type { Metadata } from "@/hooks/useProfile"; import type { QueryClient } from "@tanstack/react-query"; import { Outlet, createRootRouteWithContext } from "@tanstack/react-router"; +import { getCurrentWindow } from "@tauri-apps/api/window"; import type { OsType } from "@tauri-apps/plugin-os"; -import type { LRUCache } from "lru-cache"; +import type { NostrEvent } from "nostr-tools"; +import { useEffect } from "react"; interface RouterContext { queryClient: QueryClient; - chatManager: LRUCache; platform: OsType; } @@ -15,7 +17,29 @@ export const Route = createRootRouteWithContext()({ }); function RootComponent() { - const { platform } = Route.useRouteContext(); + const { platform, queryClient } = Route.useRouteContext(); + + useEffect(() => { + const unlisten = getCurrentWindow().listen( + "metadata", + async (data) => { + const event: NostrEvent = JSON.parse(data.payload); + const metadata: Metadata = JSON.parse(event.content); + + // Update query cache + queryClient.setQueryData(["profile", event.pubkey], () => metadata); + + // Reset query cache + await queryClient.invalidateQueries({ + queryKey: ["profile", event.pubkey], + }); + }, + ); + + return () => { + unlisten.then((f) => f()); + }; + }, []); return (