Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8398ae80d3 | ||
|
|
fe60f75e96 | ||
|
|
e098743d43 |
+33
-33
@@ -12,37 +12,37 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@getalby/bitcoin-connect-react": "^3.6.2",
|
"@getalby/bitcoin-connect-react": "^3.6.2",
|
||||||
"@phosphor-icons/react": "^2.1.7",
|
"@phosphor-icons/react": "^2.1.7",
|
||||||
"@radix-ui/react-avatar": "^1.1.0",
|
"@radix-ui/react-avatar": "^1.1.1",
|
||||||
"@radix-ui/react-checkbox": "^1.1.1",
|
"@radix-ui/react-checkbox": "^1.1.2",
|
||||||
"@radix-ui/react-popover": "^1.1.1",
|
"@radix-ui/react-popover": "^1.1.2",
|
||||||
"@radix-ui/react-scroll-area": "^1.1.0",
|
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||||
"@radix-ui/react-switch": "^1.1.0",
|
"@radix-ui/react-switch": "^1.1.1",
|
||||||
"@radix-ui/react-tabs": "^1.1.0",
|
"@radix-ui/react-tabs": "^1.1.1",
|
||||||
"@radix-ui/react-tooltip": "^1.1.2",
|
"@radix-ui/react-tooltip": "^1.1.3",
|
||||||
"@tanstack/query-persist-client-core": "^5.56.2",
|
"@tanstack/query-persist-client-core": "^5.59.0",
|
||||||
"@tanstack/react-query": "^5.56.2",
|
"@tanstack/react-query": "^5.59.0",
|
||||||
"@tanstack/react-router": "^1.58.3",
|
"@tanstack/react-router": "^1.58.16",
|
||||||
"@tanstack/react-store": "^0.5.5",
|
"@tanstack/react-store": "^0.5.5",
|
||||||
"@tanstack/store": "^0.5.5",
|
"@tanstack/store": "^0.5.5",
|
||||||
"@tauri-apps/api": "2.0.0-rc.4",
|
"@tauri-apps/api": "^2.0.1",
|
||||||
"@tauri-apps/plugin-clipboard-manager": "2.0.0-rc.1",
|
"@tauri-apps/plugin-clipboard-manager": "^2.0.0",
|
||||||
"@tauri-apps/plugin-dialog": "2.0.0-rc.1",
|
"@tauri-apps/plugin-dialog": "^2.0.0",
|
||||||
"@tauri-apps/plugin-fs": "2.0.0-rc.2",
|
"@tauri-apps/plugin-fs": "^2.0.0",
|
||||||
"@tauri-apps/plugin-http": "2.0.0-rc.2",
|
"@tauri-apps/plugin-http": "^2.0.0",
|
||||||
"@tauri-apps/plugin-os": "2.0.0-rc.1",
|
"@tauri-apps/plugin-os": "^2.0.0",
|
||||||
"@tauri-apps/plugin-process": "2.0.0-rc.1",
|
"@tauri-apps/plugin-process": "^2.0.0",
|
||||||
"@tauri-apps/plugin-shell": "2.0.0-rc.1",
|
"@tauri-apps/plugin-shell": "^2.0.0",
|
||||||
"@tauri-apps/plugin-store": "2.0.0-rc.1",
|
"@tauri-apps/plugin-store": "^2.0.0",
|
||||||
"@tauri-apps/plugin-updater": "2.0.0-rc.1",
|
"@tauri-apps/plugin-updater": "^2.0.0",
|
||||||
"@tauri-apps/plugin-upload": "2.0.0-rc.1",
|
"@tauri-apps/plugin-upload": "^2.0.0",
|
||||||
"@tauri-apps/plugin-window-state": "2.0.0-rc.1",
|
"@tauri-apps/plugin-window-state": "^2.0.0",
|
||||||
"bitcoin-units": "^1.0.0",
|
"bitcoin-units": "^1.0.0",
|
||||||
"boring-avatars": "^1.11.2",
|
"boring-avatars": "^1.11.2",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"embla-carousel-react": "^8.3.0",
|
"embla-carousel-react": "^8.3.0",
|
||||||
"i18next": "^23.15.1",
|
"i18next": "^23.15.1",
|
||||||
"i18next-resources-to-backend": "^1.2.1",
|
"i18next-resources-to-backend": "^1.2.1",
|
||||||
"light-bolt11-decoder": "^3.1.1",
|
"light-bolt11-decoder": "^3.2.0",
|
||||||
"minidenticons": "^4.2.1",
|
"minidenticons": "^4.2.1",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"nostr-tools": "^2.7.2",
|
"nostr-tools": "^2.7.2",
|
||||||
@@ -54,30 +54,30 @@
|
|||||||
"react-string-replace": "^1.1.1",
|
"react-string-replace": "^1.1.1",
|
||||||
"rich-textarea": "^0.26.3",
|
"rich-textarea": "^0.26.3",
|
||||||
"use-debounce": "^10.0.3",
|
"use-debounce": "^10.0.3",
|
||||||
"virtua": "^0.33.7"
|
"virtua": "^0.34.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.2",
|
"@biomejs/biome": "^1.9.3",
|
||||||
"@evilmartians/harmony": "^1.2.0",
|
"@evilmartians/harmony": "^1.2.0",
|
||||||
"@tailwindcss/forms": "^0.5.9",
|
"@tailwindcss/forms": "^0.5.9",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
"@tanstack/router-devtools": "^1.58.3",
|
"@tanstack/router-devtools": "^1.58.16",
|
||||||
"@tanstack/router-plugin": "^1.58.4",
|
"@tanstack/router-plugin": "^1.58.12",
|
||||||
"@tauri-apps/cli": "2.0.0-rc.8",
|
"@tauri-apps/cli": "^2.0.0",
|
||||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||||
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
||||||
"@vitejs/plugin-react": "^4.3.1",
|
"@vitejs/plugin-react": "^4.3.2",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"babel-plugin-react-compiler": "0.0.0-experimental-696af53-20240625",
|
"babel-plugin-react-compiler": "0.0.0-experimental-b4db8c3-20241001",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"tailwind-gradient-mask-image": "^1.2.0",
|
"tailwind-gradient-mask-image": "^1.2.0",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"tailwind-scrollbar": "^3.1.0",
|
"tailwind-scrollbar": "^3.1.0",
|
||||||
"tailwindcss": "^3.4.12",
|
"tailwindcss": "^3.4.13",
|
||||||
"tailwindcss-content-visibility": "^0.2.0",
|
"tailwindcss-content-visibility": "^1.0.0",
|
||||||
"typescript": "^5.6.2",
|
"typescript": "^5.6.2",
|
||||||
"vite": "^5.4.6",
|
"vite": "^5.4.8",
|
||||||
"vite-tsconfig-paths": "^5.0.1"
|
"vite-tsconfig-paths": "^5.0.1"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|||||||
Generated
+577
-555
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 191 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="32" height="36" viewBox="0 0 32 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M28.2711 21.2958C27.1084 21.2958 26.0749 21.8372 25.4037 22.6797L21.8702 20.6405C22.2577 19.8106 22.4755 18.8846 22.4755 17.908C22.4755 16.9314 22.2577 16.0053 21.8702 15.1755L25.3404 13.1742C26.0091 14.0648 27.0704 14.6442 28.2711 14.6442C30.2949 14.6442 31.9363 13.0047 31.9363 10.9831C31.9363 8.96158 30.2949 7.32208 28.2711 7.32208C26.2472 7.32208 24.6058 8.96158 24.6058 10.9831C24.6058 11.4006 24.6793 11.8003 24.8084 12.1748L21.3028 14.1963C20.2338 12.6732 18.5241 11.6333 16.5635 11.4638V7.274C18.3189 7.00076 19.6639 5.49029 19.6639 3.66104C19.6639 1.6395 18.0225 0 15.9987 0C13.9748 0 12.3334 1.6395 12.3334 3.66104C12.3334 5.49029 13.6784 7.00329 15.4338 7.274V11.4638C13.4733 11.6333 11.7635 12.6732 10.6946 14.1963L7.1889 12.1748C7.31808 11.8003 7.39154 11.4006 7.39154 10.9831C7.39154 8.96158 5.75015 7.32208 3.72629 7.32208C1.70242 7.32208 0.0610352 8.96158 0.0610352 10.9831C0.0610352 13.0047 1.70242 14.6442 3.72629 14.6442C4.92693 14.6442 5.98825 14.0648 6.65697 13.1742L10.1272 15.1755C9.73963 16.0053 9.52179 16.9314 9.52179 17.908C9.52179 18.8846 9.73963 19.8106 10.1272 20.643L6.59364 22.6822C5.9224 21.8397 4.88893 21.2983 3.72629 21.2983C1.70242 21.2983 0.0610352 22.9378 0.0610352 24.9593C0.0610352 26.9809 1.70242 28.6204 3.72629 28.6204C5.75015 28.6204 7.39154 26.9809 7.39154 24.9593C7.39154 24.5039 7.30542 24.0687 7.1509 23.6639L10.6946 21.6196C11.3329 22.5279 12.1992 23.2667 13.2098 23.7499V32.3497C13.2098 32.9721 13.5569 33.8551 13.9799 34.3131L15.2286 35.6565C15.6516 36.1145 16.3457 36.1145 16.7712 35.6565L18.02 34.3131C18.443 33.8551 18.79 32.9721 18.79 32.3497V23.7499C19.8007 23.2667 20.667 22.5304 21.3053 21.6196L24.849 23.6639C24.697 24.0662 24.6083 24.5014 24.6083 24.9593C24.6083 26.9809 26.2497 28.6204 28.2736 28.6204C30.2975 28.6204 31.9388 26.9809 31.9388 24.9593C31.9388 22.9378 30.2975 21.2983 28.2736 21.2983L28.2711 21.2958Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
Generated
+509
-127
File diff suppressed because it is too large
Load Diff
+15
-15
@@ -8,29 +8,29 @@ edition = "2021"
|
|||||||
rust-version = "1.70"
|
rust-version = "1.70"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.0.0-rc", features = [] }
|
tauri-build = { version = "2.0.0", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2.0.0-rc", features = [
|
tauri = { version = "2.0.0", features = [
|
||||||
"unstable",
|
"unstable",
|
||||||
"tray-icon",
|
"tray-icon",
|
||||||
"macos-private-api",
|
"macos-private-api",
|
||||||
"protocol-asset",
|
"protocol-asset",
|
||||||
] }
|
] }
|
||||||
tauri-plugin-window-state = "2.0.0-rc"
|
tauri-plugin-window-state = "2.0.0"
|
||||||
tauri-plugin-clipboard-manager = "2.0.0-rc"
|
tauri-plugin-clipboard-manager = "2.0.0"
|
||||||
tauri-plugin-dialog = "2.0.0-rc"
|
tauri-plugin-dialog = "2.0.0"
|
||||||
tauri-plugin-fs = "2.0.0-rc"
|
tauri-plugin-fs = "2.0.0"
|
||||||
tauri-plugin-http = "2.0.0-rc"
|
tauri-plugin-http = "2.0.0"
|
||||||
tauri-plugin-notification = "2.0.0-rc"
|
tauri-plugin-notification = "2.0.0"
|
||||||
tauri-plugin-os = "2.0.0-rc"
|
tauri-plugin-os = "2.0.0"
|
||||||
tauri-plugin-process = "2.0.0-rc"
|
tauri-plugin-process = "2.0.0"
|
||||||
tauri-plugin-shell = "2.0.0-rc"
|
tauri-plugin-shell = "2.0.0"
|
||||||
tauri-plugin-updater = "2.0.0-rc"
|
tauri-plugin-updater = "2.0.0"
|
||||||
tauri-plugin-upload = "2.0.0-rc"
|
tauri-plugin-upload = "2.0.0"
|
||||||
tauri-plugin-store = "2.0.0-rc"
|
tauri-plugin-store = "2.0.0"
|
||||||
tauri-plugin-decorum = { git = "https://github.com/clearlysid/tauri-plugin-decorum.git" }
|
tauri-plugin-decorum = { git = "https://github.com/clearlysid/tauri-plugin-decorum.git" }
|
||||||
tauri-plugin-prevent-default = "0.4"
|
tauri-plugin-prevent-default = "0.6"
|
||||||
tauri-specta = { version = "2.0.0-rc.15", features = ["derive", "typescript"] }
|
tauri-specta = { version = "2.0.0-rc.15", features = ["derive", "typescript"] }
|
||||||
|
|
||||||
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb"] }
|
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb"] }
|
||||||
|
|||||||
@@ -60,9 +60,7 @@
|
|||||||
"core:menu:allow-new",
|
"core:menu:allow-new",
|
||||||
"core:menu:allow-popup",
|
"core:menu:allow-popup",
|
||||||
"shell:allow-open",
|
"shell:allow-open",
|
||||||
"store:allow-get",
|
"store:default",
|
||||||
"store:allow-set",
|
|
||||||
"store:allow-delete",
|
|
||||||
"prevent-default:default",
|
"prevent-default:default",
|
||||||
{
|
{
|
||||||
"identifier": "http:default",
|
"identifier": "http:default",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
{"column":{"identifier":"column","description":"Capability for the column","local":true,"windows":["column-*"],"permissions":["core:resources:default","core:tray:default","os:allow-locale","os:allow-os-type","clipboard-manager:allow-write-text","dialog:allow-open","dialog:allow-ask","dialog:allow-message","fs:allow-read-file","core:menu:default","core:menu:allow-new","core:menu:allow-popup","http:default","shell:allow-open","store:allow-get","store:allow-set","store:allow-delete",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["linux","macOS","windows"]},"window":{"identifier":"window","description":"Capability for the desktop","local":true,"windows":["main","panel","settings","search-*","zap-*","event-*","user-*","editor-*"],"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:allow-get","store:allow-set","store:allow-delete","prevent-default: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"]}}
|
{"column":{"identifier":"column","description":"Capability for the column","local":true,"windows":["column-*"],"permissions":["core:resources:default","core:tray:default","os:allow-locale","os:allow-os-type","clipboard-manager:allow-write-text","dialog:allow-open","dialog:allow-ask","dialog:allow-message","fs:allow-read-file","core:menu:default","core:menu:allow-new","core:menu:allow-popup","http:default","shell:allow-open","store:allow-get","store:allow-set","store:allow-delete",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["linux","macOS","windows"]},"window":{"identifier":"window","description":"Capability for the desktop","local":true,"windows":["main","panel","settings","search-*","zap-*","event-*","user-*","editor-*"],"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",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["macOS","windows"]}}
|
||||||
@@ -1821,72 +1821,122 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"allow": {
|
"allow": {
|
||||||
"items": {
|
"items": {
|
||||||
"title": "Entry",
|
"title": "ShellScopeEntry",
|
||||||
"description": "A command allowed to be executed by the webview API.",
|
"description": "Shell scope entry.",
|
||||||
"type": "object",
|
"anyOf": [
|
||||||
"required": [
|
{
|
||||||
"args",
|
"type": "object",
|
||||||
"cmd",
|
"required": [
|
||||||
"name",
|
"cmd",
|
||||||
"sidecar"
|
"name"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"args": {
|
"args": {
|
||||||
"description": "The allowed arguments for the command execution.",
|
"description": "The allowed arguments for the command execution.",
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/ShellAllowedArgs"
|
"$ref": "#/definitions/ShellScopeEntryAllowedArgs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cmd": {
|
||||||
|
"description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"cmd": {
|
{
|
||||||
"description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
|
"type": "object",
|
||||||
"type": "string"
|
"required": [
|
||||||
},
|
"name",
|
||||||
"name": {
|
"sidecar"
|
||||||
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
],
|
||||||
"type": "string"
|
"properties": {
|
||||||
},
|
"args": {
|
||||||
"sidecar": {
|
"description": "The allowed arguments for the command execution.",
|
||||||
"description": "If this command is a sidecar command.",
|
"allOf": [
|
||||||
"type": "boolean"
|
{
|
||||||
|
"$ref": "#/definitions/ShellScopeEntryAllowedArgs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sidecar": {
|
||||||
|
"description": "If this command is a sidecar command.",
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"deny": {
|
"deny": {
|
||||||
"items": {
|
"items": {
|
||||||
"title": "Entry",
|
"title": "ShellScopeEntry",
|
||||||
"description": "A command allowed to be executed by the webview API.",
|
"description": "Shell scope entry.",
|
||||||
"type": "object",
|
"anyOf": [
|
||||||
"required": [
|
{
|
||||||
"args",
|
"type": "object",
|
||||||
"cmd",
|
"required": [
|
||||||
"name",
|
"cmd",
|
||||||
"sidecar"
|
"name"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"args": {
|
"args": {
|
||||||
"description": "The allowed arguments for the command execution.",
|
"description": "The allowed arguments for the command execution.",
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/ShellAllowedArgs"
|
"$ref": "#/definitions/ShellScopeEntryAllowedArgs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cmd": {
|
||||||
|
"description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"cmd": {
|
{
|
||||||
"description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
|
"type": "object",
|
||||||
"type": "string"
|
"required": [
|
||||||
},
|
"name",
|
||||||
"name": {
|
"sidecar"
|
||||||
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
],
|
||||||
"type": "string"
|
"properties": {
|
||||||
},
|
"args": {
|
||||||
"sidecar": {
|
"description": "The allowed arguments for the command execution.",
|
||||||
"description": "If this command is a sidecar command.",
|
"allOf": [
|
||||||
"type": "boolean"
|
{
|
||||||
|
"$ref": "#/definitions/ShellScopeEntryAllowedArgs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sidecar": {
|
||||||
|
"description": "If this command is a sidecar command.",
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5394,6 +5444,11 @@
|
|||||||
"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",
|
||||||
@@ -5454,6 +5509,11 @@
|
|||||||
"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",
|
||||||
@@ -5710,7 +5770,7 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ShellAllowedArg": {
|
"ShellScopeEntryAllowedArg": {
|
||||||
"description": "A command argument allowed to be executed by the webview API.",
|
"description": "A command argument allowed to be executed by the webview API.",
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
@@ -5738,18 +5798,18 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ShellAllowedArgs": {
|
"ShellScopeEntryAllowedArgs": {
|
||||||
"description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.",
|
"description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.",
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"description": "Use a simple boolean to allow all or disable all arguments to this command configuration.",
|
"description": "Use a simple boolean to allow all or disable all arguments to this command configuration.",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "A specific set of [`ShellAllowedArg`] that are valid to call for the command configuration.",
|
"description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/ShellAllowedArg"
|
"$ref": "#/definitions/ShellScopeEntryAllowedArg"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1821,72 +1821,122 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"allow": {
|
"allow": {
|
||||||
"items": {
|
"items": {
|
||||||
"title": "Entry",
|
"title": "ShellScopeEntry",
|
||||||
"description": "A command allowed to be executed by the webview API.",
|
"description": "Shell scope entry.",
|
||||||
"type": "object",
|
"anyOf": [
|
||||||
"required": [
|
{
|
||||||
"args",
|
"type": "object",
|
||||||
"cmd",
|
"required": [
|
||||||
"name",
|
"cmd",
|
||||||
"sidecar"
|
"name"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"args": {
|
"args": {
|
||||||
"description": "The allowed arguments for the command execution.",
|
"description": "The allowed arguments for the command execution.",
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/ShellAllowedArgs"
|
"$ref": "#/definitions/ShellScopeEntryAllowedArgs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cmd": {
|
||||||
|
"description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"cmd": {
|
{
|
||||||
"description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
|
"type": "object",
|
||||||
"type": "string"
|
"required": [
|
||||||
},
|
"name",
|
||||||
"name": {
|
"sidecar"
|
||||||
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
],
|
||||||
"type": "string"
|
"properties": {
|
||||||
},
|
"args": {
|
||||||
"sidecar": {
|
"description": "The allowed arguments for the command execution.",
|
||||||
"description": "If this command is a sidecar command.",
|
"allOf": [
|
||||||
"type": "boolean"
|
{
|
||||||
|
"$ref": "#/definitions/ShellScopeEntryAllowedArgs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sidecar": {
|
||||||
|
"description": "If this command is a sidecar command.",
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"deny": {
|
"deny": {
|
||||||
"items": {
|
"items": {
|
||||||
"title": "Entry",
|
"title": "ShellScopeEntry",
|
||||||
"description": "A command allowed to be executed by the webview API.",
|
"description": "Shell scope entry.",
|
||||||
"type": "object",
|
"anyOf": [
|
||||||
"required": [
|
{
|
||||||
"args",
|
"type": "object",
|
||||||
"cmd",
|
"required": [
|
||||||
"name",
|
"cmd",
|
||||||
"sidecar"
|
"name"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"args": {
|
"args": {
|
||||||
"description": "The allowed arguments for the command execution.",
|
"description": "The allowed arguments for the command execution.",
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/ShellAllowedArgs"
|
"$ref": "#/definitions/ShellScopeEntryAllowedArgs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cmd": {
|
||||||
|
"description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"cmd": {
|
{
|
||||||
"description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
|
"type": "object",
|
||||||
"type": "string"
|
"required": [
|
||||||
},
|
"name",
|
||||||
"name": {
|
"sidecar"
|
||||||
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
],
|
||||||
"type": "string"
|
"properties": {
|
||||||
},
|
"args": {
|
||||||
"sidecar": {
|
"description": "The allowed arguments for the command execution.",
|
||||||
"description": "If this command is a sidecar command.",
|
"allOf": [
|
||||||
"type": "boolean"
|
{
|
||||||
|
"$ref": "#/definitions/ShellScopeEntryAllowedArgs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sidecar": {
|
||||||
|
"description": "If this command is a sidecar command.",
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5394,6 +5444,11 @@
|
|||||||
"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",
|
||||||
@@ -5454,6 +5509,11 @@
|
|||||||
"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",
|
||||||
@@ -5710,7 +5770,7 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ShellAllowedArg": {
|
"ShellScopeEntryAllowedArg": {
|
||||||
"description": "A command argument allowed to be executed by the webview API.",
|
"description": "A command argument allowed to be executed by the webview API.",
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
@@ -5738,18 +5798,18 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ShellAllowedArgs": {
|
"ShellScopeEntryAllowedArgs": {
|
||||||
"description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.",
|
"description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.",
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"description": "Use a simple boolean to allow all or disable all arguments to this command configuration.",
|
"description": "Use a simple boolean to allow all or disable all arguments to this command configuration.",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "A specific set of [`ShellAllowedArg`] that are valid to call for the command configuration.",
|
"description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/ShellAllowedArg"
|
"$ref": "#/definitions/ShellScopeEntryAllowedArg"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use tauri::{Emitter, Manager, State};
|
use tauri::{Emitter, Manager, State};
|
||||||
|
|
||||||
use crate::{
|
use crate::{common::init_nip65, Nostr, NOTIFICATION_SUB_ID};
|
||||||
common::{get_user_settings, init_nip65},
|
|
||||||
Nostr, NEWSFEED_NEG_LIMIT, NOTIFICATION_NEG_LIMIT, NOTIFICATION_SUB_ID,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
|
||||||
struct Account {
|
struct Account {
|
||||||
@@ -271,104 +268,138 @@ pub async fn login(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Connect to user's relay (NIP-65)
|
// NIP-65: Connect to user's relay list
|
||||||
init_nip65(client, &public_key).await;
|
init_nip65(client, &public_key).await;
|
||||||
|
|
||||||
|
// Run seperate thread for syncing data
|
||||||
|
let pk = public_key.clone();
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
let config_dir = handle
|
let config_dir = handle.path().app_config_dir().unwrap();
|
||||||
.path()
|
|
||||||
.app_config_dir()
|
|
||||||
.expect("Error: app config directory not found.");
|
|
||||||
|
|
||||||
let state = handle.state::<Nostr>();
|
let state = handle.state::<Nostr>();
|
||||||
let client = &state.client;
|
let client = &state.client;
|
||||||
|
|
||||||
let signer = client.signer().await.unwrap();
|
// Convert current user to PublicKey
|
||||||
let public_key = signer.public_key().await.unwrap();
|
let author = PublicKey::from_str(&pk).unwrap();
|
||||||
|
|
||||||
let notification_id = SubscriptionId::new(NOTIFICATION_SUB_ID);
|
// Fetching user's metadata
|
||||||
let notification = Filter::new().pubkey(public_key).kinds(vec![
|
if let Ok(report) = client
|
||||||
Kind::TextNote,
|
|
||||||
Kind::Repost,
|
|
||||||
Kind::Reaction,
|
|
||||||
Kind::ZapReceipt,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Sync notification with negentropy
|
|
||||||
let _ = client
|
|
||||||
.reconcile(
|
.reconcile(
|
||||||
notification.clone().limit(NOTIFICATION_NEG_LIMIT),
|
Filter::new()
|
||||||
|
.author(author)
|
||||||
|
.kinds(vec![
|
||||||
|
Kind::Metadata,
|
||||||
|
Kind::ContactList,
|
||||||
|
Kind::MuteList,
|
||||||
|
Kind::Bookmarks,
|
||||||
|
Kind::Interests,
|
||||||
|
Kind::PinList,
|
||||||
|
])
|
||||||
|
.limit(1000),
|
||||||
NegentropyOptions::default(),
|
NegentropyOptions::default(),
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
{
|
||||||
|
println!("Received: {}", report.received.len())
|
||||||
|
}
|
||||||
|
|
||||||
// Subscribing for new notification...
|
// Fetching user's events
|
||||||
if let Err(e) = client
|
if let Ok(report) = client
|
||||||
|
.reconcile(
|
||||||
|
Filter::new()
|
||||||
|
.author(author)
|
||||||
|
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||||
|
.limit(200),
|
||||||
|
NegentropyOptions::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
println!("Received: {}", report.received.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetching user's notification
|
||||||
|
if let Ok(report) = client
|
||||||
|
.reconcile(
|
||||||
|
Filter::new()
|
||||||
|
.pubkey(author)
|
||||||
|
.kinds(vec![
|
||||||
|
Kind::TextNote,
|
||||||
|
Kind::Repost,
|
||||||
|
Kind::Reaction,
|
||||||
|
Kind::ZapReceipt,
|
||||||
|
])
|
||||||
|
.limit(200),
|
||||||
|
NegentropyOptions::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
println!("Received: {}", report.received.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe for new notification
|
||||||
|
if let Ok(e) = client
|
||||||
.subscribe_with_id(
|
.subscribe_with_id(
|
||||||
notification_id,
|
SubscriptionId::new(NOTIFICATION_SUB_ID),
|
||||||
vec![notification.since(Timestamp::now())],
|
vec![Filter::new()
|
||||||
|
.pubkey(author)
|
||||||
|
.kinds(vec![
|
||||||
|
Kind::TextNote,
|
||||||
|
Kind::Repost,
|
||||||
|
Kind::Reaction,
|
||||||
|
Kind::ZapReceipt,
|
||||||
|
])
|
||||||
|
.since(Timestamp::now())],
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
println!("Error: {}", e)
|
println!("Subscribed: {}", e.success.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user's settings
|
// Get user's contact list
|
||||||
if let Ok(settings) = get_user_settings(client).await {
|
let contact_list = {
|
||||||
state.settings.lock().await.clone_from(&settings);
|
let contacts = client.get_contact_list(None).await.unwrap();
|
||||||
|
// Update app's state
|
||||||
|
state.contact_list.lock().await.clone_from(&contacts);
|
||||||
|
// Return
|
||||||
|
contacts
|
||||||
};
|
};
|
||||||
|
|
||||||
let contact_list = client
|
// Get events from contact list
|
||||||
.get_contact_list(Some(Duration::from_secs(5)))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
state.contact_list.lock().await.clone_from(&contact_list);
|
|
||||||
|
|
||||||
// Get user's contact list
|
|
||||||
if !contact_list.is_empty() {
|
if !contact_list.is_empty() {
|
||||||
let authors: Vec<PublicKey> = contact_list.iter().map(|f| f.public_key).collect();
|
let authors: Vec<PublicKey> = contact_list.iter().map(|f| f.public_key).collect();
|
||||||
|
|
||||||
let metadata = Filter::new()
|
// Fetching contact's metadata
|
||||||
.authors(authors.clone())
|
|
||||||
.kind(Kind::Metadata)
|
|
||||||
.limit(authors.len());
|
|
||||||
|
|
||||||
if let Ok(report) = client
|
if let Ok(report) = client
|
||||||
.reconcile(metadata, NegentropyOptions::default())
|
.reconcile(
|
||||||
|
Filter::new()
|
||||||
|
.authors(authors.clone())
|
||||||
|
.kinds(vec![Kind::Metadata, Kind::ContactList, Kind::MuteList])
|
||||||
|
.limit(3000),
|
||||||
|
NegentropyOptions::default(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
println!("received [metadata]: {}", report.received.len())
|
println!("Received: {}", report.received.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
let newsfeed = Filter::new()
|
// Fetching contact's events
|
||||||
.authors(authors.clone())
|
if let Ok(report) = client
|
||||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
.reconcile(
|
||||||
.limit(NEWSFEED_NEG_LIMIT);
|
Filter::new()
|
||||||
|
.authors(authors.clone())
|
||||||
if client
|
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||||
.reconcile(newsfeed, NegentropyOptions::default())
|
.limit(1000),
|
||||||
|
NegentropyOptions::default(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.is_ok()
|
|
||||||
{
|
{
|
||||||
// Save state
|
println!("Received: {}", report.received.len());
|
||||||
let _ = File::create(config_dir.join(public_key.to_bech32().unwrap()));
|
|
||||||
|
// Save the process status
|
||||||
|
let _ = File::create(config_dir.join(author.to_bech32().unwrap()));
|
||||||
// Update frontend
|
// Update frontend
|
||||||
handle.emit("synchronized", ()).unwrap();
|
handle.emit("synchronized", ()).unwrap();
|
||||||
}
|
};
|
||||||
|
|
||||||
let contacts = Filter::new()
|
|
||||||
.authors(authors.clone())
|
|
||||||
.kind(Kind::ContactList)
|
|
||||||
.limit(authors.len() * 1000);
|
|
||||||
|
|
||||||
if let Ok(report) = client
|
|
||||||
.reconcile(contacts, NegentropyOptions::default())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
println!("received [contact list]: {}", report.received.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
for author in authors.into_iter() {
|
for author in authors.into_iter() {
|
||||||
let filter = Filter::new()
|
let filter = Filter::new()
|
||||||
@@ -400,7 +431,7 @@ pub async fn login(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handle.emit("synchronized", ()).unwrap();
|
handle.emit("synchronized", ()).unwrap();
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(public_key)
|
Ok(public_key)
|
||||||
|
|||||||
@@ -478,13 +478,15 @@ fn main() {
|
|||||||
{
|
{
|
||||||
let authors: Vec<PublicKey> =
|
let authors: Vec<PublicKey> =
|
||||||
contact_list.iter().map(|f| f.public_key).collect();
|
contact_list.iter().map(|f| f.public_key).collect();
|
||||||
let newsfeed = Filter::new()
|
|
||||||
.authors(authors)
|
|
||||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
|
||||||
.limit(NEWSFEED_NEG_LIMIT);
|
|
||||||
|
|
||||||
if client
|
if client
|
||||||
.reconcile(newsfeed, NegentropyOptions::default())
|
.reconcile(
|
||||||
|
Filter::new()
|
||||||
|
.authors(authors)
|
||||||
|
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||||
|
.limit(NEWSFEED_NEG_LIMIT),
|
||||||
|
NegentropyOptions::default(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||||
"productName": "Lume",
|
"productName": "Lume",
|
||||||
"version": "4.2.0",
|
"version": "4.2.1",
|
||||||
"identifier": "nu.lume.Lume",
|
"identifier": "nu.lume.Lume",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
|||||||
+10
-7
@@ -1,24 +1,27 @@
|
|||||||
|
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 { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { routeTree } from "./routes.gen"; // auto generated file
|
|
||||||
import type { LumeEvent } from "./system";
|
|
||||||
import "./app.css";
|
|
||||||
import { experimental_createPersister } from "@tanstack/query-persist-client-core";
|
|
||||||
import { Store } from "@tauri-apps/plugin-store";
|
|
||||||
import { newQueryStorage } from "./commons";
|
import { newQueryStorage } from "./commons";
|
||||||
|
import type { LumeEvent } from "./system";
|
||||||
|
|
||||||
|
import "./app.css"; // global styles
|
||||||
|
|
||||||
|
import { createStore } from "@tauri-apps/plugin-store";
|
||||||
|
import { routeTree } from "./routes.gen"; // auto generated file
|
||||||
|
|
||||||
const platform = type();
|
const platform = type();
|
||||||
const store = new Store(".cache");
|
// @ts-ignore, https://github.com/tauri-apps/plugins-workspace/pull/1860
|
||||||
|
const store = await createStore(".cache", { autoSave: 100 });
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
gcTime: 1000 * 30,
|
gcTime: 1000 * 30,
|
||||||
persister: experimental_createPersister({
|
persister: experimental_createPersister({
|
||||||
storage: newQueryStorage(store),
|
storage: newQueryStorage(store),
|
||||||
maxAge: 1000 * 60 * 60 * 12, // 12 hours
|
maxAge: 1000 * 60 * 60 * 12,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import type { SVGProps } from "react";
|
||||||
|
|
||||||
|
export const ReplyIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||||
|
<svg
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M21.75 12c0-5.156-3.792-8.25-9.75-8.25S2.25 6.844 2.25 12c0 1.337.92 3.605 1.064 3.952l.038.091c.099.27.505 1.71-1.102 3.84 2.167 1.031 4.468-.664 4.468-.664 1.592.84 3.486 1.031 5.282 1.031 5.958 0 9.75-3.094 9.75-8.25z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import type { SVGProps } from "react";
|
||||||
|
|
||||||
|
export const RepostIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||||
|
<svg
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10.75 1.5l3.5 3.25L10.75 8m2.5 8l-3.5 3.25 3.5 3.25m-2.5-3.25H14a7.25 7.25 0 004.755-12.723M13.25 4.75H10a7.25 7.25 0 00-4.754 12.724"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import type { SVGProps } from "react";
|
||||||
|
|
||||||
|
export const ZapIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||||
|
<svg
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.861 11.252l4.6-8a1 1 0 01.867-.502h7.053a1 1 0 01.895 1.447l-1.303 2.606a1 1 0 00.895 1.447h2.467c.891 0 1.337 1.077.707 1.707L9.103 20.897c-.701.7-1.885.063-1.687-.908l1.235-6.039a1 1 0 00-.98-1.2H4.728a1 1 0 01-.867-1.498z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
@@ -13,3 +13,8 @@ export * from "./reply";
|
|||||||
// Global components
|
// Global components
|
||||||
export * from "./note";
|
export * from "./note";
|
||||||
export * from "./user";
|
export * from "./user";
|
||||||
|
|
||||||
|
// Icons
|
||||||
|
export * from "./icons/reply";
|
||||||
|
export * from "./icons/repost";
|
||||||
|
export * from "./icons/zap";
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function NoteOpenThread() {
|
|||||||
</Tooltip.Trigger>
|
</Tooltip.Trigger>
|
||||||
<Tooltip.Portal>
|
<Tooltip.Portal>
|
||||||
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||||
Open
|
View thread
|
||||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||||
</Tooltip.Content>
|
</Tooltip.Content>
|
||||||
</Tooltip.Portal>
|
</Tooltip.Portal>
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { cn } from "@/commons";
|
import { cn } from "@/commons";
|
||||||
|
import { ReplyIcon } from "@/components";
|
||||||
import { LumeWindow } from "@/system";
|
import { LumeWindow } from "@/system";
|
||||||
import { ShareFat } from "@phosphor-icons/react";
|
|
||||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||||
import { useNoteContext } from "../provider";
|
import { useNoteContext } from "../provider";
|
||||||
|
|
||||||
export function NoteReply({ large = false }: { large?: boolean }) {
|
export function NoteReply({
|
||||||
|
label = false,
|
||||||
|
smol = false,
|
||||||
|
}: { label?: boolean; smol?: boolean }) {
|
||||||
const event = useNoteContext();
|
const event = useNoteContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -16,13 +19,13 @@ export function NoteReply({ large = false }: { large?: boolean }) {
|
|||||||
onClick={() => LumeWindow.openEditor(event.id)}
|
onClick={() => LumeWindow.openEditor(event.id)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
||||||
large
|
label
|
||||||
? "rounded-full h-7 gap-1.5 w-20 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
? "rounded-full h-7 gap-1.5 w-20 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
||||||
: "size-7",
|
: "size-7",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ShareFat className="shrink-0 size-4" />
|
<ReplyIcon className={cn("shrink-0", smol ? "size-4" : "size-5")} />
|
||||||
{large ? "Reply" : null}
|
{label ? "Reply" : null}
|
||||||
</button>
|
</button>
|
||||||
</Tooltip.Trigger>
|
</Tooltip.Trigger>
|
||||||
<Tooltip.Portal>
|
<Tooltip.Portal>
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { appSettings, cn } from "@/commons";
|
import { appSettings, cn } from "@/commons";
|
||||||
import { Spinner } from "@/components";
|
import { RepostIcon, Spinner } from "@/components";
|
||||||
import { LumeWindow } from "@/system";
|
import { LumeWindow } from "@/system";
|
||||||
import { Repeat } from "@phosphor-icons/react";
|
|
||||||
import { useStore } from "@tanstack/react-store";
|
import { useStore } from "@tanstack/react-store";
|
||||||
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
||||||
import { message } from "@tauri-apps/plugin-dialog";
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useNoteContext } from "../provider";
|
import { useNoteContext } from "../provider";
|
||||||
|
|
||||||
export function NoteRepost({ large = false }: { large?: boolean }) {
|
export function NoteRepost({
|
||||||
|
label = false,
|
||||||
|
smol = false,
|
||||||
|
}: { label?: boolean; smol?: boolean }) {
|
||||||
const visible = useStore(appSettings, (state) => state.display_repost_button);
|
const visible = useStore(appSettings, (state) => state.display_repost_button);
|
||||||
const event = useNoteContext();
|
const event = useNoteContext();
|
||||||
|
|
||||||
@@ -65,7 +67,7 @@ export function NoteRepost({ large = false }: { large?: boolean }) {
|
|||||||
onClick={(e) => showContextMenu(e)}
|
onClick={(e) => showContextMenu(e)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
||||||
large
|
label
|
||||||
? "rounded-full h-7 gap-1.5 w-24 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
? "rounded-full h-7 gap-1.5 w-24 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
||||||
: "size-7",
|
: "size-7",
|
||||||
)}
|
)}
|
||||||
@@ -73,9 +75,14 @@ export function NoteRepost({ large = false }: { large?: boolean }) {
|
|||||||
{loading ? (
|
{loading ? (
|
||||||
<Spinner className="size-4" />
|
<Spinner className="size-4" />
|
||||||
) : (
|
) : (
|
||||||
<Repeat className={cn("size-4", isRepost ? "text-blue-500" : "")} />
|
<RepostIcon
|
||||||
|
className={cn(
|
||||||
|
smol ? "size-4" : "size-5",
|
||||||
|
isRepost ? "text-blue-500" : "",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{large ? "Repost" : null}
|
{label ? "Repost" : null}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { appSettings, cn } from "@/commons";
|
import { appSettings, cn } from "@/commons";
|
||||||
|
import { ZapIcon } from "@/components";
|
||||||
import { LumeWindow } from "@/system";
|
import { LumeWindow } from "@/system";
|
||||||
import { Lightning } from "@phosphor-icons/react";
|
|
||||||
import { useSearch } from "@tanstack/react-router";
|
import { useSearch } from "@tanstack/react-router";
|
||||||
import { useStore } from "@tanstack/react-store";
|
import { useStore } from "@tanstack/react-store";
|
||||||
import { useNoteContext } from "../provider";
|
import { useNoteContext } from "../provider";
|
||||||
|
|
||||||
export function NoteZap({ large = false }: { large?: boolean }) {
|
export function NoteZap({
|
||||||
|
label = false,
|
||||||
|
smol = false,
|
||||||
|
}: { label?: boolean; smol?: boolean }) {
|
||||||
const search = useSearch({ strict: false });
|
const search = useSearch({ strict: false });
|
||||||
const visible = useStore(appSettings, (state) => state.display_zap_button);
|
const visible = useStore(appSettings, (state) => state.display_zap_button);
|
||||||
const event = useNoteContext();
|
const event = useNoteContext();
|
||||||
@@ -18,13 +21,13 @@ export function NoteZap({ large = false }: { large?: boolean }) {
|
|||||||
onClick={() => LumeWindow.openZap(event.id, search.account)}
|
onClick={() => LumeWindow.openZap(event.id, search.account)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
||||||
large
|
label
|
||||||
? "rounded-full h-7 gap-1.5 w-20 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
? "rounded-full h-7 gap-1.5 w-20 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
||||||
: "size-7",
|
: "size-7",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Lightning className="size-4" />
|
<ZapIcon className={smol ? "size-4" : "size-5"} />
|
||||||
{large ? "Zap" : null}
|
{label ? "Zap" : null}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,10 +99,10 @@ export const ReplyNote = memo(function ReplyNote({
|
|||||||
<span className="text-sm text-neutral-500">
|
<span className="text-sm text-neutral-500">
|
||||||
{replyTime(event.created_at)}
|
{replyTime(event.created_at)}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex items-center justify-end gap-3">
|
<div className="flex items-center justify-end gap-5">
|
||||||
<Note.Reply />
|
<Note.Reply smol />
|
||||||
<Note.Repost />
|
<Note.Repost smol />
|
||||||
<Note.Zap />
|
<Note.Zap smol />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{event.replies?.length ? (
|
{event.replies?.length ? (
|
||||||
@@ -180,10 +180,10 @@ function ChildReply({ event }: { event: LumeEvent }) {
|
|||||||
<span className="text-sm text-neutral-500">
|
<span className="text-sm text-neutral-500">
|
||||||
{replyTime(event.created_at)}
|
{replyTime(event.created_at)}
|
||||||
</span>
|
</span>
|
||||||
<div className="invisible group-hover:visible flex items-center justify-end gap-3">
|
<div className="invisible group-hover:visible flex items-center justify-end gap-5">
|
||||||
<Note.Reply />
|
<Note.Reply smol />
|
||||||
<Note.Repost />
|
<Note.Repost smol />
|
||||||
<Note.Zap />
|
<Note.Zap smol />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{event.replies?.length ? (
|
{event.replies?.length ? (
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const RepostNote = memo(function RepostNote({
|
|||||||
</div>
|
</div>
|
||||||
<Note.Content className="px-3" />
|
<Note.Content className="px-3" />
|
||||||
<div className="flex items-center justify-between px-3 mt-3 h-14">
|
<div className="flex items-center justify-between px-3 mt-3 h-14">
|
||||||
<div className="inline-flex items-center gap-3">
|
<div className="inline-flex items-center gap-6">
|
||||||
<Note.Open />
|
<Note.Open />
|
||||||
<Note.Reply />
|
<Note.Reply />
|
||||||
<Note.Repost />
|
<Note.Repost />
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const TextNote = memo(function TextNote({
|
|||||||
<Note.Menu />
|
<Note.Menu />
|
||||||
</div>
|
</div>
|
||||||
<Note.Content className="px-3" />
|
<Note.Content className="px-3" />
|
||||||
<div className="flex items-center gap-4 px-3 mt-3 h-14">
|
<div className="flex items-center gap-6 px-3 mt-3 h-14">
|
||||||
<Note.Open />
|
<Note.Open />
|
||||||
<Note.Reply />
|
<Note.Reply />
|
||||||
<Note.Repost />
|
<Note.Repost />
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ export function UserAvatar({ className }: { className?: string }) {
|
|||||||
|
|
||||||
const picture = useMemo(() => {
|
const picture = useMemo(() => {
|
||||||
if (service?.length && user.profile?.picture?.length) {
|
if (service?.length && user.profile?.picture?.length) {
|
||||||
|
if (user.profile?.picture.includes("_next/")) {
|
||||||
|
return user.profile?.picture;
|
||||||
|
}
|
||||||
return `${service}?url=${user.profile?.picture}&w=100&h=100&n=-1&default=${user.profile?.picture}`;
|
return `${service}?url=${user.profile?.picture}&w=100&h=100&n=-1&default=${user.profile?.picture}`;
|
||||||
} else {
|
} else {
|
||||||
return user.profile?.picture;
|
return user.profile?.picture;
|
||||||
|
|||||||
@@ -82,9 +82,9 @@ function RootEvent() {
|
|||||||
</div>
|
</div>
|
||||||
<Note.ContentLarge className="px-3" />
|
<Note.ContentLarge className="px-3" />
|
||||||
<div className="flex items-center gap-2 px-3 mt-6 h-12 rounded-b-xl bg-neutral-50 dark:bg-white/5">
|
<div className="flex items-center gap-2 px-3 mt-6 h-12 rounded-b-xl bg-neutral-50 dark:bg-white/5">
|
||||||
<Note.Reply large />
|
<Note.Reply label />
|
||||||
<Note.Repost large />
|
<Note.Repost label />
|
||||||
<Note.Zap large />
|
<Note.Zap label />
|
||||||
</div>
|
</div>
|
||||||
</Note.Root>
|
</Note.Root>
|
||||||
</Note.Provider>
|
</Note.Provider>
|
||||||
|
|||||||
+35
-32
@@ -2,7 +2,7 @@ import { commands } from "@/commands.gen";
|
|||||||
import { appSettings, displayNpub } from "@/commons";
|
import { appSettings, displayNpub } from "@/commons";
|
||||||
import { Frame, Spinner, User } from "@/components";
|
import { Frame, Spinner, User } from "@/components";
|
||||||
import { ArrowRight, DotsThree, GearSix, Plus } from "@phosphor-icons/react";
|
import { ArrowRight, DotsThree, GearSix, Plus } from "@phosphor-icons/react";
|
||||||
import { Link, createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
||||||
import { message } from "@tauri-apps/plugin-dialog";
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import {
|
import {
|
||||||
@@ -37,6 +37,32 @@ function Screen() {
|
|||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
|
const showContextMenu = useCallback(
|
||||||
|
async (e: React.MouseEvent, account: string) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const menuItems = await Promise.all([
|
||||||
|
MenuItem.new({
|
||||||
|
text: "Reset password",
|
||||||
|
enabled: !account.includes("_nostrconnect"),
|
||||||
|
// @ts-ignore, this is tanstack router bug
|
||||||
|
action: () => navigate({ to: "/reset", search: { account } }),
|
||||||
|
}),
|
||||||
|
MenuItem.new({
|
||||||
|
text: "Delete account",
|
||||||
|
action: async () => await deleteAccount(account),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const menu = await Menu.new({
|
||||||
|
items: menuItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
await menu.popup().catch((e) => console.error(e));
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const deleteAccount = async (account: string) => {
|
const deleteAccount = async (account: string) => {
|
||||||
const res = await commands.deleteAccount(account);
|
const res = await commands.deleteAccount(account);
|
||||||
|
|
||||||
@@ -69,12 +95,14 @@ function Screen() {
|
|||||||
if (status) {
|
if (status) {
|
||||||
navigate({
|
navigate({
|
||||||
to: "/$account/home",
|
to: "/$account/home",
|
||||||
|
// @ts-ignore, this is tanstack router bug
|
||||||
params: { account: res.data },
|
params: { account: res.data },
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
navigate({
|
navigate({
|
||||||
to: "/loading",
|
to: "/loading",
|
||||||
|
// @ts-ignore, this is tanstack router bug
|
||||||
search: { account: res.data },
|
search: { account: res.data },
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
@@ -86,31 +114,6 @@ function Screen() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const showContextMenu = useCallback(
|
|
||||||
async (e: React.MouseEvent, account: string) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
const menuItems = await Promise.all([
|
|
||||||
MenuItem.new({
|
|
||||||
text: "Reset password",
|
|
||||||
enabled: !account.includes("_nostrconnect"),
|
|
||||||
action: () => navigate({ to: "/reset", search: { account } }),
|
|
||||||
}),
|
|
||||||
MenuItem.new({
|
|
||||||
text: "Delete account",
|
|
||||||
action: async () => await deleteAccount(account),
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const menu = await Menu.new({
|
|
||||||
items: menuItems,
|
|
||||||
});
|
|
||||||
|
|
||||||
await menu.popup().catch((e) => console.error(e));
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (autoLogin) {
|
if (autoLogin) {
|
||||||
loginWith();
|
loginWith();
|
||||||
@@ -204,8 +207,8 @@ function Screen() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<Link
|
<a
|
||||||
to="/new"
|
href="/new"
|
||||||
className="flex items-center justify-between hover:bg-black/5 dark:hover:bg-white/5"
|
className="flex items-center justify-between hover:bg-black/5 dark:hover:bg-white/5"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2.5 p-3">
|
<div className="flex items-center gap-2.5 p-3">
|
||||||
@@ -216,17 +219,17 @@ function Screen() {
|
|||||||
New account
|
New account
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</a>
|
||||||
</Frame>
|
</Frame>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-2 right-2">
|
<div className="absolute bottom-2 right-2">
|
||||||
<Link
|
<a
|
||||||
to="/bootstrap-relays"
|
href="/bootstrap-relays"
|
||||||
className="h-8 w-max text-xs px-3 inline-flex items-center justify-center gap-1.5 bg-black/5 hover:bg-black/10 dark:bg-white/5 dark:hover:bg-white/10 rounded-full"
|
className="h-8 w-max text-xs px-3 inline-flex items-center justify-center gap-1.5 bg-black/5 hover:bg-black/10 dark:bg-white/5 dark:hover:bg-white/10 rounded-full"
|
||||||
>
|
>
|
||||||
<GearSix className="size-4" />
|
<GearSix className="size-4" />
|
||||||
Manage Relays
|
Manage Relays
|
||||||
</Link>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export const Route = createFileRoute("/")({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { accounts: accounts.filter((account) => !account.endsWith("Lume")) };
|
return {
|
||||||
|
accounts: accounts.filter((account) => !account.endsWith("Lume")),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Spinner } from "@/components";
|
import { Frame, Spinner } from "@/components";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
@@ -24,6 +24,7 @@ function Screen() {
|
|||||||
const unlisten = listen("synchronized", () => {
|
const unlisten = listen("synchronized", () => {
|
||||||
navigate({
|
navigate({
|
||||||
to: "/$account/home",
|
to: "/$account/home",
|
||||||
|
// @ts-ignore, this is tanstack router bug
|
||||||
params: { account: search.account },
|
params: { account: search.account },
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
@@ -36,12 +37,15 @@ function Screen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="size-full flex items-center justify-center">
|
<div className="size-full flex items-center justify-center">
|
||||||
<div className="flex flex-col gap-2 items-center justify-center text-center">
|
<Frame
|
||||||
|
className="p-6 h-36 flex flex-col gap-2 items-center justify-center text-center rounded-xl overflow-hidden"
|
||||||
|
shadow
|
||||||
|
>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
<p className="text-sm">
|
<p className="text-sm text-neutral-600 dark:text-neutral-40">
|
||||||
Fetching necessary data for the first time login...
|
Fetching necessary data for the first time login...
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</Frame>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+62
-27
@@ -1,6 +1,4 @@
|
|||||||
import { GoBack } from "@/components";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import { ArrowLeft } from "@phosphor-icons/react";
|
|
||||||
import { Link, createLazyFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/new")({
|
export const Route = createLazyFileRoute("/new")({
|
||||||
component: Screen,
|
component: Screen,
|
||||||
@@ -10,42 +8,79 @@ function Screen() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
className="relative size-full flex items-center justify-center"
|
className="bg-white/50 dark:bg-black/50 relative size-full flex items-center justify-center"
|
||||||
>
|
>
|
||||||
<div className="w-[320px] flex flex-col gap-8">
|
<div className="w-[350px] 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-lg font-semibold">
|
||||||
Welcome to Nostr.
|
How would you like to use Lume?
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<Link
|
<a
|
||||||
to="/auth/new"
|
href="/auth/connect"
|
||||||
className="w-full h-10 bg-blue-500 font-medium hover:bg-blue-600 text-white rounded-lg inline-flex items-center justify-center shadow"
|
className="w-full p-4 rounded-xl hover:shadow-lg hover:ring-0 hover:bg-white dark:hover:bg-neutral-900 ring-1 ring-black/5 dark:ring-white/5"
|
||||||
>
|
>
|
||||||
Create a new identity
|
<h3 className="mb-1.5 font-medium">Continue with Nostr Connect</h3>
|
||||||
</Link>
|
<div className="text-sm">
|
||||||
<div className="w-full h-px bg-black/5 dark:bg-white/5" />
|
<p className="text-neutral-500 dark:text-neutral-600">
|
||||||
|
Your account will be handled by a remote signer. Lume will not
|
||||||
|
store your account keys.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/auth/import"
|
||||||
|
className="w-full p-4 rounded-xl hover:shadow-lg hover:ring-0 hover:bg-white dark:hover:bg-neutral-900 ring-1 ring-black/5 dark:ring-white/5"
|
||||||
|
>
|
||||||
|
<h3 className="mb-1.5 font-medium">Continue with Secret Key</h3>
|
||||||
|
<div className="text-sm">
|
||||||
|
<p className="text-neutral-500 dark:text-neutral-600">
|
||||||
|
Lume will store your keys in secure storage. You can provide a
|
||||||
|
password to add extra security.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
<div className="flex-1 h-px bg-black/5 dark:bg-white/5" />
|
||||||
|
<div className="shrink-0 text-sm text-neutral-500 dark:text-neutral-400">
|
||||||
|
Do you not have a Nostr account yet?
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 h-px bg-black/5 dark:bg-white/5" />
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Link
|
<a
|
||||||
to="/auth/connect"
|
href="https://nsec.app"
|
||||||
className="w-full h-10 bg-white hover:bg-neutral-100 dark:hover:bg-neutral-100 dark:text-black rounded-lg inline-flex items-center justify-center"
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-sm bg-black/5 dark:bg-white/5 hover:bg-black/10 dark:hover:bg-white/10 rounded-lg flex items-center gap-1.5 h-9 px-1"
|
||||||
>
|
>
|
||||||
Login with Nostr Connect
|
<div className="size-7 rounded-md bg-black inline-flex items-center justify-center">
|
||||||
</Link>
|
<img src="/nsec_app.svg" alt="nsec.app" className="size-5" />
|
||||||
<Link
|
</div>
|
||||||
to="/auth/import"
|
Create one with nsec.app
|
||||||
className="w-full h-10 bg-white hover:bg-neutral-100 dark:hover:bg-neutral-100 dark:text-black rounded-lg inline-flex items-center justify-center"
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://nosta.me"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-sm bg-black/5 dark:bg-white/5 hover:bg-black/10 dark:hover:bg-white/10 rounded-lg flex items-center gap-1.5 h-9 px-1"
|
||||||
>
|
>
|
||||||
Login with Private Key
|
<div className="size-7 rounded-md bg-black overflow-hidden">
|
||||||
</Link>
|
<img
|
||||||
|
src="/nosta.jpg"
|
||||||
|
alt="nosta"
|
||||||
|
className="size-7 object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
Create one with nosta.me
|
||||||
|
</a>
|
||||||
|
<p className="text-xs text-neutral-400 dark:text-neutral-600">
|
||||||
|
Or you can create account from other Nostr clients.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<GoBack className="fixed top-11 left-2 flex items-center gap-1.5 text-sm font-medium">
|
|
||||||
<ArrowLeft className="size-5" />
|
|
||||||
Back
|
|
||||||
</GoBack>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
outDir: "./dist",
|
outDir: "./dist",
|
||||||
|
target: "esnext",
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user