fix: app hang when loading

This commit is contained in:
reya
2024-07-31 10:13:48 +07:00
parent 48eb2f5339
commit db4a7c1037
14 changed files with 181 additions and 219 deletions

View File

@@ -14,10 +14,8 @@
"@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-scroll-area": "^1.1.0", "@radix-ui/react-scroll-area": "^1.1.0",
"@tanstack/query-sync-storage-persister": "^5.51.15",
"@tanstack/react-query": "^5.51.15", "@tanstack/react-query": "^5.51.15",
"@tanstack/react-query-persist-client": "^5.51.15", "@tanstack/react-router": "^1.45.15",
"@tanstack/react-router": "^1.45.11",
"@tauri-apps/api": "2.0.0-beta.15", "@tauri-apps/api": "2.0.0-beta.15",
"@tauri-apps/plugin-clipboard-manager": "2.1.0-beta.5", "@tauri-apps/plugin-clipboard-manager": "2.1.0-beta.5",
"@tauri-apps/plugin-dialog": "2.0.0-beta.7", "@tauri-apps/plugin-dialog": "2.0.0-beta.7",
@@ -36,7 +34,7 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.8.3", "@biomejs/biome": "1.8.3",
"@tanstack/router-plugin": "^1.45.8", "@tanstack/router-plugin": "^1.45.13",
"@tauri-apps/cli": "2.0.0-beta.22", "@tauri-apps/cli": "2.0.0-beta.22",
"@types/react": "npm:types-react@rc", "@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc", "@types/react-dom": "npm:types-react-dom@rc",

189
pnpm-lock.yaml generated
View File

@@ -20,18 +20,12 @@ importers:
'@radix-ui/react-scroll-area': '@radix-ui/react-scroll-area':
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.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) version: 1.1.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-sync-storage-persister':
specifier: ^5.51.15
version: 5.51.15
'@tanstack/react-query': '@tanstack/react-query':
specifier: ^5.51.15 specifier: ^5.51.15
version: 5.51.15(react@19.0.0-rc-d025ddd3-20240722) version: 5.51.15(react@19.0.0-rc-d025ddd3-20240722)
'@tanstack/react-query-persist-client':
specifier: ^5.51.15
version: 5.51.15(@tanstack/react-query@5.51.15(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722)
'@tanstack/react-router': '@tanstack/react-router':
specifier: ^1.45.11 specifier: ^1.45.15
version: 1.45.11(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722) version: 1.45.15(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': '@tauri-apps/api':
specifier: 2.0.0-beta.15 specifier: 2.0.0-beta.15
version: 2.0.0-beta.15 version: 2.0.0-beta.15
@@ -82,8 +76,8 @@ importers:
specifier: 1.8.3 specifier: 1.8.3
version: 1.8.3 version: 1.8.3
'@tanstack/router-plugin': '@tanstack/router-plugin':
specifier: ^1.45.8 specifier: ^1.45.13
version: 1.45.8(vite@5.3.5) version: 1.45.13(vite@5.3.5)
'@tauri-apps/cli': '@tauri-apps/cli':
specifier: 2.0.0-beta.22 specifier: 2.0.0-beta.22
version: 2.0.0-beta.22 version: 2.0.0-beta.22
@@ -138,12 +132,12 @@ packages:
resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/compat-data@7.25.0': '@babel/compat-data@7.25.2':
resolution: {integrity: sha512-P4fwKI2mjEb3ZU5cnMJzvRsRKGBUcs8jvxIoRmr6ufAY9Xk2Bz7JubRTTivkw55c7WQJfTECeqYVa+HZ0FzREg==} resolution: {integrity: sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/core@7.24.9': '@babel/core@7.25.2':
resolution: {integrity: sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==} resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/generator@7.2.0': '@babel/generator@7.2.0':
@@ -153,16 +147,16 @@ packages:
resolution: {integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==} resolution: {integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/helper-compilation-targets@7.24.8': '@babel/helper-compilation-targets@7.25.2':
resolution: {integrity: sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==} resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/helper-module-imports@7.24.7': '@babel/helper-module-imports@7.24.7':
resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/helper-module-transforms@7.25.0': '@babel/helper-module-transforms@7.25.2':
resolution: {integrity: sha512-bIkOa2ZJYn7FHnepzr5iX9Kmz8FjIz4UKzJ9zhX3dnYuVW0xul9RuR3skBfoLu+FPTQw90EHW9rJsSZhyLQ3fQ==} resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
peerDependencies: peerDependencies:
'@babel/core': ^7.0.0 '@babel/core': ^7.0.0
@@ -228,12 +222,12 @@ packages:
resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/traverse@7.25.1': '@babel/traverse@7.25.2':
resolution: {integrity: sha512-LrHHoWq08ZpmmFqBAzN+hUdWwy5zt7FGa/hVwMcOqW6OVtwqaoD5utfuGYU87JYxdZgLUvktAsn37j/sYR9siA==} resolution: {integrity: sha512-s4/r+a7xTnny2O6FcZzqgT6nE4/GHEdcqj4qAeglbUOh0TeglEfmNJFAd/OLoVtGd6ZhAO8GCVvCNUO5t/VJVQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/types@7.25.0': '@babel/types@7.25.2':
resolution: {integrity: sha512-LcnxQSsd9aXOIgmmSpvZ/1yo46ra2ESYyqLcryaBZOghxy5qqOBjvCWP5JfkI8yl9rlxRgdLTTMCQQRcN2hdCg==} resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@biomejs/biome@1.8.3': '@biomejs/biome@1.8.3':
@@ -789,25 +783,13 @@ packages:
'@tanstack/query-core@5.51.15': '@tanstack/query-core@5.51.15':
resolution: {integrity: sha512-xyobHDJ0yhPE3+UkSQ2/4X1fLSg7ICJI5J1JyU9yf7F3deQfEwSImCDrB1WSRrauJkMtXW7YIEcC0oA6ZZWt5A==} resolution: {integrity: sha512-xyobHDJ0yhPE3+UkSQ2/4X1fLSg7ICJI5J1JyU9yf7F3deQfEwSImCDrB1WSRrauJkMtXW7YIEcC0oA6ZZWt5A==}
'@tanstack/query-persist-client-core@5.51.15':
resolution: {integrity: sha512-y/gsUFINiVkxV06Jz5TqFNrXbKs81lIfj7PDamheEk+vWTmlBKTh0rMXthoDdUIVm4JO0+No9CXYahRSm683pg==}
'@tanstack/query-sync-storage-persister@5.51.15':
resolution: {integrity: sha512-Jrqqv4zBQQmfkuArsx++JQfKi1nobFGry9T702c4h80jA01MGnAd2Eev6uG7mRYor4vnUzjP1CrGS+xzRq5oMQ==}
'@tanstack/react-query-persist-client@5.51.15':
resolution: {integrity: sha512-SxSfGc9JggTUzv/dwIirKdMOe47OAdT0LBaVrkowI9QdVxJByjo+uaPCXZLoTlkaclvzFxd4G3YmmjsHXV/33A==}
peerDependencies:
'@tanstack/react-query': ^5.51.15
react: ^18 || ^19
'@tanstack/react-query@5.51.15': '@tanstack/react-query@5.51.15':
resolution: {integrity: sha512-UgFg23SrdIYrmfTSxAUn9g+J64VQy11pb9/EefoY/u2+zWuNMeqEOnvpJhf52XQy0yztQoyM9p6x8PFyTNaxXg==} resolution: {integrity: sha512-UgFg23SrdIYrmfTSxAUn9g+J64VQy11pb9/EefoY/u2+zWuNMeqEOnvpJhf52XQy0yztQoyM9p6x8PFyTNaxXg==}
peerDependencies: peerDependencies:
react: ^18.0.0 react: ^18.0.0
'@tanstack/react-router@1.45.11': '@tanstack/react-router@1.45.15':
resolution: {integrity: sha512-unVxPYsOwwMnyAW/ZeOCymWpstEV0tiUexUPqiCuYwb2rCwsrmjsgB4tIpVONHabPlFKrxoMbkUh5UCSpL0h+w==} resolution: {integrity: sha512-QVftG8QlD8u3QB38ZkpyWUHEuuaq12fsxm7Nr52pkxueKM099j8pqouoqLb6nDDyLC54/PhLZxavrIeH7aaqaA==}
engines: {node: '>=12'} engines: {node: '>=12'}
peerDependencies: peerDependencies:
react: '>=18' react: '>=18'
@@ -823,8 +805,8 @@ packages:
resolution: {integrity: sha512-5B756YXpZO0/yr7ahsxXoBOCpqroLx/D3l6X9qPlZaP0FVHVmTR6ZKGRX6zzuAxns/VO+sxnQso4AYLdDyZ9GA==} resolution: {integrity: sha512-5B756YXpZO0/yr7ahsxXoBOCpqroLx/D3l6X9qPlZaP0FVHVmTR6ZKGRX6zzuAxns/VO+sxnQso4AYLdDyZ9GA==}
engines: {node: '>=12'} engines: {node: '>=12'}
'@tanstack/router-plugin@1.45.8': '@tanstack/router-plugin@1.45.13':
resolution: {integrity: sha512-mncDu49pBEivRHWVOdCl9fHwtyUT3QLZ0X+gYIsm7o6LhT6i7BzC++BZqOOSrjDcyXy3dx2Nf/FMrB06qlXx9Q==} resolution: {integrity: sha512-7IWdtIDC5jjhD6IZ5c/s11xNwvKDom/p6ZOO78FxeeA0FIaSx+3AQD7/iVhzfiRfepqGmecLn9a1KfNTFGQI/A==}
engines: {node: '>=12'} engines: {node: '>=12'}
peerDependencies: peerDependencies:
'@rsbuild/core': '>=0.7.9' '@rsbuild/core': '>=0.7.9'
@@ -1049,8 +1031,8 @@ packages:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
caniuse-lite@1.0.30001643: caniuse-lite@1.0.30001644:
resolution: {integrity: sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==} resolution: {integrity: sha512-YGvlOZB4QhZuiis+ETS0VXR+MExbFf4fZYYeMTEE0aTQd/RdIjkTyZjLrbYVKnHzppDvnOhritRVv+i7Go6mHw==}
chalk@2.4.2: chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
@@ -1124,8 +1106,8 @@ packages:
eastasianwidth@0.2.0: eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
electron-to-chromium@1.5.2: electron-to-chromium@1.5.4:
resolution: {integrity: sha512-kc4r3U3V3WLaaZqThjYz/Y6z8tJe+7K0bbjUVo3i+LWIypVdMx5nXCkwRe6SWbY6ILqLdc1rKcKmr3HoH7wjSQ==} resolution: {integrity: sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==}
emoji-regex@8.0.0: emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -1770,20 +1752,20 @@ snapshots:
'@babel/highlight': 7.24.7 '@babel/highlight': 7.24.7
picocolors: 1.0.1 picocolors: 1.0.1
'@babel/compat-data@7.25.0': {} '@babel/compat-data@7.25.2': {}
'@babel/core@7.24.9': '@babel/core@7.25.2':
dependencies: dependencies:
'@ampproject/remapping': 2.3.0 '@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.24.7 '@babel/code-frame': 7.24.7
'@babel/generator': 7.25.0 '@babel/generator': 7.25.0
'@babel/helper-compilation-targets': 7.24.8 '@babel/helper-compilation-targets': 7.25.2
'@babel/helper-module-transforms': 7.25.0(@babel/core@7.24.9) '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2)
'@babel/helpers': 7.25.0 '@babel/helpers': 7.25.0
'@babel/parser': 7.25.0 '@babel/parser': 7.25.0
'@babel/template': 7.25.0 '@babel/template': 7.25.0
'@babel/traverse': 7.25.1 '@babel/traverse': 7.25.2
'@babel/types': 7.25.0 '@babel/types': 7.25.2
convert-source-map: 2.0.0 convert-source-map: 2.0.0
debug: 4.3.6 debug: 4.3.6
gensync: 1.0.0-beta.2 gensync: 1.0.0-beta.2
@@ -1794,7 +1776,7 @@ snapshots:
'@babel/generator@7.2.0': '@babel/generator@7.2.0':
dependencies: dependencies:
'@babel/types': 7.25.0 '@babel/types': 7.25.2
jsesc: 2.5.2 jsesc: 2.5.2
lodash: 4.17.21 lodash: 4.17.21
source-map: 0.5.7 source-map: 0.5.7
@@ -1802,14 +1784,14 @@ snapshots:
'@babel/generator@7.25.0': '@babel/generator@7.25.0':
dependencies: dependencies:
'@babel/types': 7.25.0 '@babel/types': 7.25.2
'@jridgewell/gen-mapping': 0.3.5 '@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25 '@jridgewell/trace-mapping': 0.3.25
jsesc: 2.5.2 jsesc: 2.5.2
'@babel/helper-compilation-targets@7.24.8': '@babel/helper-compilation-targets@7.25.2':
dependencies: dependencies:
'@babel/compat-data': 7.25.0 '@babel/compat-data': 7.25.2
'@babel/helper-validator-option': 7.24.8 '@babel/helper-validator-option': 7.24.8
browserslist: 4.23.2 browserslist: 4.23.2
lru-cache: 5.1.1 lru-cache: 5.1.1
@@ -1817,18 +1799,18 @@ snapshots:
'@babel/helper-module-imports@7.24.7': '@babel/helper-module-imports@7.24.7':
dependencies: dependencies:
'@babel/traverse': 7.25.1 '@babel/traverse': 7.25.2
'@babel/types': 7.25.0 '@babel/types': 7.25.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@babel/helper-module-transforms@7.25.0(@babel/core@7.24.9)': '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)':
dependencies: dependencies:
'@babel/core': 7.24.9 '@babel/core': 7.25.2
'@babel/helper-module-imports': 7.24.7 '@babel/helper-module-imports': 7.24.7
'@babel/helper-simple-access': 7.24.7 '@babel/helper-simple-access': 7.24.7
'@babel/helper-validator-identifier': 7.24.7 '@babel/helper-validator-identifier': 7.24.7
'@babel/traverse': 7.25.1 '@babel/traverse': 7.25.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -1836,8 +1818,8 @@ snapshots:
'@babel/helper-simple-access@7.24.7': '@babel/helper-simple-access@7.24.7':
dependencies: dependencies:
'@babel/traverse': 7.25.1 '@babel/traverse': 7.25.2
'@babel/types': 7.25.0 '@babel/types': 7.25.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -1850,7 +1832,7 @@ snapshots:
'@babel/helpers@7.25.0': '@babel/helpers@7.25.0':
dependencies: dependencies:
'@babel/template': 7.25.0 '@babel/template': 7.25.0
'@babel/types': 7.25.0 '@babel/types': 7.25.2
'@babel/highlight@7.24.7': '@babel/highlight@7.24.7':
dependencies: dependencies:
@@ -1861,47 +1843,47 @@ snapshots:
'@babel/parser@7.25.0': '@babel/parser@7.25.0':
dependencies: dependencies:
'@babel/types': 7.25.0 '@babel/types': 7.25.2
'@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.9)': '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.25.2)':
dependencies: dependencies:
'@babel/core': 7.24.9 '@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8 '@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.9)': '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.25.2)':
dependencies: dependencies:
'@babel/core': 7.24.9 '@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8 '@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.24.9)': '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.2)':
dependencies: dependencies:
'@babel/core': 7.24.9 '@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8 '@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.24.9)': '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.2)':
dependencies: dependencies:
'@babel/core': 7.24.9 '@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8 '@babel/helper-plugin-utils': 7.24.8
'@babel/template@7.25.0': '@babel/template@7.25.0':
dependencies: dependencies:
'@babel/code-frame': 7.24.7 '@babel/code-frame': 7.24.7
'@babel/parser': 7.25.0 '@babel/parser': 7.25.0
'@babel/types': 7.25.0 '@babel/types': 7.25.2
'@babel/traverse@7.25.1': '@babel/traverse@7.25.2':
dependencies: dependencies:
'@babel/code-frame': 7.24.7 '@babel/code-frame': 7.24.7
'@babel/generator': 7.25.0 '@babel/generator': 7.25.0
'@babel/parser': 7.25.0 '@babel/parser': 7.25.0
'@babel/template': 7.25.0 '@babel/template': 7.25.0
'@babel/types': 7.25.0 '@babel/types': 7.25.2
debug: 4.3.6 debug: 4.3.6
globals: 11.12.0 globals: 11.12.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@babel/types@7.25.0': '@babel/types@7.25.2':
dependencies: dependencies:
'@babel/helper-string-parser': 7.24.8 '@babel/helper-string-parser': 7.24.8
'@babel/helper-validator-identifier': 7.24.7 '@babel/helper-validator-identifier': 7.24.7
@@ -2314,27 +2296,12 @@ snapshots:
'@tanstack/query-core@5.51.15': {} '@tanstack/query-core@5.51.15': {}
'@tanstack/query-persist-client-core@5.51.15':
dependencies:
'@tanstack/query-core': 5.51.15
'@tanstack/query-sync-storage-persister@5.51.15':
dependencies:
'@tanstack/query-core': 5.51.15
'@tanstack/query-persist-client-core': 5.51.15
'@tanstack/react-query-persist-client@5.51.15(@tanstack/react-query@5.51.15(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722)':
dependencies:
'@tanstack/query-persist-client-core': 5.51.15
'@tanstack/react-query': 5.51.15(react@19.0.0-rc-d025ddd3-20240722)
react: 19.0.0-rc-d025ddd3-20240722
'@tanstack/react-query@5.51.15(react@19.0.0-rc-d025ddd3-20240722)': '@tanstack/react-query@5.51.15(react@19.0.0-rc-d025ddd3-20240722)':
dependencies: dependencies:
'@tanstack/query-core': 5.51.15 '@tanstack/query-core': 5.51.15
react: 19.0.0-rc-d025ddd3-20240722 react: 19.0.0-rc-d025ddd3-20240722
'@tanstack/react-router@1.45.11(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.45.15(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722)':
dependencies: dependencies:
'@tanstack/history': 1.45.3 '@tanstack/history': 1.45.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.5(react-dom@19.0.0-rc-d025ddd3-20240722(react@19.0.0-rc-d025ddd3-20240722))(react@19.0.0-rc-d025ddd3-20240722)
@@ -2355,16 +2322,16 @@ snapshots:
prettier: 3.3.3 prettier: 3.3.3
zod: 3.23.8 zod: 3.23.8
'@tanstack/router-plugin@1.45.8(vite@5.3.5)': '@tanstack/router-plugin@1.45.13(vite@5.3.5)':
dependencies: dependencies:
'@babel/core': 7.24.9 '@babel/core': 7.25.2
'@babel/generator': 7.25.0 '@babel/generator': 7.25.0
'@babel/parser': 7.25.0 '@babel/parser': 7.25.0
'@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.9) '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2)
'@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.9) '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.25.2)
'@babel/template': 7.25.0 '@babel/template': 7.25.0
'@babel/traverse': 7.25.1 '@babel/traverse': 7.25.2
'@babel/types': 7.25.0 '@babel/types': 7.25.2
'@tanstack/router-generator': 1.45.7 '@tanstack/router-generator': 1.45.7
'@types/babel__core': 7.20.5 '@types/babel__core': 7.20.5
'@types/babel__generator': 7.6.8 '@types/babel__generator': 7.6.8
@@ -2453,23 +2420,23 @@ snapshots:
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
dependencies: dependencies:
'@babel/parser': 7.25.0 '@babel/parser': 7.25.0
'@babel/types': 7.25.0 '@babel/types': 7.25.2
'@types/babel__generator': 7.6.8 '@types/babel__generator': 7.6.8
'@types/babel__template': 7.4.4 '@types/babel__template': 7.4.4
'@types/babel__traverse': 7.20.6 '@types/babel__traverse': 7.20.6
'@types/babel__generator@7.6.8': '@types/babel__generator@7.6.8':
dependencies: dependencies:
'@babel/types': 7.25.0 '@babel/types': 7.25.2
'@types/babel__template@7.4.4': '@types/babel__template@7.4.4':
dependencies: dependencies:
'@babel/parser': 7.25.0 '@babel/parser': 7.25.0
'@babel/types': 7.25.0 '@babel/types': 7.25.2
'@types/babel__traverse@7.20.6': '@types/babel__traverse@7.20.6':
dependencies: dependencies:
'@babel/types': 7.25.0 '@babel/types': 7.25.2
'@types/estree@1.0.5': {} '@types/estree@1.0.5': {}
@@ -2499,9 +2466,9 @@ snapshots:
'@vitejs/plugin-react@4.3.1(vite@5.3.5)': '@vitejs/plugin-react@4.3.1(vite@5.3.5)':
dependencies: dependencies:
'@babel/core': 7.24.9 '@babel/core': 7.25.2
'@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.9) '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2)
'@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.9) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2)
'@types/babel__core': 7.20.5 '@types/babel__core': 7.20.5
react-refresh: 0.14.2 react-refresh: 0.14.2
vite: 5.3.5 vite: 5.3.5
@@ -2542,7 +2509,7 @@ snapshots:
autoprefixer@10.4.19(postcss@8.4.40): autoprefixer@10.4.19(postcss@8.4.40):
dependencies: dependencies:
browserslist: 4.23.2 browserslist: 4.23.2
caniuse-lite: 1.0.30001643 caniuse-lite: 1.0.30001644
fraction.js: 4.3.7 fraction.js: 4.3.7
normalize-range: 0.1.2 normalize-range: 0.1.2
picocolors: 1.0.1 picocolors: 1.0.1
@@ -2551,17 +2518,17 @@ snapshots:
babel-dead-code-elimination@1.0.6: babel-dead-code-elimination@1.0.6:
dependencies: dependencies:
'@babel/core': 7.24.9 '@babel/core': 7.25.2
'@babel/parser': 7.25.0 '@babel/parser': 7.25.0
'@babel/traverse': 7.25.1 '@babel/traverse': 7.25.2
'@babel/types': 7.25.0 '@babel/types': 7.25.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
babel-plugin-react-compiler@0.0.0-experimental-696af53-20240625: babel-plugin-react-compiler@0.0.0-experimental-696af53-20240625:
dependencies: dependencies:
'@babel/generator': 7.2.0 '@babel/generator': 7.2.0
'@babel/types': 7.25.0 '@babel/types': 7.25.2
chalk: 4.1.2 chalk: 4.1.2
invariant: 2.2.4 invariant: 2.2.4
pretty-format: 24.9.0 pretty-format: 24.9.0
@@ -2582,14 +2549,14 @@ snapshots:
browserslist@4.23.2: browserslist@4.23.2:
dependencies: dependencies:
caniuse-lite: 1.0.30001643 caniuse-lite: 1.0.30001644
electron-to-chromium: 1.5.2 electron-to-chromium: 1.5.4
node-releases: 2.0.18 node-releases: 2.0.18
update-browserslist-db: 1.1.0(browserslist@4.23.2) update-browserslist-db: 1.1.0(browserslist@4.23.2)
camelcase-css@2.0.1: {} camelcase-css@2.0.1: {}
caniuse-lite@1.0.30001643: {} caniuse-lite@1.0.30001644: {}
chalk@2.4.2: chalk@2.4.2:
dependencies: dependencies:
@@ -2656,7 +2623,7 @@ snapshots:
eastasianwidth@0.2.0: {} eastasianwidth@0.2.0: {}
electron-to-chromium@1.5.2: {} electron-to-chromium@1.5.4: {}
emoji-regex@8.0.0: {} emoji-regex@8.0.0: {}

View File

@@ -5,10 +5,10 @@ use serde::Serialize;
use std::{collections::HashSet, time::Duration}; use std::{collections::HashSet, time::Duration};
use tauri::{Emitter, Manager, State}; use tauri::{Emitter, Manager, State};
use crate::Nostr; use crate::{Nostr, BOOTSTRAP_RELAYS};
#[derive(Clone, Serialize)] #[derive(Clone, Serialize)]
struct Payload { struct EventPayload {
event: String, event: String,
sender: String, sender: String,
} }
@@ -32,7 +32,8 @@ pub async fn get_metadata(id: String, state: State<'_, Nostr>) -> Result<String,
let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?; 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);
match client.get_events_of(vec![filter], Some(Duration::from_secs(2))).await { match client.get_events_from(BOOTSTRAP_RELAYS, vec![filter], Some(Duration::from_secs(3))).await
{
Ok(events) => { Ok(events) => {
if let Some(event) = events.first() { if let Some(event) = events.first() {
Ok(Metadata::from_json(&event.content).unwrap_or(Metadata::new()).as_json()) Ok(Metadata::from_json(&event.content).unwrap_or(Metadata::new()).as_json())
@@ -150,11 +151,16 @@ pub async fn connect_account(uri: &str, state: State<'_, Nostr>) -> Result<Strin
#[tauri::command] #[tauri::command]
#[specta::specta] #[specta::specta]
pub async fn get_contact_list(state: State<'_, Nostr>) -> Result<Vec<String>, ()> { pub async fn get_contact_list(state: State<'_, Nostr>) -> Result<Vec<String>, String> {
let contact_list = state.contact_list.lock().await; let client = &state.client;
let list = contact_list.clone().into_iter().map(|c| c.public_key.to_hex()).collect::<Vec<_>>();
Ok(list) 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::<Vec<_>>();
Ok(list)
}
Err(e) => Err(e.to_string()),
}
} }
#[tauri::command] #[tauri::command]
@@ -164,7 +170,7 @@ pub async fn get_inbox(id: String, state: State<'_, Nostr>) -> Result<Vec<String
let public_key = PublicKey::parse(id).map_err(|e| e.to_string())?; let public_key = PublicKey::parse(id).map_err(|e| e.to_string())?;
let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1); let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1);
match client.get_events_of(vec![inbox], None).await { match client.get_events_from(BOOTSTRAP_RELAYS, vec![inbox], None).await {
Ok(events) => { Ok(events) => {
if let Some(event) = events.into_iter().next() { if let Some(event) = events.into_iter().next() {
let urls = event let urls = event
@@ -246,14 +252,9 @@ pub async fn login(
} }
} }
if let Ok(contacts) = client.get_contact_list(Some(Duration::from_secs(10))).await {
let mut contact_list = state.contact_list.lock().await;
*contact_list = contacts;
};
let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1); let inbox = Filter::new().kind(Kind::Custom(10050)).author(public_key).limit(1);
if let Ok(events) = client.get_events_of(vec![inbox], None).await { if let Ok(events) = client.get_events_of(vec![inbox], Some(Duration::from_secs(5))).await {
if let Some(event) = events.into_iter().next() { if let Some(event) = events.into_iter().next() {
let urls = event let urls = event
.tags() .tags()
@@ -279,36 +280,18 @@ pub async fn login(
} }
} }
let subscription_id = SubscriptionId::new("personal_inbox"); let sub_id = SubscriptionId::new("personal_inbox");
let new_message = Filter::new().kind(Kind::GiftWrap).pubkey(public_key); let new_message = Filter::new().kind(Kind::GiftWrap).pubkey(public_key).limit(0);
if client.subscription(&subscription_id).await.is_some() { if client.subscription(&sub_id).await.is_some() {
// Remove old subscriotion // Remove old subscriotion
client.unsubscribe(subscription_id.clone()).await; client.unsubscribe(sub_id.clone()).await;
// Resubscribe new message for current user // Resubscribe new message for current user
let _ = client.subscribe_with_id(subscription_id, vec![new_message], None).await; let _ = client.subscribe_with_id(sub_id.clone(), vec![new_message], None).await;
} else { } else {
let _ = client.subscribe_with_id(subscription_id, vec![new_message], None).await; let _ = client.subscribe_with_id(sub_id, vec![new_message], None).await;
} }
let handle_clone = handle.app_handle().clone();
tauri::async_runtime::spawn(async move {
let window = handle_clone.get_webview_window("main").expect("Window is terminated.");
let state = window.state::<Nostr>();
let client = &state.client;
let sync = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
if client.reconcile(sync.clone(), NegentropyOptions::default()).await.is_ok() {
handle_clone.emit("synchronized", ()).unwrap();
};
if client.get_events_of(vec![sync], Some(Duration::from_secs(20))).await.is_ok() {
handle_clone.emit("synchronized", ()).unwrap();
};
});
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
let window = handle.get_webview_window("main").expect("Window is terminated."); let window = handle.get_webview_window("main").expect("Window is terminated.");
let state = window.state::<Nostr>(); let state = window.state::<Nostr>();
@@ -323,7 +306,7 @@ pub async fn login(
{ {
if let Err(e) = window.emit( if let Err(e) = window.emit(
"event", "event",
Payload { event: rumor.as_json(), sender: sender.to_hex() }, EventPayload { event: rumor.as_json(), sender: sender.to_hex() },
) { ) {
println!("emit failed: {}", e) println!("emit failed: {}", e)
} }

View File

@@ -15,24 +15,27 @@ pub async fn get_chats(state: State<'_, Nostr>) -> Result<Vec<String>, String> {
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key); let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
let events = match client.database().query(vec![filter], Order::Desc).await { let rumors = match client.get_events_of(vec![filter], Some(Duration::from_secs(20))).await {
Ok(events) => { Ok(events) => {
stream::iter(events) stream::iter(events)
.filter_map(|ev| async move { .filter_map(|ev| async move {
if let Ok(UnwrappedGift { rumor, .. }) = client.unwrap_gift_wrap(&ev).await { if let Ok(UnwrappedGift { rumor, .. }) = client.unwrap_gift_wrap(&ev).await {
if rumor.kind == Kind::PrivateDirectMessage { if rumor.kind == Kind::PrivateDirectMessage {
return Some(rumor); Some(rumor)
} else {
None
} }
} else {
None
} }
None
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.await .await
} }
Err(err) => return Err(err.to_string()), Err(e) => return Err(e.to_string()),
}; };
let uniqs = events let uniqs = rumors
.into_iter() .into_iter()
.sorted_by_key(|ev| Reverse(ev.created_at)) .sorted_by_key(|ev| Reverse(ev.created_at))
.filter(|ev| ev.pubkey != public_key) .filter(|ev| ev.pubkey != public_key)
@@ -47,14 +50,14 @@ pub async fn get_chats(state: State<'_, Nostr>) -> Result<Vec<String>, String> {
#[specta::specta] #[specta::specta]
pub async fn get_chat_messages(id: String, state: State<'_, Nostr>) -> Result<Vec<String>, String> { pub async fn get_chat_messages(id: String, state: State<'_, Nostr>) -> Result<Vec<String>, String> {
let client = &state.client; let client = &state.client;
let signer = client.signer().await.map_err(|e| e.to_string())?;
let signer = client.signer().await.map_err(|e| e.to_string())?;
let receiver_pk = signer.public_key().await.map_err(|e| e.to_string())?; let receiver_pk = signer.public_key().await.map_err(|e| e.to_string())?;
let sender_pk = PublicKey::parse(id).map_err(|e| e.to_string())?; let sender_pk = PublicKey::parse(id).map_err(|e| e.to_string())?;
let filter = Filter::new().kind(Kind::GiftWrap).pubkeys(vec![receiver_pk, sender_pk]); let filter = Filter::new().kind(Kind::GiftWrap).pubkeys(vec![receiver_pk, sender_pk]);
let rumors = match client.database().query(vec![filter], Order::Desc).await { let rumors = match client.get_events_of(vec![filter], None).await {
Ok(events) => { Ok(events) => {
stream::iter(events) stream::iter(events)
.filter_map(|ev| async move { .filter_map(|ev| async move {

View File

@@ -16,10 +16,17 @@ mod common;
pub struct Nostr { pub struct Nostr {
client: Client, client: Client,
contact_list: Mutex<Vec<Contact>>,
inbox_relays: Mutex<HashMap<PublicKey, Vec<String>>>, inbox_relays: Mutex<HashMap<PublicKey, Vec<String>>>,
} }
// TODO: Allow user config bootstrap relays.
pub const BOOTSTRAP_RELAYS: [&str; 4] = [
"wss://relay.damus.io/",
"wss://relay.nostr.net/",
"wss://relay.0xchat.com/",
"wss://nostr.wine/",
];
fn main() { fn main() {
let invoke_handler = { let invoke_handler = {
let builder = tauri_specta::ts::builder().commands(tauri_specta::collect_commands![ let builder = tauri_specta::ts::builder().commands(tauri_specta::collect_commands![
@@ -67,6 +74,19 @@ fn main() {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
main_window.set_traffic_lights_inset(12.0, 18.0).unwrap(); main_window.set_traffic_lights_inset(12.0, 18.0).unwrap();
// Workaround for reset traffic light when window resized
#[cfg(target_os = "macos")]
let win_ = main_window.clone();
#[cfg(target_os = "macos")]
main_window.on_window_event(move |event| {
if let tauri::WindowEvent::Resized(_) = event {
win_.set_traffic_lights_inset(12.0, 18.0).unwrap();
}
if let tauri::WindowEvent::ThemeChanged(_) = event {
win_.set_traffic_lights_inset(12.0, 18.0).unwrap();
}
});
// Restore native border // Restore native border
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
main_window.add_border(None); main_window.add_border(None);
@@ -81,16 +101,14 @@ fn main() {
// Setup nostr client // Setup nostr client
let opts = Options::new() let opts = Options::new()
.timeout(Duration::from_secs(30)) .timeout(Duration::from_secs(40))
.send_timeout(Some(Duration::from_secs(5))) .send_timeout(Some(Duration::from_secs(10)))
.connection_timeout(Some(Duration::from_secs(5))); .connection_timeout(Some(Duration::from_secs(10)));
let client = ClientBuilder::default().opts(opts).database(database).build(); let client = ClientBuilder::default().opts(opts).database(database).build();
// Add bootstrap relay // Add bootstrap relay
let _ = client let _ = client.add_relays(BOOTSTRAP_RELAYS).await;
.add_relays(["wss://relay.poster.place/", "wss://bostr.nokotaro.com/"])
.await;
// Connect // Connect
client.connect().await; client.connect().await;
@@ -99,11 +117,7 @@ fn main() {
}); });
// Create global state // Create global state
app.manage(Nostr { app.manage(Nostr { client, inbox_relays: Mutex::new(HashMap::new()) });
client,
contact_list: Mutex::new(Vec::new()),
inbox_relays: Mutex::new(HashMap::new()),
});
Ok(()) Ok(())
}) })

View File

@@ -47,7 +47,7 @@ try {
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getContactList() : Promise<Result<string[], null>> { async getContactList() : Promise<Result<string[], string>> {
try { try {
return { status: "ok", data: await TAURI_INVOKE("get_contact_list") }; return { status: "ok", data: await TAURI_INVOKE("get_contact_list") };
} catch (e) { } catch (e) {

View File

@@ -1,6 +1,4 @@
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { QueryClient } from "@tanstack/react-query";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
import { RouterProvider, createRouter } from "@tanstack/react-router"; import { RouterProvider, createRouter } from "@tanstack/react-router";
import { type } from "@tauri-apps/plugin-os"; import { type } from "@tauri-apps/plugin-os";
import { StrictMode } from "react"; import { StrictMode } from "react";
@@ -9,18 +7,7 @@ import "./global.css";
// Import the generated route tree // Import the generated route tree
import { routeTree } from "./routes.gen"; import { routeTree } from "./routes.gen";
const queryClient = new QueryClient({ const queryClient = new QueryClient();
defaultOptions: {
queries: {
gcTime: 1000 * 60 * 60 * 24,
},
},
});
const persister = createSyncStoragePersister({
storage: window.localStorage,
});
const platform = type(); const platform = type();
const router = createRouter({ const router = createRouter({
@@ -44,12 +31,9 @@ if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement); const root = ReactDOM.createRoot(rootElement);
root.render( root.render(
<StrictMode> <StrictMode>
<PersistQueryClientProvider <QueryClientProvider client={queryClient}>
client={queryClient}
persistOptions={{ persister }}
>
<RouterProvider router={router} /> <RouterProvider router={router} />
</PersistQueryClientProvider> </QueryClientProvider>
</StrictMode>, </StrictMode>,
); );
} }

View File

@@ -2,7 +2,7 @@ import { commands } from "@/commands";
import { cn, getReceivers, groupEventByDate, time } from "@/commons"; import { cn, getReceivers, groupEventByDate, time } from "@/commons";
import { Spinner } from "@/components/spinner"; import { Spinner } from "@/components/spinner";
import { User } from "@/components/user"; import { User } from "@/components/user";
import { ArrowUp, Paperclip } from "@phosphor-icons/react"; import { ArrowUp } from "@phosphor-icons/react";
import * as ScrollArea from "@radix-ui/react-scroll-area"; import * as ScrollArea from "@radix-ui/react-scroll-area";
import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useQuery, useQueryClient } from "@tanstack/react-query";
import { createLazyFileRoute } from "@tanstack/react-router"; import { createLazyFileRoute } from "@tanstack/react-router";

View File

@@ -19,7 +19,7 @@ import { message } from "@tauri-apps/plugin-dialog";
import type { NostrEvent } from "nostr-tools"; import type { NostrEvent } from "nostr-tools";
import { useCallback, useEffect, useState, useTransition } from "react"; import { useCallback, useEffect, useState, useTransition } from "react";
type Payload = { type EventPayload = {
event: string; event: string;
sender: string; sender: string;
}; };
@@ -90,6 +90,8 @@ function ChatList() {
} }
}, },
select: (data) => data.sort((a, b) => b.created_at - a.created_at), select: (data) => data.sort((a, b) => b.created_at - a.created_at),
refetchOnMount: false,
refetchOnWindowFocus: false,
}); });
useEffect(() => { useEffect(() => {
@@ -103,14 +105,14 @@ function ChatList() {
}, []); }, []);
useEffect(() => { useEffect(() => {
const unlisten = listen<Payload>("event", async (data) => { const unlisten = listen<EventPayload>("event", async (data) => {
const event: NostrEvent = JSON.parse(data.payload.event); const event: NostrEvent = JSON.parse(data.payload.event);
const chats: NostrEvent[] = await queryClient.getQueryData(["chats"]); const chats: NostrEvent[] = await queryClient.getQueryData(["chats"]);
if (chats) { if (chats) {
const exist = chats.find((ev) => ev.pubkey === event.pubkey); const index = chats.findIndex((item) => item.pubkey === event.pubkey);
if (!exist) { if (index === -1) {
await queryClient.setQueryData( await queryClient.setQueryData(
["chats"], ["chats"],
(prevEvents: NostrEvent[]) => { (prevEvents: NostrEvent[]) => {
@@ -121,16 +123,12 @@ function ChatList() {
}, },
); );
} else { } else {
const index = chats.findIndex((item) => item.pubkey === event.pubkey);
const newEvents = [...chats]; const newEvents = [...chats];
newEvents[index] = {
...event,
};
if (index !== -1) { await queryClient.setQueryData(["chats"], newEvents);
newEvents[index] = {
...event,
};
await queryClient.setQueryData(["chats"], newEvents);
}
} }
} }
}); });
@@ -147,7 +145,7 @@ function ChatList() {
className="overflow-hidden flex-1 w-full" className="overflow-hidden flex-1 w-full"
> >
<ScrollArea.Viewport className="relative h-full px-1.5"> <ScrollArea.Viewport className="relative h-full px-1.5">
{isLoading ? ( {isLoading || !data.length ? (
<div> <div>
{[...Array(5).keys()].map((i) => ( {[...Array(5).keys()].map((i) => (
<div <div

View File

@@ -38,7 +38,10 @@ function Screen() {
}; };
return ( return (
<div className="size-full flex items-center justify-center"> <div
data-tauri-drag-region
className="size-full flex items-center justify-center"
>
<div className="w-[320px] flex flex-col gap-8"> <div className="w-[320px] flex flex-col gap-8">
<div className="flex flex-col gap-1 text-center"> <div className="flex flex-col gap-1 text-center">
<h1 className="leading-tight text-xl font-semibold">New Identity</h1> <h1 className="leading-tight text-xl font-semibold">New Identity</h1>
@@ -61,7 +64,7 @@ function Screen() {
placeholder="https://" placeholder="https://"
value={picture} value={picture}
onChange={(e) => setPicture(e.target.value)} onChange={(e) => setPicture(e.target.value)}
className="px-3 rounded-lg h-10 bg-transparent border border-neutral-200 dark:border-neutral-800 focus:border-blue-500 focus:outline-none" className="px-3 rounded-lg h-10 bg-transparent border border-neutral-200 dark:border-neutral-500 focus:border-blue-500 focus:outline-none"
/> />
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
@@ -76,7 +79,7 @@ function Screen() {
type="text" type="text"
value={name} value={name}
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
className="px-3 rounded-lg h-10 bg-transparent border border-neutral-200 dark:border-neutral-800 focus:border-blue-500 focus:outline-none" className="px-3 rounded-lg h-10 bg-transparent border border-neutral-200 dark:border-neutral-500 focus:border-blue-500 focus:outline-none"
/> />
</div> </div>
</Frame> </Frame>

View File

@@ -48,7 +48,10 @@ function Screen() {
}; };
return ( return (
<div className="size-full flex items-center justify-center"> <div
data-tauri-drag-region
className="size-full flex items-center justify-center"
>
<div className="w-[320px] flex flex-col gap-8"> <div className="w-[320px] flex flex-col gap-8">
<div className="flex flex-col gap-1 text-center"> <div className="flex flex-col gap-1 text-center">
<h1 className="leading-tight text-xl font-semibold"> <h1 className="leading-tight text-xl font-semibold">
@@ -75,7 +78,7 @@ function Screen() {
placeholder="nsec or ncryptsec..." placeholder="nsec or ncryptsec..."
value={key} value={key}
onChange={(e) => setKey(e.target.value)} onChange={(e) => setKey(e.target.value)}
className="pl-3 pr-12 rounded-lg w-full h-10 bg-transparent border border-neutral-200 dark:border-neutral-800 focus:border-blue-500 focus:outline-none" className="pl-3 pr-12 rounded-lg w-full h-10 bg-transparent border border-neutral-200 dark:border-neutral-500 focus:border-blue-500 focus:outline-none"
/> />
<button <button
type="button" type="button"
@@ -98,7 +101,7 @@ function Screen() {
type="password" type="password"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
className="px-3 rounded-lg h-10 bg-transparent border border-neutral-200 dark:border-neutral-800 focus:border-blue-500 focus:outline-none" className="px-3 rounded-lg h-10 bg-transparent border border-neutral-200 dark:border-neutral-500 focus:border-blue-500 focus:outline-none"
/> />
</div> </div>
</Frame> </Frame>

View File

@@ -54,7 +54,10 @@ function Screen() {
}; };
return ( return (
<div className="size-full flex items-center justify-center"> <div
data-tauri-drag-region
className="size-full flex items-center justify-center"
>
<div className="w-[320px] flex flex-col gap-8"> <div className="w-[320px] flex flex-col gap-8">
<div className="flex flex-col gap-1 text-center"> <div className="flex flex-col gap-1 text-center">
<h3 className="leading-tight text-neutral-700 dark:text-neutral-300"> <h3 className="leading-tight text-neutral-700 dark:text-neutral-300">

View File

@@ -6,7 +6,10 @@ export const Route = createLazyFileRoute("/new")({
function Screen() { function Screen() {
return ( return (
<div className="size-full flex items-center justify-center"> <div
data-tauri-drag-region
className="size-full flex items-center justify-center"
>
<div className="w-[320px] flex flex-col gap-8"> <div className="w-[320px] flex flex-col gap-8">
<div className="flex flex-col gap-1 text-center"> <div className="flex flex-col gap-1 text-center">
<h1 className="leading-tight text-xl font-semibold"> <h1 className="leading-tight text-xl font-semibold">
@@ -22,7 +25,7 @@ function Screen() {
</Link> </Link>
<Link <Link
to="/nostr-connect" to="/nostr-connect"
className="w-full h-10 bg-white hover:bg-neutral-100 dark:hover:bg-neutral-950 dark:bg-neutral-900 rounded-lg inline-flex items-center justify-center" className="w-full h-10 bg-white hover:bg-neutral-100 dark:hover:bg-neutral-100 dark:bg-white dark:text-black rounded-lg inline-flex items-center justify-center"
> >
Login with Nostr Connect Login with Nostr Connect
</Link> </Link>

View File

@@ -51,7 +51,10 @@ function Screen() {
}; };
return ( return (
<div className="size-full flex items-center justify-center"> <div
data-tauri-drag-region
className="size-full flex items-center justify-center"
>
<div className="w-[320px] flex flex-col gap-8"> <div className="w-[320px] flex flex-col gap-8">
<div className="flex flex-col gap-1 text-center"> <div className="flex flex-col gap-1 text-center">
<h1 className="leading-tight text-xl font-semibold">Nostr Connect</h1> <h1 className="leading-tight text-xl font-semibold">Nostr Connect</h1>
@@ -74,7 +77,7 @@ function Screen() {
placeholder="bunker://..." placeholder="bunker://..."
value={uri} value={uri}
onChange={(e) => setUri(e.target.value)} onChange={(e) => setUri(e.target.value)}
className="pl-3 pr-12 rounded-lg w-full h-10 bg-transparent border border-neutral-200 dark:border-neutral-800 focus:border-blue-500 focus:outline-none" className="pl-3 pr-12 rounded-lg w-full h-10 bg-transparent border border-neutral-200 dark:border-neutral-500 focus:border-blue-500 focus:outline-none"
/> />
<button <button
type="button" type="button"