feat: group metadata query
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
"@radix-ui/react-switch": "^1.1.1",
|
"@radix-ui/react-switch": "^1.1.1",
|
||||||
"@radix-ui/react-tabs": "^1.1.1",
|
"@radix-ui/react-tabs": "^1.1.1",
|
||||||
"@radix-ui/react-tooltip": "^1.1.3",
|
"@radix-ui/react-tooltip": "^1.1.3",
|
||||||
|
"@tanstack/query-broadcast-client-experimental": "^5.59.16",
|
||||||
"@tanstack/query-persist-client-core": "^5.59.16",
|
"@tanstack/query-persist-client-core": "^5.59.16",
|
||||||
"@tanstack/react-query": "^5.59.16",
|
"@tanstack/react-query": "^5.59.16",
|
||||||
"@tanstack/react-router": "^1.77.5",
|
"@tanstack/react-router": "^1.77.5",
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
"@tauri-apps/plugin-os": "^2.0.0",
|
"@tauri-apps/plugin-os": "^2.0.0",
|
||||||
"@tauri-apps/plugin-process": "^2.0.0",
|
"@tauri-apps/plugin-process": "^2.0.0",
|
||||||
"@tauri-apps/plugin-shell": "^2.0.1",
|
"@tauri-apps/plugin-shell": "^2.0.1",
|
||||||
"@tauri-apps/plugin-store": "^2.1.0",
|
"@tauri-apps/plugin-store": "github:tauri-apps/tauri-plugin-store#a564510",
|
||||||
"@tauri-apps/plugin-updater": "^2.0.0",
|
"@tauri-apps/plugin-updater": "^2.0.0",
|
||||||
"@tauri-apps/plugin-upload": "^2.0.0",
|
"@tauri-apps/plugin-upload": "^2.0.0",
|
||||||
"@tauri-apps/plugin-window-state": "^2.0.0",
|
"@tauri-apps/plugin-window-state": "^2.0.0",
|
||||||
|
|||||||
79
pnpm-lock.yaml
generated
79
pnpm-lock.yaml
generated
@@ -38,6 +38,9 @@ importers:
|
|||||||
'@radix-ui/react-tooltip':
|
'@radix-ui/react-tooltip':
|
||||||
specifier: ^1.1.3
|
specifier: ^1.1.3
|
||||||
version: 1.1.3(react-dom@19.0.0-rc-cae764ce-20241025(react@19.0.0-rc-cae764ce-20241025))(react@19.0.0-rc-cae764ce-20241025)(types-react-dom@19.0.0-rc.1)(types-react@19.0.0-rc.1)
|
version: 1.1.3(react-dom@19.0.0-rc-cae764ce-20241025(react@19.0.0-rc-cae764ce-20241025))(react@19.0.0-rc-cae764ce-20241025)(types-react-dom@19.0.0-rc.1)(types-react@19.0.0-rc.1)
|
||||||
|
'@tanstack/query-broadcast-client-experimental':
|
||||||
|
specifier: ^5.59.16
|
||||||
|
version: 5.59.16
|
||||||
'@tanstack/query-persist-client-core':
|
'@tanstack/query-persist-client-core':
|
||||||
specifier: ^5.59.16
|
specifier: ^5.59.16
|
||||||
version: 5.59.16
|
version: 5.59.16
|
||||||
@@ -78,8 +81,8 @@ importers:
|
|||||||
specifier: ^2.0.1
|
specifier: ^2.0.1
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
'@tauri-apps/plugin-store':
|
'@tauri-apps/plugin-store':
|
||||||
specifier: ^2.1.0
|
specifier: github:tauri-apps/tauri-plugin-store#a564510
|
||||||
version: 2.1.0
|
version: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/a564510
|
||||||
'@tauri-apps/plugin-updater':
|
'@tauri-apps/plugin-updater':
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
@@ -300,6 +303,10 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@babel/core': ^7.0.0-0
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/runtime@7.23.4':
|
||||||
|
resolution: {integrity: sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
'@babel/runtime@7.26.0':
|
'@babel/runtime@7.26.0':
|
||||||
resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
|
resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -1239,6 +1246,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-2CqERleeqO3hkhJmyJm37tiL3LYgeOpmo8szqdjgtnnG0z7ZpvzkZz6HkfOr9Ca/ha7mhAiouSvLYuLkM37AMg==}
|
resolution: {integrity: sha512-2CqERleeqO3hkhJmyJm37tiL3LYgeOpmo8szqdjgtnnG0z7ZpvzkZz6HkfOr9Ca/ha7mhAiouSvLYuLkM37AMg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
'@tanstack/query-broadcast-client-experimental@5.59.16':
|
||||||
|
resolution: {integrity: sha512-Gf5are+fCAfRBM7+ra/aLSsckOWVBQu9UsayMQ7tT1ZQ/HCQcNU6e08dskGeWeScsK7752M6mhk8dR8UljG1uQ==}
|
||||||
|
|
||||||
'@tanstack/query-core@5.59.16':
|
'@tanstack/query-core@5.59.16':
|
||||||
resolution: {integrity: sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==}
|
resolution: {integrity: sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==}
|
||||||
|
|
||||||
@@ -1390,8 +1400,9 @@ packages:
|
|||||||
'@tauri-apps/plugin-shell@2.0.1':
|
'@tauri-apps/plugin-shell@2.0.1':
|
||||||
resolution: {integrity: sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==}
|
resolution: {integrity: sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==}
|
||||||
|
|
||||||
'@tauri-apps/plugin-store@2.1.0':
|
'@tauri-apps/plugin-store@https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/a564510':
|
||||||
resolution: {integrity: sha512-GADqrc17opUKYIAKnGHIUgEeTZ2wJGu1ZITKQ1WMuOFdv8fvXRFBAqsqPjE3opgWohbczX6e1NpwmZK1AnuWVw==}
|
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/a564510}
|
||||||
|
version: 2.0.0
|
||||||
|
|
||||||
'@tauri-apps/plugin-updater@2.0.0':
|
'@tauri-apps/plugin-updater@2.0.0':
|
||||||
resolution: {integrity: sha512-N0cl71g7RPr7zK2Fe5aoIwzw14NcdLcz7XMGFWZVjprsqgDRWoxbnUkknyCQMZthjhGkppCd/wN2MIsUz+eAhQ==}
|
resolution: {integrity: sha512-N0cl71g7RPr7zK2Fe5aoIwzw14NcdLcz7XMGFWZVjprsqgDRWoxbnUkknyCQMZthjhGkppCd/wN2MIsUz+eAhQ==}
|
||||||
@@ -1520,6 +1531,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
broadcast-channel@7.0.0:
|
||||||
|
resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==}
|
||||||
|
|
||||||
browserslist@4.24.2:
|
browserslist@4.24.2:
|
||||||
resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==}
|
resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==}
|
||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
@@ -1639,6 +1653,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
eventemitter3@4.0.7:
|
||||||
|
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||||
|
|
||||||
eventemitter3@5.0.1:
|
eventemitter3@5.0.1:
|
||||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||||
|
|
||||||
@@ -1892,6 +1909,22 @@ packages:
|
|||||||
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||||
engines: {node: '>= 6'}
|
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:
|
package-json-from-dist@1.0.1:
|
||||||
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
||||||
|
|
||||||
@@ -2220,6 +2253,9 @@ packages:
|
|||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
unload@2.4.1:
|
||||||
|
resolution: {integrity: sha512-IViSAm8Z3sRBYA+9wc0fLQmU9Nrxb16rcDmIiR6Y9LJSZzI7QY5QsDhqPpKOjAn0O9/kfK1TfNEMMAGPTIraPw==}
|
||||||
|
|
||||||
unplugin@1.14.1:
|
unplugin@1.14.1:
|
||||||
resolution: {integrity: sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==}
|
resolution: {integrity: sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
@@ -2498,6 +2534,10 @@ snapshots:
|
|||||||
'@babel/core': 7.26.0
|
'@babel/core': 7.26.0
|
||||||
'@babel/helper-plugin-utils': 7.25.9
|
'@babel/helper-plugin-utils': 7.25.9
|
||||||
|
|
||||||
|
'@babel/runtime@7.23.4':
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime: 0.14.1
|
||||||
|
|
||||||
'@babel/runtime@7.26.0':
|
'@babel/runtime@7.26.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime: 0.14.1
|
regenerator-runtime: 0.14.1
|
||||||
@@ -3249,6 +3289,11 @@ snapshots:
|
|||||||
|
|
||||||
'@tanstack/history@1.61.1': {}
|
'@tanstack/history@1.61.1': {}
|
||||||
|
|
||||||
|
'@tanstack/query-broadcast-client-experimental@5.59.16':
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/query-core': 5.59.16
|
||||||
|
broadcast-channel: 7.0.0
|
||||||
|
|
||||||
'@tanstack/query-core@5.59.16': {}
|
'@tanstack/query-core@5.59.16': {}
|
||||||
|
|
||||||
'@tanstack/query-persist-client-core@5.59.16':
|
'@tanstack/query-persist-client-core@5.59.16':
|
||||||
@@ -3398,7 +3443,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 2.0.3
|
'@tauri-apps/api': 2.0.3
|
||||||
|
|
||||||
'@tauri-apps/plugin-store@2.1.0':
|
'@tauri-apps/plugin-store@https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/a564510':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 2.0.3
|
'@tauri-apps/api': 2.0.3
|
||||||
|
|
||||||
@@ -3550,6 +3595,13 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fill-range: 7.1.1
|
fill-range: 7.1.1
|
||||||
|
|
||||||
|
broadcast-channel@7.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.23.4
|
||||||
|
oblivious-set: 1.4.0
|
||||||
|
p-queue: 6.6.2
|
||||||
|
unload: 2.4.1
|
||||||
|
|
||||||
browserslist@4.24.2:
|
browserslist@4.24.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001673
|
caniuse-lite: 1.0.30001673
|
||||||
@@ -3695,6 +3747,8 @@ snapshots:
|
|||||||
|
|
||||||
escalade@3.2.0: {}
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
|
eventemitter3@4.0.7: {}
|
||||||
|
|
||||||
eventemitter3@5.0.1: {}
|
eventemitter3@5.0.1: {}
|
||||||
|
|
||||||
fast-glob@3.3.2:
|
fast-glob@3.3.2:
|
||||||
@@ -3912,6 +3966,19 @@ snapshots:
|
|||||||
|
|
||||||
object-hash@3.0.0: {}
|
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: {}
|
package-json-from-dist@1.0.1: {}
|
||||||
|
|
||||||
path-key@3.1.1: {}
|
path-key@3.1.1: {}
|
||||||
@@ -4230,6 +4297,8 @@ snapshots:
|
|||||||
|
|
||||||
typescript@5.6.3: {}
|
typescript@5.6.3: {}
|
||||||
|
|
||||||
|
unload@2.4.1: {}
|
||||||
|
|
||||||
unplugin@1.14.1(webpack-sources@3.2.3):
|
unplugin@1.14.1(webpack-sources@3.2.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.14.0
|
acorn: 8.14.0
|
||||||
|
|||||||
3
src-tauri/Cargo.lock
generated
3
src-tauri/Cargo.lock
generated
@@ -6079,8 +6079,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-store"
|
name = "tauri-plugin-store"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/tauri-apps/plugins-workspace?rev=8c67d44#8c67d44aef60b1427019538d8420787ef35bd3d5"
|
||||||
checksum = "5058f179f7215390fc5a68eeffcb805b7e2681d6e817a5d08094fae7ab649e68"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
"dunce",
|
||||||
"log",
|
"log",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ tauri-plugin-process = "2.0.0"
|
|||||||
tauri-plugin-shell = "2.0.0"
|
tauri-plugin-shell = "2.0.0"
|
||||||
tauri-plugin-updater = "2.0.0"
|
tauri-plugin-updater = "2.0.0"
|
||||||
tauri-plugin-upload = "2.0.0"
|
tauri-plugin-upload = "2.0.0"
|
||||||
tauri-plugin-store = "2.0.0"
|
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", rev = "8c67d44" }
|
||||||
tauri-plugin-prevent-default = "0.6"
|
tauri-plugin-prevent-default = "0.6"
|
||||||
tauri-plugin-theme = "2.1.2"
|
tauri-plugin-theme = "2.1.2"
|
||||||
tauri-plugin-decorum = { git = "https://github.com/clearlysid/tauri-plugin-decorum" }
|
tauri-plugin-decorum = { git = "https://github.com/clearlysid/tauri-plugin-decorum" }
|
||||||
|
|||||||
@@ -17,15 +17,6 @@
|
|||||||
"core:resources:default",
|
"core:resources:default",
|
||||||
"core:menu:default",
|
"core:menu:default",
|
||||||
"core:tray:default",
|
"core:tray:default",
|
||||||
"notification:allow-is-permission-granted",
|
|
||||||
"notification:allow-request-permission",
|
|
||||||
"notification:default",
|
|
||||||
"os:allow-locale",
|
|
||||||
"os:allow-platform",
|
|
||||||
"os:allow-os-type",
|
|
||||||
"updater:default",
|
|
||||||
"updater:allow-check",
|
|
||||||
"updater:allow-download-and-install",
|
|
||||||
"core:window:allow-create",
|
"core:window:allow-create",
|
||||||
"core:window:allow-close",
|
"core:window:allow-close",
|
||||||
"core:window:allow-destroy",
|
"core:window:allow-destroy",
|
||||||
@@ -36,22 +27,31 @@
|
|||||||
"core:window:allow-set-size",
|
"core:window:allow-set-size",
|
||||||
"core:window:allow-start-dragging",
|
"core:window:allow-start-dragging",
|
||||||
"core:window:allow-toggle-maximize",
|
"core:window:allow-toggle-maximize",
|
||||||
"decorum:allow-show-snap-overlay",
|
|
||||||
"clipboard-manager:allow-write-text",
|
|
||||||
"clipboard-manager:allow-read-text",
|
|
||||||
"core:webview:allow-create-webview-window",
|
"core:webview:allow-create-webview-window",
|
||||||
"core:webview:allow-create-webview",
|
"core:webview:allow-create-webview",
|
||||||
"core:webview:allow-set-webview-size",
|
"core:webview:allow-set-webview-size",
|
||||||
"core:webview:allow-set-webview-position",
|
"core:webview:allow-set-webview-position",
|
||||||
"core:webview:allow-webview-close",
|
"core:webview:allow-webview-close",
|
||||||
|
"core:menu:allow-new",
|
||||||
|
"core:menu:allow-popup",
|
||||||
|
"notification:allow-is-permission-granted",
|
||||||
|
"notification:allow-request-permission",
|
||||||
|
"notification:default",
|
||||||
|
"os:allow-locale",
|
||||||
|
"os:allow-platform",
|
||||||
|
"os:allow-os-type",
|
||||||
|
"updater:default",
|
||||||
|
"updater:allow-check",
|
||||||
|
"updater:allow-download-and-install",
|
||||||
|
"decorum:allow-show-snap-overlay",
|
||||||
|
"clipboard-manager:allow-write-text",
|
||||||
|
"clipboard-manager:allow-read-text",
|
||||||
"dialog:allow-open",
|
"dialog:allow-open",
|
||||||
"dialog:allow-ask",
|
"dialog:allow-ask",
|
||||||
"dialog:allow-message",
|
"dialog:allow-message",
|
||||||
"process:allow-restart",
|
"process:allow-restart",
|
||||||
"process:allow-exit",
|
"process:allow-exit",
|
||||||
"fs:allow-read-file",
|
"fs:allow-read-file",
|
||||||
"core:menu:allow-new",
|
|
||||||
"core:menu:allow-popup",
|
|
||||||
"shell:allow-open",
|
"shell:allow-open",
|
||||||
"store:default",
|
"store:default",
|
||||||
"prevent-default:default",
|
"prevent-default:default",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
{"window":{"identifier":"window","description":"Capability for the desktop","local":true,"windows":["*"],"permissions":["core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","os:allow-os-type","updater:default","updater:allow-check","updater:allow-download-and-install","core:window:allow-create","core:window:allow-close","core:window:allow-destroy","core:window:allow-set-focus","core:window:allow-center","core:window:allow-minimize","core:window:allow-maximize","core:window:allow-set-size","core:window:allow-start-dragging","core:window:allow-toggle-maximize","decorum:allow-show-snap-overlay","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-set-webview-size","core:webview:allow-set-webview-position","core:webview:allow-webview-close","dialog:allow-open","dialog:allow-ask","dialog:allow-message","process:allow-restart","process:allow-exit","fs:allow-read-file","core:menu:allow-new","core:menu:allow-popup","shell:allow-open","store:default","prevent-default:default","theme:default",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["macOS","windows"]}}
|
{"window":{"identifier":"window","description":"Capability for the desktop","local":true,"windows":["*"],"permissions":["core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-close","core:window:allow-destroy","core:window:allow-set-focus","core:window:allow-center","core:window:allow-minimize","core:window:allow-maximize","core:window:allow-set-size","core:window:allow-start-dragging","core:window:allow-toggle-maximize","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-set-webview-size","core:webview:allow-set-webview-position","core:webview:allow-webview-close","core:menu:allow-new","core:menu:allow-popup","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","os:allow-os-type","updater:default","updater:allow-check","updater:allow-download-and-install","decorum:allow-show-snap-overlay","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-ask","dialog:allow-message","process:allow-restart","process:allow-exit","fs:allow-read-file","shell:allow-open","store:default","prevent-default:default","theme:default",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["macOS","windows"]}}
|
||||||
@@ -5444,11 +5444,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:allow-clear"
|
"const": "store:allow-clear"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"description": "Enables the create_store command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "store:allow-create-store"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "Enables the delete command without any pre-configured scope.",
|
"description": "Enables the delete command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5464,6 +5459,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:allow-get"
|
"const": "store:allow-get"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the get_store command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "store:allow-get-store"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the has command without any pre-configured scope.",
|
"description": "Enables the has command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5484,6 +5484,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:allow-load"
|
"const": "store:allow-load"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the reload command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "store:allow-reload"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the reset command without any pre-configured scope.",
|
"description": "Enables the reset command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5509,11 +5514,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:deny-clear"
|
"const": "store:deny-clear"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"description": "Denies the create_store command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "store:deny-create-store"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "Denies the delete command without any pre-configured scope.",
|
"description": "Denies the delete command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5529,6 +5529,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:deny-get"
|
"const": "store:deny-get"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the get_store command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "store:deny-get-store"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the has command without any pre-configured scope.",
|
"description": "Denies the has command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5549,6 +5554,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:deny-load"
|
"const": "store:deny-load"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the reload command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "store:deny-reload"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the reset command without any pre-configured scope.",
|
"description": "Denies the reset command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -5444,11 +5444,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:allow-clear"
|
"const": "store:allow-clear"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"description": "Enables the create_store command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "store:allow-create-store"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "Enables the delete command without any pre-configured scope.",
|
"description": "Enables the delete command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5464,6 +5459,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:allow-get"
|
"const": "store:allow-get"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the get_store command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "store:allow-get-store"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the has command without any pre-configured scope.",
|
"description": "Enables the has command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5484,6 +5484,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:allow-load"
|
"const": "store:allow-load"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the reload command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "store:allow-reload"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the reset command without any pre-configured scope.",
|
"description": "Enables the reset command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5509,11 +5514,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:deny-clear"
|
"const": "store:deny-clear"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"description": "Denies the create_store command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "store:deny-create-store"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "Denies the delete command without any pre-configured scope.",
|
"description": "Denies the delete command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5529,6 +5529,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:deny-get"
|
"const": "store:deny-get"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the get_store command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "store:deny-get-store"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the has command without any pre-configured scope.",
|
"description": "Denies the has command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5549,6 +5554,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "store:deny-load"
|
"const": "store:deny-load"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the reload command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "store:deny-reload"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the reset command without any pre-configured scope.",
|
"description": "Denies the reset command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -32,41 +32,28 @@ pub struct Mention {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub async fn get_profile(
|
pub async fn get_profile(id: String, state: State<'_, Nostr>) -> Result<String, String> {
|
||||||
id: String,
|
|
||||||
cache_only: bool,
|
|
||||||
state: State<'_, Nostr>,
|
|
||||||
) -> Result<String, String> {
|
|
||||||
let client = &state.client;
|
let client = &state.client;
|
||||||
let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?;
|
let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
if cache_only {
|
|
||||||
let profile = client
|
|
||||||
.database()
|
|
||||||
.profile(public_key)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
return Ok(profile.metadata().as_json());
|
|
||||||
};
|
|
||||||
|
|
||||||
let filter = Filter::new()
|
let filter = Filter::new()
|
||||||
.author(public_key)
|
.author(public_key)
|
||||||
.kind(Kind::Metadata)
|
.kind(Kind::Metadata)
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
let mut metadata = Metadata::new();
|
let events = client
|
||||||
|
.database()
|
||||||
let mut rx = client
|
.query(vec![filter])
|
||||||
.stream_events(vec![filter], Some(Duration::from_secs(5)))
|
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
while let Some(event) = rx.next().await {
|
match events.first() {
|
||||||
metadata = Metadata::from_json(&event.content).map_err(|e| e.to_string())?;
|
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()),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(metadata.as_json())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -627,15 +614,15 @@ pub async fn get_notifications(id: String, state: State<'_, Nostr>) -> Result<Ve
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub fn get_user_settings(state: State<'_, Nostr>) -> Result<Settings, String> {
|
pub async fn get_user_settings(state: State<'_, Nostr>) -> Result<Settings, String> {
|
||||||
Ok(state.settings.lock().unwrap().clone())
|
Ok(state.settings.lock().await.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub async fn set_user_settings(settings: String, state: State<'_, Nostr>) -> Result<(), String> {
|
pub async fn set_user_settings(settings: String, state: State<'_, Nostr>) -> Result<(), String> {
|
||||||
let parsed: Settings = serde_json::from_str(&settings).map_err(|e| e.to_string())?;
|
let parsed: Settings = serde_json::from_str(&settings).map_err(|e| e.to_string())?;
|
||||||
state.settings.lock().unwrap().clone_from(&parsed);
|
state.settings.lock().await.clone_from(&parsed);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,25 +12,32 @@ use serde::{Deserialize, Serialize};
|
|||||||
use specta::Type;
|
use specta::Type;
|
||||||
use specta_typescript::Typescript;
|
use specta_typescript::Typescript;
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
fs,
|
fs,
|
||||||
io::{self, BufRead},
|
io::{self, BufRead},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::Mutex,
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use tauri::{path::BaseDirectory, Emitter, EventTarget, Manager};
|
use tauri::{path::BaseDirectory, Emitter, EventTarget, Listener, Manager};
|
||||||
use tauri_plugin_decorum::WebviewWindowExt;
|
use tauri_plugin_decorum::WebviewWindowExt;
|
||||||
use tauri_plugin_notification::{NotificationExt, PermissionState};
|
use tauri_plugin_notification::{NotificationExt, PermissionState};
|
||||||
use tauri_specta::{collect_commands, Builder};
|
use tauri_specta::{collect_commands, Builder};
|
||||||
|
use tokio::{sync::Mutex, sync::RwLock, time::sleep};
|
||||||
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
|
||||||
pub struct Nostr {
|
pub struct Nostr {
|
||||||
client: Client,
|
client: Client,
|
||||||
|
queue: RwLock<HashSet<PublicKey>>,
|
||||||
settings: Mutex<Settings>,
|
settings: Mutex<Settings>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Payload {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Type)]
|
#[derive(Clone, Serialize, Deserialize, Type)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
proxy: Option<String>,
|
proxy: Option<String>,
|
||||||
@@ -146,6 +153,7 @@ fn main() {
|
|||||||
.setup(move |app| {
|
.setup(move |app| {
|
||||||
let handle = app.handle();
|
let handle = app.handle();
|
||||||
let handle_clone = handle.clone();
|
let handle_clone = handle.clone();
|
||||||
|
let handle_clone_child = handle_clone.clone();
|
||||||
let main_window = app.get_webview_window("main").unwrap();
|
let main_window = app.get_webview_window("main").unwrap();
|
||||||
|
|
||||||
let config_dir = handle
|
let config_dir = handle
|
||||||
@@ -174,7 +182,7 @@ fn main() {
|
|||||||
|
|
||||||
// Config
|
// Config
|
||||||
let opts = Options::new()
|
let opts = Options::new()
|
||||||
.gossip(false)
|
.gossip(true)
|
||||||
.max_avg_latency(Duration::from_millis(300))
|
.max_avg_latency(Duration::from_millis(300))
|
||||||
.automatic_authentication(true)
|
.automatic_authentication(true)
|
||||||
.connection_timeout(Some(Duration::from_secs(5)))
|
.connection_timeout(Some(Duration::from_secs(5)))
|
||||||
@@ -229,9 +237,40 @@ fn main() {
|
|||||||
// Create global state
|
// Create global state
|
||||||
app.manage(Nostr {
|
app.manage(Nostr {
|
||||||
client,
|
client,
|
||||||
|
queue: RwLock::new(HashSet::new()),
|
||||||
settings: Mutex::new(Settings::default()),
|
settings: Mutex::new(Settings::default()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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_child.clone();
|
||||||
|
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
let state = handle.state::<Nostr>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(Duration::from_millis(300)).await;
|
||||||
|
|
||||||
|
let read_queue = state.queue.read().await;
|
||||||
|
let authors: Vec<PublicKey> = read_queue.iter().copied().collect();
|
||||||
|
let filter = Filter::new().authors(authors).kind(Kind::Metadata);
|
||||||
|
let opts = SubscribeAutoCloseOptions::default()
|
||||||
|
.filter(FilterOptions::WaitDurationAfterEOSE(Duration::from_secs(3)));
|
||||||
|
|
||||||
|
if client.subscribe(vec![filter], Some(opts)).await.is_ok() {
|
||||||
|
let mut write_queue = state.queue.write().await;
|
||||||
|
write_queue.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Run notification thread
|
// Run notification thread
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
let state = handle_clone.state::<Nostr>();
|
let state = handle_clone.state::<Nostr>();
|
||||||
@@ -278,13 +317,14 @@ fn main() {
|
|||||||
|
|
||||||
let _ = client
|
let _ = client
|
||||||
.handle_notifications(|notification| async {
|
.handle_notifications(|notification| async {
|
||||||
if let RelayPoolNotification::Event {
|
#[allow(clippy::collapsible_match)]
|
||||||
|
if let RelayPoolNotification::Message { message, .. } = notification {
|
||||||
|
if let RelayMessage::Event {
|
||||||
event,
|
event,
|
||||||
subscription_id,
|
subscription_id,
|
||||||
..
|
..
|
||||||
} = notification
|
} = message
|
||||||
{
|
{
|
||||||
// Handle events from notification subscription
|
|
||||||
if subscription_id == notification_id {
|
if subscription_id == notification_id {
|
||||||
// Send native notification
|
// Send native notification
|
||||||
if allow_notification {
|
if allow_notification {
|
||||||
@@ -302,6 +342,10 @@ fn main() {
|
|||||||
&handle_clone,
|
&handle_clone,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if event.kind == Kind::Metadata {
|
||||||
|
if let Err(e) = handle_clone.emit("metadata", event.as_json()) {
|
||||||
|
println!("Emitter error: {}", e)
|
||||||
|
}
|
||||||
} else if event.kind != Kind::RelayList {
|
} else if event.kind != Kind::RelayList {
|
||||||
let payload = RichEvent {
|
let payload = RichEvent {
|
||||||
raw: event.as_json(),
|
raw: event.as_json(),
|
||||||
@@ -321,6 +365,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(false)
|
Ok(false)
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
26
src/app.tsx
26
src/app.tsx
@@ -1,8 +1,12 @@
|
|||||||
|
import { broadcastQueryClient } from "@tanstack/query-broadcast-client-experimental";
|
||||||
|
import { experimental_createPersister } from "@tanstack/query-persist-client-core";
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
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 { Store } from "@tauri-apps/plugin-store";
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { newQueryStorage } from "./commons";
|
||||||
import type { LumeEvent } from "./system";
|
import type { LumeEvent } from "./system";
|
||||||
|
|
||||||
import { routeTree } from "./routes.gen"; // auto generated file
|
import { routeTree } from "./routes.gen"; // auto generated file
|
||||||
@@ -18,7 +22,27 @@ declare module "@tanstack/react-router" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const platform = type();
|
const platform = type();
|
||||||
const queryClient = new QueryClient();
|
// @ts-ignore, won't fix
|
||||||
|
const store = await Store.load(".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
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make sure all webviews use same query client
|
||||||
|
broadcastQueryClient({
|
||||||
|
queryClient,
|
||||||
|
broadcastChannel: "lume",
|
||||||
|
});
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
routeTree,
|
routeTree,
|
||||||
context: { queryClient, platform },
|
context: { queryClient, platform },
|
||||||
|
|||||||
@@ -128,9 +128,9 @@ async setSigner(id: string) : Promise<Result<null, string>> {
|
|||||||
else return { status: "error", error: e as any };
|
else return { status: "error", error: e as any };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getProfile(id: string, cacheOnly: boolean) : Promise<Result<string, string>> {
|
async getProfile(id: string) : Promise<Result<string, string>> {
|
||||||
try {
|
try {
|
||||||
return { status: "ok", data: await TAURI_INVOKE("get_profile", { id, cacheOnly }) };
|
return { status: "ok", data: await TAURI_INVOKE("get_profile", { id }) };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if(e instanceof Error) throw e;
|
if(e instanceof Error) throw e;
|
||||||
else return { status: "error", error: e as any };
|
else return { status: "error", error: e as any };
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type {
|
|||||||
MaybePromise,
|
MaybePromise,
|
||||||
PersistedQuery,
|
PersistedQuery,
|
||||||
} from "@tanstack/query-persist-client-core";
|
} from "@tanstack/query-persist-client-core";
|
||||||
import { Store } from "@tanstack/store";
|
import { Store } from "@tanstack/react-store";
|
||||||
import { ask, message, open } from "@tauri-apps/plugin-dialog";
|
import { ask, message, open } from "@tauri-apps/plugin-dialog";
|
||||||
import { readFile } from "@tauri-apps/plugin-fs";
|
import { readFile } from "@tauri-apps/plugin-fs";
|
||||||
import { relaunch } from "@tauri-apps/plugin-process";
|
import { relaunch } from "@tauri-apps/plugin-process";
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function NoteRepost({
|
|||||||
const list: Promise<MenuItem>[] = [];
|
const list: Promise<MenuItem>[] = [];
|
||||||
|
|
||||||
for (const account of accounts) {
|
for (const account of accounts) {
|
||||||
const res = await commands.getProfile(account, true);
|
const res = await commands.getProfile(account);
|
||||||
let name = "unknown";
|
let name = "unknown";
|
||||||
|
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { memo } from "react";
|
|||||||
export const MentionUser = memo(function MentionUser({
|
export const MentionUser = memo(function MentionUser({
|
||||||
pubkey,
|
pubkey,
|
||||||
}: { pubkey: string }) {
|
}: { pubkey: string }) {
|
||||||
const { isLoading, isError, profile } = useProfile(pubkey);
|
const { isLoading, profile } = useProfile(pubkey);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
@@ -14,8 +14,6 @@ export const MentionUser = memo(function MentionUser({
|
|||||||
className="break-words text-start text-blue-500 hover:text-blue-600"
|
className="break-words text-start text-blue-500 hover:text-blue-600"
|
||||||
>
|
>
|
||||||
{isLoading
|
{isLoading
|
||||||
? "@anon"
|
|
||||||
: isError
|
|
||||||
? displayNpub(pubkey, 16)
|
? displayNpub(pubkey, 16)
|
||||||
: `@${profile?.name || profile?.display_name || displayNpub(pubkey, 16)}`}
|
: `@${profile?.name || profile?.display_name || displayNpub(pubkey, 16)}`}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -2,26 +2,27 @@ import { useProfile } from "@/system";
|
|||||||
import type { Metadata } from "@/types";
|
import type { Metadata } from "@/types";
|
||||||
import { type ReactNode, createContext, useContext } from "react";
|
import { type ReactNode, createContext, useContext } from "react";
|
||||||
|
|
||||||
const UserContext = createContext<{
|
interface UserContext {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
profile: Metadata;
|
profile: Metadata | undefined;
|
||||||
isError: boolean;
|
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}>(null);
|
}
|
||||||
|
|
||||||
|
const UserContext = createContext<UserContext | null>(null);
|
||||||
|
|
||||||
export function UserProvider({
|
export function UserProvider({
|
||||||
pubkey,
|
pubkey,
|
||||||
children,
|
children,
|
||||||
embedProfile,
|
data,
|
||||||
}: {
|
}: {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
embedProfile?: string;
|
data?: string;
|
||||||
}) {
|
}) {
|
||||||
const { isLoading, isError, profile } = useProfile(pubkey, embedProfile);
|
const { isLoading, profile } = useProfile(pubkey, data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UserContext.Provider value={{ pubkey, profile, isError, isLoading }}>
|
<UserContext.Provider value={{ pubkey, isLoading, profile }}>
|
||||||
{children}
|
{children}
|
||||||
</UserContext.Provider>
|
</UserContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import type { RichEvent } from "@/commands.gen";
|
|
||||||
import { Spinner } from "@/components";
|
import { Spinner } from "@/components";
|
||||||
|
import type { Metadata, NostrEvent } from "@/types";
|
||||||
import type { QueryClient } from "@tanstack/react-query";
|
import type { QueryClient } from "@tanstack/react-query";
|
||||||
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
|
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||||
import type { OsType } from "@tauri-apps/plugin-os";
|
import type { OsType } from "@tauri-apps/plugin-os";
|
||||||
import { nip19 } from "nostr-tools";
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
interface RouterContext {
|
interface RouterContext {
|
||||||
@@ -22,16 +21,16 @@ function Screen() {
|
|||||||
const { queryClient } = Route.useRouteContext();
|
const { queryClient } = Route.useRouteContext();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unlisten = listen<RichEvent>("event", async (data) => {
|
const unlisten = getCurrentWindow().listen<string>(
|
||||||
const event = JSON.parse(data.payload.raw);
|
"metadata",
|
||||||
|
async (data) => {
|
||||||
|
const payload = data.payload;
|
||||||
|
const event: NostrEvent = JSON.parse(payload);
|
||||||
|
const metadata: Metadata = JSON.parse(event.content);
|
||||||
|
|
||||||
if (event.kind === 0) {
|
queryClient.setQueryData(["profile", event.pubkey], () => metadata);
|
||||||
const npub = nip19.npubEncode(event.pubkey);
|
},
|
||||||
await queryClient.invalidateQueries({
|
);
|
||||||
queryKey: ["profile", npub, event.pubkey],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unlisten.then((f) => f());
|
unlisten.then((f) => f());
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ function OpenLaunchpad() {
|
|||||||
const list: Promise<MenuItem>[] = [];
|
const list: Promise<MenuItem>[] = [];
|
||||||
|
|
||||||
for (const account of accounts) {
|
for (const account of accounts) {
|
||||||
const res = await commands.getProfile(account, true);
|
const res = await commands.getProfile(account);
|
||||||
let name = "unknown";
|
let name = "unknown";
|
||||||
|
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ function Screen() {
|
|||||||
const list: Promise<MenuItem>[] = [];
|
const list: Promise<MenuItem>[] = [];
|
||||||
|
|
||||||
for (const account of accounts) {
|
for (const account of accounts) {
|
||||||
const res = await commands.getProfile(account, true);
|
const res = await commands.getProfile(account);
|
||||||
let name = "unknown";
|
let name = "unknown";
|
||||||
|
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createFileRoute } from "@tanstack/react-router";
|
|||||||
|
|
||||||
export const Route = createFileRoute("/settings/$id/profile")({
|
export const Route = createFileRoute("/settings/$id/profile")({
|
||||||
beforeLoad: async ({ params }) => {
|
beforeLoad: async ({ params }) => {
|
||||||
const res = await commands.getProfile(params.id, true);
|
const res = await commands.getProfile(params.id);
|
||||||
|
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
const profile: Profile = JSON.parse(res.data);
|
const profile: Profile = JSON.parse(res.data);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ function Screen() {
|
|||||||
const list: Promise<MenuItem>[] = [];
|
const list: Promise<MenuItem>[] = [];
|
||||||
|
|
||||||
for (const account of accounts) {
|
for (const account of accounts) {
|
||||||
const res = await commands.getProfile(account, true);
|
const res = await commands.getProfile(account);
|
||||||
let name = "unknown";
|
let name = "unknown";
|
||||||
|
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
|
|||||||
@@ -1,43 +1,54 @@
|
|||||||
import { commands } from "@/commands.gen";
|
import { commands } from "@/commands.gen";
|
||||||
import type { Metadata } from "@/types";
|
import type { Metadata } from "@/types";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
export function useProfile(pubkey: string, embed?: string) {
|
export function useProfile(pubkey: string, data?: string) {
|
||||||
const {
|
const hex = useMemo(() => {
|
||||||
isLoading,
|
try {
|
||||||
isError,
|
const normalized = pubkey.replace("nostr:", "").replace(/[^\w\s]/gi, "");
|
||||||
data: profile,
|
const decoded = nip19.decode(normalized);
|
||||||
} = useQuery({
|
|
||||||
queryKey: ["metadata", "profile", pubkey],
|
switch (decoded.type) {
|
||||||
|
case "npub":
|
||||||
|
return decoded.data;
|
||||||
|
case "nprofile":
|
||||||
|
return decoded.data.pubkey;
|
||||||
|
case "naddr":
|
||||||
|
return decoded.data.pubkey;
|
||||||
|
default:
|
||||||
|
return pubkey;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return pubkey;
|
||||||
|
}
|
||||||
|
}, [pubkey]);
|
||||||
|
|
||||||
|
const { isLoading, data: profile } = useQuery({
|
||||||
|
queryKey: ["profile", hex],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (embed) {
|
if (data) {
|
||||||
const metadata: Metadata = JSON.parse(embed);
|
const metadata: Metadata = JSON.parse(data);
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
let normalizedId = pubkey.replace("nostr:", "").replace(/[^\w\s]/gi, "");
|
const query = await commands.getProfile(hex);
|
||||||
|
|
||||||
if (normalizedId.startsWith("nprofile")) {
|
|
||||||
const decoded = nip19.decode(normalizedId);
|
|
||||||
|
|
||||||
if (decoded.type === "nprofile") {
|
|
||||||
normalizedId = decoded.data.pubkey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = await commands.getProfile(normalizedId, false);
|
|
||||||
|
|
||||||
if (query.status === "ok") {
|
if (query.status === "ok") {
|
||||||
return JSON.parse(query.data) as Metadata;
|
const metadata: Metadata = JSON.parse(query.data);
|
||||||
|
return metadata;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(query.error);
|
await getCurrentWindow().emit("request_metadata", { id: hex });
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
refetchOnReconnect: false,
|
refetchOnReconnect: false,
|
||||||
|
enabled: !!hex,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { isLoading, isError, profile };
|
return { isLoading, profile };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user