add notifications screen

This commit is contained in:
Ren Amamiya 2023-08-22 16:34:47 +07:00
parent 4830f0b236
commit 0912948b31
18 changed files with 375 additions and 301 deletions

View File

@ -19,7 +19,7 @@
"dependencies": {
"@ctrl/magnet-link": "^3.1.2",
"@headlessui/react": "^1.7.17",
"@nostr-dev-kit/ndk": "^0.8.19",
"@nostr-dev-kit/ndk": "^0.8.21",
"@nostr-fetch/adapter-ndk": "^0.12.2",
"@radix-ui/react-alert-dialog": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
@ -62,7 +62,7 @@
"lru-cache": "^10.0.1",
"minidenticons": "^4.2.0",
"nostr-fetch": "^0.12.2",
"nostr-tools": "^1.14.0",
"nostr-tools": "^1.14.2",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@ -80,12 +80,12 @@
"@tailwindcss/typography": "^0.5.9",
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/html-to-text": "^9.0.1",
"@types/node": "^20.5.1",
"@types/node": "^20.5.2",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@types/youtube-player": "^5.5.7",
"@typescript-eslint/eslint-plugin": "^6.4.0",
"@typescript-eslint/parser": "^6.4.0",
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.1",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.15",
"clsx": "^2.0.0",
@ -98,7 +98,7 @@
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-simple-import-sort": "^10.0.0",
"husky": "^8.0.3",
"lint-staged": "^14.0.0",
"lint-staged": "^14.0.1",
"postcss": "^8.4.28",
"prettier": "^3.0.2",
"prettier-plugin-tailwindcss": "^0.5.3",

View File

@ -8,11 +8,11 @@ dependencies:
specifier: ^1.7.17
version: 1.7.17(react-dom@18.2.0)(react@18.2.0)
'@nostr-dev-kit/ndk':
specifier: ^0.8.19
version: 0.8.19(typescript@5.1.6)
specifier: ^0.8.21
version: 0.8.21(typescript@5.1.6)
'@nostr-fetch/adapter-ndk':
specifier: ^0.12.2
version: 0.12.2(@nostr-dev-kit/ndk@0.8.19)(nostr-fetch@0.12.2)
version: 0.12.2(@nostr-dev-kit/ndk@0.8.21)(nostr-fetch@0.12.2)
'@radix-ui/react-alert-dialog':
specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0)
@ -137,8 +137,8 @@ dependencies:
specifier: ^0.12.2
version: 0.12.2
nostr-tools:
specifier: ^1.14.0
version: 1.14.0
specifier: ^1.14.2
version: 1.14.2
qrcode.react:
specifier: ^3.1.0
version: 3.1.0(react@18.2.0)
@ -187,8 +187,8 @@ devDependencies:
specifier: ^9.0.1
version: 9.0.1
'@types/node':
specifier: ^20.5.1
version: 20.5.1
specifier: ^20.5.2
version: 20.5.2
'@types/react':
specifier: ^18.2.20
version: 18.2.20
@ -199,11 +199,11 @@ devDependencies:
specifier: ^5.5.7
version: 5.5.7
'@typescript-eslint/eslint-plugin':
specifier: ^6.4.0
version: 6.4.0(@typescript-eslint/parser@6.4.0)(eslint@8.47.0)(typescript@5.1.6)
specifier: ^6.4.1
version: 6.4.1(@typescript-eslint/parser@6.4.1)(eslint@8.47.0)(typescript@5.1.6)
'@typescript-eslint/parser':
specifier: ^6.4.0
version: 6.4.0(eslint@8.47.0)(typescript@5.1.6)
specifier: ^6.4.1
version: 6.4.1(eslint@8.47.0)(typescript@5.1.6)
'@vitejs/plugin-react-swc':
specifier: ^3.3.2
version: 3.3.2(vite@4.4.9)
@ -241,8 +241,8 @@ devDependencies:
specifier: ^8.0.3
version: 8.0.3
lint-staged:
specifier: ^14.0.0
version: 14.0.0
specifier: ^14.0.1
version: 14.0.1
postcss:
specifier: ^8.4.28
version: 8.4.28
@ -266,7 +266,7 @@ devDependencies:
version: 5.1.6
vite:
specifier: ^4.4.9
version: 4.4.9(@types/node@20.5.1)
version: 4.4.9(@types/node@20.5.2)
vite-tsconfig-paths:
specifier: ^4.2.0
version: 4.2.0(typescript@5.1.6)(vite@4.4.9)
@ -817,8 +817,8 @@ packages:
eslint: 8.47.0
eslint-visitor-keys: 3.4.3
/@eslint-community/regexpp@4.6.2:
resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==}
/@eslint-community/regexpp@4.7.0:
resolution: {integrity: sha512-+HencqxU7CFJnQb7IKtuNBqS6Yx3Tz4kOL8BJXo+JyeiBm5MEX6pO8onXDkjrkCRlfYXS1Axro15ZjVFe9YgsA==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
/@eslint/eslintrc@2.1.2:
@ -898,8 +898,8 @@ packages:
/@humanwhocodes/object-schema@1.2.1:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
/@jest/schemas@29.6.0:
resolution: {integrity: sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==}
/@jest/schemas@29.6.3:
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@sinclair/typebox': 0.27.8
@ -935,6 +935,10 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
/@noble/ciphers@0.2.0:
resolution: {integrity: sha512-6YBxJDAapHSdd3bLDv6x2wRPwq4QFMUaB3HvljNBUTThDd12eSm7/3F+2lnfzx2jvM+S6Nsy0jEt9QbPqSwqRw==}
dev: false
/@noble/curves@1.1.0:
resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==}
dependencies:
@ -968,8 +972,8 @@ packages:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.15.0
/@nostr-dev-kit/ndk@0.8.19(typescript@5.1.6):
resolution: {integrity: sha512-CyqZZiwhsWCCkfoT9JUAyDoQnfyFUVvb7vFhmsxg51eICqLV8SXNxKEyNaMFN1UJdqqi0GamFwisitC65c12jw==}
/@nostr-dev-kit/ndk@0.8.21(typescript@5.1.6):
resolution: {integrity: sha512-nthH3uoFuaXWqhZt96/p/iXTbYcMdkcee6pDm+Wi+MIcEsbj4o7r+ChoQ757TQZtEBLkOmku8/loi2LnHpkI9A==}
dependencies:
'@noble/hashes': 1.3.1
'@noble/secp256k1': 2.0.0
@ -986,7 +990,7 @@ packages:
eventemitter3: 5.0.1
light-bolt11-decoder: 3.0.0
node-fetch: 3.3.2
nostr-tools: 1.14.0
nostr-tools: 1.14.2
tsd: 0.28.1
utf8-buffer: 1.0.0
websocket-polyfill: 0.0.3
@ -997,13 +1001,13 @@ packages:
- typescript
dev: false
/@nostr-fetch/adapter-ndk@0.12.2(@nostr-dev-kit/ndk@0.8.19)(nostr-fetch@0.12.2):
/@nostr-fetch/adapter-ndk@0.12.2(@nostr-dev-kit/ndk@0.8.21)(nostr-fetch@0.12.2):
resolution: {integrity: sha512-+7EVuxS5DDZvNo6qbfFp7xRHwIyjyi36hYkiQFDjbQ4gX5LKo9RIPB1P+1XGkOSDFshypTbovZCaFunscJ/zhQ==}
peerDependencies:
'@nostr-dev-kit/ndk': ^0.7.5
nostr-fetch: ^0.12.2
dependencies:
'@nostr-dev-kit/ndk': 0.8.19(typescript@5.1.6)
'@nostr-dev-kit/ndk': 0.8.21(typescript@5.1.6)
'@nostr-fetch/kernel': 0.12.2
nostr-fetch: 0.12.2
dev: false
@ -2229,7 +2233,7 @@ packages:
prosemirror-state: 1.4.3
prosemirror-tables: 1.3.4
prosemirror-trailing-node: 2.0.7(prosemirror-model@1.19.3)(prosemirror-state@1.4.3)(prosemirror-view@1.31.7)
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
prosemirror-view: 1.31.7
dev: false
@ -2357,8 +2361,8 @@ packages:
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
dev: false
/@types/node@20.5.1:
resolution: {integrity: sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==}
/@types/node@20.5.2:
resolution: {integrity: sha512-5j/lXt7unfPOUlrKC34HIaedONleyLtwkKggiD/0uuMfT8gg2EOpg0dz4lCD15Ga7muC+1WzJZAjIB9simWd6Q==}
dev: true
/@types/normalize-package-data@2.4.1:
@ -2417,7 +2421,7 @@ packages:
typescript:
optional: true
dependencies:
'@eslint-community/regexpp': 4.6.2
'@eslint-community/regexpp': 4.7.0
'@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@5.1.6)
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/type-utils': 5.62.0(eslint@8.47.0)(typescript@5.1.6)
@ -2434,8 +2438,8 @@ packages:
- supports-color
dev: false
/@typescript-eslint/eslint-plugin@6.4.0(@typescript-eslint/parser@6.4.0)(eslint@8.47.0)(typescript@5.1.6):
resolution: {integrity: sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg==}
/@typescript-eslint/eslint-plugin@6.4.1(@typescript-eslint/parser@6.4.1)(eslint@8.47.0)(typescript@5.1.6):
resolution: {integrity: sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
'@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
@ -2445,12 +2449,12 @@ packages:
typescript:
optional: true
dependencies:
'@eslint-community/regexpp': 4.6.2
'@typescript-eslint/parser': 6.4.0(eslint@8.47.0)(typescript@5.1.6)
'@typescript-eslint/scope-manager': 6.4.0
'@typescript-eslint/type-utils': 6.4.0(eslint@8.47.0)(typescript@5.1.6)
'@typescript-eslint/utils': 6.4.0(eslint@8.47.0)(typescript@5.1.6)
'@typescript-eslint/visitor-keys': 6.4.0
'@eslint-community/regexpp': 4.7.0
'@typescript-eslint/parser': 6.4.1(eslint@8.47.0)(typescript@5.1.6)
'@typescript-eslint/scope-manager': 6.4.1
'@typescript-eslint/type-utils': 6.4.1(eslint@8.47.0)(typescript@5.1.6)
'@typescript-eslint/utils': 6.4.1(eslint@8.47.0)(typescript@5.1.6)
'@typescript-eslint/visitor-keys': 6.4.1
debug: 4.3.4
eslint: 8.47.0
graphemer: 1.4.0
@ -2483,8 +2487,8 @@ packages:
- supports-color
dev: false
/@typescript-eslint/parser@6.4.0(eslint@8.47.0)(typescript@5.1.6):
resolution: {integrity: sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg==}
/@typescript-eslint/parser@6.4.1(eslint@8.47.0)(typescript@5.1.6):
resolution: {integrity: sha512-610G6KHymg9V7EqOaNBMtD1GgpAmGROsmfHJPXNLCU9bfIuLrkdOygltK784F6Crboyd5tBFayPB7Sf0McrQwg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
@ -2493,10 +2497,10 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/scope-manager': 6.4.0
'@typescript-eslint/types': 6.4.0
'@typescript-eslint/typescript-estree': 6.4.0(typescript@5.1.6)
'@typescript-eslint/visitor-keys': 6.4.0
'@typescript-eslint/scope-manager': 6.4.1
'@typescript-eslint/types': 6.4.1
'@typescript-eslint/typescript-estree': 6.4.1(typescript@5.1.6)
'@typescript-eslint/visitor-keys': 6.4.1
debug: 4.3.4
eslint: 8.47.0
typescript: 5.1.6
@ -2512,12 +2516,12 @@ packages:
'@typescript-eslint/visitor-keys': 5.62.0
dev: false
/@typescript-eslint/scope-manager@6.4.0:
resolution: {integrity: sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==}
/@typescript-eslint/scope-manager@6.4.1:
resolution: {integrity: sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A==}
engines: {node: ^16.0.0 || >=18.0.0}
dependencies:
'@typescript-eslint/types': 6.4.0
'@typescript-eslint/visitor-keys': 6.4.0
'@typescript-eslint/types': 6.4.1
'@typescript-eslint/visitor-keys': 6.4.1
dev: true
/@typescript-eslint/type-utils@5.62.0(eslint@8.47.0)(typescript@5.1.6):
@ -2540,8 +2544,8 @@ packages:
- supports-color
dev: false
/@typescript-eslint/type-utils@6.4.0(eslint@8.47.0)(typescript@5.1.6):
resolution: {integrity: sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg==}
/@typescript-eslint/type-utils@6.4.1(eslint@8.47.0)(typescript@5.1.6):
resolution: {integrity: sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
@ -2550,8 +2554,8 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 6.4.0(typescript@5.1.6)
'@typescript-eslint/utils': 6.4.0(eslint@8.47.0)(typescript@5.1.6)
'@typescript-eslint/typescript-estree': 6.4.1(typescript@5.1.6)
'@typescript-eslint/utils': 6.4.1(eslint@8.47.0)(typescript@5.1.6)
debug: 4.3.4
eslint: 8.47.0
ts-api-utils: 1.0.2(typescript@5.1.6)
@ -2565,8 +2569,8 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: false
/@typescript-eslint/types@6.4.0:
resolution: {integrity: sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==}
/@typescript-eslint/types@6.4.1:
resolution: {integrity: sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg==}
engines: {node: ^16.0.0 || >=18.0.0}
dev: true
@ -2591,8 +2595,8 @@ packages:
- supports-color
dev: false
/@typescript-eslint/typescript-estree@6.4.0(typescript@5.1.6):
resolution: {integrity: sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==}
/@typescript-eslint/typescript-estree@6.4.1(typescript@5.1.6):
resolution: {integrity: sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
typescript: '*'
@ -2600,8 +2604,8 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/types': 6.4.0
'@typescript-eslint/visitor-keys': 6.4.0
'@typescript-eslint/types': 6.4.1
'@typescript-eslint/visitor-keys': 6.4.1
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
@ -2632,8 +2636,8 @@ packages:
- typescript
dev: false
/@typescript-eslint/utils@6.4.0(eslint@8.47.0)(typescript@5.1.6):
resolution: {integrity: sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw==}
/@typescript-eslint/utils@6.4.1(eslint@8.47.0)(typescript@5.1.6):
resolution: {integrity: sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
@ -2641,9 +2645,9 @@ packages:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.47.0)
'@types/json-schema': 7.0.12
'@types/semver': 7.5.0
'@typescript-eslint/scope-manager': 6.4.0
'@typescript-eslint/types': 6.4.0
'@typescript-eslint/typescript-estree': 6.4.0(typescript@5.1.6)
'@typescript-eslint/scope-manager': 6.4.1
'@typescript-eslint/types': 6.4.1
'@typescript-eslint/typescript-estree': 6.4.1(typescript@5.1.6)
eslint: 8.47.0
semver: 7.5.4
transitivePeerDependencies:
@ -2659,11 +2663,11 @@ packages:
eslint-visitor-keys: 3.4.3
dev: false
/@typescript-eslint/visitor-keys@6.4.0:
resolution: {integrity: sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==}
/@typescript-eslint/visitor-keys@6.4.1:
resolution: {integrity: sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ==}
engines: {node: ^16.0.0 || >=18.0.0}
dependencies:
'@typescript-eslint/types': 6.4.0
'@typescript-eslint/types': 6.4.1
eslint-visitor-keys: 3.4.3
dev: true
@ -2673,7 +2677,7 @@ packages:
vite: ^4
dependencies:
'@swc/core': 1.3.78
vite: 4.4.9(@types/node@20.5.1)
vite: 4.4.9(@types/node@20.5.2)
transitivePeerDependencies:
- '@swc/helpers'
dev: true
@ -2927,7 +2931,7 @@ packages:
hasBin: true
dependencies:
caniuse-lite: 1.0.30001522
electron-to-chromium: 1.4.496
electron-to-chromium: 1.4.498
node-releases: 2.0.13
update-browserslist-db: 1.0.11(browserslist@4.21.10)
dev: true
@ -3018,7 +3022,7 @@ packages:
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.2
fsevents: 2.3.3
dev: true
/cli-cursor@4.0.0:
@ -3249,8 +3253,8 @@ packages:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
dev: true
/diff-sequences@29.4.3:
resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==}
/diff-sequences@29.6.3:
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: false
@ -3312,8 +3316,8 @@ packages:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true
/electron-to-chromium@1.4.496:
resolution: {integrity: sha512-qeXC3Zbykq44RCrBa4kr8v/dWzYJA8rAwpyh9Qd+NKWoJfjG5vvJqy9XOJ9H4P/lqulZBCgUWAYi+FeK5AuJ8g==}
/electron-to-chromium@1.4.498:
resolution: {integrity: sha512-4LODxAzKGVy7CJyhhN5mebwe7U2L29P+0G+HUriHnabm0d7LSff8Yn7t+Wq+2/9ze2Fu1dhX7mww090xfv7qXQ==}
dev: true
/emoji-regex@8.0.0:
@ -3730,7 +3734,7 @@ packages:
hasBin: true
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.47.0)
'@eslint-community/regexpp': 4.6.2
'@eslint-community/regexpp': 4.7.0
'@eslint/eslintrc': 2.1.2
'@eslint/js': 8.47.0
'@humanwhocodes/config-array': 0.11.10
@ -3927,8 +3931,8 @@ packages:
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
/fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
@ -4449,18 +4453,18 @@ packages:
resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
dev: true
/jest-diff@29.6.2:
resolution: {integrity: sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==}
/jest-diff@29.6.3:
resolution: {integrity: sha512-3sw+AdWnwH9sSNohMRKA7JiYUJSRr/WS6+sEFfBuhxU5V5GlEVKfvUn8JuMHE0wqKowemR1C2aHy8VtXbaV8dQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
chalk: 4.1.2
diff-sequences: 29.4.3
jest-get-type: 29.4.3
pretty-format: 29.6.2
diff-sequences: 29.6.3
jest-get-type: 29.6.3
pretty-format: 29.6.3
dev: false
/jest-get-type@29.4.3:
resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==}
/jest-get-type@29.6.3:
resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: false
@ -4566,8 +4570,8 @@ packages:
uc.micro: 1.0.6
dev: false
/lint-staged@14.0.0:
resolution: {integrity: sha512-0tLf0pqZYkar/wu3nTctk4rVIG+d7PanDYv4/IQR4qwdqfQkTDziLRFnqMcLuLBTuUqmcLwsHPD2EjQ18d/oaA==}
/lint-staged@14.0.1:
resolution: {integrity: sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==}
engines: {node: ^16.14.0 || >=18.0.0}
hasBin: true
dependencies:
@ -5284,9 +5288,10 @@ packages:
'@nostr-fetch/kernel': 0.12.2
dev: false
/nostr-tools@1.14.0:
resolution: {integrity: sha512-hwq2i1z5/DneXRE5Zu/TzQuKzVLcB+gOdfT9CeoiScvNw/2dWRGJvyTXIdF92d7NQ7nMcEwqVJPDytLpEpiiKw==}
/nostr-tools@1.14.2:
resolution: {integrity: sha512-QEe8+tMDW0632eNDcQ+EG1edmsCXLV4WPiWLDcdT3uoE+GM15pVcy18sKwbN7SmgO4GKFEqQ49k45eANC6++SQ==}
dependencies:
'@noble/ciphers': 0.2.0
'@noble/curves': 1.1.0
'@noble/hashes': 1.3.1
'@scure/base': 1.1.1
@ -5719,11 +5724,11 @@ packages:
hasBin: true
dev: true
/pretty-format@29.6.2:
resolution: {integrity: sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==}
/pretty-format@29.6.3:
resolution: {integrity: sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/schemas': 29.6.0
'@jest/schemas': 29.6.3
ansi-styles: 5.2.0
react-is: 18.2.0
dev: false
@ -5742,7 +5747,7 @@ packages:
/prosemirror-changeset@2.2.1:
resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==}
dependencies:
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
dev: false
/prosemirror-collab@1.3.1:
@ -5756,14 +5761,14 @@ packages:
dependencies:
prosemirror-model: 1.19.3
prosemirror-state: 1.4.3
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
dev: false
/prosemirror-dropcursor@1.8.1:
resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==}
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
prosemirror-view: 1.31.7
dev: false
@ -5780,7 +5785,7 @@ packages:
resolution: {integrity: sha512-/zm0XoU/N/+u7i5zepjmZAEnpvjDtzoPWW6VmKptcAnPadN/SStsBjMImdCEbb3seiNTpveziPTIrXQbHLtU1g==}
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
prosemirror-view: 1.31.7
rope-sequence: 1.3.4
dev: false
@ -5789,7 +5794,7 @@ packages:
resolution: {integrity: sha512-3LrWJX1+ULRh5SZvbIQlwZafOXqp1XuV21MGBu/i5xsztd+9VD15x6OtN6mdqSFI7/8Y77gYUbQ6vwwJ4mr6QQ==}
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
dev: false
/prosemirror-keymap@1.2.2:
@ -5832,14 +5837,14 @@ packages:
dependencies:
prosemirror-model: 1.19.3
prosemirror-state: 1.4.3
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
dev: false
/prosemirror-state@1.4.3:
resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==}
dependencies:
prosemirror-model: 1.19.3
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
prosemirror-view: 1.31.7
dev: false
@ -5849,7 +5854,7 @@ packages:
prosemirror-keymap: 1.2.2
prosemirror-model: 1.19.3
prosemirror-state: 1.4.3
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
prosemirror-view: 1.31.7
dev: false
@ -5868,8 +5873,8 @@ packages:
prosemirror-view: 1.31.7
dev: false
/prosemirror-transform@1.7.4:
resolution: {integrity: sha512-GO38mvqJ2yeI0BbL5E1CdHcly032Dlfn9nHqlnCHqlNf9e9jZwJixxp6VRtOeDZ1uTDpDIziezMKbA41LpAx3A==}
/prosemirror-transform@1.7.5:
resolution: {integrity: sha512-U/fWB6frEzY7dzwJUo+ir8dU1JEanaI/RwL12Imy9js/527N0v/IRUKewocP1kTq998JNT18IGtThaDLwLOBxQ==}
dependencies:
prosemirror-model: 1.19.3
dev: false
@ -5879,7 +5884,7 @@ packages:
dependencies:
prosemirror-model: 1.19.3
prosemirror-state: 1.4.3
prosemirror-transform: 1.7.4
prosemirror-transform: 1.7.5
dev: false
/punycode@2.3.0:
@ -6220,12 +6225,12 @@ packages:
dependencies:
glob: 7.2.3
/rollup@3.28.0:
resolution: {integrity: sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==}
/rollup@3.28.1:
resolution: {integrity: sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
fsevents: 2.3.2
fsevents: 2.3.3
dev: true
/rope-sequence@1.3.4:
@ -6676,7 +6681,7 @@ packages:
'@tsd/typescript': 5.0.4
eslint-formatter-pretty: 4.1.0
globby: 11.1.0
jest-diff: 29.6.2
jest-diff: 29.6.3
meow: 9.0.0
path-exists: 4.0.0
read-pkg-up: 7.0.1
@ -6988,13 +6993,13 @@ packages:
debug: 4.3.4
globrex: 0.1.2
tsconfck: 2.1.2(typescript@5.1.6)
vite: 4.4.9(@types/node@20.5.1)
vite: 4.4.9(@types/node@20.5.2)
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/vite@4.4.9(@types/node@20.5.1):
/vite@4.4.9(@types/node@20.5.2):
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
@ -7022,12 +7027,12 @@ packages:
terser:
optional: true
dependencies:
'@types/node': 20.5.1
'@types/node': 20.5.2
esbuild: 0.18.20
postcss: 8.4.28
rollup: 3.28.0
rollup: 3.28.1
optionalDependencies:
fsevents: 2.3.2
fsevents: 2.3.3
dev: true
/w3c-keyname@2.2.8:

28
src-tauri/Cargo.lock generated
View File

@ -2047,9 +2047,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.20"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049"
checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
dependencies = [
"bytes",
"fnv",
@ -3845,9 +3845,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.11.18"
version = "0.11.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
checksum = "20b9b67e2ca7dd9e9f9285b759de30ff538aab981abaaf7bc9bd90b84a0126c3"
dependencies = [
"base64 0.21.2",
"bytes",
@ -3876,7 +3876,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"winreg 0.10.1",
"winreg 0.50.0",
]
[[package]]
@ -4017,9 +4017,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.101.3"
version = "0.101.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0"
checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d"
dependencies = [
"ring",
"untrusted",
@ -5947,9 +5947,9 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "wasm-streams"
version = "0.2.3"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078"
checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7"
dependencies = [
"futures-util",
"js-sys",
@ -6422,6 +6422,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "wry"
version = "0.31.1"

View File

@ -75,6 +75,13 @@ const router = createBrowserRouter([
return { Component: ChatScreen };
},
},
{
path: 'notifications',
async lazy() {
const { NotificationScreen } = await import('@app/notification');
return { Component: NotificationScreen };
},
},
],
},
{

View File

@ -18,16 +18,19 @@ export function ImportStep3Screen() {
const [loading, setLoading] = useState(false);
const { db } = useStorage();
const { fetchUserData } = useNostr();
const { fetchUserData, prefetchEvents } = useNostr();
const submit = async () => {
try {
// show loading indicator
setLoading(true);
const data = await fetchUserData();
// prefetch data
const user = await fetchUserData();
const data = await prefetchEvents();
if (data.status === 'ok') {
// redirect to next step
if (user.status === 'ok' && data.status === 'ok') {
navigate('/auth/onboarding/step-2', { replace: true });
} else {
console.log('error: ', data.message);

View File

@ -0,0 +1,27 @@
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { Hashtag, MentionUser } from '@shared/notes';
import { RichContent } from '@utils/types';
export function NotiContent({ content }: { content: RichContent }) {
return (
<>
<ReactMarkdown
className="markdown"
remarkPlugins={[remarkGfm]}
components={{
del: ({ children }) => {
const key = children[0] as string;
if (key.startsWith('pub') && key.length > 50 && key.length < 100)
return <MentionUser pubkey={key.replace('pub-', '')} />;
if (key.startsWith('tag')) return <Hashtag tag={key.replace('tag-', '')} />;
},
}}
>
{content?.parsed}
</ReactMarkdown>
</>
);
}

View File

@ -1,14 +1,13 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useMemo } from 'react';
import { MentionNote, NoteContent } from '@shared/notes';
import { NotiUser } from '@shared/notification';
import { NotiContent } from '@app/notification/components/content';
import { NotiUser } from '@app/notification/components/user';
import { formatCreatedAt } from '@utils/createdAt';
import { parser } from '@utils/parser';
export function NotiMention({ event }: { event: NDKEvent }) {
const replyTo = event.tags.find((e) => e[0] === 'e')?.[1];
const createdAt = formatCreatedAt(event.created_at);
const content = useMemo(() => parser(event), [event]);
@ -17,13 +16,12 @@ export function NotiMention({ event }: { event: NDKEvent }) {
<div className="flex items-start justify-between">
<div className="flex items-start gap-1">
<NotiUser pubkey={event.pubkey} />
<p className="leading-none text-white/50">reply your postr</p>
<p className="leading-none text-white/50">mention you · {createdAt}</p>
</div>
<span className="leading-none text-white/50">{createdAt}</span>
</div>
<div className="-mt-3 pl-[44px]">
<NoteContent content={content} />
{replyTo && <MentionNote id={replyTo} />}
<div className="relative z-10 -mt-6 flex gap-3">
<div className="h-10 w-10 shrink-0" />
<NotiContent content={content} />
</div>
</div>
);

View File

@ -1,7 +1,8 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { NotiUser } from '@app/notification/components/user';
import { MentionNote } from '@shared/notes';
import { NotiUser } from '@shared/notification';
import { formatCreatedAt } from '@utils/createdAt';
@ -14,13 +15,15 @@ export function NotiReaction({ event }: { event: NDKEvent }) {
<div className="flex items-start justify-between">
<div className="flex items-start gap-1">
<NotiUser pubkey={event.pubkey} />
<p className="leading-none text-white/50">reacted {event.content}</p>
</div>
<div>
<span className="leading-none text-white/50">{createdAt}</span>
<p className="leading-none text-white/50">
reacted {event.content} · {createdAt}
</p>
</div>
</div>
<div className="-mt-5 pl-[44px]">{root && <MentionNote id={root} />}</div>
<div className="relative z-10 -mt-6 flex gap-3">
<div className="h-10 w-10 shrink-0" />
<div className="flex-1">{root && <MentionNote id={root} />}</div>
</div>
</div>
);
}

View File

@ -1,7 +1,8 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { NotiUser } from '@app/notification/components/user';
import { MentionNote } from '@shared/notes';
import { NotiUser } from '@shared/notification';
import { formatCreatedAt } from '@utils/createdAt';
@ -14,13 +15,13 @@ export function NotiRepost({ event }: { event: NDKEvent }) {
<div className="flex items-start justify-between">
<div className="flex items-start gap-1">
<NotiUser pubkey={event.pubkey} />
<p className="leading-none text-white/50">repostr your postr</p>
</div>
<div>
<span className="leading-none text-white/50">{createdAt}</span>
<p className="leading-none text-white/50">repostr your postr · {createdAt}</p>
</div>
</div>
<div className="-mt-5 pl-[44px]">{root && <MentionNote id={root} />}</div>
<div className="relative z-10 -mt-6 flex gap-3">
<div className="h-10 w-10 shrink-0" />
<div className="flex-1">{root && <MentionNote id={root} />}</div>
</div>
</div>
);
}

View File

@ -0,0 +1,79 @@
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
import { useQuery } from '@tanstack/react-query';
import { useCallback, useEffect } from 'react';
import { NotiMention } from '@app/notification/components/mention';
import { NotiReaction } from '@app/notification/components/reaction';
import { NotiRepost } from '@app/notification/components/repost';
import { useStorage } from '@libs/storage/provider';
import { LoaderIcon } from '@shared/icons';
import { useNostr } from '@utils/hooks/useNostr';
export function NotificationScreen() {
const { db } = useStorage();
const { sub, fetchActivities } = useNostr();
const { status, data } = useQuery(
['notification', db.account.pubkey],
async () => {
return await fetchActivities();
},
{ refetchOnWindowFocus: false }
);
const renderItem = useCallback(
(event: NDKEvent) => {
switch (event.kind) {
case 1:
return <NotiMention key={event.id} event={event} />;
case 6:
return <NotiRepost key={event.id} event={event} />;
case 7:
return <NotiReaction key={event.id} event={event} />;
default:
return null;
}
},
[data]
);
useEffect(() => {
const filter: NDKFilter = {
'#p': [db.account.pubkey],
kinds: [1, 3, 6, 7, 9735],
since: db.account.last_login_at ?? Math.floor(Date.now() / 1000),
};
sub(filter, async (event) => {
console.log('[notify] new noti', event.id);
});
}, []);
return (
<div className="h-full w-full overflow-y-auto bg-white/10 px-3">
<div className="mb-3 px-3 pt-11">
<h3 className="text-xl font-bold">Notifications</h3>
</div>
<div className="grid grid-cols-3 gap-3">
<div className="col-span-2 flex flex-col">
{status === 'loading' ? (
<div className="inline-flex items-center justify-center px-4 py-3">
<LoaderIcon className="h-5 w-5 animate-spin text-black dark:text-white" />
</div>
) : data?.length < 1 ? (
<div className="flex h-full w-full flex-col items-center justify-center">
<p className="mb-1 text-4xl">🎉</p>
<p className="font-medium text-white/50">
Yo!, you&apos;ve no new notifications
</p>
</div>
) : (
data.map((event) => renderItem(event))
)}
</div>
</div>
</div>
);
}

View File

@ -17,42 +17,49 @@ export const NDKInstance = () => {
// TODO: fully support NIP-11
async function verifyRelays(relays: string[]) {
const verifiedRelays: string[] = [];
try {
const urls: string[] = relays.map((relay) => {
if (relay.startsWith('ws')) {
return relay.replace('ws', 'http');
}
if (relay.startsWith('wss')) {
return relay.replace('wss', 'https');
}
});
for (const relay of relays) {
let url: string;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort('timeout'), 5000);
if (relay.startsWith('ws')) {
url = relay.replace('ws', 'http');
}
if (relay.startsWith('wss')) {
url = relay.replace('wss', 'https');
}
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort('timeout'), 5000);
const res = await fetch(url, {
const requests = urls.map((url) =>
fetch(url, {
headers: { Accept: 'application/nostr+json' },
signal: controller.signal,
});
})
);
const responses = await Promise.all(requests);
const errors = responses.filter((response) => !response.ok);
if (res.ok) {
const data = await res.json();
console.log('relay information: ', data);
verifiedRelays.push(relay);
clearTimeout(timeoutId);
} else {
console.log('relay not working: ', res);
}
} catch (e) {
console.log('fetch error', e);
if (errors.length > 0) {
throw errors.map((response) => Error(response.statusText));
}
}
return verifiedRelays;
const verifiedRelays: string[] = responses.map((res) => {
if (res.url.startsWith('http')) {
return res.url.replace('htto', 'ws');
}
if (res.url.startsWith('https')) {
return res.url.replace('https', 'wss');
}
});
// clear timeout
clearTimeout(timeoutId);
// return all validate relays
return verifiedRelays;
} catch (e) {
e.forEach((error) => console.error(error));
}
}
async function initNDK() {

View File

@ -21,7 +21,7 @@ export function ComposerModal() {
<Dialog.Trigger asChild>
<button
type="button"
className="inline-flex h-9 w-min items-center justify-center gap-1 rounded-md bg-white/10 px-8 text-sm font-medium text-white hover:bg-fuchsia-500 focus:outline-none active:translate-y-1 disabled:pointer-events-none disabled:opacity-50"
className="inline-flex h-9 w-min items-center justify-center gap-1 rounded-md bg-fuchsia-500 px-8 text-sm font-medium text-white hover:bg-fuchsia-600 focus:outline-none active:translate-y-1 disabled:pointer-events-none disabled:opacity-50"
>
<ComposeIcon className="h-4 w-4" />
Postr

View File

@ -10,16 +10,18 @@ export function LumeBar() {
const { db } = useStorage();
return (
<div className="rounded-xl bg-white/10 p-2 backdrop-blur-xl">
<div className="flex items-center justify-between">
<ActiveAccount data={db.account} />
<Link
to="/settings/general"
className="inline-flex h-9 w-9 transform items-center justify-center rounded-md bg-white/20 active:translate-y-1"
>
<SettingsIcon className="h-4 w-4 text-white" />
</Link>
<Logout />
<div className="absolute bottom-3 left-1/2 w-max -translate-x-1/2 transform">
<div className="rounded-xl bg-white/10 p-2 backdrop-blur-xl">
<div className="flex items-center gap-2">
<ActiveAccount data={db.account} />
<Link
to="/settings/general"
className="inline-flex h-9 w-9 transform items-center justify-center rounded-md bg-white/20 active:translate-y-1"
>
<SettingsIcon className="h-4 w-4 text-white" />
</Link>
<Logout />
</div>
</div>
</div>
);

View File

@ -8,6 +8,7 @@ import { ComposerModal } from '@shared/composer/modal';
import {
ArrowLeftIcon,
ArrowRightIcon,
BellIcon,
NavArrowDownIcon,
SpaceIcon,
} from '@shared/icons';
@ -79,16 +80,29 @@ export function Navigation() {
<span className="inline-flex h-6 w-6 items-center justify-center rounded bg-white/10">
<SpaceIcon className="h-3 w-3 text-white" />
</span>
<span className="font-medium">Space</span>
<span className="text-sm font-medium">Space</span>
</NavLink>
<NavLink
to="/notifications"
preventScrollReset={true}
className={({ isActive }) =>
twMerge(
'flex h-9 items-center gap-2.5 rounded-md px-2',
isActive ? 'bg-white/10 text-white' : 'text-white/80'
)
}
>
<span className="inline-flex h-6 w-6 items-center justify-center rounded bg-white/10">
<BellIcon className="h-3 w-3 text-white" />
</span>
<span className="text-sm font-medium">Notifications</span>
</NavLink>
</div>
</Collapsible.Content>
</div>
</Collapsible.Root>
</div>
<div className="absolute bottom-3 left-0 w-full px-10">
<LumeBar />
</div>
<LumeBar />
</div>
);
}

View File

@ -1,5 +0,0 @@
export * from './user';
export * from './modal';
export * from './types/reaction';
export * from './types/repost';
export * from './types/mention';

View File

@ -1,100 +0,0 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import * as Dialog from '@radix-ui/react-dialog';
import { useQuery } from '@tanstack/react-query';
import { useCallback } from 'react';
import { useNDK } from '@libs/ndk/provider';
import { BellIcon, CancelIcon, LoaderIcon } from '@shared/icons';
import { NotiMention, NotiReaction, NotiRepost } from '@shared/notification';
import { nHoursAgo } from '@utils/date';
export function NotificationModal({ pubkey }: { pubkey: string }) {
const { ndk } = useNDK();
const { status, data } = useQuery(
['notification', pubkey],
async () => {
const events = await ndk.fetchEvents({
'#p': [pubkey],
kinds: [1, 6, 7, 9735],
since: nHoursAgo(24),
});
const filterSelf = [...events].filter((el) => el.pubkey !== pubkey);
const sorted = filterSelf.sort((a, b) => a.created_at - b.created_at);
return sorted as unknown as NDKEvent[];
},
{
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
}
);
const renderItem = useCallback(
(event: NDKEvent) => {
switch (event.kind) {
case 1:
return <NotiMention key={event.id} event={event} />;
case 6:
return <NotiRepost key={event.id} event={event} />;
case 7:
return <NotiReaction key={event.id} event={event} />;
default:
return null;
}
},
[data]
);
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button
type="button"
className="inline-flex h-9 w-9 transform items-center justify-center rounded-md bg-white/20 active:translate-y-1"
>
<BellIcon className="h-4 w-4 text-white" />
</button>
</Dialog.Trigger>
<Dialog.Portal className="relative z-10">
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<div className="relative h-min w-full max-w-xl rounded-xl bg-white/10">
<div className="h-min w-full shrink-0 rounded-t-xl border-b border-white/10 bg-white/5 px-5 py-5">
<div className="flex flex-col gap-1">
<div className="flex items-center justify-between">
<Dialog.Title className="text-lg font-semibold leading-none text-white">
Notification
</Dialog.Title>
<Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md hover:bg-white/10">
<CancelIcon className="h-4 w-4 text-white/50" />
</Dialog.Close>
</div>
<Dialog.Description className="text-sm leading-tight text-white/50">
All things happen when you rest in 24 hours ago
</Dialog.Description>
</div>
</div>
<div className="scrollbar-hide flex h-[500px] flex-col divide-y divide-white/10 overflow-y-auto overflow-x-hidden">
{status === 'loading' ? (
<div className="inline-flex items-center justify-center px-4 py-3">
<LoaderIcon className="h-5 w-5 animate-spin text-black dark:text-white" />
</div>
) : data?.length < 1 ? (
<div className="flex h-full w-full flex-col items-center justify-center">
<p className="mb-1 text-4xl">🎉</p>
<p className="font-medium text-white/50">
Yo!, you&apos;ve no new notifications
</p>
</div>
) : (
data.map((event) => renderItem(event))
)}
</div>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}

View File

@ -91,8 +91,6 @@ export function useNostr() {
const prefetchEvents = async () => {
try {
if (!ndk) return { status: 'failed', data: [], message: 'NDK instance not found' };
const fetcher = NostrFetcher.withCustomPool(ndkAdapter(ndk));
const dbEventsEmpty = await db.isEventsEmpty();
@ -100,13 +98,13 @@ export function useNostr() {
if (dbEventsEmpty || db.account.last_login_at === 0) {
since = nHoursAgo(24);
} else {
since = db.account.last_login_at ?? nHoursAgo(24);
since = db.account.last_login_at;
}
console.log("prefetching events with user's network: ", db.account.network.length);
console.log('prefetching events since: ', since);
const events = fetcher.allEventsIterator(
const events = await fetcher.fetchAllEvents(
relayUrls,
{
kinds: [NDKKind.Text, NDKKind.Repost, 1063, NDKKind.Article],
@ -116,7 +114,7 @@ export function useNostr() {
);
// save all events to database
for await (const event of events) {
for (const event of events) {
let root: string;
let reply: string;
if (event.tags?.[0]?.[0] === 'e' && !event.tags?.[0]?.[3]) {
@ -125,7 +123,7 @@ export function useNostr() {
root = event.tags.find((el) => el[3] === 'root')?.[1];
reply = event.tags.find((el) => el[3] === 'reply')?.[1];
}
db.createEvent(
await db.createEvent(
event.id,
JSON.stringify(event),
event.pubkey,
@ -143,6 +141,31 @@ export function useNostr() {
}
};
const fetchActivities = async () => {
try {
const fetcher = NostrFetcher.withCustomPool(ndkAdapter(ndk));
const events = await fetcher.fetchAllEvents(
relayUrls,
{
kinds: [
NDKKind.Text,
NDKKind.Contacts,
NDKKind.Repost,
NDKKind.Reaction,
NDKKind.Zap,
],
'#p': [db.account.pubkey],
},
{ since: nHoursAgo(24) },
{ sort: true }
);
return events as unknown as NDKEvent[];
} catch (e) {
console.error('Error fetching activities', e);
}
};
const publish = async ({
content,
kind,
@ -184,5 +207,5 @@ export function useNostr() {
return res;
};
return { sub, fetchUserData, prefetchEvents, publish, createZap };
return { sub, fetchUserData, prefetchEvents, fetchActivities, publish, createZap };
}