diff --git a/package.json b/package.json
index f35906d9..8f0c4687 100644
--- a/package.json
+++ b/package.json
@@ -57,17 +57,17 @@
"@types/throttle-debounce": "^5.0.2",
"@vite-pwa/assets-generator": "^0.2.6",
"@vite-pwa/sveltekit": "^0.6.6",
- "@welshman/app": "^0.4.4",
- "@welshman/content": "^0.4.4",
- "@welshman/editor": "^0.4.4",
- "@welshman/feeds": "^0.4.4",
- "@welshman/lib": "^0.4.4",
- "@welshman/net": "^0.4.4",
- "@welshman/relay": "^0.4.4",
- "@welshman/router": "^0.4.4",
- "@welshman/signer": "^0.4.4",
- "@welshman/store": "^0.4.4",
- "@welshman/util": "^0.4.4",
+ "@welshman/app": "^0.4.6",
+ "@welshman/content": "^0.4.6",
+ "@welshman/editor": "^0.4.6",
+ "@welshman/feeds": "^0.4.6",
+ "@welshman/lib": "^0.4.6",
+ "@welshman/net": "^0.4.6",
+ "@welshman/relay": "^0.4.6",
+ "@welshman/router": "^0.4.6",
+ "@welshman/signer": "^0.4.6",
+ "@welshman/store": "^0.4.6",
+ "@welshman/util": "^0.4.6",
"compressorjs": "^1.2.1",
"daisyui": "^4.12.10",
"date-picker-svelte": "^2.13.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d5237497..27990320 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -66,38 +66,38 @@ importers:
specifier: ^0.6.6
version: 0.6.8(@sveltejs/kit@2.20.5(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.25.10)(vite@5.4.17(@types/node@22.14.0)(terser@5.39.0)))(svelte@5.25.10)(vite@5.4.17(@types/node@22.14.0)(terser@5.39.0)))(@vite-pwa/assets-generator@0.2.6)(vite-plugin-pwa@0.21.2(@vite-pwa/assets-generator@0.2.6)(vite@5.4.17(@types/node@22.14.0)(terser@5.39.0))(workbox-build@7.3.0)(workbox-window@7.3.0))
'@welshman/app':
- specifier: ^0.4.4
- version: 0.4.4(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
+ specifier: ^0.4.6
+ version: 0.4.6(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
'@welshman/content':
- specifier: ^0.4.4
- version: 0.4.4(typescript@5.8.3)
+ specifier: ^0.4.6
+ version: 0.4.6(typescript@5.8.3)
'@welshman/editor':
- specifier: ^0.4.4
- version: 0.4.4(@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.1)(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(typescript@5.8.3)
+ specifier: ^0.4.6
+ version: 0.4.6(@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.1)(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(typescript@5.8.3)
'@welshman/feeds':
- specifier: ^0.4.4
- version: 0.4.4(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
+ specifier: ^0.4.6
+ version: 0.4.6(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
'@welshman/lib':
- specifier: ^0.4.4
- version: 0.4.4
+ specifier: ^0.4.6
+ version: 0.4.6
'@welshman/net':
- specifier: ^0.4.4
- version: 0.4.4(typescript@5.8.3)(ws@8.18.3)
+ specifier: ^0.4.6
+ version: 0.4.6(typescript@5.8.3)(ws@8.18.3)
'@welshman/relay':
- specifier: ^0.4.4
- version: 0.4.4(typescript@5.8.3)
+ specifier: ^0.4.6
+ version: 0.4.6(typescript@5.8.3)
'@welshman/router':
- specifier: ^0.4.4
- version: 0.4.4(typescript@5.8.3)
+ specifier: ^0.4.6
+ version: 0.4.6(typescript@5.8.3)
'@welshman/signer':
- specifier: ^0.4.4
- version: 0.4.4(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
+ specifier: ^0.4.6
+ version: 0.4.6(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
'@welshman/store':
- specifier: ^0.4.4
- version: 0.4.4(typescript@5.8.3)
+ specifier: ^0.4.6
+ version: 0.4.6(typescript@5.8.3)
'@welshman/util':
- specifier: ^0.4.4
- version: 0.4.4(typescript@5.8.3)
+ specifier: ^0.4.6
+ version: 0.4.6(typescript@5.8.3)
compressorjs:
specifier: ^1.2.1
version: 1.2.1
@@ -1640,41 +1640,41 @@ packages:
'@vite-pwa/assets-generator':
optional: true
- '@welshman/app@0.4.4':
- resolution: {integrity: sha512-QFOIiAokbI/sK0q3tmThjc6CaJmTLeOc4RdXL7CzMrMdV6Dkv0WKjLUdP+1nqxCrVlarIcY7gH0Ruw++LHx5cQ==}
+ '@welshman/app@0.4.6':
+ resolution: {integrity: sha512-u3BUyOCiBivJ847t7JvicgeedLpYDLg/2bie4d+OGJpdoNsI73/raGIfP5KBUqy2pgnZMIm4kOFztF46oDaSGQ==}
- '@welshman/content@0.4.4':
- resolution: {integrity: sha512-OUlEIrWpn03440yRkdcJ3tkXLyn5yxAZTDbvX9lbz7SxWZjg1jXdALq1izOVNC+E44e79oKj1HiqrSPFvFclsQ==}
+ '@welshman/content@0.4.6':
+ resolution: {integrity: sha512-1eqNroerKXcj7kI/X7oJq8h0F2Gn09RIgyNLWLds9qBXjIyK6JdpS7j8wj667NqXlCVkXbq49X1FNkWohtqdCw==}
- '@welshman/editor@0.4.4':
- resolution: {integrity: sha512-JmtenW/2Qtmv6J+KiTN0PgzJzuKA8+PVlJqQI7xGQ+C/b2ESbTRd/M6JV5a4OyYYB3pGChwcHnSHGIvHaYWpKA==}
+ '@welshman/editor@0.4.6':
+ resolution: {integrity: sha512-oFEIgoA/G7zjdw2QmrghNkaGUKYXI1axOYaW+PlPMOvx5iVFup9KYI8/M5eFtjndBlGC0qg/TR8fZYC/AUQmXg==}
- '@welshman/feeds@0.4.4':
- resolution: {integrity: sha512-KWon9e7Ad5oUrAXVS5NMa/YMnKhq8EGglwhtc93ad1e3tHpO9ekHfKrFaovYtfeHfgbBUi9SkJq/rJTC16IAxg==}
+ '@welshman/feeds@0.4.6':
+ resolution: {integrity: sha512-oVQASdYqwspAJLDYJUBI5ZNRp0Fl5gROqqulC3ftACvj6HNNjzeGfd9obYyJfyU7zNLHs8eEuLuSBBQoeOOoQg==}
- '@welshman/lib@0.4.4':
- resolution: {integrity: sha512-mHntmhEv9JOHWxD1dmmwoz3h8EjHh29ws0a57rWWAZgLq4u0U4pAwJlSgqm4FZdCn8TC5YHjdkIEGqfE55lPhw==}
+ '@welshman/lib@0.4.6':
+ resolution: {integrity: sha512-CyIlIPKxv/xaF+1U0wjA6MMiTOnoInkcECTaSzRXTaiFAwMl91MJpmV9X0zGK+SgUMoq91hM2GCUt301fcCXVg==}
engines: {node: '>=12.0.0'}
- '@welshman/net@0.4.4':
- resolution: {integrity: sha512-d98+QFRsUhkuwRe3Cvp3r60yBxNRye/HGpJW/oxTL1EeSA2EEYwbi5nIu7dAEwTEPA9Z5i+DFnEi04nHsYgihw==}
+ '@welshman/net@0.4.6':
+ resolution: {integrity: sha512-sivSyKKTqYaUyU8335SoOwXi5IW/wz3rru97GrrmtLDPDWPbuLN5DBI8WL42YFxjIlIqCKzgagkMWwEWkYZpsA==}
- '@welshman/relay@0.4.4':
- resolution: {integrity: sha512-NRdFQFIP2/bxUC5fKPk0sLqwdsaNFGcQnxlRC/t0GwPgTnzL5p701XOkaV3yEVLz6N5l/vyttJ++7pNxEvLAWg==}
+ '@welshman/relay@0.4.6':
+ resolution: {integrity: sha512-Mg820k7bYR5PLEjtf0BNYXHMuJC4jELwjajqNYZ04gtcifyyY3x7uWnBBSVr6Hfipx97PCsrxWZiuuACI9vfsQ==}
- '@welshman/router@0.4.4':
- resolution: {integrity: sha512-P/PEhG+vbVjDg+9FRQYuAg/NarXoF02jbMkikEnOtgIHMOh9DKilujzdAu0Ch4lUfb5koDLV3p2GFNG+j4lLPA==}
+ '@welshman/router@0.4.6':
+ resolution: {integrity: sha512-5Yxhh+o8OA2MYgOOAKsd3CrOeR2im1Wc9st6bfj6fRNRuZ4AkHi8O1JRCdWEDEwYST/1G0x69mSPuYr3bub0Gw==}
- '@welshman/signer@0.4.4':
- resolution: {integrity: sha512-Ajv4uuBhS+WzpzkyjjkH5exsw4VSpV1j/0GwCmtNTNaYgDq+MjlH4gpvYj8QVDGXoeXHf1Lbr6WQz7i7mJSK9A==}
+ '@welshman/signer@0.4.6':
+ resolution: {integrity: sha512-KkmPPwyWRTDQQxed0cHZE2LxlsdzqnwqOzx9aKoSPh82QWR/3/K+EkZYtU6QMllwc/VUoYJUyHQRPOi0J012WA==}
peerDependencies:
nostr-signer-capacitor-plugin: ~0.0.4
- '@welshman/store@0.4.4':
- resolution: {integrity: sha512-v9L5aw/K9JtdVkvEx6bVSqQ03Ghyy2py2PEEiT9c9zGeklKDjvDce52kDWFomV5Nlo/bgrCmpbdAQwo2Q9hRaQ==}
+ '@welshman/store@0.4.6':
+ resolution: {integrity: sha512-NU+5p1jQ6pG21QZmfVE9fXd85C+2aKxv5gCX1YY8uDOU1kqyTP4SmQtRmllx3ABxsOtCErayn3vGP6rekKy5Lw==}
- '@welshman/util@0.4.4':
- resolution: {integrity: sha512-iOTVqqB4Ef1T36dvQjvQv9EabjgYWgsKq1vFvCAFoKfLarnJJyqtMqRNumiicFWXcs+4xG1fBs1HN3kQXYan9A==}
+ '@welshman/util@0.4.6':
+ resolution: {integrity: sha512-Fc4Lz+goLNAKBMdQF19IeT6KJWPpk+dLdlS9H+ctIQNLCYbviz1yyKNCQ12MjkEjEQy0LEJGxo/yIPffA9x36g==}
'@xml-tools/parser@1.0.11':
resolution: {integrity: sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==}
@@ -6512,17 +6512,17 @@ snapshots:
optionalDependencies:
'@vite-pwa/assets-generator': 0.2.6
- '@welshman/app@0.4.4(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
+ '@welshman/app@0.4.6(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
dependencies:
'@types/throttle-debounce': 5.0.2
- '@welshman/feeds': 0.4.4(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
- '@welshman/lib': 0.4.4
- '@welshman/net': 0.4.4(typescript@5.8.3)(ws@8.18.3)
- '@welshman/relay': 0.4.4(typescript@5.8.3)
- '@welshman/router': 0.4.4(typescript@5.8.3)
- '@welshman/signer': 0.4.4(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
- '@welshman/store': 0.4.4(typescript@5.8.3)
- '@welshman/util': 0.4.4(typescript@5.8.3)
+ '@welshman/feeds': 0.4.6(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
+ '@welshman/lib': 0.4.6
+ '@welshman/net': 0.4.6(typescript@5.8.3)(ws@8.18.3)
+ '@welshman/relay': 0.4.6(typescript@5.8.3)
+ '@welshman/router': 0.4.6(typescript@5.8.3)
+ '@welshman/signer': 0.4.6(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
+ '@welshman/store': 0.4.6(typescript@5.8.3)
+ '@welshman/util': 0.4.6(typescript@5.8.3)
fuse.js: 7.1.0
idb: 8.0.2
svelte: 4.2.20
@@ -6532,14 +6532,14 @@ snapshots:
- typescript
- ws
- '@welshman/content@0.4.4(typescript@5.8.3)':
+ '@welshman/content@0.4.6(typescript@5.8.3)':
dependencies:
'@braintree/sanitize-url': 7.1.1
nostr-tools: 2.14.2(typescript@5.8.3)
transitivePeerDependencies:
- typescript
- '@welshman/editor@0.4.4(@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.1)(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(typescript@5.8.3)':
+ '@welshman/editor@0.4.6(@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.1)(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(typescript@5.8.3)':
dependencies:
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
'@tiptap/extension-code': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
@@ -6554,8 +6554,8 @@ snapshots:
'@tiptap/extension-text': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))
'@tiptap/pm': 2.12.0
'@tiptap/suggestion': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)
- '@welshman/lib': 0.4.4
- '@welshman/util': 0.4.4(typescript@5.8.3)
+ '@welshman/lib': 0.4.6
+ '@welshman/util': 0.4.6(typescript@5.8.3)
nostr-editor: 1.0.0(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/extension-image@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)(linkifyjs@4.3.1)(nostr-tools@2.14.2(typescript@5.8.3))(prosemirror-markdown@1.13.2)(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.39.3)(tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))
nostr-tools: 2.14.2(typescript@5.8.3)
tippy.js: 6.3.7
@@ -6570,78 +6570,78 @@ snapshots:
- tiptap-markdown
- typescript
- '@welshman/feeds@0.4.4(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
+ '@welshman/feeds@0.4.6(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
dependencies:
- '@welshman/lib': 0.4.4
- '@welshman/net': 0.4.4(typescript@5.8.3)(ws@8.18.3)
- '@welshman/relay': 0.4.4(typescript@5.8.3)
- '@welshman/router': 0.4.4(typescript@5.8.3)
- '@welshman/signer': 0.4.4(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
- '@welshman/util': 0.4.4(typescript@5.8.3)
+ '@welshman/lib': 0.4.6
+ '@welshman/net': 0.4.6(typescript@5.8.3)(ws@8.18.3)
+ '@welshman/relay': 0.4.6(typescript@5.8.3)
+ '@welshman/router': 0.4.6(typescript@5.8.3)
+ '@welshman/signer': 0.4.6(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)
+ '@welshman/util': 0.4.6(typescript@5.8.3)
trava: 1.2.1
transitivePeerDependencies:
- nostr-signer-capacitor-plugin
- typescript
- ws
- '@welshman/lib@0.4.4':
+ '@welshman/lib@0.4.6':
dependencies:
'@scure/base': 1.2.6
'@types/events': 3.0.3
events: 3.3.0
- '@welshman/net@0.4.4(typescript@5.8.3)(ws@8.18.3)':
+ '@welshman/net@0.4.6(typescript@5.8.3)(ws@8.18.3)':
dependencies:
- '@welshman/lib': 0.4.4
- '@welshman/relay': 0.4.4(typescript@5.8.3)
- '@welshman/util': 0.4.4(typescript@5.8.3)
+ '@welshman/lib': 0.4.6
+ '@welshman/relay': 0.4.6(typescript@5.8.3)
+ '@welshman/util': 0.4.6(typescript@5.8.3)
events: 3.3.0
isomorphic-ws: 5.0.0(ws@8.18.3)
transitivePeerDependencies:
- typescript
- ws
- '@welshman/relay@0.4.4(typescript@5.8.3)':
+ '@welshman/relay@0.4.6(typescript@5.8.3)':
dependencies:
- '@welshman/lib': 0.4.4
- '@welshman/util': 0.4.4(typescript@5.8.3)
+ '@welshman/lib': 0.4.6
+ '@welshman/util': 0.4.6(typescript@5.8.3)
transitivePeerDependencies:
- typescript
- '@welshman/router@0.4.4(typescript@5.8.3)':
+ '@welshman/router@0.4.6(typescript@5.8.3)':
dependencies:
- '@welshman/lib': 0.4.4
- '@welshman/relay': 0.4.4(typescript@5.8.3)
- '@welshman/util': 0.4.4(typescript@5.8.3)
+ '@welshman/lib': 0.4.6
+ '@welshman/relay': 0.4.6(typescript@5.8.3)
+ '@welshman/util': 0.4.6(typescript@5.8.3)
transitivePeerDependencies:
- typescript
- '@welshman/signer@0.4.4(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
+ '@welshman/signer@0.4.6(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)':
dependencies:
'@noble/curves': 1.9.2
'@noble/hashes': 1.8.0
- '@welshman/lib': 0.4.4
- '@welshman/net': 0.4.4(typescript@5.8.3)(ws@8.18.3)
- '@welshman/util': 0.4.4(typescript@5.8.3)
+ '@welshman/lib': 0.4.6
+ '@welshman/net': 0.4.6(typescript@5.8.3)(ws@8.18.3)
+ '@welshman/util': 0.4.6(typescript@5.8.3)
nostr-signer-capacitor-plugin: 0.0.4(@capacitor/core@7.2.0)
nostr-tools: 2.14.2(typescript@5.8.3)
transitivePeerDependencies:
- typescript
- ws
- '@welshman/store@0.4.4(typescript@5.8.3)':
+ '@welshman/store@0.4.6(typescript@5.8.3)':
dependencies:
- '@welshman/lib': 0.4.4
- '@welshman/relay': 0.4.4(typescript@5.8.3)
- '@welshman/util': 0.4.4(typescript@5.8.3)
+ '@welshman/lib': 0.4.6
+ '@welshman/relay': 0.4.6(typescript@5.8.3)
+ '@welshman/util': 0.4.6(typescript@5.8.3)
svelte: 4.2.20
transitivePeerDependencies:
- typescript
- '@welshman/util@0.4.4(typescript@5.8.3)':
+ '@welshman/util@0.4.6(typescript@5.8.3)':
dependencies:
'@types/ws': 8.18.1
- '@welshman/lib': 0.4.4
+ '@welshman/lib': 0.4.6
js-base64: 3.7.7
nostr-tools: 2.14.2(typescript@5.8.3)
nostr-wasm: 0.1.0
diff --git a/src/app/components/AlertAdd.svelte b/src/app/components/AlertAdd.svelte
index 5e52e26f..b6dd6dad 100644
--- a/src/app/components/AlertAdd.svelte
+++ b/src/app/components/AlertAdd.svelte
@@ -1,17 +1,8 @@
+
+{#if $status}
+ {@const statusText = getTagValue("status", $status.tags) || "error"}
+ {#if statusText === "ok"}
+
+ Active
+
+ {:else if statusText === "pending"}
+
+ Pending
+
+ {:else}
+
+ {statusText.replace("-", " ").replace(/^(.)/, x => x.toUpperCase())}
+
+ {/if}
+{:else}
+
+ Inactive
+
+{/if}
diff --git a/src/app/components/Alerts.svelte b/src/app/components/Alerts.svelte
index 6b9891c4..8516f54e 100644
--- a/src/app/components/Alerts.svelte
+++ b/src/app/components/Alerts.svelte
@@ -1,5 +1,7 @@
-
-
-
-
- Alerts
-
-
+
+
+
+
+
+ Alerts
+
+
+
+
+ {#each filteredAlerts as alert (alert.event.id)}
+
+ {:else}
+
Nothing here yet!
+ {/each}
+
-
- {#each filteredAlerts as alert (alert.event.id)}
-
- {:else}
-
Nothing here yet!
- {/each}
+
+
+
Notify me about new direct messages
+
+
+ {#if $dmStatus}
+ {@const status = getTagValue("status", $dmStatus.tags) || "error"}
+ {#if status !== "ok"}
+
+
+ {getTagValue("message", $dmStatus.tags) ||
+ "The notification server did not respond to your request."}
+
+
+ {/if}
+ {/if}
diff --git a/src/app/components/ChatEnable.svelte b/src/app/components/ChatEnable.svelte
index cd9ecde0..7abc8198 100644
--- a/src/app/components/ChatEnable.svelte
+++ b/src/app/components/ChatEnable.svelte
@@ -1,7 +1,5 @@
@@ -24,4 +65,19 @@
Mark all read
+ {#if (!enablingAlert && $dmAlert) || disablingAlert}
+
+ {:else}
+
+ {/if}
diff --git a/src/app/components/MenuSettings.svelte b/src/app/components/MenuSettings.svelte
index 61722afa..553240d9 100644
--- a/src/app/components/MenuSettings.svelte
+++ b/src/app/components/MenuSettings.svelte
@@ -4,6 +4,8 @@
import Settings from "@assets/icons/settings-minimalistic.svg?dataurl"
import Code2 from "@assets/icons/code-2.svg?dataurl"
import Exit from "@assets/icons/logout-3.svg?dataurl"
+ import Bell from "@assets/icons/bell.svg?dataurl"
+ import Wallet from "@assets/icons/wallet.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Link from "@lib/components/Link.svelte"
import Button from "@lib/components/Button.svelte"
@@ -29,6 +31,32 @@
{/snippet}
+
+
+ {#snippet icon()}
+
+ {/snippet}
+ {#snippet title()}
+ Alerts
+ {/snippet}
+ {#snippet info()}
+ Set up email digests and push notifications
+ {/snippet}
+
+
+
+
+ {#snippet icon()}
+
+ {/snippet}
+ {#snippet title()}
+ Wallet
+ {/snippet}
+ {#snippet info()}
+ Connect a bitcoin wallet for sending social tips
+ {/snippet}
+
+
{#snippet icon()}
@@ -42,7 +70,7 @@
{/snippet}
-
+
{#snippet icon()}
diff --git a/src/app/components/ProfileDelete.svelte b/src/app/components/ProfileDelete.svelte
index 079c7b95..29136621 100644
--- a/src/app/components/ProfileDelete.svelte
+++ b/src/app/components/ProfileDelete.svelte
@@ -7,9 +7,8 @@
DELETE,
isReplaceable,
getAddress,
- getRelaysFromList,
} from "@welshman/util"
- import {pubkey, userRelaySelections, publishThunk, repository} from "@welshman/app"
+ import {pubkey, publishThunk, repository} from "@welshman/app"
import {preventDefault} from "@lib/html"
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
@@ -20,7 +19,13 @@
import ModalFooter from "@lib/components/ModalFooter.svelte"
import {pushToast} from "@app/util/toast"
import {logout} from "@app/core/commands"
- import {INDEXER_RELAYS, PLATFORM_NAME, userMembership, getMembershipUrls} from "@app/core/state"
+ import {
+ INDEXER_RELAYS,
+ PLATFORM_NAME,
+ userMembership,
+ getMembershipUrls,
+ userWriteRelays,
+ } from "@app/core/state"
let progress: number | undefined = $state(undefined)
let confirmText = $state("")
@@ -43,7 +48,7 @@
const denominator = chunks.length + 2
const relays = uniq([
...INDEXER_RELAYS,
- ...getRelaysFromList($userRelaySelections),
+ ...$userWriteRelays,
...getMembershipUrls($userMembership),
])
diff --git a/src/app/components/ThunkPending.svelte b/src/app/components/ThunkPending.svelte
index 4223a96c..218fbf48 100644
--- a/src/app/components/ThunkPending.svelte
+++ b/src/app/components/ThunkPending.svelte
@@ -25,6 +25,7 @@
class="underline transition-all"
class:link={isSending}
class:opacity-25={!isSending}
+ class:pointer-events-none={!isSending}
onclick={stopPropagation(abort)}>
Cancel
diff --git a/src/app/core/commands.ts b/src/app/core/commands.ts
index f806dae1..6d256bef 100644
--- a/src/app/core/commands.ts
+++ b/src/app/core/commands.ts
@@ -1,6 +1,7 @@
import {nwc} from "@getalby/sdk"
import * as nip19 from "nostr-tools/nip19"
import {get} from "svelte/store"
+import type {Override, MakeOptional} from "@welshman/lib"
import {
randomId,
append,
@@ -11,10 +12,15 @@ import {
equals,
TIMEZONE,
LOCALE,
+ parseJson,
+ fromPairs,
} from "@welshman/lib"
+import {decrypt} from "@welshman/signer"
import type {Feed} from "@welshman/feeds"
+import {makeIntersectionFeed, feedFromFilters, makeRelayFeed} from "@welshman/feeds"
import type {TrustedEvent, EventContent} from "@welshman/util"
import {
+ WRAP,
DELETE,
REPORT,
PROFILE,
@@ -44,6 +50,8 @@ import {
toNostrURI,
getRelaysFromList,
RelayMode,
+ getAddress,
+ getTagValue,
} from "@welshman/util"
import {Pool, AuthStatus, SocketStatus} from "@welshman/net"
import {Router} from "@welshman/router"
@@ -54,7 +62,6 @@ import {
repository,
publishThunk,
profilesByPubkey,
- relaySelectionsByPubkey,
tagEvent,
tagEventForReaction,
userRelaySelections,
@@ -66,8 +73,9 @@ import {
tagEventForComment,
tagEventForQuote,
waitForThunkError,
+ getPubkeyRelays,
} from "@welshman/app"
-import type {SettingsValues} from "@app/core/state"
+import type {SettingsValues, Alert} from "@app/core/state"
import {
SETTINGS,
PROTECTED,
@@ -77,14 +85,18 @@ import {
NOTIFIER_RELAY,
userRoomsByUrl,
userSettingsValues,
+ canDecrypt,
+ ensureUnwrapped,
+ userInboxRelays,
} from "@app/core/state"
+import {loadAlertStatuses} from "@app/core/requests"
+import {platform, platformName, getPushInfo} from "@app/util/push"
import {preferencesStorageProvider} from "@src/lib/storage"
// Utils
export const getPubkeyHints = (pubkey: string) => {
- const selections = relaySelectionsByPubkey.get().get(pubkey)
- const relays = selections ? getRelaysFromList(selections, RelayMode.Write) : []
+ const relays = getPubkeyRelays(pubkey, RelayMode.Write)
const hints = relays.length ? relays : INDEXER_RELAYS
return hints
@@ -417,27 +429,35 @@ export const publishComment = ({relays, ...params}: CommentParams & {relays: str
// Alerts
+export type AlertParamsEmail = {
+ cron: string
+ email: string
+ handler: string[]
+}
+
+export type AlertParamsWeb = {
+ endpoint: string
+ p256dh: string
+ auth: string
+}
+
+export type AlertParamsIos = {
+ device_token: string
+ bundle_identifier: string
+}
+
+export type AlertParamsAndroid = {
+ device_token: string
+}
+
export type AlertParams = {
feed: Feed
description: string
- claims: Record
- email?: {
- cron: string
- email: string
- handler: string[]
- }
- web?: {
- endpoint: string
- p256dh: string
- auth: string
- }
- ios?: {
- device_token: string
- bundle_identifier: string
- }
- android?: {
- device_token: string
- }
+ claims?: Record
+ email?: AlertParamsEmail
+ web?: AlertParamsWeb
+ ios?: AlertParamsIos
+ android?: AlertParamsAndroid
}
export const makeAlert = async (params: AlertParams) => {
@@ -448,7 +468,7 @@ export const makeAlert = async (params: AlertParams) => {
["description", params.description],
]
- for (const [relay, claim] of Object.entries(params.claims)) {
+ for (const [relay, claim] of Object.entries(params.claims || [])) {
tags.push(["claim", relay, claim])
}
@@ -481,6 +501,88 @@ export const makeAlert = async (params: AlertParams) => {
export const publishAlert = async (params: AlertParams) =>
publishThunk({event: await makeAlert(params), relays: [NOTIFIER_RELAY]})
+export const deleteAlert = (alert: Alert) => {
+ const relays = [NOTIFIER_RELAY]
+ const tags = [["p", NOTIFIER_PUBKEY]]
+
+ return publishDelete({event: alert.event, relays, tags, protect: false})
+}
+
+export type CreateAlertParams = Override<
+ AlertParams,
+ {
+ email?: MakeOptional
+ }
+>
+
+export type CreateAlertResult = {
+ ok?: true
+ error?: string
+}
+
+export const createAlert = async (params: CreateAlertParams): Promise => {
+ if (params.email) {
+ const cadence = params.email.cron.endsWith("1") ? "Weekly" : "Daily"
+ const handler = [
+ "31990:97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322:1737058597050",
+ "wss://relay.nostr.band/",
+ "web",
+ ]
+
+ params.email = {handler, ...params.email}
+ params.description = `${cadence} alert ${params.description}, sent via email.`
+ } else {
+ try {
+ // @ts-ignore
+ params[platform] = await getPushInfo()
+ params.description = `${platformName} push notification ${params.description}.`
+ } catch (e: any) {
+ return {error: String(e)}
+ }
+ }
+
+ // If we don't do this we'll get an event rejection
+ await attemptAuth(NOTIFIER_RELAY)
+
+ const thunk = await publishAlert(params as AlertParams)
+ const error = await waitForThunkError(thunk)
+
+ if (error) {
+ return {error}
+ }
+
+ // Fetch our new status to make sure it's active
+ const $pubkey = pubkey.get()!
+ const address = getAddress(thunk.event)
+ const statusEvents = await loadAlertStatuses($pubkey!)
+ const statusEvent = statusEvents.find(event => getTagValue("d", event.tags) === address)
+ const statusTags = statusEvent
+ ? parseJson(await decrypt(signer.get(), NOTIFIER_PUBKEY, statusEvent.content))
+ : []
+ const {status = "error", message = "Your alert was not activated"}: Record =
+ fromPairs(statusTags)
+
+ if (status === "error") {
+ return {error: message}
+ }
+
+ return {ok: true}
+}
+
+export const createDmAlert = async () => {
+ if (!get(canDecrypt)) {
+ enableGiftWraps()
+ }
+
+ return createAlert({
+ description: `for direct messages.`,
+ feed: makeIntersectionFeed(
+ feedFromFilters([{kinds: [WRAP], "#p": [pubkey.get()!]}]),
+ makeRelayFeed(...get(userInboxRelays)),
+ ),
+ })
+}
+
// Settings
export const makeSettings = async (params: Partial) => {
@@ -532,3 +634,13 @@ export const payInvoice = async (invoice: string) => {
.then(() => getWebLn().sendPayment(invoice))
}
}
+
+// Gift Wraps
+
+export const enableGiftWraps = () => {
+ canDecrypt.set(true)
+
+ for (const event of repository.query([{kinds: [WRAP]}])) {
+ ensureUnwrapped(event)
+ }
+}
diff --git a/src/app/core/state.ts b/src/app/core/state.ts
index 636487f8..475968e9 100644
--- a/src/app/core/state.ts
+++ b/src/app/core/state.ts
@@ -21,6 +21,7 @@ import {
identity,
groupBy,
always,
+ tryCatch,
} from "@welshman/lib"
import type {Socket} from "@welshman/net"
import {Pool, load, AuthStateEvent, SocketEvent, netContext} from "@welshman/net"
@@ -32,6 +33,7 @@ import {
withGetter,
synced,
} from "@welshman/store"
+import {isKindFeed, findFeed} from "@welshman/feeds"
import {
getIdFilters,
WRAP,
@@ -70,6 +72,8 @@ import {
getTagValues,
verifyEvent,
makeEvent,
+ RelayMode,
+ getRelaysFromList,
} from "@welshman/util"
import type {TrustedEvent, SignedEvent, PublishedList, List, Filter} from "@welshman/util"
import {Nip59, decrypt} from "@welshman/signer"
@@ -94,6 +98,8 @@ import {
appContext,
getThunkError,
publishThunk,
+ userRelaySelections,
+ userInboxRelaySelections,
} from "@welshman/app"
import type {Thunk, Relay} from "@welshman/app"
import {preferencesStorageProvider} from "@src/lib/storage"
@@ -375,6 +381,18 @@ export const relaysPendingTrust = writable([])
export const relaysMostlyRestricted = writable>({})
+// Relay selections
+
+export const userReadRelays = derived(userRelaySelections, $l =>
+ getRelaysFromList($l, RelayMode.Read),
+)
+
+export const userWriteRelays = derived(userRelaySelections, $l =>
+ getRelaysFromList($l, RelayMode.Write),
+)
+
+export const userInboxRelays = derived(userInboxRelaySelections, $l => getRelaysFromList($l))
+
// Alerts
export type Alert = {
@@ -394,6 +412,17 @@ export const alerts = withGetter(
}),
)
+export const getAlertFeed = (alert: Alert) =>
+ tryCatch(() => JSON.parse(getTagValue("feed", alert.tags)!))
+
+export const dmAlert = derived(alerts, $alerts =>
+ $alerts.find(alert => {
+ const feed = getAlertFeed(alert)
+
+ return findFeed(feed, f => isKindFeed(f) && f.includes(WRAP))
+ }),
+)
+
// Alert Statuses
export type AlertStatus = {
diff --git a/src/app/util/push.ts b/src/app/util/push.ts
index 0eae805d..6c069404 100644
--- a/src/app/util/push.ts
+++ b/src/app/util/push.ts
@@ -2,7 +2,7 @@ import * as nip19 from "nostr-tools/nip19"
import {Capacitor} from "@capacitor/core"
import type {ActionPerformed, RegistrationError, Token} from "@capacitor/push-notifications"
import {PushNotifications} from "@capacitor/push-notifications"
-import {parseJson, poll} from "@welshman/lib"
+import {parseJson, sleep, poll} from "@welshman/lib"
import {isSignedEvent} from "@welshman/util"
import {goto} from "$app/navigation"
import {ucFirst} from "@lib/util"
@@ -50,6 +50,8 @@ export const getWebPushInfo = async () => {
}
const registration = await navigator.serviceWorker.ready
+
+ // This will hang on firefox in development builds, but works in production
let subscription = await registration.pushManager.getSubscription()
if (!subscription) {
@@ -118,14 +120,19 @@ export const getCapacitorPushInfo = async () => {
return info
}
-export const getPushInfo = (): Promise> => {
- switch (platform) {
- case "web":
- return getWebPushInfo()
- case "ios":
- case "android":
- return getCapacitorPushInfo()
- default:
- throw new Error(`Invalid push platform: ${platform}`)
- }
-}
+export const getPushInfo = (): Promise> =>
+ new Promise((resolve, reject) => {
+ sleep(3000).then(() => reject("Failed to request notification permissions"))
+
+ switch (platform) {
+ case "web":
+ getWebPushInfo().then(resolve, reject)
+ break
+ case "ios":
+ case "android":
+ getCapacitorPushInfo().then(resolve, reject)
+ break
+ default:
+ reject(`Invalid push platform: ${platform}`)
+ }
+ })
diff --git a/src/app/util/routes.ts b/src/app/util/routes.ts
index c06328e8..117af85d 100644
--- a/src/app/util/routes.ts
+++ b/src/app/util/routes.ts
@@ -14,8 +14,10 @@ import {
THREAD,
ZAP_GOAL,
EVENT_TIME,
+ getPubkeyTagValues,
} from "@welshman/util"
import {
+ ensureUnwrapped,
makeChatId,
entityLink,
decodeRelay,
@@ -74,24 +76,28 @@ export const getPrimaryNavItemIndex = ($page: Page) => {
}
export const goToEvent = async (event: TrustedEvent, options: Record = {}) => {
- if (event.kind === DIRECT_MESSAGE || event.kind === DIRECT_MESSAGE_FILE) {
- await scrollToEvent(event.id)
- }
+ const unwrapped = await ensureUnwrapped(event)
- const urls = Array.from(tracker.getRelays(event.id))
- const path = await getEventPath(event, urls)
+ if (unwrapped) {
+ const urls = Array.from(tracker.getRelays(unwrapped.id))
+ const path = await getEventPath(unwrapped, urls)
- if (path.includes("://")) {
- window.open(path)
- } else {
- goto(path, options)
+ if (path.includes("://")) {
+ window.open(path)
+ } else {
+ goto(path, options)
- await sleep(300)
- await scrollToEvent(event.id)
+ await sleep(300)
+ await scrollToEvent(unwrapped.id)
+ }
}
}
export const getEventPath = async (event: TrustedEvent, urls: string[]) => {
+ if (event.kind === DIRECT_MESSAGE || event.kind === DIRECT_MESSAGE_FILE) {
+ return makeChatPath([event.pubkey, ...getPubkeyTagValues(event.tags)])
+ }
+
const room = getTagValue(ROOM, event.tags)
if (urls.length > 0) {
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index c6ce64a8..a3a9e085 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -26,6 +26,11 @@
import type {TrustedEvent, StampedEvent} from "@welshman/util"
import {
WRAP,
+ ALERT_STATUS,
+ ALERT_EMAIL,
+ ALERT_WEB,
+ ALERT_IOS,
+ ALERT_ANDROID,
EVENT_TIME,
APP_DATA,
THREAD,
@@ -39,7 +44,6 @@
RELAYS,
BLOSSOM_SERVERS,
ROOMS,
- getRelaysFromList,
} from "@welshman/util"
import {Nip46Broker, makeSecret} from "@welshman/signer"
import type {Socket, RelayMessage, ClientMessage} from "@welshman/net"
@@ -67,7 +71,6 @@
signerLog,
dropSession,
defaultStorageAdapters,
- userInboxRelaySelections,
loginWithNip01,
loginWithNip46,
EventsStorageAdapter,
@@ -96,6 +99,7 @@
canDecrypt,
getSetting,
relaysMostlyRestricted,
+ userInboxRelays,
} from "@app/core/state"
import {loadUserData, listenForNotifications} from "@app/core/requests"
import {theme} from "@app/util/theme"
@@ -290,6 +294,11 @@
INBOX_RELAYS,
ROOMS,
APP_DATA,
+ ALERT_STATUS,
+ ALERT_EMAIL,
+ ALERT_WEB,
+ ALERT_IOS,
+ ALERT_ANDROID,
].includes(e.kind)
) {
return 1
@@ -437,19 +446,19 @@
// Listen for chats, populate chat-based notifications
let controller: AbortController
- derived([pubkey, canDecrypt, userInboxRelaySelections], identity).subscribe(
- ([$pubkey, $canDecrypt, $userInboxRelaySelections]) => {
+ derived([pubkey, canDecrypt, userInboxRelays], identity).subscribe(
+ ([$pubkey, $canDecrypt, $userInboxRelays]) => {
controller?.abort()
controller = new AbortController()
if ($pubkey && $canDecrypt) {
request({
signal: controller.signal,
+ relays: $userInboxRelays,
filters: [
{kinds: [WRAP], "#p": [$pubkey], since: ago(WEEK, 2)},
{kinds: [WRAP], "#p": [$pubkey], limit: 100},
],
- relays: getRelaysFromList($userInboxRelaySelections),
})
}
},
diff --git a/src/routes/chat/[chat]/+page.svelte b/src/routes/chat/[chat]/+page.svelte
index b5e71acb..c44568fa 100644
--- a/src/routes/chat/[chat]/+page.svelte
+++ b/src/routes/chat/[chat]/+page.svelte
@@ -1,11 +1,13 @@
diff --git a/src/routes/settings/+layout.svelte b/src/routes/settings/+layout.svelte
index ee96332f..818ddc3a 100644
--- a/src/routes/settings/+layout.svelte
+++ b/src/routes/settings/+layout.svelte
@@ -8,6 +8,8 @@
import Moon from "@assets/icons/moon.svg?dataurl"
import InfoSquare from "@assets/icons/info-square.svg?dataurl"
import Exit from "@assets/icons/logout-3.svg?dataurl"
+ import GalleryMinimalistic from "@assets/icons/gallery-minimalistic.svg?dataurl"
+ import Bell from "@assets/icons/bell.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Page from "@lib/components/Page.svelte"
import SecondaryNav from "@lib/components/SecondaryNav.svelte"
@@ -30,37 +32,45 @@
+
+ Your Settings
+
Profile
-
+
+
+ Alerts
+
+
+
Wallet
-
+
Relays
-
-
- Settings
+
+
+ Content
-
+
Theme
-
+
About
-
+
Log Out
diff --git a/src/routes/settings/alerts/+page.svelte b/src/routes/settings/alerts/+page.svelte
new file mode 100644
index 00000000..9e3b7c25
--- /dev/null
+++ b/src/routes/settings/alerts/+page.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/content/+page.svelte
similarity index 100%
rename from src/routes/settings/+page.svelte
rename to src/routes/settings/content/+page.svelte
diff --git a/src/routes/settings/profile/+page.svelte b/src/routes/settings/profile/+page.svelte
index 79c8d6d4..b1ce4837 100644
--- a/src/routes/settings/profile/+page.svelte
+++ b/src/routes/settings/profile/+page.svelte
@@ -22,7 +22,6 @@
import ProfileDelete from "@app/components/ProfileDelete.svelte"
import SignerStatus from "@app/components/SignerStatus.svelte"
import InfoKeys from "@app/components/InfoKeys.svelte"
- import Alerts from "@app/components/Alerts.svelte"
import {PLATFORM_NAME} from "@app/core/state"
import {pushModal} from "@app/util/modal"
import {clip} from "@app/util/toast"
@@ -141,7 +140,6 @@
{/if}
-