chore(git): merge pull request #230 from 92-send-completion-dm into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m53s
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m53s
Reviewed-on: #230 Reviewed-by: s <s@noreply.git.nostrdev.com>
This commit is contained in:
commit
093416a481
196
package-lock.json
generated
196
package-lock.json
generated
@ -20,8 +20,8 @@
|
|||||||
"@mui/lab": "5.0.0-alpha.166",
|
"@mui/lab": "5.0.0-alpha.166",
|
||||||
"@mui/material": "5.15.11",
|
"@mui/material": "5.15.11",
|
||||||
"@noble/hashes": "^1.4.0",
|
"@noble/hashes": "^1.4.0",
|
||||||
"@nostr-dev-kit/ndk": "2.10.0",
|
"@nostr-dev-kit/ndk": "2.11.0",
|
||||||
"@nostr-dev-kit/ndk-cache-dexie": "2.5.1",
|
"@nostr-dev-kit/ndk-cache-dexie": "2.5.9",
|
||||||
"@pdf-lib/fontkit": "^1.1.1",
|
"@pdf-lib/fontkit": "^1.1.1",
|
||||||
"@reduxjs/toolkit": "2.2.1",
|
"@reduxjs/toolkit": "2.2.1",
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.7.4",
|
||||||
@ -1698,15 +1698,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@noble/secp256k1": {
|
"node_modules/@noble/secp256k1": {
|
||||||
"version": "2.0.0",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-2.2.3.tgz",
|
||||||
"integrity": "sha512-rUGBd95e2a45rlmFTqQJYEFA4/gdIARFfuTuTqLglz0PZ6AKyzyXsEZZq7UZn8hZsvaBgpCzKKBJizT2cJERXw==",
|
"integrity": "sha512-l7r5oEQym9Us7EAigzg30/PQAvynhMt2uoYtT3t26eGDVm9Yii5mZ5jWSWmZ/oSIR2Et0xfc6DXrG0bZ787V3w==",
|
||||||
"funding": [
|
"license": "MIT",
|
||||||
{
|
"funding": {
|
||||||
"type": "individual",
|
"url": "https://paulmillr.com/funding/"
|
||||||
"url": "https://paulmillr.com/funding/"
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
@ -1744,19 +1742,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nostr-dev-kit/ndk": {
|
"node_modules/@nostr-dev-kit/ndk": {
|
||||||
"version": "2.10.0",
|
"version": "2.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.11.0.tgz",
|
||||||
"integrity": "sha512-TqCAAo6ylORraAXrzRkCGFN2xTMiFbdER8Y8CtUT0HwOpFG/Wn+PBNeDeDmqkl/6LaPdeyXmVwCWj2KcUjIwYA==",
|
"integrity": "sha512-FKIMtcVsVcquzrC+yir9lOXHCIHmQ3IKEVCMohqEB7N96HjP2qrI9s5utbjI3lkavFNF5tXg1Gp9ODEo7XCfLA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/curves": "^1.4.0",
|
"@noble/curves": "^1.6.0",
|
||||||
"@noble/hashes": "^1.3.1",
|
"@noble/hashes": "^1.5.0",
|
||||||
"@noble/secp256k1": "^2.0.0",
|
"@noble/secp256k1": "^2.1.0",
|
||||||
"@scure/base": "^1.1.1",
|
"@scure/base": "^1.1.9",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.6",
|
||||||
"light-bolt11-decoder": "^3.0.0",
|
"light-bolt11-decoder": "^3.2.0",
|
||||||
"node-fetch": "^3.3.1",
|
|
||||||
"nostr-tools": "^2.7.1",
|
"nostr-tools": "^2.7.1",
|
||||||
"tseep": "^1.1.1",
|
"tseep": "^1.2.2",
|
||||||
"typescript-lru-cache": "^2.0.0",
|
"typescript-lru-cache": "^2.0.0",
|
||||||
"utf8-buffer": "^1.0.0",
|
"utf8-buffer": "^1.0.0",
|
||||||
"websocket-polyfill": "^0.0.3"
|
"websocket-polyfill": "^0.0.3"
|
||||||
@ -1766,17 +1764,41 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nostr-dev-kit/ndk-cache-dexie": {
|
"node_modules/@nostr-dev-kit/ndk-cache-dexie": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.9",
|
||||||
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-cache-dexie/-/ndk-cache-dexie-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-cache-dexie/-/ndk-cache-dexie-2.5.9.tgz",
|
||||||
"integrity": "sha512-tUwEy68bd9GL5JVuZIjcpdwuDEBnaXen3WJ64/GRDtbyE1RB01Y6hHC7IQC9bcQ6SC7XBGyPd+2nuTyR7+Mffg==",
|
"integrity": "sha512-SZ5FjON0QPekiC7oW9Hy3JQxG0Oxxtud9LBa1q/A49JV/Qppv1x37nFHxi0XLxEbDgFTNYbaN27Zjfp2NPem2g==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nostr-dev-kit/ndk": "2.10.0",
|
"@nostr-dev-kit/ndk": "2.11.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.7",
|
||||||
"dexie": "^4.0.2",
|
"dexie": "^4.0.8",
|
||||||
"nostr-tools": "^2.4.0",
|
"nostr-tools": "^2.4.0",
|
||||||
"typescript-lru-cache": "^2.0.0"
|
"typescript-lru-cache": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-dexie/node_modules/debug": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-dexie/node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@nostr-dev-kit/ndk/node_modules/@noble/curves": {
|
"node_modules/@nostr-dev-kit/ndk/node_modules/@noble/curves": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz",
|
||||||
@ -1802,6 +1824,15 @@
|
|||||||
"url": "https://paulmillr.com/funding/"
|
"url": "https://paulmillr.com/funding/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk/node_modules/@scure/base": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nostr-dev-kit/ndk/node_modules/nostr-tools": {
|
"node_modules/@nostr-dev-kit/ndk/node_modules/nostr-tools": {
|
||||||
"version": "2.10.4",
|
"version": "2.10.4",
|
||||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.10.4.tgz",
|
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.10.4.tgz",
|
||||||
@ -1859,6 +1890,24 @@
|
|||||||
"url": "https://paulmillr.com/funding/"
|
"url": "https://paulmillr.com/funding/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk/node_modules/nostr-tools/node_modules/@scure/base": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk/node_modules/tseep": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tseep/-/tseep-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-ZPtfk1tQnZVyr7BPtbJ93qaAh2lZuIOpTMjhrYa4XctT8xe7t4SAW9LIxrySDuYMsfNNayE51E/WNGrNVgVicQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@octokit/auth-token": {
|
"node_modules/@octokit/auth-token": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz",
|
||||||
@ -6141,14 +6190,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/data-uri-to-buffer": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dateformat": {
|
"node_modules/dateformat": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
||||||
@ -7370,28 +7411,6 @@
|
|||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fetch-blob": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
|
||||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/jimmywarting"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "paypal",
|
|
||||||
"url": "https://paypal.me/jimmywarting"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"node-domexception": "^1.0.0",
|
|
||||||
"web-streams-polyfill": "^3.0.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^12.20 || >= 14.13"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/figures": {
|
"node_modules/figures": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||||
@ -7570,17 +7589,6 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/formdata-polyfill": {
|
|
||||||
"version": "4.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
|
||||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
|
||||||
"dependencies": {
|
|
||||||
"fetch-blob": "^3.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.20.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/from2": {
|
"node_modules/from2": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
|
||||||
@ -9180,9 +9188,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/light-bolt11-decoder": {
|
"node_modules/light-bolt11-decoder": {
|
||||||
"version": "3.0.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/light-bolt11-decoder/-/light-bolt11-decoder-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/light-bolt11-decoder/-/light-bolt11-decoder-3.2.0.tgz",
|
||||||
"integrity": "sha512-AKvOigD2pmC8ktnn2TIqdJu0K0qk6ukUmTvHwF3JNkm8uWCqt18Ijn33A/a7gaRZ4PghJ59X+8+MXrzLKdBTmQ==",
|
"integrity": "sha512-3QEofgiBOP4Ehs9BI+RkZdXZNtSys0nsJ6fyGeSiAGCBsMwHGUDS/JQlY/sTnWs91A2Nh0S9XXfA8Sy9g6QpuQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scure/base": "1.1.1"
|
"@scure/base": "1.1.1"
|
||||||
}
|
}
|
||||||
@ -10257,24 +10266,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||||
},
|
},
|
||||||
"node_modules/node-domexception": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/jimmywarting"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://paypal.me/jimmywarting"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-emoji": {
|
"node_modules/node-emoji": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
|
||||||
@ -10306,23 +10297,6 @@
|
|||||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-fetch": {
|
|
||||||
"version": "3.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
|
||||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
|
||||||
"dependencies": {
|
|
||||||
"data-uri-to-buffer": "^4.0.0",
|
|
||||||
"fetch-blob": "^3.1.4",
|
|
||||||
"formdata-polyfill": "^4.0.10"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/node-fetch"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-gyp-build": {
|
"node_modules/node-gyp-build": {
|
||||||
"version": "4.8.0",
|
"version": "4.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz",
|
||||||
@ -17462,14 +17436,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/web-streams-polyfill": {
|
|
||||||
"version": "3.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
|
||||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
@ -31,8 +31,8 @@
|
|||||||
"@mui/lab": "5.0.0-alpha.166",
|
"@mui/lab": "5.0.0-alpha.166",
|
||||||
"@mui/material": "5.15.11",
|
"@mui/material": "5.15.11",
|
||||||
"@noble/hashes": "^1.4.0",
|
"@noble/hashes": "^1.4.0",
|
||||||
"@nostr-dev-kit/ndk": "2.10.0",
|
"@nostr-dev-kit/ndk": "2.11.0",
|
||||||
"@nostr-dev-kit/ndk-cache-dexie": "2.5.1",
|
"@nostr-dev-kit/ndk-cache-dexie": "2.5.9",
|
||||||
"@pdf-lib/fontkit": "^1.1.1",
|
"@pdf-lib/fontkit": "^1.1.1",
|
||||||
"@reduxjs/toolkit": "2.2.1",
|
"@reduxjs/toolkit": "2.2.1",
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.7.4",
|
||||||
|
20
src/App.tsx
20
src/App.tsx
@ -4,8 +4,6 @@ import { Navigate, Route, Routes } from 'react-router-dom'
|
|||||||
import { useAppSelector, useAuth } from './hooks'
|
import { useAppSelector, useAuth } from './hooks'
|
||||||
|
|
||||||
import { MainLayout } from './layouts/Main'
|
import { MainLayout } from './layouts/Main'
|
||||||
|
|
||||||
import { appPrivateRoutes, appPublicRoutes } from './routes'
|
|
||||||
import {
|
import {
|
||||||
privateRoutes,
|
privateRoutes,
|
||||||
publicRoutes,
|
publicRoutes,
|
||||||
@ -16,7 +14,7 @@ import './App.scss'
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const { checkSession } = useAuth()
|
const { checkSession } = useAuth()
|
||||||
const authState = useAppSelector((state) => state.auth)
|
const isLoggedIn = useAppSelector((state) => state.auth?.loggedIn)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (window.location.hostname === '0.0.0.0') {
|
if (window.location.hostname === '0.0.0.0') {
|
||||||
@ -29,19 +27,9 @@ const App = () => {
|
|||||||
checkSession()
|
checkSession()
|
||||||
}, [checkSession])
|
}, [checkSession])
|
||||||
|
|
||||||
const handleRootRedirect = () => {
|
|
||||||
if (authState.loggedIn) return appPrivateRoutes.homePage
|
|
||||||
|
|
||||||
const callbackPathEncoded = btoa(
|
|
||||||
window.location.href.split(`${window.location.origin}/#`)[1]
|
|
||||||
)
|
|
||||||
|
|
||||||
return `${appPublicRoutes.landingPage}?callbackPath=${callbackPathEncoded}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide route only if loggedIn and r.hiddenWhenLoggedIn are both true
|
// Hide route only if loggedIn and r.hiddenWhenLoggedIn are both true
|
||||||
const publicRoutesList = recursiveRouteRenderer(publicRoutes, (r) => {
|
const publicRoutesList = recursiveRouteRenderer(publicRoutes, (r) => {
|
||||||
return !authState.loggedIn || !r.hiddenWhenLoggedIn
|
return !isLoggedIn || !r.hiddenWhenLoggedIn
|
||||||
})
|
})
|
||||||
|
|
||||||
const privateRouteList = recursiveRouteRenderer(privateRoutes)
|
const privateRouteList = recursiveRouteRenderer(privateRoutes)
|
||||||
@ -49,9 +37,9 @@ const App = () => {
|
|||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<MainLayout />}>
|
<Route element={<MainLayout />}>
|
||||||
{authState?.loggedIn && privateRouteList}
|
|
||||||
{publicRoutesList}
|
{publicRoutesList}
|
||||||
<Route path="*" element={<Navigate to={handleRootRedirect()} />} />
|
{privateRouteList}
|
||||||
|
<Route path="*" element={<Navigate to={'/'} />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
)
|
||||||
|
@ -56,8 +56,7 @@ export const useAuth = () => {
|
|||||||
* method will be chosen (extension or keys)
|
* method will be chosen (extension or keys)
|
||||||
*
|
*
|
||||||
* @param pubkey of the user trying to login
|
* @param pubkey of the user trying to login
|
||||||
* @returns url to redirect if authentication successfull
|
* @returns url to redirect if user has no relays set
|
||||||
* or error if otherwise
|
|
||||||
*/
|
*/
|
||||||
const authAndGetMetadataAndRelaysMap = useCallback(
|
const authAndGetMetadataAndRelaysMap = useCallback(
|
||||||
async (pubkey: string) => {
|
async (pubkey: string) => {
|
||||||
@ -108,7 +107,7 @@ export const useAuth = () => {
|
|||||||
dispatch(setRelayMapAction(relayMap))
|
dispatch(setRelayMapAction(relayMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
return appPrivateRoutes.homePage
|
return
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
dispatch,
|
dispatch,
|
||||||
|
@ -12,7 +12,9 @@ import {
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import {
|
import {
|
||||||
Event,
|
Event,
|
||||||
|
finalizeEvent,
|
||||||
generateSecretKey,
|
generateSecretKey,
|
||||||
|
getEventHash,
|
||||||
getPublicKey,
|
getPublicKey,
|
||||||
kinds,
|
kinds,
|
||||||
UnsignedEvent
|
UnsignedEvent
|
||||||
@ -40,17 +42,21 @@ import {
|
|||||||
getDTagForUserAppData,
|
getDTagForUserAppData,
|
||||||
getUserAppDataFromBlossom,
|
getUserAppDataFromBlossom,
|
||||||
hexToNpub,
|
hexToNpub,
|
||||||
|
nip44Encrypt,
|
||||||
parseJson,
|
parseJson,
|
||||||
|
randomTimeUpTo2DaysInThePast,
|
||||||
SIGIT_RELAY,
|
SIGIT_RELAY,
|
||||||
unixNow,
|
unixNow,
|
||||||
uploadUserAppDataToBlossom
|
uploadUserAppDataToBlossom
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
import { SendDMError, SendDMErrorType } from '../types/errors/SendDMError'
|
||||||
|
|
||||||
export const useNDK = () => {
|
export const useNDK = () => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const {
|
const {
|
||||||
ndk,
|
ndk,
|
||||||
fetchEvent,
|
fetchEvent,
|
||||||
|
fetchEventFromUserRelays,
|
||||||
fetchEventsFromUserRelays,
|
fetchEventsFromUserRelays,
|
||||||
publish,
|
publish,
|
||||||
getNDKRelayList
|
getNDKRelayList
|
||||||
@ -503,10 +509,139 @@ export const useNDK = () => {
|
|||||||
[ndk, usersPubkey, getNDKRelayList]
|
[ndk, usersPubkey, getNDKRelayList]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modified {@link UnsignedEvent Unsigned Event} that includes an id
|
||||||
|
*
|
||||||
|
* Fields id and created_at are required.
|
||||||
|
* @see {@link UnsignedEvent}
|
||||||
|
* @see {@link https://github.com/nostr-protocol/nips/blob/master/17.md#direct-message-kind}
|
||||||
|
*/
|
||||||
|
type UnsignedEventWithId = UnsignedEvent & {
|
||||||
|
id?: string
|
||||||
|
}
|
||||||
|
const sendPrivateDirectMessage = useCallback(
|
||||||
|
async (message: string, receiver: string, subject?: string) => {
|
||||||
|
if (!receiver) throw new SendDMError(SendDMErrorType.MISSING_RECIEVER)
|
||||||
|
|
||||||
|
// Get the direct message preferred relays list
|
||||||
|
// https://github.com/nostr-protocol/nips/blob/master/17.md#publishing
|
||||||
|
const preferredRelaysListEvent = await fetchEventFromUserRelays(
|
||||||
|
{
|
||||||
|
kinds: [NDKKind.DirectMessageReceiveRelayList],
|
||||||
|
authors: [receiver]
|
||||||
|
},
|
||||||
|
receiver,
|
||||||
|
UserRelaysType.Read
|
||||||
|
)
|
||||||
|
|
||||||
|
const isRelayTag = (tag: string[]): boolean => tag[0] === 'relay'
|
||||||
|
const finalRelaysList: string[] = []
|
||||||
|
if (preferredRelaysListEvent) {
|
||||||
|
const preferredRelaysList = preferredRelaysListEvent.tags
|
||||||
|
.filter((t) => isRelayTag(t))
|
||||||
|
.map((t) => t[1])
|
||||||
|
|
||||||
|
finalRelaysList.push(...preferredRelaysList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!finalRelaysList.length) {
|
||||||
|
// Get receiver's read relay list
|
||||||
|
const ndkRelayList = await getNDKRelayList(receiver).catch((err) => {
|
||||||
|
// Log an error if retrieving relay list metadata fails
|
||||||
|
console.log(
|
||||||
|
`An error occurred while finding relay list metadata for ${hexToNpub(receiver)}`,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
if (ndkRelayList?.readRelayUrls) {
|
||||||
|
finalRelaysList.push(...ndkRelayList.readRelayUrls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!finalRelaysList.includes(SIGIT_RELAY)) {
|
||||||
|
finalRelaysList.push(SIGIT_RELAY)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate "sender"
|
||||||
|
const senderSecret = generateSecretKey()
|
||||||
|
const senderPubkey = getPublicKey(senderSecret)
|
||||||
|
|
||||||
|
// Prepare tags for the message
|
||||||
|
const tags: string[][] = [['p', receiver]]
|
||||||
|
|
||||||
|
// Conversation title
|
||||||
|
if (subject) tags.push(['subject', subject])
|
||||||
|
|
||||||
|
// Create private DM event containing the message and relevant metadata
|
||||||
|
// TODO: kinds.PrivateDirectMessage (unavailabe in nostr-tools 10/10/2024 at v2.7.0)
|
||||||
|
const dm: UnsignedEventWithId = {
|
||||||
|
pubkey: senderPubkey,
|
||||||
|
created_at: unixNow(),
|
||||||
|
kind: 14,
|
||||||
|
tags,
|
||||||
|
content: message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the hash based on the UnverifiedEvent
|
||||||
|
dm.id = getEventHash(dm)
|
||||||
|
|
||||||
|
// Encrypt the private dm using the sender secret and the receiver's public key
|
||||||
|
const encryptedDm = nip44Encrypt(dm, senderSecret, receiver)
|
||||||
|
if (!encryptedDm) {
|
||||||
|
throw new SendDMError(SendDMErrorType.ENCRYPTION_FAILED, {
|
||||||
|
context: {
|
||||||
|
receiver,
|
||||||
|
message,
|
||||||
|
kind: dm.kind
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seal the message
|
||||||
|
// TODO: kinds.Seal (unavailabe in nostr-tools 10/10/2024 at v2.7.0)
|
||||||
|
const sealedMessage: UnsignedEvent = {
|
||||||
|
kind: 13, // seal
|
||||||
|
pubkey: senderPubkey,
|
||||||
|
content: encryptedDm,
|
||||||
|
created_at: randomTimeUpTo2DaysInThePast(),
|
||||||
|
tags: [] // no tags
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize and sign the sealed event
|
||||||
|
const finalizedSeal = finalizeEvent(sealedMessage, senderSecret)
|
||||||
|
|
||||||
|
// Encrypt the seal and gift wrap
|
||||||
|
const finalizedGiftWrap = createWrap(finalizedSeal, receiver)
|
||||||
|
|
||||||
|
const ndkEvent = new NDKEvent(ndk, finalizedGiftWrap)
|
||||||
|
|
||||||
|
// Publish the finalized gift wrap event (the encrypted DM) to the relays
|
||||||
|
const publishedOnRelays = await ndkEvent.publish(
|
||||||
|
NDKRelaySet.fromRelayUrls(finalRelaysList, ndk, true)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handle cases where publishing to the relays failed
|
||||||
|
if (publishedOnRelays.size === 0) {
|
||||||
|
throw new SendDMError(SendDMErrorType.ENCRYPTION_FAILED, {
|
||||||
|
context: {
|
||||||
|
receiver,
|
||||||
|
count: publishedOnRelays.size
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true indicating that the DM was successfully sent
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
[fetchEventFromUserRelays, getNDKRelayList, ndk]
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getUsersAppData,
|
getUsersAppData,
|
||||||
subscribeForSigits,
|
subscribeForSigits,
|
||||||
updateUsersAppData,
|
updateUsersAppData,
|
||||||
sendNotification
|
sendNotification,
|
||||||
|
sendPrivateDirectMessage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { Outlet, useNavigate, useSearchParams } from 'react-router-dom'
|
import { Outlet, useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
|
|
||||||
import { getPublicKey, nip19 } from 'nostr-tools'
|
import { getPublicKey, nip19 } from 'nostr-tools'
|
||||||
|
|
||||||
import { init as initNostrLogin } from 'nostr-login'
|
import { init as initNostrLogin } from 'nostr-login'
|
||||||
import { NostrLoginAuthOptions } from 'nostr-login/dist/types'
|
import { NostrLoginAuthOptions } from 'nostr-login/dist/types'
|
||||||
|
|
||||||
import { AppBar } from '../components/AppBar/AppBar'
|
import { AppBar } from '../components/AppBar/AppBar'
|
||||||
import { LoadingSpinner } from '../components/LoadingSpinner'
|
import { LoadingSpinner } from '../components/LoadingSpinner'
|
||||||
|
|
||||||
import { NostrController } from '../controllers'
|
import { NostrController } from '../controllers'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useAppDispatch,
|
useAppDispatch,
|
||||||
useAppSelector,
|
useAppSelector,
|
||||||
@ -19,7 +14,6 @@ import {
|
|||||||
useNDK,
|
useNDK,
|
||||||
useNDKContext
|
useNDKContext
|
||||||
} from '../hooks'
|
} from '../hooks'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
restoreState,
|
restoreState,
|
||||||
setUserProfile,
|
setUserProfile,
|
||||||
@ -30,9 +24,7 @@ import {
|
|||||||
setUserRobotImage
|
setUserRobotImage
|
||||||
} from '../store/actions'
|
} from '../store/actions'
|
||||||
import { LoginMethod } from '../store/auth/types'
|
import { LoginMethod } from '../store/auth/types'
|
||||||
|
|
||||||
import { getRoboHashPicture, loadState } from '../utils'
|
import { getRoboHashPicture, loadState } from '../utils'
|
||||||
|
|
||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
|
|
||||||
export const MainLayout = () => {
|
export const MainLayout = () => {
|
||||||
@ -53,29 +45,32 @@ export const MainLayout = () => {
|
|||||||
// Ref to track if `subscribeForSigits` has been called
|
// Ref to track if `subscribeForSigits` has been called
|
||||||
const hasSubscribed = useRef(false)
|
const hasSubscribed = useRef(false)
|
||||||
|
|
||||||
const navigateAfterLogin = (path: string) => {
|
const navigateAfterLogin = useCallback(
|
||||||
const callbackPath = searchParams.get('callbackPath')
|
(path: string | undefined) => {
|
||||||
|
const isCallback = window.location.hash.startsWith('#/?callbackPath=')
|
||||||
if (callbackPath) {
|
if (isCallback) {
|
||||||
// base64 decoded path
|
const path = atob(window.location.hash.replace('#/?callbackPath=', ''))
|
||||||
const path = atob(callbackPath)
|
setSearchParams((prev) => {
|
||||||
navigate(path)
|
prev.delete('callbackPath')
|
||||||
return
|
return prev
|
||||||
}
|
})
|
||||||
|
navigate(path)
|
||||||
navigate(path)
|
return
|
||||||
}
|
}
|
||||||
|
if (path) navigate(path)
|
||||||
|
},
|
||||||
|
[navigate, setSearchParams]
|
||||||
|
)
|
||||||
|
|
||||||
const login = useCallback(async () => {
|
const login = useCallback(async () => {
|
||||||
dispatch(updateLoginMethod(LoginMethod.nostrLogin))
|
try {
|
||||||
|
dispatch(updateLoginMethod(LoginMethod.nostrLogin))
|
||||||
const nostrController = NostrController.getInstance()
|
const nostrController = NostrController.getInstance()
|
||||||
const pubkey = await nostrController.capturePublicKey()
|
const pubkey = await nostrController.capturePublicKey()
|
||||||
|
const redirectPath = await authAndGetMetadataAndRelaysMap(pubkey)
|
||||||
const redirectPath = await authAndGetMetadataAndRelaysMap(pubkey)
|
|
||||||
|
|
||||||
if (redirectPath) {
|
|
||||||
navigateAfterLogin(redirectPath)
|
navigateAfterLogin(redirectPath)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error occured during login`, error)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [dispatch])
|
}, [dispatch])
|
||||||
|
@ -45,6 +45,7 @@ import {
|
|||||||
uploadToFileStorage,
|
uploadToFileStorage,
|
||||||
DEFAULT_TOOLBOX,
|
DEFAULT_TOOLBOX,
|
||||||
settleAllFullfilfedPromises,
|
settleAllFullfilfedPromises,
|
||||||
|
parseNostrEvent,
|
||||||
uploadMetaToFileStorage,
|
uploadMetaToFileStorage,
|
||||||
timeout
|
timeout
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
@ -73,6 +74,7 @@ import { getSigitFile, SigitFile } from '../../utils/file.ts'
|
|||||||
import { generateTimestamp } from '../../utils/opentimestamps.ts'
|
import { generateTimestamp } from '../../utils/opentimestamps.ts'
|
||||||
import { Autocomplete } from '@mui/material'
|
import { Autocomplete } from '@mui/material'
|
||||||
import _, { truncate } from 'lodash'
|
import _, { truncate } from 'lodash'
|
||||||
|
import { SendDMError } from '../../types/errors/SendDMError.ts'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { AvatarIconButton } from '../../components/UserAvatarIconButton'
|
import { AvatarIconButton } from '../../components/UserAvatarIconButton'
|
||||||
import { NDKUserProfile, NostrEvent } from '@nostr-dev-kit/ndk'
|
import { NDKUserProfile, NostrEvent } from '@nostr-dev-kit/ndk'
|
||||||
@ -88,7 +90,8 @@ export const CreatePage = () => {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const { findMetadata, fetchEventsFromUserRelays } = useNDKContext()
|
const { findMetadata, fetchEventsFromUserRelays } = useNDKContext()
|
||||||
const { updateUsersAppData, sendNotification } = useNDK()
|
const { updateUsersAppData, sendNotification, sendPrivateDirectMessage } =
|
||||||
|
useNDK()
|
||||||
|
|
||||||
const { uploadedFiles } = location.state || {}
|
const { uploadedFiles } = location.state || {}
|
||||||
const [currentFile, setCurrentFile] = useState<File>()
|
const [currentFile, setCurrentFile] = useState<File>()
|
||||||
@ -935,7 +938,29 @@ export const CreatePage = () => {
|
|||||||
toast.error('Failed to publish notifications')
|
toast.error('Failed to publish notifications')
|
||||||
})
|
})
|
||||||
|
|
||||||
const isFirstSigner = signers[0].pubkey === usersPubkey
|
const isFirstSigner =
|
||||||
|
signers.length > 0 && signers[0].pubkey === usersPubkey
|
||||||
|
|
||||||
|
// Don't send notification if creator is next signer
|
||||||
|
if (signers.length > 0 && !isFirstSigner) {
|
||||||
|
// Send DM to the next signer
|
||||||
|
setLoadingSpinnerDesc('Sending DMs')
|
||||||
|
const nextSigner = signers[0].pubkey
|
||||||
|
const createSignatureEvent = parseNostrEvent(meta.createSignature)
|
||||||
|
const { id } = createSignatureEvent
|
||||||
|
try {
|
||||||
|
await sendPrivateDirectMessage(
|
||||||
|
`Sigit created, visit ${window.location.origin}/#/sign/${id}`,
|
||||||
|
npubToHex(nextSigner)!
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof SendDMError) {
|
||||||
|
toast.error(error.message)
|
||||||
|
}
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isFirstSigner) {
|
if (isFirstSigner) {
|
||||||
navigate(appPrivateRoutes.sign, { state: { meta } })
|
navigate(appPrivateRoutes.sign, { state: { meta } })
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { Box, Button } from '@mui/material'
|
import { Box, Button } from '@mui/material'
|
||||||
import { useEffect } from 'react'
|
import { Outlet } from 'react-router-dom'
|
||||||
import { Outlet, useLocation } from 'react-router-dom'
|
|
||||||
import { saveVisitedLink } from '../../utils'
|
|
||||||
import { CardComponent } from '../../components/Landing/CardComponent/CardComponent'
|
import { CardComponent } from '../../components/Landing/CardComponent/CardComponent'
|
||||||
import { Container } from '../../components/Container'
|
import { Container } from '../../components/Container'
|
||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
@ -20,13 +18,19 @@ import {
|
|||||||
import { FontAwesomeIconStack } from '../../components/FontAwesomeIconStack'
|
import { FontAwesomeIconStack } from '../../components/FontAwesomeIconStack'
|
||||||
import { Footer } from '../../components/Footer/Footer'
|
import { Footer } from '../../components/Footer/Footer'
|
||||||
import { launch as launchNostrLoginDialog } from 'nostr-login'
|
import { launch as launchNostrLoginDialog } from 'nostr-login'
|
||||||
|
import { useDidMount } from '../../hooks'
|
||||||
|
|
||||||
export const LandingPage = () => {
|
export const LandingPage = () => {
|
||||||
const location = useLocation()
|
|
||||||
|
|
||||||
const onSignInClick = async () => {
|
const onSignInClick = async () => {
|
||||||
launchNostrLoginDialog()
|
launchNostrLoginDialog()
|
||||||
}
|
}
|
||||||
|
useDidMount(() => {
|
||||||
|
const isCallback = window.location.hash.startsWith('#/?callbackPath=')
|
||||||
|
// Open nostr login if detect callback
|
||||||
|
if (isCallback) {
|
||||||
|
onSignInClick()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const cards = [
|
const cards = [
|
||||||
{
|
{
|
||||||
@ -101,10 +105,6 @@ export const LandingPage = () => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
saveVisitedLink(location.pathname, location.search)
|
|
||||||
}, [location])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.background}>
|
<div className={styles.background}>
|
||||||
<div
|
<div
|
||||||
|
33
src/pages/settings/cache/index.tsx
vendored
33
src/pages/settings/cache/index.tsx
vendored
@ -1,4 +1,3 @@
|
|||||||
import ClearIcon from '@mui/icons-material/Clear'
|
|
||||||
import InputIcon from '@mui/icons-material/Input'
|
import InputIcon from '@mui/icons-material/Input'
|
||||||
import IosShareIcon from '@mui/icons-material/IosShare'
|
import IosShareIcon from '@mui/icons-material/IosShare'
|
||||||
import {
|
import {
|
||||||
@ -9,36 +8,12 @@ import {
|
|||||||
ListSubheader,
|
ListSubheader,
|
||||||
useTheme
|
useTheme
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import { useState } from 'react'
|
|
||||||
import { toast } from 'react-toastify'
|
|
||||||
import { localCache } from '../../../services'
|
|
||||||
import { LoadingSpinner } from '../../../components/LoadingSpinner'
|
|
||||||
import { Container } from '../../../components/Container'
|
import { Container } from '../../../components/Container'
|
||||||
import { Footer } from '../../../components/Footer/Footer'
|
import { Footer } from '../../../components/Footer/Footer'
|
||||||
|
|
||||||
export const CacheSettingsPage = () => {
|
export const CacheSettingsPage = () => {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
|
||||||
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
|
|
||||||
|
|
||||||
const handleClearData = async () => {
|
|
||||||
setIsLoading(true)
|
|
||||||
setLoadingSpinnerDesc('Clearing cache data')
|
|
||||||
localCache
|
|
||||||
.clearCacheData()
|
|
||||||
.then(() => {
|
|
||||||
toast.success('cleared cached data')
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log('An error occurred in clearing cache data', err)
|
|
||||||
toast.error(err.message || 'An error occurred in clearing cache data')
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoading(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const listItem = (label: string) => {
|
const listItem = (label: string) => {
|
||||||
return (
|
return (
|
||||||
<ListItemText
|
<ListItemText
|
||||||
@ -53,7 +28,6 @@ export const CacheSettingsPage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Container>
|
<Container>
|
||||||
{isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
|
|
||||||
<List
|
<List
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -87,13 +61,6 @@ export const CacheSettingsPage = () => {
|
|||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
{listItem('Import (coming soon)')}
|
{listItem('Import (coming soon)')}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
|
||||||
<ListItemButton onClick={handleClearData}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ClearIcon sx={{ color: theme.palette.error.main }} />
|
|
||||||
</ListItemIcon>
|
|
||||||
{listItem('Clear Cache')}
|
|
||||||
</ListItemButton>
|
|
||||||
</List>
|
</List>
|
||||||
</Container>
|
</Container>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
@ -28,7 +28,8 @@ import {
|
|||||||
signEventForMetaFile,
|
signEventForMetaFile,
|
||||||
unixNow,
|
unixNow,
|
||||||
updateMarks,
|
updateMarks,
|
||||||
uploadMetaToFileStorage
|
uploadMetaToFileStorage,
|
||||||
|
parseNostrEvent
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
import { CurrentUserMark, Mark } from '../../types/mark.ts'
|
import { CurrentUserMark, Mark } from '../../types/mark.ts'
|
||||||
import PdfMarking from '../../components/PDFView/PdfMarking.tsx'
|
import PdfMarking from '../../components/PDFView/PdfMarking.tsx'
|
||||||
@ -36,12 +37,14 @@ import { convertToSigitFile, SigitFile } from '../../utils/file.ts'
|
|||||||
import { generateTimestamp } from '../../utils/opentimestamps.ts'
|
import { generateTimestamp } from '../../utils/opentimestamps.ts'
|
||||||
import { MARK_TYPE_CONFIG } from '../../components/MarkTypeStrategy/MarkStrategy.tsx'
|
import { MARK_TYPE_CONFIG } from '../../components/MarkTypeStrategy/MarkStrategy.tsx'
|
||||||
import { useNDK } from '../../hooks/useNDK.ts'
|
import { useNDK } from '../../hooks/useNDK.ts'
|
||||||
|
import { SendDMError } from '../../types/errors/SendDMError.ts'
|
||||||
|
|
||||||
export const SignPage = () => {
|
export const SignPage = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const { updateUsersAppData, sendNotification } = useNDK()
|
const { updateUsersAppData, sendNotification, sendPrivateDirectMessage } =
|
||||||
|
useNDK()
|
||||||
|
|
||||||
const usersAppData = useAppSelector((state) => state.userAppData)
|
const usersAppData = useAppSelector((state) => state.userAppData)
|
||||||
|
|
||||||
@ -602,6 +605,66 @@ export const SignPage = () => {
|
|||||||
toast.error('Failed to publish notifications')
|
toast.error('Failed to publish notifications')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Send DMs
|
||||||
|
setLoadingSpinnerDesc('Sending DMs')
|
||||||
|
const createSignatureEvent = parseNostrEvent(meta.createSignature)
|
||||||
|
const { id } = createSignatureEvent
|
||||||
|
|
||||||
|
if (isLastSigner) {
|
||||||
|
// Final sign sends to everyone (creator, signers, viewers - /verify)
|
||||||
|
const areSent: boolean[] = Array(users.length).fill(false)
|
||||||
|
for (let i = 0; i < users.length; i++) {
|
||||||
|
try {
|
||||||
|
areSent[i] = await sendPrivateDirectMessage(
|
||||||
|
`Sigit completed, visit ${window.location.origin}/#/verify/${id}`,
|
||||||
|
npubToHex(users[i])!
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof SendDMError) {
|
||||||
|
toast.error(error.message)
|
||||||
|
}
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areSent.some((r) => r)) {
|
||||||
|
toast.success(
|
||||||
|
`DMs sent ${areSent.filter((r) => r).length}/${users.length}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Notify the creator and
|
||||||
|
// the next signer (/sign).
|
||||||
|
try {
|
||||||
|
await sendPrivateDirectMessage(
|
||||||
|
`Sigit signed by ${usersNpub}, visit ${window.location.origin}/#/sign/${id}`,
|
||||||
|
npubToHex(submittedBy!)!
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof SendDMError) {
|
||||||
|
toast.error(error.message)
|
||||||
|
}
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to notify creator twice, skipping
|
||||||
|
const currentSignerIndex = signers.indexOf(usersNpub)
|
||||||
|
const nextSigner = npubToHex(signers[currentSignerIndex + 1])
|
||||||
|
if (nextSigner !== submittedBy) {
|
||||||
|
try {
|
||||||
|
await sendPrivateDirectMessage(
|
||||||
|
`You're the next signer, visit ${window.location.origin}/#/sign/${id}`,
|
||||||
|
nextSigner!
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof SendDMError) {
|
||||||
|
toast.error(error.message)
|
||||||
|
}
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
src/routes/PrivateRoute.tsx
Normal file
21
src/routes/PrivateRoute.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Navigate, useLocation } from 'react-router-dom'
|
||||||
|
import { useAppSelector } from '../hooks'
|
||||||
|
import { appPublicRoutes } from '.'
|
||||||
|
|
||||||
|
export function PrivateRoute({ children }: { children: JSX.Element }) {
|
||||||
|
const location = useLocation()
|
||||||
|
const isLoggedIn = useAppSelector((state) => state.auth?.loggedIn)
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return (
|
||||||
|
<Navigate
|
||||||
|
to={{
|
||||||
|
pathname: appPublicRoutes.landingPage,
|
||||||
|
search: `?callbackPath=${btoa(location.pathname)}`
|
||||||
|
}}
|
||||||
|
replace
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return children
|
||||||
|
}
|
@ -11,6 +11,7 @@ import { RelaysPage } from '../pages/settings/relays'
|
|||||||
import { SettingsPage } from '../pages/settings/Settings'
|
import { SettingsPage } from '../pages/settings/Settings'
|
||||||
import { SignPage } from '../pages/sign'
|
import { SignPage } from '../pages/sign'
|
||||||
import { VerifyPage } from '../pages/verify'
|
import { VerifyPage } from '../pages/verify'
|
||||||
|
import { PrivateRoute } from './PrivateRoute'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper type allows for extending react-router-dom's **RouteProps** with generic type
|
* Helper type allows for extending react-router-dom's **RouteProps** with generic type
|
||||||
@ -70,34 +71,66 @@ export const publicRoutes: PublicRouteProps[] = [
|
|||||||
export const privateRoutes = [
|
export const privateRoutes = [
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.homePage,
|
path: appPrivateRoutes.homePage,
|
||||||
element: <HomePage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<HomePage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.create,
|
path: appPrivateRoutes.create,
|
||||||
element: <CreatePage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<CreatePage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${appPrivateRoutes.sign}/:id?`,
|
path: `${appPrivateRoutes.sign}/:id?`,
|
||||||
element: <SignPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<SignPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.settings,
|
path: appPrivateRoutes.settings,
|
||||||
element: <SettingsPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<SettingsPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.profileSettings,
|
path: appPrivateRoutes.profileSettings,
|
||||||
element: <ProfileSettingsPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<ProfileSettingsPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.cacheSettings,
|
path: appPrivateRoutes.cacheSettings,
|
||||||
element: <CacheSettingsPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<CacheSettingsPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.relays,
|
path: appPrivateRoutes.relays,
|
||||||
element: <RelaysPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<RelaysPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: appPrivateRoutes.nostrLogin,
|
path: appPrivateRoutes.nostrLogin,
|
||||||
element: <NostrLoginPage />
|
element: (
|
||||||
|
<PrivateRoute>
|
||||||
|
<NostrLoginPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
86
src/services/cache/index.ts
vendored
86
src/services/cache/index.ts
vendored
@ -1,86 +0,0 @@
|
|||||||
import { IDBPDatabase, openDB } from 'idb'
|
|
||||||
import { Event } from 'nostr-tools'
|
|
||||||
import { CachedEvent } from '../../types'
|
|
||||||
import { SchemaV2 } from './schema'
|
|
||||||
|
|
||||||
class LocalCache {
|
|
||||||
// Static property to hold the single instance of LocalCache
|
|
||||||
private static instance: LocalCache | null = null
|
|
||||||
private db!: IDBPDatabase<SchemaV2>
|
|
||||||
|
|
||||||
// Private constructor to prevent direct instantiation
|
|
||||||
private constructor() {}
|
|
||||||
|
|
||||||
// Method to initialize the database
|
|
||||||
private async init() {
|
|
||||||
this.db = await openDB<SchemaV2>('sigit-cache', 2, {
|
|
||||||
upgrade(db, oldVersion) {
|
|
||||||
if (oldVersion < 1) {
|
|
||||||
db.createObjectStore('userMetadata', { keyPath: 'event.pubkey' })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldVersion < 2) {
|
|
||||||
const v6 = db as unknown as IDBPDatabase<SchemaV2>
|
|
||||||
|
|
||||||
v6.createObjectStore('userRelayListMetadata', {
|
|
||||||
keyPath: 'event.pubkey'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Static method to get the single instance of LocalCache
|
|
||||||
public static async getInstance(): Promise<LocalCache> {
|
|
||||||
// If the instance doesn't exist, create it
|
|
||||||
if (!LocalCache.instance) {
|
|
||||||
LocalCache.instance = new LocalCache()
|
|
||||||
await LocalCache.instance.init()
|
|
||||||
}
|
|
||||||
// Return the single instance of LocalCache
|
|
||||||
return LocalCache.instance
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to add user metadata
|
|
||||||
public async addUserMetadata(event: Event) {
|
|
||||||
await this.db.put('userMetadata', { event, cachedAt: Date.now() })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to get user metadata by key
|
|
||||||
public async getUserMetadata(key: string): Promise<CachedEvent | null> {
|
|
||||||
const data = await this.db.get('userMetadata', key)
|
|
||||||
return data || null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to delete user metadata by key
|
|
||||||
public async deleteUserMetadata(key: string) {
|
|
||||||
await this.db.delete('userMetadata', key)
|
|
||||||
}
|
|
||||||
|
|
||||||
public async addUserRelayListMetadata(event: Event) {
|
|
||||||
await this.db.put('userRelayListMetadata', { event, cachedAt: Date.now() })
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getUserRelayListMetadata(
|
|
||||||
key: string
|
|
||||||
): Promise<CachedEvent | null> {
|
|
||||||
const data = await this.db.get('userRelayListMetadata', key)
|
|
||||||
return data || null
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deleteUserRelayListMetadata(key: string) {
|
|
||||||
await this.db.delete('userRelayListMetadata', key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to clear cache data
|
|
||||||
public async clearCacheData() {
|
|
||||||
// Clear the 'userMetadata' store in the IndexedDB database
|
|
||||||
await this.db.clear('userMetadata')
|
|
||||||
|
|
||||||
// Reload the current page to ensure any cached data is reset
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export the single instance of LocalCache
|
|
||||||
export const localCache = await LocalCache.getInstance()
|
|
16
src/services/cache/schema.ts
vendored
16
src/services/cache/schema.ts
vendored
@ -1,16 +0,0 @@
|
|||||||
import { DBSchema } from 'idb'
|
|
||||||
import { CachedEvent } from '../../types'
|
|
||||||
|
|
||||||
export interface SchemaV1 extends DBSchema {
|
|
||||||
userMetadata: {
|
|
||||||
key: string
|
|
||||||
value: CachedEvent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SchemaV2 extends SchemaV1 {
|
|
||||||
userRelayListMetadata: {
|
|
||||||
key: string
|
|
||||||
value: CachedEvent
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +1 @@
|
|||||||
export * from './cache'
|
|
||||||
export * from './signer'
|
export * from './signer'
|
||||||
|
23
src/types/errors/SendDMError.ts
Normal file
23
src/types/errors/SendDMError.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Jsonable } from '.'
|
||||||
|
|
||||||
|
export enum SendDMErrorType {
|
||||||
|
'MISSING_RECIEVER' = 'Sending DM failed. Reciever is required.',
|
||||||
|
'ENCRYPTION_FAILED' = 'Sending DM failed. An error occurred in encrypting dm message.',
|
||||||
|
'RELAY_PUBLISH_FAILED' = 'Sending DM failed. Publishing events failed.'
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SendDMError extends Error {
|
||||||
|
public readonly context?: Jsonable
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
message: string,
|
||||||
|
options: { cause?: Error; context?: Jsonable } = {}
|
||||||
|
) {
|
||||||
|
const { cause, context } = options
|
||||||
|
|
||||||
|
super(message, { cause })
|
||||||
|
this.name = this.constructor.name
|
||||||
|
|
||||||
|
this.context = context
|
||||||
|
}
|
||||||
|
}
|
@ -26,30 +26,6 @@ export const clearState = () => {
|
|||||||
localStorage.removeItem('state')
|
localStorage.removeItem('state')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const saveVisitedLink = (pathname: string, search: string) => {
|
|
||||||
localStorage.setItem(
|
|
||||||
'visitedLink',
|
|
||||||
JSON.stringify({
|
|
||||||
pathname,
|
|
||||||
search
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getVisitedLink = () => {
|
|
||||||
const visitedLink = localStorage.getItem('visitedLink')
|
|
||||||
if (!visitedLink) return null
|
|
||||||
|
|
||||||
try {
|
|
||||||
return JSON.parse(visitedLink) as {
|
|
||||||
pathname: string
|
|
||||||
search: string
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const saveAuthToken = (token: string) => {
|
export const saveAuthToken = (token: string) => {
|
||||||
localStorage.setItem('authToken', token)
|
localStorage.setItem('authToken', token)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
Event,
|
Event,
|
||||||
EventTemplate,
|
EventTemplate,
|
||||||
UnsignedEvent,
|
UnsignedEvent,
|
||||||
|
VerifiedEvent,
|
||||||
finalizeEvent,
|
finalizeEvent,
|
||||||
generateSecretKey,
|
generateSecretKey,
|
||||||
getEventHash,
|
getEventHash,
|
||||||
@ -214,6 +215,12 @@ export const toUnixTimestamp = (date: number | Date) => {
|
|||||||
export const fromUnixTimestamp = (unix: number) => {
|
export const fromUnixTimestamp = (unix: number) => {
|
||||||
return unix * 1000
|
return unix * 1000
|
||||||
}
|
}
|
||||||
|
export const randomTimeUpTo2DaysInThePast = (): number => {
|
||||||
|
const now = Date.now()
|
||||||
|
const twoDaysInMilliseconds = 2 * 24 * 60 * 60 * 1000
|
||||||
|
const randomPastTime = now - Math.floor(Math.random() * twoDaysInMilliseconds)
|
||||||
|
return toUnixTimestamp(randomPastTime)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate nip44 conversation key
|
* Generate nip44 conversation key
|
||||||
@ -263,19 +270,21 @@ export const countLeadingZeroes = (hex: string) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to create a wrapped event with PoW
|
* Function to create a wrapped event with PoW
|
||||||
* @param event Original event to be wrapped
|
* @param event Original event to be wrapped (can be unsigned or verified)
|
||||||
* @param receiver Public key of the receiver
|
* @param receiver Public key of the receiver
|
||||||
* @param difficulty PoW difficulty level (default is 20)
|
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
export const createWrap = (unsignedEvent: UnsignedEvent, receiver: string) => {
|
export const createWrap = (
|
||||||
|
event: UnsignedEvent | VerifiedEvent,
|
||||||
|
receiver: string
|
||||||
|
) => {
|
||||||
// Generate a random secret key and its corresponding public key
|
// Generate a random secret key and its corresponding public key
|
||||||
const randomKey = generateSecretKey()
|
const randomKey = generateSecretKey()
|
||||||
const pubkey = getPublicKey(randomKey)
|
const pubkey = getPublicKey(randomKey)
|
||||||
|
|
||||||
// Encrypt the event content using nip44 encryption
|
// Encrypt the event content using nip44 encryption
|
||||||
const content = nip44Encrypt(unsignedEvent, randomKey, receiver)
|
const content = nip44Encrypt(event, randomKey, receiver)
|
||||||
|
|
||||||
// Initialize nonce and leadingZeroes for PoW calculation
|
// Initialize nonce and leadingZeroes for PoW calculation
|
||||||
let nonce = 0
|
let nonce = 0
|
||||||
@ -286,11 +295,12 @@ export const createWrap = (unsignedEvent: UnsignedEvent, receiver: string) => {
|
|||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
// Create an unsigned event with the necessary fields
|
// Create an unsigned event with the necessary fields
|
||||||
|
// TODO: kinds.GiftWrap (wrong kind number in nostr-tools 10/11/2024 at v2.7.2)
|
||||||
const event: UnsignedEvent = {
|
const event: UnsignedEvent = {
|
||||||
kind: 1059, // Event kind
|
kind: 1059, // Event kind
|
||||||
content, // Encrypted content
|
content, // Encrypted content
|
||||||
pubkey, // Public key of the creator
|
pubkey, // Public key of the creator
|
||||||
created_at: unixNow(), // Current timestamp
|
created_at: randomTimeUpTo2DaysInThePast(),
|
||||||
tags: [
|
tags: [
|
||||||
// Tags including receiver and nonce
|
// Tags including receiver and nonce
|
||||||
['p', receiver],
|
['p', receiver],
|
||||||
|
@ -37,7 +37,6 @@ export const getRelayMapFromNDKRelayList = (ndkRelayList: NDKRelayList) => {
|
|||||||
export const getDefaultRelayMap = (): RelayMap => ({
|
export const getDefaultRelayMap = (): RelayMap => ({
|
||||||
[SIGIT_RELAY]: { write: true, read: true }
|
[SIGIT_RELAY]: { write: true, read: true }
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publishes relay map.
|
* Publishes relay map.
|
||||||
* @param relayMap - relay map.
|
* @param relayMap - relay map.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user