mirror of
https://github.com/luminous-devs/lume.git
synced 2024-10-02 18:00:47 +00:00
wip: new import account
This commit is contained in:
parent
620e763380
commit
cd3b9ada5a
@ -20,8 +20,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@evilmartians/harmony": "^1.1.0",
|
"@evilmartians/harmony": "^1.1.0",
|
||||||
"@getalby/sdk": "^2.4.0",
|
"@getalby/sdk": "^2.4.0",
|
||||||
"@nostr-dev-kit/ndk": "^2.0.1",
|
"@nostr-dev-kit/ndk": "^2.0.2",
|
||||||
"@nostr-dev-kit/ndk-cache-dexie": "^2.0.1",
|
"@nostr-dev-kit/ndk-cache-dexie": "^2.0.2",
|
||||||
"@nostr-fetch/adapter-ndk": "^0.12.2",
|
"@nostr-fetch/adapter-ndk": "^0.12.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-avatar": "^1.0.4",
|
"@radix-ui/react-avatar": "^1.0.4",
|
||||||
@ -56,6 +56,7 @@
|
|||||||
"@tiptap/suggestion": "^2.1.12",
|
"@tiptap/suggestion": "^2.1.12",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"destr": "^2.0.1",
|
"destr": "^2.0.1",
|
||||||
|
"framer-motion": "^10.16.4",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"light-bolt11-decoder": "^3.0.0",
|
"light-bolt11-decoder": "^3.0.0",
|
||||||
"lru-cache": "^10.0.1",
|
"lru-cache": "^10.0.1",
|
||||||
@ -75,6 +76,7 @@
|
|||||||
"react-string-replace": "^1.1.1",
|
"react-string-replace": "^1.1.1",
|
||||||
"reactflow": "^11.9.3",
|
"reactflow": "^11.9.3",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
|
"sonner": "^1.0.3",
|
||||||
"tailwind-scrollbar": "^3.0.5",
|
"tailwind-scrollbar": "^3.0.5",
|
||||||
"tauri-controls": "^0.2.0",
|
"tauri-controls": "^0.2.0",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
|
@ -12,14 +12,14 @@ dependencies:
|
|||||||
specifier: ^2.4.0
|
specifier: ^2.4.0
|
||||||
version: 2.4.0
|
version: 2.4.0
|
||||||
'@nostr-dev-kit/ndk':
|
'@nostr-dev-kit/ndk':
|
||||||
specifier: ^2.0.1
|
specifier: ^2.0.2
|
||||||
version: 2.0.1(typescript@5.2.2)
|
version: 2.0.2(typescript@5.2.2)
|
||||||
'@nostr-dev-kit/ndk-cache-dexie':
|
'@nostr-dev-kit/ndk-cache-dexie':
|
||||||
specifier: ^2.0.1
|
specifier: ^2.0.2
|
||||||
version: 2.0.1(typescript@5.2.2)
|
version: 2.0.2(typescript@5.2.2)
|
||||||
'@nostr-fetch/adapter-ndk':
|
'@nostr-fetch/adapter-ndk':
|
||||||
specifier: ^0.12.2
|
specifier: ^0.12.2
|
||||||
version: 0.12.2(@nostr-dev-kit/ndk@2.0.1)(nostr-fetch@0.13.0)
|
version: 0.12.2(@nostr-dev-kit/ndk@2.0.2)(nostr-fetch@0.13.0)
|
||||||
'@radix-ui/react-alert-dialog':
|
'@radix-ui/react-alert-dialog':
|
||||||
specifier: ^1.0.5
|
specifier: ^1.0.5
|
||||||
version: 1.0.5(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
|
version: 1.0.5(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
|
||||||
@ -119,6 +119,9 @@ dependencies:
|
|||||||
destr:
|
destr:
|
||||||
specifier: ^2.0.1
|
specifier: ^2.0.1
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
|
framer-motion:
|
||||||
|
specifier: ^10.16.4
|
||||||
|
version: 10.16.4(react-dom@18.2.0)(react@18.2.0)
|
||||||
html-to-text:
|
html-to-text:
|
||||||
specifier: ^9.0.5
|
specifier: ^9.0.5
|
||||||
version: 9.0.5
|
version: 9.0.5
|
||||||
@ -176,6 +179,9 @@ dependencies:
|
|||||||
remark-gfm:
|
remark-gfm:
|
||||||
specifier: ^3.0.1
|
specifier: ^3.0.1
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
|
sonner:
|
||||||
|
specifier: ^1.0.3
|
||||||
|
version: 1.0.3(react-dom@18.2.0)(react@18.2.0)
|
||||||
tailwind-scrollbar:
|
tailwind-scrollbar:
|
||||||
specifier: ^3.0.5
|
specifier: ^3.0.5
|
||||||
version: 3.0.5(tailwindcss@3.3.3)
|
version: 3.0.5(tailwindcss@3.3.3)
|
||||||
@ -551,6 +557,20 @@ packages:
|
|||||||
'@babel/helper-validator-identifier': 7.22.20
|
'@babel/helper-validator-identifier': 7.22.20
|
||||||
to-fast-properties: 2.0.0
|
to-fast-properties: 2.0.0
|
||||||
|
|
||||||
|
/@emotion/is-prop-valid@0.8.8:
|
||||||
|
resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
'@emotion/memoize': 0.7.4
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@emotion/memoize@0.7.4:
|
||||||
|
resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@esbuild/android-arm64@0.18.20:
|
/@esbuild/android-arm64@0.18.20:
|
||||||
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
|
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -920,10 +940,10 @@ packages:
|
|||||||
'@nodelib/fs.scandir': 2.1.5
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
fastq: 1.15.0
|
fastq: 1.15.0
|
||||||
|
|
||||||
/@nostr-dev-kit/ndk-cache-dexie@2.0.1(typescript@5.2.2):
|
/@nostr-dev-kit/ndk-cache-dexie@2.0.2(typescript@5.2.2):
|
||||||
resolution: {integrity: sha512-O1ngV95yuZPhV0PB6JQAMHQkZvGtcW6qEY1jawvrZCfYLf2vdHWuzMN2rXYiSdrx6mMsnqB17bq5Lg3r8Coslw==}
|
resolution: {integrity: sha512-v6dq82Gzw/AoDMtkjCeTg+gx9n6sX3xReaMpIUbKL5W+E7z1lqnR0RmYkeNxUhd7Tlg0FQ+Ywqq7nZs+UmGDEA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nostr-dev-kit/ndk': 2.0.1(typescript@5.2.2)
|
'@nostr-dev-kit/ndk': 2.0.2(typescript@5.2.2)
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
dexie: 3.2.4
|
dexie: 3.2.4
|
||||||
nostr-tools: 1.16.0(typescript@5.2.2)
|
nostr-tools: 1.16.0(typescript@5.2.2)
|
||||||
@ -933,8 +953,8 @@ packages:
|
|||||||
- typescript
|
- typescript
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@nostr-dev-kit/ndk@2.0.1(typescript@5.2.2):
|
/@nostr-dev-kit/ndk@2.0.2(typescript@5.2.2):
|
||||||
resolution: {integrity: sha512-LZ7h4HL2B0Yek3Pr276OMaiVzr6WYXSWExZKn8bdpZ5lIzt5t1j4bi8kxwfUZti1Z/nIY7Hq7tIguty39YBs/g==}
|
resolution: {integrity: sha512-EwaOJVS0FOCXlIffiVceKrK+QtbaRTG6QYdoQchMAe+ag2C3jl7nAoDTWlixv/WgJOFl4KPQkS8r0sEkGmXsjQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.3.2
|
'@noble/hashes': 1.3.2
|
||||||
'@noble/secp256k1': 2.0.0
|
'@noble/secp256k1': 2.0.0
|
||||||
@ -952,13 +972,13 @@ packages:
|
|||||||
- typescript
|
- typescript
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@nostr-fetch/adapter-ndk@0.12.2(@nostr-dev-kit/ndk@2.0.1)(nostr-fetch@0.13.0):
|
/@nostr-fetch/adapter-ndk@0.12.2(@nostr-dev-kit/ndk@2.0.2)(nostr-fetch@0.13.0):
|
||||||
resolution: {integrity: sha512-+7EVuxS5DDZvNo6qbfFp7xRHwIyjyi36hYkiQFDjbQ4gX5LKo9RIPB1P+1XGkOSDFshypTbovZCaFunscJ/zhQ==}
|
resolution: {integrity: sha512-+7EVuxS5DDZvNo6qbfFp7xRHwIyjyi36hYkiQFDjbQ4gX5LKo9RIPB1P+1XGkOSDFshypTbovZCaFunscJ/zhQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@nostr-dev-kit/ndk': ^0.7.5
|
'@nostr-dev-kit/ndk': ^0.7.5
|
||||||
nostr-fetch: ^0.12.2
|
nostr-fetch: ^0.12.2
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nostr-dev-kit/ndk': 2.0.1(typescript@5.2.2)
|
'@nostr-dev-kit/ndk': 2.0.2(typescript@5.2.2)
|
||||||
'@nostr-fetch/kernel': 0.12.2
|
'@nostr-fetch/kernel': 0.12.2
|
||||||
nostr-fetch: 0.13.0
|
nostr-fetch: 0.13.0
|
||||||
dev: false
|
dev: false
|
||||||
@ -3082,7 +3102,7 @@ packages:
|
|||||||
postcss: ^8.1.0
|
postcss: ^8.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.22.1
|
browserslist: 4.22.1
|
||||||
caniuse-lite: 1.0.30001547
|
caniuse-lite: 1.0.30001549
|
||||||
fraction.js: 4.3.7
|
fraction.js: 4.3.7
|
||||||
normalize-range: 0.1.2
|
normalize-range: 0.1.2
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
@ -3134,7 +3154,7 @@ packages:
|
|||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001547
|
caniuse-lite: 1.0.30001549
|
||||||
electron-to-chromium: 1.4.554
|
electron-to-chromium: 1.4.554
|
||||||
node-releases: 2.0.13
|
node-releases: 2.0.13
|
||||||
update-browserslist-db: 1.0.13(browserslist@4.22.1)
|
update-browserslist-db: 1.0.13(browserslist@4.22.1)
|
||||||
@ -3163,8 +3183,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
|
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
/caniuse-lite@1.0.30001547:
|
/caniuse-lite@1.0.30001549:
|
||||||
resolution: {integrity: sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==}
|
resolution: {integrity: sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==}
|
||||||
|
|
||||||
/case-anything@2.1.13:
|
/case-anything@2.1.13:
|
||||||
resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==}
|
resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==}
|
||||||
@ -3997,6 +4017,24 @@ packages:
|
|||||||
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/framer-motion@10.16.4(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-p9V9nGomS3m6/CALXqv6nFGMuFOxbWsmaOrdmhyQimMIlLl3LC7h7l86wge/Js/8cRu5ktutS/zlzgR7eBOtFA==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18.0.0
|
||||||
|
react-dom: ^18.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react:
|
||||||
|
optional: true
|
||||||
|
react-dom:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
tslib: 2.6.2
|
||||||
|
optionalDependencies:
|
||||||
|
'@emotion/is-prop-valid': 0.8.8
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fs.realpath@1.0.0:
|
/fs.realpath@1.0.0:
|
||||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||||
|
|
||||||
@ -5464,7 +5502,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lilconfig: 2.1.0
|
lilconfig: 2.1.0
|
||||||
postcss: 8.4.31
|
postcss: 8.4.31
|
||||||
yaml: 2.3.2
|
yaml: 2.3.3
|
||||||
|
|
||||||
/postcss-nested@6.0.1(postcss@8.4.31):
|
/postcss-nested@6.0.1(postcss@8.4.31):
|
||||||
resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
|
resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
|
||||||
@ -5801,7 +5839,7 @@ packages:
|
|||||||
remark-parse: 10.0.2
|
remark-parse: 10.0.2
|
||||||
remark-rehype: 10.1.0
|
remark-rehype: 10.1.0
|
||||||
space-separated-tokens: 2.0.2
|
space-separated-tokens: 2.0.2
|
||||||
style-to-object: 0.4.2
|
style-to-object: 0.4.3
|
||||||
unified: 10.1.2
|
unified: 10.1.2
|
||||||
unist-util-visit: 4.1.2
|
unist-util-visit: 4.1.2
|
||||||
vfile: 5.3.7
|
vfile: 5.3.7
|
||||||
@ -6140,6 +6178,16 @@ packages:
|
|||||||
is-fullwidth-code-point: 4.0.0
|
is-fullwidth-code-point: 4.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/sonner@1.0.3(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-hBoA2zKuYW3lUnpx4K0vAn8j77YuYiwvP9sLQfieNS2pd5FkT20sMyPTDJnl9S+5T27ZJbwQRPiujwvDBwhZQg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18.0.0
|
||||||
|
react-dom: ^18.0.0
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/source-map-js@1.0.2:
|
/source-map-js@1.0.2:
|
||||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -6230,8 +6278,8 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/style-to-object@0.4.2:
|
/style-to-object@0.4.3:
|
||||||
resolution: {integrity: sha512-1JGpfPB3lo42ZX8cuPrheZbfQ6kqPPnPHlKMyeRYtfKD+0jG+QsXgXN57O/dvJlzlB2elI6dGmrPnl5VPQFPaA==}
|
resolution: {integrity: sha512-RP9icVx0g3Pt0CyNiC2qvBkqMTHD5uBVC2XYcSr/ag8QWKApx/oXEh2ehMGSyzkjK0+ySkukMuO+mz+DNQq57Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
inline-style-parser: 0.1.1
|
inline-style-parser: 0.1.1
|
||||||
dev: false
|
dev: false
|
||||||
@ -6850,8 +6898,8 @@ packages:
|
|||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/yaml@2.3.2:
|
/yaml@2.3.3:
|
||||||
resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==}
|
resolution: {integrity: sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
/yocto-queue@0.1.0:
|
/yocto-queue@0.1.0:
|
||||||
|
30
src-tauri/Cargo.lock
generated
30
src-tauri/Cargo.lock
generated
@ -1087,6 +1087,7 @@ version = "0.3.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
|
checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3445,6 +3446,12 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@ -3681,14 +3688,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.0"
|
version = "1.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87"
|
checksum = "aaac441002f822bc9705a681810a4dd2963094b9ca0ddc41cb963a4c189189ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.1",
|
"regex-automata 0.4.2",
|
||||||
"regex-syntax 0.8.1",
|
"regex-syntax 0.8.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3702,13 +3709,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b"
|
checksum = "5011c7e263a695dc8ca064cddb722af1be54e517a280b12a5356f98366899e5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax 0.8.1",
|
"regex-syntax 0.8.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3719,9 +3726,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.1"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56d84fdd47036b038fc80dd333d10b6aab10d5d31f4a366e20014def75328d33"
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
@ -5322,12 +5329,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.29"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe"
|
checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa 1.0.9",
|
"itoa 1.0.9",
|
||||||
|
"powerfmt",
|
||||||
"serde",
|
"serde",
|
||||||
"time-core",
|
"time-core",
|
||||||
"time-macros",
|
"time-macros",
|
||||||
|
43
src/app.tsx
43
src/app.tsx
@ -3,8 +3,7 @@ import { fetch } from '@tauri-apps/plugin-http';
|
|||||||
import { RouterProvider, createBrowserRouter, defer, redirect } from 'react-router-dom';
|
import { RouterProvider, createBrowserRouter, defer, redirect } from 'react-router-dom';
|
||||||
import { ReactFlowProvider } from 'reactflow';
|
import { ReactFlowProvider } from 'reactflow';
|
||||||
|
|
||||||
import { AuthCreateScreen } from '@app/auth/create';
|
import { CreateAccountScreen } from '@app/auth/create';
|
||||||
import { AuthImportScreen } from '@app/auth/import';
|
|
||||||
import { OnboardingScreen } from '@app/auth/onboarding';
|
import { OnboardingScreen } from '@app/auth/onboarding';
|
||||||
import { ChatsScreen } from '@app/chats';
|
import { ChatsScreen } from '@app/chats';
|
||||||
import { ErrorScreen } from '@app/error';
|
import { ErrorScreen } from '@app/error';
|
||||||
@ -28,7 +27,7 @@ export default function App() {
|
|||||||
const totalAccount = await db.checkAccount();
|
const totalAccount = await db.checkAccount();
|
||||||
|
|
||||||
const onboarding = localStorage.getItem('onboarding');
|
const onboarding = localStorage.getItem('onboarding');
|
||||||
const step = JSON.parse(onboarding).state.step || null;
|
const step = onboarding ? JSON.parse(onboarding).state.step : null;
|
||||||
|
|
||||||
// redirect to welcome screen if none user exist
|
// redirect to welcome screen if none user exist
|
||||||
if (totalAccount === 0) return redirect('/auth/welcome');
|
if (totalAccount === 0) return redirect('/auth/welcome');
|
||||||
@ -168,48 +167,18 @@ export default function App() {
|
|||||||
return { Component: WelcomeScreen };
|
return { Component: WelcomeScreen };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'import',
|
|
||||||
element: <AuthImportScreen />,
|
|
||||||
errorElement: <ErrorScreen />,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
async lazy() {
|
|
||||||
const { ImportStep1Screen } = await import('@app/auth/import/step-1');
|
|
||||||
return { Component: ImportStep1Screen };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'step-2',
|
|
||||||
async lazy() {
|
|
||||||
const { ImportStep2Screen } = await import('@app/auth/import/step-2');
|
|
||||||
return { Component: ImportStep2Screen };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
element: <AuthCreateScreen />,
|
element: <CreateAccountScreen />,
|
||||||
errorElement: <ErrorScreen />,
|
errorElement: <ErrorScreen />,
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
async lazy() {
|
|
||||||
const { CreateStep1Screen } = await import('@app/auth/create/step-1');
|
|
||||||
return { Component: CreateStep1Screen };
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'step-2',
|
path: 'import',
|
||||||
async lazy() {
|
async lazy() {
|
||||||
const { CreateStep2Screen } = await import('@app/auth/create/step-2');
|
const { ImportAccountScreen } = await import('@app/auth/import');
|
||||||
return { Component: CreateStep2Screen };
|
return { Component: ImportAccountScreen };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'onboarding',
|
path: 'onboarding',
|
||||||
element: <OnboardingScreen />,
|
element: <OnboardingScreen />,
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
export function AuthCreateScreen() {
|
export function CreateAccountScreen() {
|
||||||
return (
|
return <Outlet />;
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ export function CreateStep1Screen() {
|
|||||||
const { db } = useStorage();
|
const { db } = useStorage();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const setTempPrivkey = useOnboarding((state) => state.setTempPrivkey);
|
|
||||||
const setPubkey = useOnboarding((state) => state.setPubkey);
|
const setPubkey = useOnboarding((state) => state.setPubkey);
|
||||||
const setStep = useOnboarding((state) => state.setStep);
|
const setStep = useOnboarding((state) => state.setStep);
|
||||||
|
|
||||||
@ -29,6 +28,17 @@ export function CreateStep1Screen() {
|
|||||||
const npub = nip19.npubEncode(pubkey);
|
const npub = nip19.npubEncode(pubkey);
|
||||||
const nsec = nip19.nsecEncode(privkey);
|
const nsec = nip19.nsecEncode(privkey);
|
||||||
|
|
||||||
|
const copyPrivkey = async () => {
|
||||||
|
try {
|
||||||
|
await writeText(nsec);
|
||||||
|
setCopied(true);
|
||||||
|
|
||||||
|
setTimeout(() => setCopied(false), 2000);
|
||||||
|
} catch (e) {
|
||||||
|
await message(e, { title: 'Cannot copy private key', type: 'error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const download = async () => {
|
const download = async () => {
|
||||||
try {
|
try {
|
||||||
const downloadPath = await downloadDir();
|
const downloadPath = await downloadDir();
|
||||||
@ -50,29 +60,22 @@ export function CreateStep1Screen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyPrivkey = async () => {
|
|
||||||
try {
|
|
||||||
await writeText(nsec);
|
|
||||||
setCopied(true);
|
|
||||||
|
|
||||||
setTimeout(() => setCopied(false), 3000);
|
|
||||||
} catch (e) {
|
|
||||||
await message(e, { title: 'Cannot copy private key', type: 'error' });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// update state
|
|
||||||
setTempPrivkey(privkey); // only use if user close app and reopen it
|
|
||||||
setPubkey(pubkey);
|
setPubkey(pubkey);
|
||||||
|
|
||||||
|
// save privkey
|
||||||
|
await db.secureSave(privkey, pubkey);
|
||||||
|
|
||||||
// save to database
|
// save to database
|
||||||
await db.createAccount(npub, pubkey);
|
await db.createAccount(npub, pubkey);
|
||||||
|
|
||||||
// redirect to next step
|
// redirect to next step
|
||||||
navigate('/auth/create/step-2', { replace: true });
|
navigate('/auth/create/step-2', { replace: true });
|
||||||
|
} catch (e) {
|
||||||
|
await message(e, { title: 'Something went wrong!', type: 'error' });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -81,71 +84,87 @@ export function CreateStep1Screen() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<div className="mb-4 border-b border-white/10 pb-4">
|
<div className="mx-auto flex w-full max-w-md flex-col gap-10">
|
||||||
<h1 className="mb-2 text-center text-2xl font-semibold text-white">
|
<div>
|
||||||
|
<h1 className="mb-2 text-center text-2xl font-semibold text-neutral-900 dark:text-neutral-100">
|
||||||
This is your new Nostr account
|
This is your new Nostr account
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mb-2 text-white/70">
|
<p className="mb-2 select-text text-neutral-600 dark:text-neutral-300">
|
||||||
Your private key is your password. If you lose this key, you will lose access to
|
Your private key is your password. If you lose this key, you will lose access
|
||||||
your account! Copy it and keep it in a safe place. There is no way to reset your
|
to your account! Copy it and keep it in a safe place.{' '}
|
||||||
private key.
|
<span className="text-red-500">
|
||||||
|
There is no way to reset your private key.
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-white/70">
|
<p className="select-text text-neutral-600 dark:text-neutral-300">
|
||||||
Public key is used for sharing with other people so that they can find you using
|
Public key is used for sharing with other people so that they can find you
|
||||||
the public key.
|
using the public key.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-3">
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<span className="font-medium text-white">Private Key</span>
|
<label
|
||||||
|
htmlFor="nsec"
|
||||||
|
className="font-medium text-neutral-900 dark:text-neutral-100"
|
||||||
|
>
|
||||||
|
Private Key
|
||||||
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
readOnly
|
readOnly
|
||||||
|
name="nsec"
|
||||||
value={nsec.substring(0, 5) + '**************************************'}
|
value={nsec.substring(0, 5) + '**************************************'}
|
||||||
className="relative h-12 w-full rounded-lg border-t border-white/10 bg-white/20 py-1 pl-3.5 pr-11 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
|
className="relative h-12 w-full rounded-lg bg-neutral-200 py-1 pl-3.5 pr-11 text-neutral-900 !outline-none dark:bg-neutral-800 dark:text-neutral-100"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => copyPrivkey()}
|
onClick={() => copyPrivkey()}
|
||||||
className="group absolute right-2 top-1/2 inline-flex h-7 -translate-y-1/2 transform items-center gap-1.5 rounded-md bg-white/20 px-2.5 text-sm hover:bg-white/30"
|
className="group absolute right-2 top-1/2 inline-flex h-7 -translate-y-1/2 transform items-center gap-1.5 rounded-md bg-neutral-300 px-2.5 text-sm text-neutral-800 hover:bg-neutral-400 hover:text-neutral-900 dark:bg-neutral-700 dark:text-neutral-200 dark:hover:bg-neutral-600 dark:hover:text-neutral-100"
|
||||||
>
|
>
|
||||||
<CopyIcon className="h-4 w-4 text-white/70 group-hover:text-white" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
{copied ? 'Copied' : 'Copy'}
|
{copied ? 'Copied' : 'Copy'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<span className="font-medium text-white">Public Key</span>
|
<label
|
||||||
|
htmlFor="npub"
|
||||||
|
className="font-medium text-neutral-900 dark:text-neutral-100"
|
||||||
|
>
|
||||||
|
Public Key
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
readOnly
|
readOnly
|
||||||
|
name="npub"
|
||||||
value={npub}
|
value={npub}
|
||||||
className="relative h-12 w-full rounded-lg border-t border-white/10 bg-white/20 px-3.5 py-1 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
|
className="relative h-12 w-full rounded-lg bg-neutral-200 px-3.5 py-1 text-neutral-900 !outline-none dark:bg-neutral-800 dark:text-neutral-100"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => download()}
|
onClick={() => download()}
|
||||||
className="inline-flex h-12 w-full items-center justify-center rounded-lg bg-blue-500 px-6 font-medium leading-none text-white hover:bg-blue-600 focus:outline-none"
|
className="inline-flex h-10 w-full items-center justify-center rounded-lg bg-blue-500 px-6 font-medium leading-none text-white hover:bg-blue-600 focus:outline-none"
|
||||||
>
|
>
|
||||||
{downloaded ? 'Downloaded' : 'Download account keys'}
|
{downloaded ? 'Downloaded' : 'Download account keys'}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
className="inline-flex h-12 w-full items-center justify-center rounded-lg border-t border-white/10 bg-white/20 px-6 font-medium leading-none text-white hover:bg-white/30 focus:outline-none"
|
className="inline-flex h-10 w-full items-center justify-center rounded-lg bg-neutral-200 px-6 font-medium leading-none text-neutral-900 hover:bg-neutral-300 focus:outline-none dark:bg-neutral-800 dark:text-neutral-100 dark:hover:bg-neutral-700"
|
||||||
>
|
>
|
||||||
{loading ? 'Creating...' : 'Continue'}
|
{loading ? 'Creating...' : 'Continue'}
|
||||||
</button>
|
</button>
|
||||||
<span className="text-center text-sm text-white/50">
|
<span className="select-text text-center text-sm text-neutral-400 dark:text-neutral-600">
|
||||||
By clicking 'Continue', you are ensuring that your keys are saved in
|
By clicking 'Continue', you are ensuring that your keys are saved
|
||||||
a safe place. You cannot recover these keys if they are lost.
|
in a safe place. You cannot recover these keys if they are lost.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
225
src/app/auth/import.tsx
Normal file
225
src/app/auth/import.tsx
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { nip19 } from 'nostr-tools';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
|
import { User } from '@shared/user';
|
||||||
|
|
||||||
|
export function ImportAccountScreen() {
|
||||||
|
const { db } = useStorage();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [npub, setNpub] = useState<string>('');
|
||||||
|
const [nsec, setNsec] = useState<string>('');
|
||||||
|
const [pubkey, setPubkey] = useState<undefined | string>(undefined);
|
||||||
|
const [created, setCreated] = useState(false);
|
||||||
|
const [savedPrivkey, setSavedPrivkey] = useState(false);
|
||||||
|
|
||||||
|
const submitNpub = async () => {
|
||||||
|
if (npub.length < 6) return toast('You must enter valid npub');
|
||||||
|
if (!npub.startsWith('npub1')) return toast('npub must be starts with npub1');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pubkey = nip19.decode(npub).data as string;
|
||||||
|
setPubkey(pubkey);
|
||||||
|
} catch (e) {
|
||||||
|
return toast(`npub invalid: ${e}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeAccount = async () => {
|
||||||
|
setNpub('');
|
||||||
|
setPubkey('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAccount = async () => {
|
||||||
|
try {
|
||||||
|
await db.createAccount(npub, pubkey);
|
||||||
|
setCreated(true);
|
||||||
|
} catch (e) {
|
||||||
|
return toast(`Create account failed: ${e}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitNsec = async () => {
|
||||||
|
if (savedPrivkey) return;
|
||||||
|
if (nsec.length > 50 && nsec.startsWith('nsec1')) {
|
||||||
|
try {
|
||||||
|
const privkey = nip19.decode(nsec).data as string;
|
||||||
|
await db.secureSave(pubkey, privkey);
|
||||||
|
setSavedPrivkey(true);
|
||||||
|
} catch (e) {
|
||||||
|
return toast(`nsec invalid: ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const finish = async () => {
|
||||||
|
navigate('/auth/onboarding');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<div className="mx-auto flex w-full max-w-md flex-col gap-10">
|
||||||
|
<h1 className="text-center text-2xl font-semibold text-neutral-900 dark:text-neutral-100">
|
||||||
|
Import your Nostr account
|
||||||
|
</h1>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<label htmlFor="npub" className="font-semibold">
|
||||||
|
Enter your nostr npub:
|
||||||
|
</label>
|
||||||
|
<div className="inline-flex w-full items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={npub}
|
||||||
|
onChange={(e) => setNpub(e.target.value)}
|
||||||
|
spellCheck={false}
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="off"
|
||||||
|
autoCapitalize="off"
|
||||||
|
placeholder="npub1"
|
||||||
|
className="h-11 flex-1 rounded-lg bg-neutral-200 px-3 placeholder:text-neutral-500 dark:bg-neutral-800 dark:placeholder:text-neutral-400"
|
||||||
|
/>
|
||||||
|
{!pubkey ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={submitNpub}
|
||||||
|
className="h-11 w-24 shrink-0 rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600"
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{pubkey ? (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 50 }}
|
||||||
|
animate={{
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
}}
|
||||||
|
transition={{ y: { velocity: -100 } }}
|
||||||
|
className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200"
|
||||||
|
>
|
||||||
|
<h5 className="mb-1.5 font-semibold">Account found</h5>
|
||||||
|
<div className="flex w-full flex-col gap-2">
|
||||||
|
<div className="inline-flex h-full flex-1 items-center rounded-lg bg-neutral-200 p-2">
|
||||||
|
<User pubkey={pubkey} variant="simple" />
|
||||||
|
</div>
|
||||||
|
{!created ? (
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={changeAccount}
|
||||||
|
className="h-9 flex-1 shrink-0 rounded-lg bg-neutral-200 font-semibold text-neutral-800 hover:bg-neutral-300 dark:bg-neutral-800 dark:text-neutral-200 dark:hover:bg-neutral-700"
|
||||||
|
>
|
||||||
|
Change account
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={createAccount}
|
||||||
|
className="h-9 flex-1 shrink-0 rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600"
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
) : null}
|
||||||
|
{created ? (
|
||||||
|
<>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 50 }}
|
||||||
|
animate={{
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
}}
|
||||||
|
transition={{ y: { velocity: -100 } }}
|
||||||
|
className="rounded-lg bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<label htmlFor="npub" className="font-semibold">
|
||||||
|
Enter your nostr nsec (optional):
|
||||||
|
</label>
|
||||||
|
<div className="inline-flex w-full items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={nsec}
|
||||||
|
onChange={(e) => setNsec(e.target.value)}
|
||||||
|
spellCheck={false}
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="off"
|
||||||
|
autoCapitalize="off"
|
||||||
|
placeholder="nsec1"
|
||||||
|
className="h-11 flex-1 rounded-lg bg-neutral-200 px-3 placeholder:text-neutral-500 dark:bg-neutral-800 dark:placeholder:text-neutral-400"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={submitNsec}
|
||||||
|
className={twMerge(
|
||||||
|
'h-11 w-24 shrink-0 rounded-lg font-semibold text-white',
|
||||||
|
!savedPrivkey
|
||||||
|
? 'bg-blue-500 hover:bg-blue-600'
|
||||||
|
: 'bg-teal-500 hover:bg-teal-600'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{savedPrivkey ? 'Saved' : 'Save'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 select-text">
|
||||||
|
<p className="text-sm">
|
||||||
|
<b>nsec</b> is used to sign your event. For example, if you want to
|
||||||
|
make a new post or send a message to your contact, you need to use
|
||||||
|
nsec to sign this event.
|
||||||
|
</p>
|
||||||
|
<h5 className="mt-2 font-semibold">
|
||||||
|
1. In case you store nsec in Lume
|
||||||
|
</h5>
|
||||||
|
<p className="text-sm">
|
||||||
|
Lume will put your nsec to{' '}
|
||||||
|
{db.platform === 'macos'
|
||||||
|
? 'Apple Keychain (macOS)'
|
||||||
|
: db.platform === 'windows'
|
||||||
|
? 'Credential Manager (Windows)'
|
||||||
|
: 'Secret Service (Linux)'}
|
||||||
|
, it will be secured by your OS
|
||||||
|
</p>
|
||||||
|
<h5 className="mt-2 font-semibold">
|
||||||
|
2. In case you do not store nsec in Lume
|
||||||
|
</h5>
|
||||||
|
<p className="text-sm">
|
||||||
|
When you make an event that requires a sign by your nsec, Lume will
|
||||||
|
show a prompt popup for you to enter nsec. It will be cleared after
|
||||||
|
signing and not stored anywhere.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
<motion.button
|
||||||
|
type="button"
|
||||||
|
onClick={finish}
|
||||||
|
initial={{ opacity: 0, y: 80 }}
|
||||||
|
animate={{
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
}}
|
||||||
|
transition={{ y: { velocity: -130 } }}
|
||||||
|
className="h-9 w-full shrink-0 rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600"
|
||||||
|
>
|
||||||
|
Finish
|
||||||
|
</motion.button>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
import { Outlet } from 'react-router-dom';
|
|
||||||
|
|
||||||
export function AuthImportScreen() {
|
|
||||||
return (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,156 +0,0 @@
|
|||||||
import { getPublicKey, nip19 } from 'nostr-tools';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { Resolver, useForm } from 'react-hook-form';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { useStorage } from '@libs/storage/provider';
|
|
||||||
|
|
||||||
import { EyeOffIcon, EyeOnIcon, LoaderIcon } from '@shared/icons';
|
|
||||||
import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle';
|
|
||||||
|
|
||||||
import { useOnboarding } from '@stores/onboarding';
|
|
||||||
|
|
||||||
type FormValues = {
|
|
||||||
privkey: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const resolver: Resolver<FormValues> = async (values) => {
|
|
||||||
return {
|
|
||||||
values: values.privkey ? values : {},
|
|
||||||
errors: !values.privkey
|
|
||||||
? {
|
|
||||||
privkey: {
|
|
||||||
type: 'required',
|
|
||||||
message: 'This is required.',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function ImportStep1Screen() {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [passwordInput, setPasswordInput] = useState('password');
|
|
||||||
const [setStep, setPubkey, setTempPrivkey] = useOnboarding((state) => [
|
|
||||||
state.setStep,
|
|
||||||
state.setPubkey,
|
|
||||||
state.setTempPrivkey,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { db } = useStorage();
|
|
||||||
const {
|
|
||||||
register,
|
|
||||||
setError,
|
|
||||||
handleSubmit,
|
|
||||||
formState: { errors, isDirty, isValid },
|
|
||||||
} = useForm<FormValues>({ resolver });
|
|
||||||
|
|
||||||
const onSubmit = async (data: { [x: string]: string }) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
let privkey = data['privkey'];
|
|
||||||
if (privkey.substring(0, 4) === 'nsec') {
|
|
||||||
privkey = nip19.decode(privkey).data as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof getPublicKey(privkey) === 'string') {
|
|
||||||
const pubkey = getPublicKey(privkey);
|
|
||||||
const npub = nip19.npubEncode(pubkey);
|
|
||||||
|
|
||||||
setTempPrivkey(privkey);
|
|
||||||
setPubkey(pubkey);
|
|
||||||
|
|
||||||
// add account to local database
|
|
||||||
await db.createAccount(npub, pubkey);
|
|
||||||
|
|
||||||
// redirect to step 2 with delay 1.2s
|
|
||||||
setTimeout(() => navigate('/auth/import/step-2', { replace: true }), 1200);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setLoading(false);
|
|
||||||
setError('privkey', {
|
|
||||||
type: 'custom',
|
|
||||||
message: 'Private key is invalid, please check again',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// toggle private key
|
|
||||||
const showPassword = () => {
|
|
||||||
if (passwordInput === 'password') {
|
|
||||||
setPasswordInput('text');
|
|
||||||
} else {
|
|
||||||
setPasswordInput('password');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// save current step, if user close app and reopen it
|
|
||||||
setStep('/auth/import');
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mx-auto w-full max-w-md">
|
|
||||||
<div className="mb-4 pb-4">
|
|
||||||
<h1 className="text-center text-2xl font-semibold text-white">
|
|
||||||
Import your Nostr key
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col gap-3">
|
|
||||||
<div className="flex flex-col gap-1">
|
|
||||||
<label htmlFor="privkey" className="font-medium text-white">
|
|
||||||
Insert your nostr private key, in nsec or hex format
|
|
||||||
</label>
|
|
||||||
<div className="relative">
|
|
||||||
<input
|
|
||||||
{...register('privkey', { required: true, minLength: 32 })}
|
|
||||||
type={passwordInput}
|
|
||||||
placeholder="nsec1..."
|
|
||||||
className="relative h-12 w-full rounded-lg border-t border-white/10 bg-white/20 px-3 py-1 text-white backdrop-blur-xl placeholder:text-white/70 focus:outline-none"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => showPassword()}
|
|
||||||
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 backdrop-blur-xl hover:bg-white/20"
|
|
||||||
>
|
|
||||||
{passwordInput === 'password' ? (
|
|
||||||
<EyeOffIcon className="h-4 w-4 text-white/50 group-hover:text-white" />
|
|
||||||
) : (
|
|
||||||
<EyeOnIcon className="h-4 w-4 text-white/50 group-hover:text-white" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<span className="text-sm text-red-500">
|
|
||||||
{errors.privkey && <p>{errors.privkey.message}</p>}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={!isDirty || !isValid}
|
|
||||||
className="inline-flex h-12 w-full items-center justify-between gap-2 rounded-lg bg-blue-500 px-6 font-medium leading-none text-white hover:bg-blue-600 focus:outline-none"
|
|
||||||
>
|
|
||||||
{loading ? (
|
|
||||||
<>
|
|
||||||
<span className="w-5" />
|
|
||||||
<span>Importing...</span>
|
|
||||||
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<span className="w-5" />
|
|
||||||
<span>Continue</span>
|
|
||||||
<ArrowRightCircleIcon className="h-5 w-5" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { useStorage } from '@libs/storage/provider';
|
|
||||||
|
|
||||||
import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons';
|
|
||||||
import { User } from '@shared/user';
|
|
||||||
|
|
||||||
import { useOnboarding } from '@stores/onboarding';
|
|
||||||
import { WidgetKinds } from '@stores/widgets';
|
|
||||||
|
|
||||||
import { useNostr } from '@utils/hooks/useNostr';
|
|
||||||
|
|
||||||
export function ImportStep2Screen() {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const setStep = useOnboarding((state) => state.setStep);
|
|
||||||
|
|
||||||
const { db } = useStorage();
|
|
||||||
const { fetchUserData } = useNostr();
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
const submit = async () => {
|
|
||||||
try {
|
|
||||||
// show loading indicator
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
// prefetch data
|
|
||||||
const user = await fetchUserData();
|
|
||||||
|
|
||||||
// create default widget
|
|
||||||
await db.createWidget(WidgetKinds.other.learnNostr, 'Learn Nostr', '');
|
|
||||||
|
|
||||||
// redirect to next step
|
|
||||||
if (user.status === 'ok') {
|
|
||||||
navigate('/auth/onboarding/step-2', { replace: true });
|
|
||||||
} else {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('error: ', e);
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// save current step, if user close app and reopen it
|
|
||||||
setStep('/auth/import/step-3');
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mx-auto w-full max-w-md">
|
|
||||||
<div className="mb-4 pb-4">
|
|
||||||
<h1 className="text-center text-2xl font-semibold text-white">
|
|
||||||
{loading ? 'Downloading...' : 'Your Nostr profile'}
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<div className="rounded-lg border-t border-white/10 bg-white/20 px-3 py-3">
|
|
||||||
<User pubkey={db.account.pubkey} variant="simple" />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="inline-flex h-12 w-full items-center justify-between gap-2 rounded-lg bg-blue-500 px-6 font-medium leading-none text-white hover:bg-blue-600 focus:outline-none"
|
|
||||||
onClick={() => submit()}
|
|
||||||
>
|
|
||||||
{loading ? (
|
|
||||||
<>
|
|
||||||
<span className="w-5" />
|
|
||||||
<span>It might take a bit, please patient...</span>
|
|
||||||
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<span className="w-5" />
|
|
||||||
<span>Continue</span>
|
|
||||||
<ArrowRightCircleIcon className="h-5 w-5" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
<span className="text-center text-sm text-white/50">
|
|
||||||
By clicking 'Continue', Lume will download your old relay list and
|
|
||||||
metadata. It may take a bit
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -19,13 +19,11 @@ export class LumeStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async secureSave(value: string, key?: string) {
|
public async secureSave(value: string, key?: string) {
|
||||||
return await invoke('secure_save', { key: this.account.pubkey ?? key, value });
|
return await invoke('secure_save', { key, value });
|
||||||
}
|
}
|
||||||
|
|
||||||
public async secureLoad(key?: string) {
|
public async secureLoad(key?: string) {
|
||||||
const value: string = await invoke('secure_load', {
|
const value: string = await invoke('secure_load', { key });
|
||||||
key: this.account.pubkey ?? key,
|
|
||||||
});
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import { Toaster } from 'sonner';
|
||||||
|
|
||||||
import { NDKProvider } from '@libs/ndk/provider';
|
import { NDKProvider } from '@libs/ndk/provider';
|
||||||
import { StorageProvider } from '@libs/storage/provider';
|
import { StorageProvider } from '@libs/storage/provider';
|
||||||
@ -15,6 +16,7 @@ root.render(
|
|||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<StorageProvider>
|
<StorageProvider>
|
||||||
<NDKProvider>
|
<NDKProvider>
|
||||||
|
<Toaster />
|
||||||
<App />
|
<App />
|
||||||
</NDKProvider>
|
</NDKProvider>
|
||||||
</StorageProvider>
|
</StorageProvider>
|
||||||
|
@ -162,18 +162,18 @@ export const User = memo(function User({
|
|||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
style={{ contentVisibility: 'auto' }}
|
style={{ contentVisibility: 'auto' }}
|
||||||
className="h-10 w-10 rounded-lg"
|
className="h-11 w-11 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<Avatar.Fallback delayMs={300}>
|
<Avatar.Fallback delayMs={300}>
|
||||||
<img
|
<img
|
||||||
src={svgURI}
|
src={svgURI}
|
||||||
alt={pubkey}
|
alt={pubkey}
|
||||||
className="h-10 w-10 rounded-lg bg-black dark:bg-white"
|
className="h-11 w-11 rounded-lg bg-black dark:bg-white"
|
||||||
/>
|
/>
|
||||||
</Avatar.Fallback>
|
</Avatar.Fallback>
|
||||||
</Avatar.Root>
|
</Avatar.Root>
|
||||||
<div className="flex w-full flex-col items-start">
|
<div className="flex w-full flex-col items-start">
|
||||||
<h3 className="max-w-[15rem] truncate font-medium text-neutral-900 dark:text-neutral-100">
|
<h3 className="max-w-[15rem] truncate text-base font-semibold text-neutral-900 dark:text-neutral-100">
|
||||||
{user?.name || user?.display_name || user?.displayName}
|
{user?.name || user?.display_name || user?.displayName}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="max-w-[10rem] truncate text-sm text-neutral-900 dark:text-neutral-100/70">
|
<p className="max-w-[10rem] truncate text-sm text-neutral-900 dark:text-neutral-100/70">
|
||||||
|
Loading…
Reference in New Issue
Block a user