mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-11 05:09:36 +02:00
add wallet send view
This commit is contained in:
parent
4ed1a0b528
commit
5982ce2a4e
@ -30,7 +30,7 @@
|
||||
"@chakra-ui/theme-tools": "^2.2.6",
|
||||
"@codemirror/autocomplete": "^6.18.6",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/language": "^6.10.8",
|
||||
"@codemirror/language": "^6.11.0",
|
||||
"@codemirror/view": "^6.36.4",
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
@ -130,7 +130,7 @@
|
||||
"workbox-core": "7.0.0",
|
||||
"workbox-precaching": "7.0.0",
|
||||
"workbox-routing": "7.0.0",
|
||||
"yet-another-react-lightbox": "^3.21.7"
|
||||
"yet-another-react-lightbox": "^3.21.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@capacitor-community/http": "^1.4.1",
|
||||
@ -173,7 +173,8 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "^18.2.22",
|
||||
"@types/react-dom": "^18.2.7"
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"three-render-objects": "1.39.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "lightning",
|
||||
|
253
pnpm-lock.yaml
generated
253
pnpm-lock.yaml
generated
@ -7,6 +7,7 @@ settings:
|
||||
overrides:
|
||||
'@types/react': ^18.2.22
|
||||
'@types/react-dom': ^18.2.7
|
||||
three-render-objects: 1.39.0
|
||||
|
||||
importers:
|
||||
|
||||
@ -46,8 +47,8 @@ importers:
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1
|
||||
'@codemirror/language':
|
||||
specifier: ^6.10.8
|
||||
version: 6.10.8
|
||||
specifier: ^6.11.0
|
||||
version: 6.11.0
|
||||
'@codemirror/view':
|
||||
specifier: ^6.36.4
|
||||
version: 6.36.4
|
||||
@ -92,10 +93,10 @@ importers:
|
||||
version: 1.3.1
|
||||
'@uiw/codemirror-theme-github':
|
||||
specifier: ^4.23.10
|
||||
version: 4.23.10(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)
|
||||
version: 4.23.10(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)
|
||||
'@uiw/react-codemirror':
|
||||
specifier: ^4.23.10
|
||||
version: 4.23.10(@babel/runtime@7.26.10)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.4)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 4.23.10(@babel/runtime@7.26.10)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.4)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@webscopeio/react-textarea-autocomplete':
|
||||
specifier: ^4.9.2
|
||||
version: 4.9.2(prop-types@15.8.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
@ -104,34 +105,34 @@ importers:
|
||||
version: 0.7.2
|
||||
applesauce-accounts:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-actions:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-content:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-core:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-factory:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-loaders:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-react:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(react-dom@19.0.0(react@19.0.0))(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(react-dom@19.0.0(react@19.0.0))(typescript@5.8.2)
|
||||
applesauce-relay:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-signers:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-wallet:
|
||||
specifier: next
|
||||
version: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
version: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
bech32:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
@ -155,7 +156,7 @@ importers:
|
||||
version: 6.0.1
|
||||
codemirror-json-schema:
|
||||
specifier: ^0.7.9
|
||||
version: 0.7.9(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)(@lezer/common@1.2.3)
|
||||
version: 0.7.9(@codemirror/language@6.11.0)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)(@lezer/common@1.2.3)
|
||||
dayjs:
|
||||
specifier: ^1.11.13
|
||||
version: 1.11.13
|
||||
@ -346,8 +347,8 @@ importers:
|
||||
specifier: 7.0.0
|
||||
version: 7.0.0
|
||||
yet-another-react-lightbox:
|
||||
specifier: ^3.21.7
|
||||
version: 3.21.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
specifier: ^3.21.8
|
||||
version: 3.21.8(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
devDependencies:
|
||||
'@capacitor-community/http':
|
||||
specifier: ^1.4.1
|
||||
@ -477,6 +478,9 @@ packages:
|
||||
peerDependencies:
|
||||
ajv: '>=8'
|
||||
|
||||
'@apocentre/alias-sampling@0.5.3':
|
||||
resolution: {integrity: sha512-7UDWIIF9hIeJqfKXkNIzkVandlwLf1FWTSdrb9iXvOP8oF544JRXQjCbiTmCv2c9n44n/FIWtehhBfNuAx2CZA==}
|
||||
|
||||
'@babel/code-frame@7.26.2':
|
||||
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@ -1241,8 +1245,8 @@ packages:
|
||||
'@codemirror/lang-yaml@6.1.2':
|
||||
resolution: {integrity: sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==}
|
||||
|
||||
'@codemirror/language@6.10.8':
|
||||
resolution: {integrity: sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw==}
|
||||
'@codemirror/language@6.11.0':
|
||||
resolution: {integrity: sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==}
|
||||
|
||||
'@codemirror/lint@6.8.4':
|
||||
resolution: {integrity: sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==}
|
||||
@ -1473,6 +1477,9 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@gandlaf21/bc-ur@1.1.12':
|
||||
resolution: {integrity: sha512-AQfbZJ1o1AdK9/W9VcTyMkwp6iZWDWQQV2SGep2ygJUkTNaafSjdWLUgpc6Uo/VLlGYaS9A28gCh+GVtAdwTpA==}
|
||||
|
||||
'@getalby/bitcoin-connect-react@3.7.0':
|
||||
resolution: {integrity: sha512-wO8RhUlxJ4ub6vl8x8BScUaG4Z/tnLcDvJd9V4V7AOlrmrItMJfViZmc14c/WVU/RREeE3MSY2GZ0wYoH2TzxA==}
|
||||
peerDependencies:
|
||||
@ -2198,35 +2205,35 @@ packages:
|
||||
engines: {node: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
applesauce-accounts@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-WikbLZdD7BRx03I51CMSiTu6TH41xGHpHSBqHzGC+7MldDcRG7aGzlmmTTuxrkvAbbMrYxabVgiIoVUzW3hkHg==}
|
||||
applesauce-accounts@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-2bWwif44iIi/3ZQJwgTmMT9lIi+8m43W18FGc3esgnuXdux0gb36Nyk3xMD+4HIvxa1E/KJnl2MeNnQf5/rBFA==}
|
||||
|
||||
applesauce-actions@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-MiAxNKn2X+VAii34TigZkWyjV56nfp5mN6CW9fP7Acm/YGMlhnFVNZvZyMZ3I7riLlV+s3bALy5Ag3Iqin1gZQ==}
|
||||
applesauce-actions@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-F/yQ1su5njzvmC09SbzyCJpgRC/t7URt3ZogEXFas9r1k3rllodXhC5ZM3b+QUU1XQUQQcX1ZBHBK6oTzF20Qw==}
|
||||
|
||||
applesauce-content@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-t4ElFBTE3/kaeZo5DvgIq8xsDYKZGLcVtJWhWjwi27iM9IouRUiN9wc1YuGsPrAK3K3WaIVENq9wcdErzfr6Ug==}
|
||||
applesauce-content@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-JMvpH9a7s5dTFvk5ey4t/wd1K0YRId3IHGa8hMKEoa7ZRm3PHmCDKhK8KFgjAKF2nd6erIIOkCyfDmEO+7p+vQ==}
|
||||
|
||||
applesauce-core@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-IK0y6eFZNY14Y/wnquaX+zZRUqCuwSH6ufULPffvImS7nb2bGEcNJPWcbIcl3OYvnk6QwVVjJmt6O2pAX+Mtvg==}
|
||||
applesauce-core@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-HZeDganvR9kdAA7qexnkEXzSG+bdqMGOvUWgljZrvhjoAwazwX7XaOyj2vVmyQdmA8jY0V6pz+5RRTDgqCrx5A==}
|
||||
|
||||
applesauce-factory@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-PCEpm7l7jm+oh8eKltPERVZaVBSLQgBu84FKbO1M0K7LDVQANAkL0Q06vBdiOzbwEuEH2rbYRFsshJeRKfSuIQ==}
|
||||
applesauce-factory@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-4xLAhram5hxgFkw2ATRIrAAg0r7FAVFLbwRfn6rxasYjrC0NMGGURWULWpHs/o3L2VQGM0iDcdhEYKvn+zIIrQ==}
|
||||
|
||||
applesauce-loaders@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-VnpzR+Awf4B6cX9FQMK8iMBigsWf64ve6nvjN31LRCZaH5rZBdQkd+Vu8Sy6uOuCktoen7lFWkcWb0H6jAfnCQ==}
|
||||
applesauce-loaders@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-oyLU8fNObK/bfrLS099dfE3HlLj2Btc/MDBhN4FGBCnu8E3ya/r6bFyt0NOcUhxelJle4qi/pWMaRNUhiHdaqQ==}
|
||||
|
||||
applesauce-react@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-khhLt7zIb+FPX6l3N0YweqeehRgYNJ49dahW4VFqsVRwjaoo4qwKsrO4hzyxj9sdg2tBUTM6UyS9xeDUmxSIWg==}
|
||||
applesauce-react@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-ry8xatCIBQm/+1SlG8NMchFCeQMOSPZwUAnO/qw1etW4X5c8PuIkYdQCOQYBoLp37YW0nXGgxImNwxhiz5FoGA==}
|
||||
|
||||
applesauce-relay@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-zyU4/joBrcgVb1NlgYawMeXigNrszRpZTmzCbFhqMRlIQ/ELPBcEuNSGvPUfiPH4qTU7IopBHNRRunFprjT7UQ==}
|
||||
applesauce-relay@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-0ZL1iHm0FcsFkO2d7Vjl06mBC2QugtD0+coHy1xP0fs92zGQKnoTXyAS6B3M014vg2x+mH1hwHuOCaQ9rjeEbw==}
|
||||
|
||||
applesauce-signers@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-L4wCgv9gRdbj9sAA6d9hmpR6uQEDlaWORJ5GNie//PTA8GUzs5QQcQ2fGvNazns7pZKWH/Vhniwr7Q+rewz4jA==}
|
||||
applesauce-signers@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-yvaxUFgmZhbN28B/6skhgoZxdF4/aU9Y2DXoh4ZvmSiJaNJLv1MVE2s7c/vblcsyO7OqQCYKA78jryDLHh3l+w==}
|
||||
|
||||
applesauce-wallet@0.0.0-next-20250312201602:
|
||||
resolution: {integrity: sha512-g3uhg3TqQ/XHoH7wAy8ho7sj/6VMCyTLqeYPwVxZUgKDCWOZHAwki0oThBFYGV5B97+6Lqp9U1q8lMN2lQ9tuQ==}
|
||||
applesauce-wallet@0.0.0-next-20250313155042:
|
||||
resolution: {integrity: sha512-a5wk1LgHk4dr0B9+Jc2aIy4SglWv7y95Y/jtArWFLA75K3J0Y00NgJ69xsp5ZOQfwBV/iDfQTfOlIqSD9u+HLA==}
|
||||
|
||||
arg@4.1.3:
|
||||
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
|
||||
@ -2361,6 +2368,9 @@ packages:
|
||||
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
bignumber.js@9.1.2:
|
||||
resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==}
|
||||
|
||||
bindings@1.5.0:
|
||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||
|
||||
@ -2463,8 +2473,8 @@ packages:
|
||||
resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
caniuse-lite@1.0.30001703:
|
||||
resolution: {integrity: sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==}
|
||||
caniuse-lite@1.0.30001704:
|
||||
resolution: {integrity: sha512-+L2IgBbV6gXB4ETf0keSvLr7JUrRVbIaB/lrQ1+z8mRcQiisG5k+lG6O4n6Y5q6f5EuNfaYXKgymucphlEXQew==}
|
||||
|
||||
canvas-color-tracker@1.3.1:
|
||||
resolution: {integrity: sha512-eNycxGS7oQ3IS/9QQY41f/aQjiO9Y/MtedhCgSdsbLSxC9EyUD8L3ehl/Q3Kfmvt8um79S45PBV+5Rxm5ztdSw==}
|
||||
@ -2473,6 +2483,13 @@ packages:
|
||||
canvas-confetti@1.9.3:
|
||||
resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==}
|
||||
|
||||
cbor-sync@1.0.4:
|
||||
resolution: {integrity: sha512-GWlXN4wiz0vdWWXBU71Dvc1q3aBo0HytqwAZnXF1wOwjqNnDWA1vZ1gDMFLlqohak31VQzmhiYfiCX5QSSfagA==}
|
||||
|
||||
cborg@4.2.8:
|
||||
resolution: {integrity: sha512-z9M+TZCWQbf89Gl8ulpYThM9fqmkjBDdMiq+wS72OAK2zqDaXNquoAWFDrAKHQAukVtPspmadB9chuFC0ut7ew==}
|
||||
hasBin: true
|
||||
|
||||
ccount@2.0.1:
|
||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||
|
||||
@ -3042,8 +3059,8 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
hasBin: true
|
||||
|
||||
electron-to-chromium@1.5.115:
|
||||
resolution: {integrity: sha512-MN1nahVHAQMOz6dz6bNZ7apgqc9InZy7Ja4DBEVCTdeiUcegbyOYE9bi/f2Z/z6ZxLi0RxLpyJ3EGe+4h3w73A==}
|
||||
electron-to-chromium@1.5.116:
|
||||
resolution: {integrity: sha512-mufxTCJzLBQVvSdZzX1s5YAuXsN1M4tTyYxOOL1TcSKtIzQ9rjIrm7yFK80rN5dwGTePgdoABDSHpuVtRQh0Zw==}
|
||||
|
||||
elementtree@0.1.7:
|
||||
resolution: {integrity: sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==}
|
||||
@ -3860,6 +3877,9 @@ packages:
|
||||
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
||||
hasBin: true
|
||||
|
||||
jsbi@3.1.5:
|
||||
resolution: {integrity: sha512-w2BY0VOYC1ahe+w6Qhl4SFoPvPsZ9NPHY4bwass+LCgU7RK3PBoVQlQ3G1s7vI8W3CYyJiEXcbKF7FIM/L8q3Q==}
|
||||
|
||||
jsesc@3.0.2:
|
||||
resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==}
|
||||
engines: {node: '>=6'}
|
||||
@ -5730,8 +5750,8 @@ packages:
|
||||
undici-types@6.20.0:
|
||||
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
|
||||
|
||||
undici@6.21.1:
|
||||
resolution: {integrity: sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==}
|
||||
undici@6.21.2:
|
||||
resolution: {integrity: sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==}
|
||||
engines: {node: '>=18.17'}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1:
|
||||
@ -6142,12 +6162,19 @@ packages:
|
||||
yauzl@2.10.0:
|
||||
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
|
||||
|
||||
yet-another-react-lightbox@3.21.7:
|
||||
resolution: {integrity: sha512-dcdokNuCIl92f0Vl+uzeKULnQhztIGpoZFUMvtVNUPmtwsQWpqWufeieDPeg9JtFyVCcbj4vYw3V00DS0QNoWA==}
|
||||
yet-another-react-lightbox@3.21.8:
|
||||
resolution: {integrity: sha512-8DnjpSmWF+WjGXX+NIJx0V/naUhUYxLt6RIBJZoQ4y1GJVKwiUO2RuRvUBvYQTwAwFqwhJSvxfIhCU3VyCj9WQ==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
'@types/react': ^18.2.22
|
||||
'@types/react-dom': ^18.2.7
|
||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||
react-dom: ^16.8.0 || ^17 || ^18 || ^19
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
yn@3.1.1:
|
||||
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
||||
@ -6196,6 +6223,8 @@ snapshots:
|
||||
jsonpointer: 5.0.1
|
||||
leven: 3.1.0
|
||||
|
||||
'@apocentre/alias-sampling@0.5.3': {}
|
||||
|
||||
'@babel/code-frame@7.26.2':
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.25.9
|
||||
@ -7310,27 +7339,27 @@ snapshots:
|
||||
|
||||
'@codemirror/autocomplete@6.18.6':
|
||||
dependencies:
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.36.4
|
||||
'@lezer/common': 1.2.3
|
||||
|
||||
'@codemirror/commands@6.8.0':
|
||||
dependencies:
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.36.4
|
||||
'@lezer/common': 1.2.3
|
||||
|
||||
'@codemirror/lang-json@6.0.1':
|
||||
dependencies:
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@lezer/json': 1.0.3
|
||||
|
||||
'@codemirror/lang-yaml@6.1.2':
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.18.6
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@codemirror/state': 6.5.2
|
||||
'@lezer/common': 1.2.3
|
||||
'@lezer/highlight': 1.2.1
|
||||
@ -7338,7 +7367,7 @@ snapshots:
|
||||
'@lezer/yaml': 1.0.3
|
||||
optional: true
|
||||
|
||||
'@codemirror/language@6.10.8':
|
||||
'@codemirror/language@6.11.0':
|
||||
dependencies:
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.36.4
|
||||
@ -7365,7 +7394,7 @@ snapshots:
|
||||
|
||||
'@codemirror/theme-one-dark@6.1.2':
|
||||
dependencies:
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.36.4
|
||||
'@lezer/highlight': 1.2.1
|
||||
@ -7557,6 +7586,16 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@gandlaf21/bc-ur@1.1.12':
|
||||
dependencies:
|
||||
'@apocentre/alias-sampling': 0.5.3
|
||||
'@noble/hashes': 1.7.1
|
||||
bignumber.js: 9.1.2
|
||||
buffer: 6.0.3
|
||||
cbor-sync: 1.0.4
|
||||
cborg: 4.2.8
|
||||
jsbi: 3.1.5
|
||||
|
||||
'@getalby/bitcoin-connect-react@3.7.0(@types/react@18.3.18)(react@19.0.0)(typescript@5.8.2)':
|
||||
dependencies:
|
||||
'@getalby/bitcoin-connect': 3.7.0(@types/react@18.3.18)(react@19.0.0)(typescript@5.8.2)
|
||||
@ -8304,38 +8343,38 @@ snapshots:
|
||||
|
||||
'@types/webxr@0.5.21': {}
|
||||
|
||||
'@uiw/codemirror-extensions-basic-setup@4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)':
|
||||
'@uiw/codemirror-extensions-basic-setup@4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)':
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.18.6
|
||||
'@codemirror/commands': 6.8.0
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@codemirror/lint': 6.8.4
|
||||
'@codemirror/search': 6.5.10
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.36.4
|
||||
|
||||
'@uiw/codemirror-theme-github@4.23.10(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)':
|
||||
'@uiw/codemirror-theme-github@4.23.10(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)':
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.23.10(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)
|
||||
'@uiw/codemirror-themes': 4.23.10(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)
|
||||
transitivePeerDependencies:
|
||||
- '@codemirror/language'
|
||||
- '@codemirror/state'
|
||||
- '@codemirror/view'
|
||||
|
||||
'@uiw/codemirror-themes@4.23.10(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)':
|
||||
'@uiw/codemirror-themes@4.23.10(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)':
|
||||
dependencies:
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.36.4
|
||||
|
||||
'@uiw/react-codemirror@4.23.10(@babel/runtime@7.26.10)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.4)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@uiw/react-codemirror@4.23.10(@babel/runtime@7.26.10)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.4)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
'@codemirror/commands': 6.8.0
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/theme-one-dark': 6.1.2
|
||||
'@codemirror/view': 6.36.4
|
||||
'@uiw/codemirror-extensions-basic-setup': 4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)
|
||||
'@uiw/codemirror-extensions-basic-setup': 4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)
|
||||
codemirror: 6.0.1
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
@ -8441,10 +8480,10 @@ snapshots:
|
||||
dependencies:
|
||||
entities: 2.2.0
|
||||
|
||||
applesauce-accounts@0.0.0-next-20250312201602(typescript@5.8.2):
|
||||
applesauce-accounts@0.0.0-next-20250313155042(typescript@5.8.2):
|
||||
dependencies:
|
||||
'@noble/hashes': 1.7.1
|
||||
applesauce-signers: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-signers: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
nanoid: 5.1.3
|
||||
nostr-tools: 2.10.4(typescript@5.8.2)
|
||||
rxjs: 7.8.2
|
||||
@ -8452,22 +8491,22 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-actions@0.0.0-next-20250312201602(typescript@5.8.2):
|
||||
applesauce-actions@0.0.0-next-20250313155042(typescript@5.8.2):
|
||||
dependencies:
|
||||
applesauce-core: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-factory: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-factory: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
nostr-tools: 2.10.4(typescript@5.8.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-content@0.0.0-next-20250312201602(typescript@5.8.2):
|
||||
applesauce-content@0.0.0-next-20250313155042(typescript@5.8.2):
|
||||
dependencies:
|
||||
'@cashu/cashu-ts': 2.0.0-rc1
|
||||
'@types/hast': 3.0.4
|
||||
'@types/mdast': 4.0.4
|
||||
'@types/unist': 3.0.3
|
||||
applesauce-core: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
mdast-util-find-and-replace: 3.0.2
|
||||
nostr-tools: 2.10.4(typescript@5.8.2)
|
||||
remark: 15.0.1
|
||||
@ -8478,7 +8517,7 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-core@0.0.0-next-20250312201602(typescript@5.8.2):
|
||||
applesauce-core@0.0.0-next-20250313155042(typescript@5.8.2):
|
||||
dependencies:
|
||||
'@noble/hashes': 1.7.1
|
||||
'@scure/base': 1.2.4
|
||||
@ -8493,19 +8532,19 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-factory@0.0.0-next-20250312201602(typescript@5.8.2):
|
||||
applesauce-factory@0.0.0-next-20250313155042(typescript@5.8.2):
|
||||
dependencies:
|
||||
applesauce-content: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-content: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
nanoid: 5.1.3
|
||||
nostr-tools: 2.10.4(typescript@5.8.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-loaders@0.0.0-next-20250312201602(typescript@5.8.2):
|
||||
applesauce-loaders@0.0.0-next-20250313155042(typescript@5.8.2):
|
||||
dependencies:
|
||||
applesauce-core: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
nanoid: 5.1.3
|
||||
nostr-tools: 2.10.4(typescript@5.8.2)
|
||||
rx-nostr: 3.5.0
|
||||
@ -8514,13 +8553,13 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-react@0.0.0-next-20250312201602(react-dom@19.0.0(react@19.0.0))(typescript@5.8.2):
|
||||
applesauce-react@0.0.0-next-20250313155042(react-dom@19.0.0(react@19.0.0))(typescript@5.8.2):
|
||||
dependencies:
|
||||
applesauce-accounts: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-actions: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-content: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-factory: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-accounts: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-actions: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-content: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-factory: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
nostr-tools: 2.10.4(typescript@5.8.2)
|
||||
observable-hooks: 4.2.4(react-dom@19.0.0(react@19.0.0))(react@18.3.1)(rxjs@7.8.2)
|
||||
react: 18.3.1
|
||||
@ -8530,9 +8569,9 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-relay@0.0.0-next-20250312201602(typescript@5.8.2):
|
||||
applesauce-relay@0.0.0-next-20250313155042(typescript@5.8.2):
|
||||
dependencies:
|
||||
applesauce-core: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
nanoid: 5.1.3
|
||||
nostr-tools: 2.10.4(typescript@5.8.2)
|
||||
rxjs: 7.8.2
|
||||
@ -8540,12 +8579,12 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-signers@0.0.0-next-20250312201602(typescript@5.8.2):
|
||||
applesauce-signers@0.0.0-next-20250313155042(typescript@5.8.2):
|
||||
dependencies:
|
||||
'@noble/hashes': 1.7.1
|
||||
'@noble/secp256k1': 1.7.1
|
||||
'@scure/base': 1.2.4
|
||||
applesauce-core: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
debug: 4.4.0
|
||||
nanoid: 5.1.3
|
||||
nostr-tools: 2.10.4(typescript@5.8.2)
|
||||
@ -8553,13 +8592,14 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
applesauce-wallet@0.0.0-next-20250312201602(typescript@5.8.2):
|
||||
applesauce-wallet@0.0.0-next-20250313155042(typescript@5.8.2):
|
||||
dependencies:
|
||||
'@cashu/cashu-ts': 2.0.0-rc1
|
||||
'@gandlaf21/bc-ur': 1.1.12
|
||||
'@noble/hashes': 1.7.1
|
||||
applesauce-actions: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-factory: 0.0.0-next-20250312201602(typescript@5.8.2)
|
||||
applesauce-actions: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-core: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
applesauce-factory: 0.0.0-next-20250313155042(typescript@5.8.2)
|
||||
nostr-tools: 2.10.4(typescript@5.8.2)
|
||||
rxjs: 7.8.2
|
||||
transitivePeerDependencies:
|
||||
@ -8697,6 +8737,8 @@ snapshots:
|
||||
|
||||
big-integer@1.6.52: {}
|
||||
|
||||
bignumber.js@9.1.2: {}
|
||||
|
||||
bindings@1.5.0:
|
||||
dependencies:
|
||||
file-uri-to-path: 1.0.0
|
||||
@ -8782,8 +8824,8 @@ snapshots:
|
||||
|
||||
browserslist@4.24.4:
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001703
|
||||
electron-to-chromium: 1.5.115
|
||||
caniuse-lite: 1.0.30001704
|
||||
electron-to-chromium: 1.5.116
|
||||
node-releases: 2.0.19
|
||||
update-browserslist-db: 1.1.3(browserslist@4.24.4)
|
||||
|
||||
@ -8832,7 +8874,7 @@ snapshots:
|
||||
|
||||
camelcase@8.0.0: {}
|
||||
|
||||
caniuse-lite@1.0.30001703: {}
|
||||
caniuse-lite@1.0.30001704: {}
|
||||
|
||||
canvas-color-tracker@1.3.1:
|
||||
dependencies:
|
||||
@ -8840,6 +8882,10 @@ snapshots:
|
||||
|
||||
canvas-confetti@1.9.3: {}
|
||||
|
||||
cbor-sync@1.0.4: {}
|
||||
|
||||
cborg@4.2.8: {}
|
||||
|
||||
ccount@2.0.1: {}
|
||||
|
||||
chalk@2.4.2:
|
||||
@ -8893,7 +8939,7 @@ snapshots:
|
||||
parse5: 7.2.1
|
||||
parse5-htmlparser2-tree-adapter: 7.1.0
|
||||
parse5-parser-stream: 7.1.2
|
||||
undici: 6.21.1
|
||||
undici: 6.21.2
|
||||
whatwg-mimetype: 4.0.0
|
||||
|
||||
chevrotain@7.1.1:
|
||||
@ -8930,9 +8976,9 @@ snapshots:
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
|
||||
codemirror-json-schema@0.7.9(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)(@lezer/common@1.2.3):
|
||||
codemirror-json-schema@0.7.9(@codemirror/language@6.11.0)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)(@lezer/common@1.2.3):
|
||||
dependencies:
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@codemirror/lint': 6.8.4
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.36.4
|
||||
@ -8955,7 +9001,7 @@ snapshots:
|
||||
|
||||
codemirror-json5@1.0.3:
|
||||
dependencies:
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.36.4
|
||||
'@lezer/common': 1.2.3
|
||||
@ -8974,7 +9020,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.18.6
|
||||
'@codemirror/commands': 6.8.0
|
||||
'@codemirror/language': 6.10.8
|
||||
'@codemirror/language': 6.11.0
|
||||
'@codemirror/lint': 6.8.4
|
||||
'@codemirror/search': 6.5.10
|
||||
'@codemirror/state': 6.5.2
|
||||
@ -9480,7 +9526,7 @@ snapshots:
|
||||
dependencies:
|
||||
jake: 10.9.2
|
||||
|
||||
electron-to-chromium@1.5.115: {}
|
||||
electron-to-chromium@1.5.116: {}
|
||||
|
||||
elementtree@0.1.7:
|
||||
dependencies:
|
||||
@ -10379,6 +10425,8 @@ snapshots:
|
||||
argparse: 1.0.10
|
||||
esprima: 4.0.1
|
||||
|
||||
jsbi@3.1.5: {}
|
||||
|
||||
jsesc@3.0.2: {}
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
@ -12639,7 +12687,7 @@ snapshots:
|
||||
|
||||
undici-types@6.20.0: {}
|
||||
|
||||
undici@6.21.1: {}
|
||||
undici@6.21.2: {}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
||||
|
||||
@ -13110,10 +13158,13 @@ snapshots:
|
||||
buffer-crc32: 0.2.13
|
||||
fd-slicer: 1.1.0
|
||||
|
||||
yet-another-react-lightbox@3.21.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
yet-another-react-lightbox@3.21.8(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.3.18
|
||||
'@types/react-dom': 18.3.5(@types/react@18.3.18)
|
||||
|
||||
yn@3.1.1: {}
|
||||
|
||||
|
78
src/components/qr-code/native-scanner.ts
Normal file
78
src/components/qr-code/native-scanner.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { Barcode, BarcodeScannerPlugin } from "@capacitor-mlkit/barcode-scanning";
|
||||
import { from, Observable, switchMap } from "rxjs";
|
||||
import { PluginListenerHandle } from "@capacitor/core";
|
||||
|
||||
import { logger } from "../../helpers/debug";
|
||||
|
||||
const log = logger.extend("NativeQrCodeScanner");
|
||||
|
||||
export async function getNativeScanner(): Promise<BarcodeScannerPlugin> {
|
||||
const { BarcodeScanner, GoogleBarcodeScannerModuleInstallState } = await import("@capacitor-mlkit/barcode-scanning");
|
||||
|
||||
const { available } = await BarcodeScanner.isGoogleBarcodeScannerModuleAvailable();
|
||||
if (!available) {
|
||||
// install barcode scanner
|
||||
await BarcodeScanner.installGoogleBarcodeScannerModule();
|
||||
await new Promise<void>(async (res, rej) => {
|
||||
const sub = await BarcodeScanner.addListener("googleBarcodeScannerModuleInstallProgress", (event) => {
|
||||
log("Installing google barcode scanner", event.progress);
|
||||
switch (event.state) {
|
||||
case GoogleBarcodeScannerModuleInstallState.COMPLETED:
|
||||
sub.remove();
|
||||
res();
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.PENDING:
|
||||
log("Pending download");
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.DOWNLOADING:
|
||||
log("Downloading");
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.DOWNLOAD_PAUSED:
|
||||
log("Download paused");
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.INSTALLING:
|
||||
log("Installing");
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.FAILED:
|
||||
sub.remove();
|
||||
rej(new Error("Failed to install"));
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.CANCELED:
|
||||
sub.remove();
|
||||
rej(new Error("Canceled install"));
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const { supported } = await BarcodeScanner.isSupported();
|
||||
if (!supported) throw new Error("Unsupported");
|
||||
const { camera } = await BarcodeScanner.requestPermissions();
|
||||
const granted = camera === "granted" || camera === "limited";
|
||||
if (!granted) throw new Error("Camera access denied");
|
||||
|
||||
return BarcodeScanner;
|
||||
}
|
||||
|
||||
export function getNativeScanStream(scanner: BarcodeScannerPlugin): Observable<Barcode> {
|
||||
return new Observable<Barcode>((observer) => {
|
||||
const sub = scanner.addListener("barcodesScanned", (event) => {
|
||||
for (const barcode of event.barcodes) {
|
||||
observer.next(barcode);
|
||||
}
|
||||
});
|
||||
|
||||
scanner.startScan();
|
||||
|
||||
let handle: PluginListenerHandle | undefined = undefined;
|
||||
sub.then((e) => (handle = e));
|
||||
|
||||
return () => {
|
||||
if (handle) handle.remove();
|
||||
else sub.then((handle) => handle.remove);
|
||||
|
||||
scanner.stopScan();
|
||||
};
|
||||
});
|
||||
}
|
@ -1,103 +1,128 @@
|
||||
import { Suspense, lazy, useCallback } from "react";
|
||||
import { IconButton, IconButtonProps, useDisclosure, useToast } from "@chakra-ui/react";
|
||||
import { Suspense, lazy, useCallback, useEffect, useState } from "react";
|
||||
import { filter, map, merge, Observable, Subject } from "rxjs";
|
||||
import {
|
||||
Button,
|
||||
IconButton,
|
||||
IconButtonProps,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalOverlay,
|
||||
Progress,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { receiveAnimated } from "applesauce-wallet/helpers/animated-qr";
|
||||
|
||||
import { type QrScannerModalProps } from "./qr-scanner-modal";
|
||||
import { CAP_IS_NATIVE } from "../../env";
|
||||
import { logger } from "../../helpers/debug";
|
||||
import { QrCodeIcon } from "../icons";
|
||||
import { getNativeScanner, getNativeScanStream } from "./native-scanner";
|
||||
|
||||
const QrScannerModal = lazy(() => import("./qr-scanner-modal"));
|
||||
const BarcodeScannerComponent = lazy(() => import("react-qr-barcode-scanner"));
|
||||
const log = logger.extend("QRCodeScanner");
|
||||
|
||||
async function scanWithNative() {
|
||||
const { BarcodeScanner, BarcodeFormat, GoogleBarcodeScannerModuleInstallState } = await import(
|
||||
"@capacitor-mlkit/barcode-scanning"
|
||||
);
|
||||
const { available } = await BarcodeScanner.isGoogleBarcodeScannerModuleAvailable();
|
||||
if (!available) {
|
||||
await BarcodeScanner.installGoogleBarcodeScannerModule();
|
||||
await new Promise<void>(async (res, rej) => {
|
||||
const sub = await BarcodeScanner.addListener("googleBarcodeScannerModuleInstallProgress", (event) => {
|
||||
log("Installing google barcode scanner", event.progress);
|
||||
switch (event.state) {
|
||||
case GoogleBarcodeScannerModuleInstallState.COMPLETED:
|
||||
sub.remove();
|
||||
res();
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.PENDING:
|
||||
log("Pending download");
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.DOWNLOADING:
|
||||
log("Downloading");
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.DOWNLOAD_PAUSED:
|
||||
log("Download paused");
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.INSTALLING:
|
||||
log("Installing");
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.FAILED:
|
||||
sub.remove();
|
||||
rej(new Error("Failed to install"));
|
||||
break;
|
||||
case GoogleBarcodeScannerModuleInstallState.CANCELED:
|
||||
sub.remove();
|
||||
rej(new Error("Canceled install"));
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const { supported } = await BarcodeScanner.isSupported();
|
||||
if (!supported) throw new Error("Unsupported");
|
||||
const { camera } = await BarcodeScanner.requestPermissions();
|
||||
const granted = camera === "granted" || camera === "limited";
|
||||
|
||||
if (!granted) throw new Error("Camera access denied");
|
||||
|
||||
try {
|
||||
const { barcodes } = await BarcodeScanner.scan({
|
||||
formats: [BarcodeFormat.QrCode],
|
||||
});
|
||||
|
||||
const barcode = barcodes[0];
|
||||
if (!barcode) return null;
|
||||
|
||||
return barcode.rawValue;
|
||||
} catch (error) {
|
||||
// user closed scanner
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default function QRCodeScannerButton({
|
||||
onData,
|
||||
onResult,
|
||||
...props
|
||||
}: { onData: QrScannerModalProps["onData"] } & Omit<IconButtonProps, "icon" | "aria-label">) {
|
||||
}: { onResult: (data: string) => void } & Omit<IconButtonProps, "icon" | "aria-label">) {
|
||||
const toast = useToast();
|
||||
const modal = useDisclosure();
|
||||
|
||||
const [progress, setProgress] = useState<number>();
|
||||
const [stream, setStream] = useState<Observable<string> | Subject<string>>();
|
||||
|
||||
const openModal = useCallback(() => {
|
||||
setStream(new Subject());
|
||||
modal.onOpen();
|
||||
}, [modal.onOpen, setStream]);
|
||||
|
||||
const [stopStream, setStopStream] = useState(false);
|
||||
const closeModal = useCallback(() => {
|
||||
// Stop the QR Reader stream (fixes issue where the browser freezes when closing the modal) and then dismiss the modal one tick later
|
||||
setStopStream(true);
|
||||
setTimeout(() => modal.onClose(), 0);
|
||||
}, [setStopStream, modal.onClose]);
|
||||
|
||||
const openNative = useCallback(async () => {
|
||||
const scanner = await getNativeScanner();
|
||||
const stream = getNativeScanStream(scanner);
|
||||
setStream(stream.pipe(map((barcode) => barcode.rawValue)));
|
||||
}, [setStream]);
|
||||
|
||||
const handleClick = useCallback(async () => {
|
||||
if (CAP_IS_NATIVE) {
|
||||
try {
|
||||
const result = await scanWithNative();
|
||||
if (result) onData(result);
|
||||
await openNative();
|
||||
} catch (error) {
|
||||
log(error);
|
||||
if (import.meta.env.DEV && error instanceof Error) toast({ status: "error", description: error.message });
|
||||
|
||||
modal.onOpen();
|
||||
openModal();
|
||||
}
|
||||
} else modal.onOpen();
|
||||
}, [modal.onOpen]);
|
||||
} else openModal();
|
||||
}, [openModal, openNative]);
|
||||
|
||||
// listen to the scanning stream
|
||||
useEffect(() => {
|
||||
if (stream) {
|
||||
setProgress(undefined);
|
||||
|
||||
const normal = stream.pipe(filter((part) => !part.startsWith("ur:bytes")));
|
||||
const animated = stream.pipe(receiveAnimated);
|
||||
|
||||
const sub = merge(normal, animated).subscribe({
|
||||
next: (part) => {
|
||||
if (typeof part === "number") {
|
||||
// progress
|
||||
setProgress(part);
|
||||
} else if (part) {
|
||||
// close the javascript scanner
|
||||
closeModal();
|
||||
// wait for steam to be stopped before returning data
|
||||
setTimeout(() => {
|
||||
onResult(part);
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
if (err instanceof Error) toast({ status: "error", description: err.message });
|
||||
closeModal();
|
||||
},
|
||||
});
|
||||
return () => sub.unsubscribe();
|
||||
}
|
||||
}, [stream, closeModal, onResult, setProgress]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton onClick={handleClick} icon={<QrCodeIcon boxSize={6} />} aria-label="Qr Scanner" {...props} />
|
||||
{modal.isOpen && (
|
||||
<Suspense fallback={null}>
|
||||
<QrScannerModal isOpen={modal.isOpen} onClose={modal.onClose} onData={onData} />
|
||||
<Modal isOpen={modal.isOpen} onClose={closeModal}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalBody p="2">
|
||||
<BarcodeScannerComponent
|
||||
stopStream={stopStream}
|
||||
onUpdate={(err, result) => {
|
||||
if (stream instanceof Subject && result && result.getText()) stream.next(result.getText());
|
||||
}}
|
||||
onError={(err) => {
|
||||
if (!(stream instanceof Subject)) return;
|
||||
if (err instanceof Error) stream.error(err);
|
||||
else stream.error(new Error(err));
|
||||
}}
|
||||
/>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter px="2" pb="2" pt="0" alignItems="center" gap="2">
|
||||
{progress !== undefined && <Progress hasStripe value={progress * 100} w="full" />}
|
||||
<Button onClick={closeModal}>Cancel</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</Suspense>
|
||||
)}
|
||||
</>
|
||||
|
@ -1,37 +0,0 @@
|
||||
import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalOverlay, ModalProps } from "@chakra-ui/react";
|
||||
import { useState } from "react";
|
||||
import BarcodeScannerComponent from "react-qr-barcode-scanner";
|
||||
|
||||
export type QrScannerModalProps = { onData: (text: string) => void } & Pick<ModalProps, "isOpen" | "onClose">;
|
||||
export default function QrScannerModal({ isOpen, onClose, onData }: QrScannerModalProps) {
|
||||
const [stopStream, setStopStream] = useState(false);
|
||||
const handleClose = () => {
|
||||
// Stop the QR Reader stream (fixes issue where the browser freezes when closing the modal) and then dismiss the modal one tick later
|
||||
setStopStream(true);
|
||||
setTimeout(() => onClose(), 0);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={handleClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalBody p="2">
|
||||
<BarcodeScannerComponent
|
||||
stopStream={stopStream}
|
||||
onUpdate={(err, result) => {
|
||||
if (result && result.getText()) {
|
||||
handleClose();
|
||||
// wait for steam to be stopped before returning data
|
||||
setTimeout(() => onData(result.getText()), 0);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter px="2" pb="2" pt="0">
|
||||
<Button onClick={handleClose}>Cancel</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -48,7 +48,7 @@ export default function WebRtcConnectView() {
|
||||
|
||||
<Flex as="form" gap="2" onSubmit={connect}>
|
||||
<Input placeholder="webrtc+nostr:npub1..." {...register("uri")} autoComplete="off" />
|
||||
<QRCodeScannerButton onData={(data) => setValue("uri", data)} />
|
||||
<QRCodeScannerButton onResult={(data) => setValue("uri", data)} />
|
||||
<Button colorScheme="primary" type="submit" isLoading={formState.isSubmitting}>
|
||||
Connect
|
||||
</Button>
|
||||
|
@ -76,7 +76,7 @@ export function SearchPage() {
|
||||
<VerticalPageLayout>
|
||||
<Flex as="form" gap="2" wrap="wrap" onSubmit={submit}>
|
||||
<ButtonGroup>
|
||||
<QRCodeScannerButton onData={handleSearchText} />
|
||||
<QRCodeScannerButton onResult={handleSearchText} />
|
||||
{!!navigator.clipboard?.readText && (
|
||||
<IconButton
|
||||
onClick={readClipboard}
|
||||
|
@ -37,7 +37,7 @@ function ConnectForm() {
|
||||
<FormLabel>Bakery URL</FormLabel>
|
||||
<Flex gap="2">
|
||||
<Input type="text" {...register("url", { required: true })} isRequired placeholder="ws://localhost:2012" />
|
||||
<QRCodeScannerButton onData={handleScanData} />
|
||||
<QRCodeScannerButton onResult={handleScanData} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
|
||||
|
@ -93,7 +93,7 @@ export default function BakerySetupView() {
|
||||
<FormLabel>Owner</FormLabel>
|
||||
<Flex gap="2">
|
||||
<Input {...register("owner", { required: true })} isRequired placeholder="john@example.com" />
|
||||
<QRCodeScannerButton onData={(url) => setValue("owner", url)} />
|
||||
<QRCodeScannerButton onResult={(url) => setValue("owner", url)} />
|
||||
</Flex>
|
||||
<FormHelperText>Enter the NIP-05, npub, or hex pubkey of the owner of this node</FormHelperText>
|
||||
</FormControl>
|
||||
|
@ -131,7 +131,7 @@ export default function LoginNostrAddressView() {
|
||||
onChange={(e) => setAddress(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<QRCodeScannerButton onData={(v) => setAddress(v)} />
|
||||
<QRCodeScannerButton onResult={(v) => setAddress(v)} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
{renderStatus()}
|
||||
|
@ -151,7 +151,7 @@ export default function LoginNostrConnectView() {
|
||||
onChange={(e) => setConnection(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<QRCodeScannerButton onData={(v) => setConnection(v)} />
|
||||
<QRCodeScannerButton onResult={(v) => setConnection(v)} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
)}
|
||||
|
@ -32,7 +32,7 @@ export default function LoginNpubView() {
|
||||
<FormLabel>Enter user npub</FormLabel>
|
||||
<Flex gap="2">
|
||||
<Input type="text" placeholder="npub1" isRequired value={npub} onChange={(e) => setNpub(e.target.value)} />
|
||||
<QRCodeScannerButton onData={(v) => setNpub(v)} />
|
||||
<QRCodeScannerButton onResult={(v) => setNpub(v)} />
|
||||
</Flex>
|
||||
<FormHelperText>
|
||||
Enter any npub you want.{" "}
|
||||
|
@ -127,7 +127,7 @@ export default function LoginNsecView() {
|
||||
/>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
<QRCodeScannerButton onData={(v) => setValue("value", v)} />
|
||||
<QRCodeScannerButton onResult={(v) => setValue("value", v)} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
|
||||
|
@ -1,19 +1,31 @@
|
||||
import { useCallback } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Button, Card, CardBody, CardHeader, CardProps, Flex, Text } from "@chakra-ui/react";
|
||||
import { useStoreQuery } from "applesauce-react/hooks";
|
||||
import { WalletBalanceQuery } from "applesauce-wallet/queries";
|
||||
import { ECashIcon } from "../../components/icons";
|
||||
import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
||||
import { WALLET_KIND } from "applesauce-wallet/helpers";
|
||||
import useEventUpdate from "../../hooks/use-event-update";
|
||||
import QRCodeScannerButton from "../../components/qr-code/qr-code-scanner-button";
|
||||
import RouterLink from "../../components/router-link";
|
||||
|
||||
import { ECashIcon } from "../../../components/icons";
|
||||
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
|
||||
import useEventUpdate from "../../../hooks/use-event-update";
|
||||
import QRCodeScannerButton from "../../../components/qr-code/qr-code-scanner-button";
|
||||
import RouterLink from "../../../components/router-link";
|
||||
|
||||
export default function WalletBalanceCard({ pubkey, ...props }: { pubkey: string } & Omit<CardProps, "children">) {
|
||||
const navigate = useNavigate();
|
||||
const wallet = useReplaceableEvent({ kind: WALLET_KIND, pubkey });
|
||||
useEventUpdate(wallet?.id);
|
||||
|
||||
const balance = useStoreQuery(WalletBalanceQuery, [pubkey]);
|
||||
|
||||
const handleScan = useCallback(
|
||||
(data: string) => {
|
||||
if (data.startsWith("cashuA") || data.startsWith("cashuB"))
|
||||
navigate("/wallet/receive", { state: { input: data } });
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
|
||||
return (
|
||||
<Card {...props}>
|
||||
<CardHeader gap="4" display="flex" justifyContent="center" alignItems="center" pt="10">
|
||||
@ -24,10 +36,10 @@ export default function WalletBalanceCard({ pubkey, ...props }: { pubkey: string
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Flex gap="2" w="full">
|
||||
<Button isDisabled w="full" size="lg">
|
||||
<Button as={RouterLink} w="full" size="lg" to="/wallet/send">
|
||||
Send
|
||||
</Button>
|
||||
<QRCodeScannerButton onData={() => {}} isDisabled size="lg" />
|
||||
<QRCodeScannerButton onResult={handleScan} size="lg" />
|
||||
<Button as={RouterLink} w="full" size="lg" to="/wallet/receive">
|
||||
Receive
|
||||
</Button>
|
25
src/views/wallet/components/wallet-unlock-button.tsx
Normal file
25
src/views/wallet/components/wallet-unlock-button.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { Button, ButtonProps } from "@chakra-ui/react";
|
||||
import { useActionHub, useActiveAccount } from "applesauce-react/hooks";
|
||||
import { UnlockWallet } from "applesauce-wallet/actions";
|
||||
|
||||
import useUserWallet from "../../../hooks/use-user-wallet";
|
||||
import useAsyncErrorHandler from "../../../hooks/use-async-error-handler";
|
||||
|
||||
export default function WalletUnlockButton({ children, ...props }: Omit<ButtonProps, "onClick" | "isLoading">) {
|
||||
const account = useActiveAccount()!;
|
||||
const wallet = useUserWallet(account.pubkey);
|
||||
|
||||
const actions = useActionHub();
|
||||
const unlock = useAsyncErrorHandler(async () => {
|
||||
if (!wallet) throw new Error("Missing wallet");
|
||||
if (wallet.locked === false) return;
|
||||
|
||||
await actions.run(UnlockWallet, { history: true, tokens: true });
|
||||
}, [wallet, actions]);
|
||||
|
||||
return (
|
||||
<Button onClick={unlock} {...props}>
|
||||
{children || "Unlock"}
|
||||
</Button>
|
||||
);
|
||||
}
|
@ -1,22 +1,21 @@
|
||||
import { Button, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
|
||||
import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
|
||||
import { kinds } from "nostr-tools";
|
||||
import { WalletBalanceQuery } from "applesauce-wallet/queries";
|
||||
import { UnlockWallet } from "applesauce-wallet/actions";
|
||||
import { WALLET_HISTORY_KIND, WALLET_TOKEN_KIND } from "applesauce-wallet/helpers";
|
||||
|
||||
import { useActiveAccount, useStoreQuery, useActionHub } from "applesauce-react/hooks";
|
||||
import useAsyncErrorHandler from "../../hooks/use-async-error-handler";
|
||||
import { useActiveAccount, useStoreQuery } from "applesauce-react/hooks";
|
||||
import SimpleView from "../../components/layout/presets/simple-view";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import useUserMailboxes from "../../hooks/use-user-mailboxes";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
|
||||
import WalletBalanceCard from "./balance-card";
|
||||
import WalletBalanceCard from "./components/balance-card";
|
||||
import WalletTokensTab from "./tabs/tokens";
|
||||
import WalletHistoryTab from "./tabs/history";
|
||||
import WalletMintsTab from "./tabs/mints";
|
||||
import useUserWallet from "../../hooks/use-user-wallet";
|
||||
import WalletUnlockButton from "./components/wallet-unlock-button";
|
||||
|
||||
export default function WalletHomeView() {
|
||||
const account = useActiveAccount()!;
|
||||
@ -33,34 +32,16 @@ export default function WalletHomeView() {
|
||||
]);
|
||||
const balance = useStoreQuery(WalletBalanceQuery, [account.pubkey]);
|
||||
|
||||
const actions = useActionHub();
|
||||
const unlock = useAsyncErrorHandler(async () => {
|
||||
if (!wallet) throw new Error("Missing wallet");
|
||||
if (wallet.locked === false) return;
|
||||
|
||||
await actions.run(UnlockWallet, { history: true, tokens: true });
|
||||
}, [wallet, actions]);
|
||||
|
||||
const callback = useTimelineCurserIntersectionCallback(loader);
|
||||
|
||||
return (
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
<SimpleView
|
||||
title="Wallet"
|
||||
actions={
|
||||
wallet?.locked && (
|
||||
<Button onClick={unlock} colorScheme="primary" ms="auto" size="sm">
|
||||
Unlock
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
actions={wallet?.locked && <WalletUnlockButton colorScheme="primary" ms="auto" size="sm" />}
|
||||
>
|
||||
<WalletBalanceCard pubkey={account.pubkey} w="full" maxW="2xl" mx="auto" />
|
||||
{wallet?.locked && (
|
||||
<Button onClick={unlock} colorScheme="primary" mx="auto" size="lg" w="sm">
|
||||
Unlock
|
||||
</Button>
|
||||
)}
|
||||
{wallet?.locked && <WalletUnlockButton colorScheme="primary" mx="auto" size="lg" w="sm" />}
|
||||
|
||||
<Tabs isFitted maxW="2xl" mx="auto" w="full" isLazy>
|
||||
<TabList mb="1em">
|
||||
|
5
src/views/wallet/pay/lightning.tsx
Normal file
5
src/views/wallet/pay/lightning.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import SimpleView from "../../../components/layout/presets/simple-view";
|
||||
|
||||
export default function WalletPayLightning() {
|
||||
return <SimpleView title="Pay lightning" maxW="2xl" center></SimpleView>;
|
||||
}
|
@ -1,36 +1,41 @@
|
||||
import { useState } from "react";
|
||||
import { useActionHub } from "applesauce-react/hooks";
|
||||
import { Button, Flex, Textarea, useToast } from "@chakra-ui/react";
|
||||
import { Button, Flex, Spacer, Textarea, useToast } from "@chakra-ui/react";
|
||||
import { getDecodedToken, Token } from "@cashu/cashu-ts";
|
||||
import { ReceiveToken } from "applesauce-wallet/actions";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
import SimpleView from "../../components/layout/presets/simple-view";
|
||||
import { getCashuWallet } from "../../services/cashu-mints";
|
||||
import RouterLink from "../../components/router-link";
|
||||
import QRCodeScannerButton from "../../components/qr-code/qr-code-scanner-button";
|
||||
|
||||
export default function WalletReceiveView() {
|
||||
const location = useLocation();
|
||||
const actions = useActionHub();
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
|
||||
const [input, setInput] = useState("");
|
||||
const [input, setInput] = useState(location.state?.input ?? "");
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const receive = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const decoded = getDecodedToken(input.trim());
|
||||
const originalAmount = decoded.proofs.reduce((t, p) => t + p.amount, 0);
|
||||
|
||||
// swap tokens
|
||||
const wallet = await getCashuWallet(decoded.mint);
|
||||
const proofs = await wallet.receive(decoded);
|
||||
const token: Token = { mint: decoded.mint, proofs };
|
||||
|
||||
// save new tokens
|
||||
await actions.run(ReceiveToken, token);
|
||||
|
||||
const amount = token.proofs.reduce((t, p) => t + p.amount, 0);
|
||||
const fee = originalAmount - amount;
|
||||
|
||||
// save new tokens
|
||||
await actions.run(ReceiveToken, token, undefined, fee || undefined);
|
||||
|
||||
toast({ status: "success", description: `Received ${amount} sats` });
|
||||
|
||||
navigate("/wallet");
|
||||
@ -48,7 +53,9 @@ export default function WalletReceiveView() {
|
||||
<Button as={RouterLink} to="/wallet">
|
||||
Back
|
||||
</Button>
|
||||
<Button colorScheme="primary" onClick={receive} isLoading={loading} ms="auto">
|
||||
<Spacer />
|
||||
<QRCodeScannerButton onResult={setInput} />
|
||||
<Button colorScheme="primary" onClick={receive} isLoading={loading}>
|
||||
Receive
|
||||
</Button>
|
||||
</Flex>
|
||||
|
@ -4,6 +4,9 @@ import { lazy } from "react";
|
||||
|
||||
const WalletHomeView = lazy(() => import("."));
|
||||
const WalletReceiveView = lazy(() => import("./receive"));
|
||||
const WalletSendView = lazy(() => import("./send/index"));
|
||||
const WalletSendCashuView = lazy(() => import("./send/cashu"));
|
||||
const WalletSendTokenView = lazy(() => import("./send/token"));
|
||||
|
||||
export default [
|
||||
{
|
||||
@ -15,4 +18,12 @@ export default [
|
||||
),
|
||||
},
|
||||
{ path: "receive", Component: WalletReceiveView },
|
||||
{
|
||||
path: "send",
|
||||
children: [
|
||||
{ index: true, Component: WalletSendView },
|
||||
{ path: "cashu", Component: WalletSendCashuView },
|
||||
{ path: "token", Component: WalletSendTokenView },
|
||||
],
|
||||
},
|
||||
] satisfies RouteObject[];
|
||||
|
101
src/views/wallet/send/cashu.tsx
Normal file
101
src/views/wallet/send/cashu.tsx
Normal file
@ -0,0 +1,101 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
Input,
|
||||
NumberDecrementStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
Select,
|
||||
} from "@chakra-ui/react";
|
||||
import { WalletBalanceQuery, WalletQuery, WalletTokensQuery } from "applesauce-wallet/queries";
|
||||
import { useActionHub, useActiveAccount, useStoreQuery } from "applesauce-react/hooks";
|
||||
import { CompleteSpend } from "applesauce-wallet/actions";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import SimpleView from "../../../components/layout/presets/simple-view";
|
||||
import CashuMintName from "../../../components/cashu/cashu-mint-name";
|
||||
import WalletUnlockButton from "../components/wallet-unlock-button";
|
||||
import RouterLink from "../../../components/router-link";
|
||||
import { getEncodedToken, Proof, Token } from "@cashu/cashu-ts";
|
||||
import { dumbTokenSelection, getTokenContent } from "applesauce-wallet/helpers";
|
||||
import { getCashuWallet } from "../../../services/cashu-mints";
|
||||
|
||||
export default function WalletSendCashuView() {
|
||||
const navigate = useNavigate();
|
||||
const account = useActiveAccount()!;
|
||||
const wallet = useStoreQuery(WalletQuery, [account.pubkey]);
|
||||
const balance = useStoreQuery(WalletBalanceQuery, [account.pubkey]);
|
||||
const tokens = useStoreQuery(WalletTokensQuery, [account.pubkey, false]);
|
||||
|
||||
const { register, getValues, watch, handleSubmit, formState } = useForm({
|
||||
defaultValues: { amount: 0, mint: "" },
|
||||
mode: "all",
|
||||
});
|
||||
|
||||
watch("mint");
|
||||
|
||||
const actions = useActionHub();
|
||||
const submit = handleSubmit(async (values) => {
|
||||
if (!tokens) return;
|
||||
const selected = dumbTokenSelection(tokens, values.amount, values.mint);
|
||||
const wallet = await getCashuWallet(values.mint);
|
||||
|
||||
// get the proofs
|
||||
const selectedProofs = selected
|
||||
.map((t) => getTokenContent(t)!)
|
||||
.reduce((arr, token) => [...arr, ...token.proofs], [] as Proof[]);
|
||||
|
||||
// swap
|
||||
const send = await wallet.send(values.amount, selectedProofs);
|
||||
|
||||
// save the change
|
||||
await actions.run(CompleteSpend, selected, { proofs: send.keep, mint: values.mint });
|
||||
|
||||
// redirect to the token view
|
||||
const token: Token = {
|
||||
mint: values.mint,
|
||||
proofs: send.send,
|
||||
};
|
||||
navigate("/wallet/send/token", { state: { token: getEncodedToken(token) } });
|
||||
});
|
||||
|
||||
return (
|
||||
<SimpleView as="form" title="Send Cashu" maxW="xl" center onSubmit={submit}>
|
||||
{wallet?.locked && <WalletUnlockButton colorScheme="primary" mx="auto" size="lg" w="sm" />}
|
||||
|
||||
<Select {...register("mint", { required: true })} isRequired>
|
||||
{balance &&
|
||||
Object.entries(balance).map(([mint, amount]) => (
|
||||
<option key={mint} value={mint}>
|
||||
<CashuMintName mint={mint} /> ({amount})
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
<Input
|
||||
size="lg"
|
||||
type="number"
|
||||
min={1}
|
||||
max={getValues("mint") && balance ? balance[getValues("mint")] : undefined}
|
||||
{...register("amount", {
|
||||
required: true,
|
||||
min: 1,
|
||||
max: getValues("mint") && balance ? balance[getValues("mint")] : undefined,
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
|
||||
<Flex direction="row-reverse">
|
||||
<Button type="submit" colorScheme="primary" isLoading={formState.isSubmitting} isDisabled={!formState.isValid}>
|
||||
Create
|
||||
</Button>
|
||||
<Button as={RouterLink} to="/wallet" me="auto">
|
||||
Cancel
|
||||
</Button>
|
||||
</Flex>
|
||||
</SimpleView>
|
||||
);
|
||||
}
|
32
src/views/wallet/send/index.tsx
Normal file
32
src/views/wallet/send/index.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { Button, Card, LinkBox, Text } from "@chakra-ui/react";
|
||||
|
||||
import SimpleView from "../../../components/layout/presets/simple-view";
|
||||
import { ECashIcon, LightningIcon } from "../../../components/icons";
|
||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||
import RouterLink from "../../../components/router-link";
|
||||
|
||||
export default function WalletSendView() {
|
||||
return (
|
||||
<SimpleView title="Send" maxW="xl" center>
|
||||
<Card as={LinkBox} p="4" gap="4" display="flex" flexDirection="row" alignItems="center">
|
||||
<ECashIcon boxSize={10} />
|
||||
<HoverLinkOverlay as={RouterLink} to="/wallet/send/cashu">
|
||||
<Text fontWeight="bold" fontSize="xl">
|
||||
ECash
|
||||
</Text>
|
||||
</HoverLinkOverlay>
|
||||
</Card>
|
||||
<Card as={LinkBox} p="4" gap="4" display="flex" flexDirection="row" alignItems="center">
|
||||
<LightningIcon boxSize={10} color="yellow.400" />
|
||||
<HoverLinkOverlay as={RouterLink} to="/wallet/pay/lightning">
|
||||
<Text fontWeight="bold" fontSize="xl">
|
||||
Lightning
|
||||
</Text>
|
||||
</HoverLinkOverlay>
|
||||
</Card>
|
||||
<Button as={RouterLink} to="/wallet" me="auto">
|
||||
Back
|
||||
</Button>
|
||||
</SimpleView>
|
||||
);
|
||||
}
|
116
src/views/wallet/send/token.tsx
Normal file
116
src/views/wallet/send/token.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button, ButtonGroup, Flex, Spacer, useToast } from "@chakra-ui/react";
|
||||
import { ANIMATED_QR_INTERVAL, sendAnimated } from "applesauce-wallet/helpers";
|
||||
import { getDecodedToken, Proof, ProofState } from "@cashu/cashu-ts";
|
||||
import { ReceiveToken } from "applesauce-wallet/actions";
|
||||
import { useActionHub } from "applesauce-react/hooks";
|
||||
import { Navigate, useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
import SimpleView from "../../../components/layout/presets/simple-view";
|
||||
import RouterLink from "../../../components/router-link";
|
||||
import { CopyIconButton } from "../../../components/copy-icon-button";
|
||||
import QrCodeSvg from "../../../components/qr-code/qr-code-svg";
|
||||
import { filter, from, Observable, switchMap, take } from "rxjs";
|
||||
import { getCashuWallet } from "../../../services/cashu-mints";
|
||||
|
||||
export default function WalletSendTokenView() {
|
||||
const toast = useToast();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const token: string = location.state?.token;
|
||||
if (!token) return <Navigate to="/wallet" />;
|
||||
|
||||
const actions = useActionHub();
|
||||
|
||||
const [speed, setSpeed] = useState(ANIMATED_QR_INTERVAL.MEDIUM);
|
||||
const [data, setData] = useState<string>();
|
||||
|
||||
const shouldAnimate = token.length > 256;
|
||||
|
||||
// update qr code data
|
||||
useEffect(() => {
|
||||
if (shouldAnimate) {
|
||||
const sub = sendAnimated(token, { interval: speed }).subscribe((part) => setData(part));
|
||||
return () => sub.unsubscribe();
|
||||
} else setData(token);
|
||||
}, [token, speed, shouldAnimate]);
|
||||
|
||||
// subscribe to redeemed state
|
||||
useEffect(() => {
|
||||
const decoded = getDecodedToken(token);
|
||||
const sub = from(getCashuWallet(decoded.mint))
|
||||
.pipe(
|
||||
switchMap((wallet) => {
|
||||
// subscribe to proof states
|
||||
return new Observable<ProofState & { proof: Proof }>((observer) => {
|
||||
// TODO: cancel subscription
|
||||
wallet.onProofStateUpdates(
|
||||
decoded.proofs,
|
||||
(state) => observer.next(state),
|
||||
(err) => observer.error(err),
|
||||
);
|
||||
});
|
||||
}),
|
||||
// look for spent proofs
|
||||
filter((state) => state.state === "SPENT"),
|
||||
// only wait for one to be spent
|
||||
take(1),
|
||||
)
|
||||
.subscribe(() => {
|
||||
toast({ status: "success", description: "Tokens sent" });
|
||||
navigate("/wallet");
|
||||
});
|
||||
|
||||
return () => sub.unsubscribe();
|
||||
}, [token]);
|
||||
|
||||
const [canceling, setCanceling] = useState(false);
|
||||
const cancel = async () => {
|
||||
setCanceling(true);
|
||||
try {
|
||||
await actions.run(ReceiveToken, getDecodedToken(token));
|
||||
navigate("/wallet");
|
||||
} catch (error) {}
|
||||
setCanceling(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<SimpleView title="Cashu Token" maxW="xl" center>
|
||||
{data && <QrCodeSvg content={data} w="full" aspectRatio={1} />}
|
||||
|
||||
{shouldAnimate && (
|
||||
<ButtonGroup size="xs" mx="auto">
|
||||
<Button
|
||||
colorScheme={speed === ANIMATED_QR_INTERVAL.SLOW ? "primary" : undefined}
|
||||
onClick={() => setSpeed(ANIMATED_QR_INTERVAL.SLOW)}
|
||||
>
|
||||
Slow
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme={speed === ANIMATED_QR_INTERVAL.MEDIUM ? "primary" : undefined}
|
||||
onClick={() => setSpeed(ANIMATED_QR_INTERVAL.MEDIUM)}
|
||||
>
|
||||
Normal
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme={speed === ANIMATED_QR_INTERVAL.FAST ? "primary" : undefined}
|
||||
onClick={() => setSpeed(ANIMATED_QR_INTERVAL.FAST)}
|
||||
>
|
||||
Fast
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)}
|
||||
|
||||
<Flex gap="2">
|
||||
<CopyIconButton value={token} aria-label="Copy token" />
|
||||
<Spacer />
|
||||
<Button onClick={cancel} isLoading={canceling}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button as={RouterLink} to="/wallet" colorScheme="primary">
|
||||
Done
|
||||
</Button>
|
||||
</Flex>
|
||||
</SimpleView>
|
||||
);
|
||||
}
|
@ -34,12 +34,15 @@ import useSingleEvents from "../../../hooks/use-single-events";
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import CashuMintFavicon from "../../../components/cashu/cashu-mint-favicon";
|
||||
import CashuMintName from "../../../components/cashu/cashu-mint-name";
|
||||
import { usePublishEvent } from "../../../providers/global/publish-provider";
|
||||
import factory from "../../../services/event-factory";
|
||||
|
||||
function HistoryEntry({ entry }: { entry: NostrEvent }) {
|
||||
const account = useActiveAccount()!;
|
||||
const eventStore = useEventStore();
|
||||
const locked = isHistoryContentLocked(entry);
|
||||
const details = !locked ? getHistoryContent(entry) : undefined;
|
||||
const publish = usePublishEvent();
|
||||
useEventUpdate(entry.id);
|
||||
|
||||
const ref = useEventIntersectionRef(entry);
|
||||
@ -95,7 +98,7 @@ function HistoryEntry({ entry }: { entry: NostrEvent }) {
|
||||
<Text mr="2">Redeemed zaps from:</Text>
|
||||
<AvatarGroup size="sm">
|
||||
{redeemed.map((event) => (
|
||||
<UserAvatarLink pubkey={event.pubkey} />
|
||||
<UserAvatarLink key={event.id} pubkey={event.pubkey} />
|
||||
))}
|
||||
</AvatarGroup>
|
||||
</>
|
||||
@ -114,6 +117,7 @@ function HistoryEntry({ entry }: { entry: NostrEvent }) {
|
||||
export default function WalletHistoryTab() {
|
||||
const account = useActiveAccount()!;
|
||||
const eventStore = useEventStore();
|
||||
const publish = usePublishEvent();
|
||||
|
||||
const history = useStoreQuery(WalletHistoryQuery, [account.pubkey]) ?? [];
|
||||
const locked = useStoreQuery(WalletHistoryQuery, [account.pubkey, true]) ?? [];
|
||||
@ -126,6 +130,12 @@ export default function WalletHistoryTab() {
|
||||
}
|
||||
}, [locked, account, eventStore]);
|
||||
|
||||
const clear = useAsyncErrorHandler(async () => {
|
||||
if (confirm("Are you sure you want to clear history?") !== true) return;
|
||||
const draft = await factory.delete(history);
|
||||
await publish("Clear history", draft);
|
||||
}, [factory, publish, history]);
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="2" w="full">
|
||||
{locked && locked.length > 0 && (
|
||||
@ -134,6 +144,11 @@ export default function WalletHistoryTab() {
|
||||
</Button>
|
||||
)}
|
||||
{history?.map((entry) => <HistoryEntry key={entry.id} entry={entry} />)}
|
||||
{history.length > 0 && (
|
||||
<Button variant="link" onClick={clear} ms="auto">
|
||||
Clear history
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user