From a8d1c4bbbc7886959fa0031300f78a8ac6751395 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Tue, 30 Sep 2025 16:11:49 -0700 Subject: [PATCH] Refactor storage --- package.json | 22 +- pnpm-lock.yaml | 313 +++++++++--------- src/app/components/SpaceQuickLinks.svelte | 6 +- .../WalletAsReceivingAddress.svelte | 2 +- src/app/components/WalletConnect.svelte | 3 +- .../WalletUpdateReceivingAddress.svelte | 1 - src/app/core/commands.ts | 9 +- src/app/util/storage.ts | 211 ++++++++++++ src/lib/storage.ts | 160 +++++---- src/lib/storage/events.ts | 102 ------ src/lib/storage/freshness.ts | 45 --- src/lib/storage/handles.ts | 40 --- src/lib/storage/plaintext.ts | 45 --- src/lib/storage/relays.ts | 40 --- src/lib/storage/tracker.ts | 80 ----- src/lib/storage/zappers.ts | 41 --- src/routes/+layout.svelte | 79 +---- 17 files changed, 488 insertions(+), 711 deletions(-) create mode 100644 src/app/util/storage.ts delete mode 100644 src/lib/storage/events.ts delete mode 100644 src/lib/storage/freshness.ts delete mode 100644 src/lib/storage/handles.ts delete mode 100644 src/lib/storage/plaintext.ts delete mode 100644 src/lib/storage/relays.ts delete mode 100644 src/lib/storage/tracker.ts delete mode 100644 src/lib/storage/zappers.ts diff --git a/package.json b/package.json index 7de750cb..59f89338 100644 --- a/package.json +++ b/package.json @@ -59,17 +59,17 @@ "@types/throttle-debounce": "^5.0.2", "@vite-pwa/assets-generator": "^0.2.6", "@vite-pwa/sveltekit": "^0.6.6", - "@welshman/app": "^0.4.7", - "@welshman/content": "^0.4.7", - "@welshman/editor": "^0.4.7", - "@welshman/feeds": "^0.4.7", - "@welshman/lib": "^0.4.7", - "@welshman/net": "^0.4.7", - "@welshman/relay": "^0.4.7", - "@welshman/router": "^0.4.7", - "@welshman/signer": "^0.4.7", - "@welshman/store": "^0.4.7", - "@welshman/util": "^0.4.7", + "@welshman/app": "^0.5.0", + "@welshman/content": "^0.5.0", + "@welshman/editor": "^0.5.0", + "@welshman/feeds": "^0.5.0", + "@welshman/lib": "^0.5.0", + "@welshman/net": "^0.5.0", + "@welshman/relay": "^0.5.0", + "@welshman/router": "^0.5.0", + "@welshman/signer": "^0.5.0", + "@welshman/store": "^0.5.0", + "@welshman/util": "^0.5.0", "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 5b4d399d..9c550054 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -72,38 +72,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.7 - version: 0.4.7(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3) + specifier: ^0.5.0 + version: 0.5.0(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.7 - version: 0.4.7(typescript@5.8.3) + specifier: ^0.5.0 + version: 0.5.0(typescript@5.8.3) '@welshman/editor': - specifier: ^0.4.7 - version: 0.4.7(@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.5.0 + version: 0.5.0(@tiptap/extension-image@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.2)(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.7 - version: 0.4.7(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3) + specifier: ^0.5.0 + version: 0.5.0(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.7 - version: 0.4.7 + specifier: ^0.5.0 + version: 0.5.0 '@welshman/net': - specifier: ^0.4.7 - version: 0.4.7(typescript@5.8.3)(ws@8.18.3) + specifier: ^0.5.0 + version: 0.5.0(typescript@5.8.3)(ws@8.18.3) '@welshman/relay': - specifier: ^0.4.7 - version: 0.4.7(typescript@5.8.3) + specifier: ^0.5.0 + version: 0.5.0(typescript@5.8.3) '@welshman/router': - specifier: ^0.4.7 - version: 0.4.7(typescript@5.8.3) + specifier: ^0.5.0 + version: 0.5.0(typescript@5.8.3) '@welshman/signer': - specifier: ^0.4.7 - version: 0.4.7(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3) + specifier: ^0.5.0 + version: 0.5.0(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.7 - version: 0.4.7(typescript@5.8.3) + specifier: ^0.5.0 + version: 0.5.0(typescript@5.8.3) '@welshman/util': - specifier: ^0.4.7 - version: 0.4.7(typescript@5.8.3) + specifier: ^0.5.0 + version: 0.5.0(typescript@5.8.3) compressorjs: specifier: ^1.2.1 version: 1.2.1 @@ -1100,8 +1100,8 @@ packages: '@noble/curves@1.2.0': resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} - '@noble/curves@1.9.2': - resolution: {integrity: sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==} + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} engines: {node: ^14.21.3 || >=16} '@noble/hashes@1.3.1': @@ -1430,77 +1430,77 @@ packages: peerDependencies: '@tiptap/pm': ^2.7.0 - '@tiptap/extension-code-block@2.26.1': - resolution: {integrity: sha512-/TDDOwONl0qEUc4+B6V9NnWtSjz95eg7/8uCb8Y8iRbGvI9vT4/znRKofFxstvKmW4URu/H74/g0ywV57h0B+A==} + '@tiptap/extension-code-block@2.26.2': + resolution: {integrity: sha512-MJZ4QtziIWJ1zuSW2ogAHv+UHGk3DvGbVi+Dfmo0ybonXX7vRVHE+3qT7OcdTRBF+pC2oCnsjzqwFcGBP3BbZw==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-code@2.26.1': - resolution: {integrity: sha512-GU9deB1A/Tr4FMPu71CvlcjGKwRhGYz60wQ8m4aM+ELZcVIcZRa1ebR8bExRIEWnvRztQuyRiCQzw2N0xQJ1QQ==} + '@tiptap/extension-code@2.26.2': + resolution: {integrity: sha512-xnKJvzlAp75dheyaK5tLKAksHf9PtSr8a7OuPjf2IXS5K+QMtnwxx7KAHHijmecfWjLR0wyu9AvT/FWFfKi5LQ==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-document@2.26.1': - resolution: {integrity: sha512-2P2IZp1NRAE+21mRuFBiP3X2WKfZ6kUC23NJKpn8bcOamY3obYqCt0ltGPhE4eR8n8QAl2fI/3jIgjR07dC8ow==} + '@tiptap/extension-document@2.26.2': + resolution: {integrity: sha512-s0/P3A8zxWL/h3e20xWMTT/rcwD0+57I6mT9JgNBPtvhPePy8d698G6/qFK+x+GdIyjJylfsq2BrSE9H+QhIBg==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-dropcursor@2.26.1': - resolution: {integrity: sha512-JkDQU2ZYFOuT5mNYb8OiWGwD1HcjbtmX8tLNugQbToECmz9WvVPqJmn7V/q8VGpP81iEECz/IsyRmuf2kSD4uA==} + '@tiptap/extension-dropcursor@2.26.2': + resolution: {integrity: sha512-o5j4Gkurb/WBu1wP2tihYnZ8dENzmlxFWWMx++g6abEpn9xdud7VxHT5Ny7mBSBptI8uMwKT53weYC0on38n3g==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-gapcursor@2.26.1': - resolution: {integrity: sha512-KOiMZc3PwJS3hR0nSq5d0TJi2jkNZkLZElcT6pCEnhRHzPH6dRMu9GM5Jj798ZRUy0T9UFcKJalFZaDxnmRnpg==} + '@tiptap/extension-gapcursor@2.26.2': + resolution: {integrity: sha512-a68mi8V0mh058UrBIk23f50K5JGVeRZnF6ViptIleAD/Ny1K6VLjGCz6k190de+Tb9tnQLPEwwwDcy+ZnvCmYQ==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-hard-break@2.26.1': - resolution: {integrity: sha512-d6uStdNKi8kjPlHAyO59M6KGWATNwhLCD7dng0NXfwGndc22fthzIk/6j9F6ltQx30huy5qQram6j3JXwNACoA==} + '@tiptap/extension-hard-break@2.26.2': + resolution: {integrity: sha512-OLpeTey7p3ChyEsABLPvNv7rD/8E4k1JTt+H+MUjyL0dnrZuIWluckUJCJKnV8PhR9Mifngk1MTFUilpooiv1g==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-history@2.26.1': - resolution: {integrity: sha512-m6YR1gkkauIDo3PRl0gP+7Oc4n5OqDzcjVh6LvWREmZP8nmi94hfseYbqOXUb6RPHIc0JKF02eiRifT4MSd2nw==} + '@tiptap/extension-history@2.26.2': + resolution: {integrity: sha512-X/cu79AV5D2Z1QtuvKo/4/Rgl/Uti/n5V3QgCxFLQRCKTxHOCis+RlBCjBfOPztJX4T9QUE6lq20KqB47rsNwQ==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-image@2.26.1': - resolution: {integrity: sha512-96+MaYBJebQlR/ik5W72GLUfXdEoxFs+6jsoERxbM5qEdhb7TEnodBFtWZOwgDO27kFd6rSNZuW9r5KJNtljEg==} + '@tiptap/extension-image@2.26.2': + resolution: {integrity: sha512-3gK+ETLiWGAUdyPDXDheNJ38OgQabSzZJ+1nQo9KWjI7P3LQ7/ctxLtT+hAFpxX0qMK4bnu5vZaItSXxE3ZtpQ==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-link@2.26.1': - resolution: {integrity: sha512-7yfum5Jymkue/uOSTQPt2SmkZIdZx7t3QhZLqBU7R9ettkdSCBgEGok6N+scJM1R1Zes+maSckLm0JZw5BKYNA==} + '@tiptap/extension-link@2.26.2': + resolution: {integrity: sha512-rzYxx5wI1551ubPfW2pJ3V957cX/WAmbUI3q8Un+LlOsSmbddl+5BjlF5t/vl/pwaOv7FJAz9e29n877zkGOVQ==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-paragraph@2.26.1': - resolution: {integrity: sha512-UezvM9VDRAVJlX1tykgHWSD1g3MKfVMWWZ+Tg+PE4+kizOwoYkRWznVPgCAxjmyHajxpCKRXgqTZkOxjJ9Kjzg==} + '@tiptap/extension-paragraph@2.26.2': + resolution: {integrity: sha512-dccyffm95nNT9arjxGOyv4/cOPF4XS5Osylccp9KYLrmiSTXEuzVIYtMXhXbc0UUdABGvbFQWi88tRxgeDTkgA==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-placeholder@2.26.1': - resolution: {integrity: sha512-MBlqbkd+63btY7Qu+SqrXvWjPwooGZDsLTtl7jp52BczBl61cq9yygglt9XpM11TFMBdySgdLHBrLtQ0B7fBlw==} + '@tiptap/extension-placeholder@2.26.2': + resolution: {integrity: sha512-XBTDcpEo7Zo/1+RhGnRxA2TF0elQW7EayUcV+lJ3f7HQ5lrb5NTnakYc1ydeZ8Ih6vUqbK2CQUsESe3UWHHgHg==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-text@2.26.1': - resolution: {integrity: sha512-p2n8WVMd/2vckdJlol24acaTDIZAhI7qle5cM75bn01sOEZoFlSw6SwINOULrUCzNJsYb43qrLEibZb4j2LeQw==} + '@tiptap/extension-text@2.26.2': + resolution: {integrity: sha512-Rb8Le/Li+EixQNc/pGkEJpLjozTjWYP9glaYfnjPtRVw4tHcd7khVm5mer0TQjonbBUjVC1zSuXv9gifXOv6DQ==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm@2.12.0': resolution: {integrity: sha512-TNzVwpeNzFfHAcYTOKqX9iU4fRxliyoZrCnERR+RRzeg7gWrXrCLubQt1WEx0sojMAfznshSL3M5HGsYjEbYwA==} - '@tiptap/suggestion@2.26.1': - resolution: {integrity: sha512-iNWJdQN7h01keNoVwyCsdI7ZX11YkrexZjCnutWK17Dd72s3NYVTmQXu7saftwddT4nDdlczNxAFosrt0zMhcg==} + '@tiptap/suggestion@2.26.2': + resolution: {integrity: sha512-BtigI3xOJQbdNh2OeKN5wQDI/EPGN4GXdpoMHhENZeXKrX6PvWNaqjyLVsV3QiLgv6P352nZWcgo51wPXY2JgQ==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 @@ -1659,41 +1659,41 @@ packages: '@vite-pwa/assets-generator': optional: true - '@welshman/app@0.4.7': - resolution: {integrity: sha512-JEgr3NhzDLeOoTSZ4+AESKhW+kwqYClLbLd6ccHV+Wa7hjoPJy2FlY3rDuppw9QSeTNCDEOvqCsjeWYz83lUBA==} + '@welshman/app@0.5.0': + resolution: {integrity: sha512-Vj5urVSylqmlpmwmZv6Q9u7FzWAW1XfbTytheenfv1gsL3HAfJm9gnW9hInPix9GOC3jdT9XKCZmH9+9Sfn7TA==} - '@welshman/content@0.4.7': - resolution: {integrity: sha512-PF2FqiE3QUybl0CwwaEI2aGCZIis2rbdvBgeafgPiHW7fRYROxUZdtcMy+MyiRfiV40uoBcYwu/N6hHCRU7Pag==} + '@welshman/content@0.5.0': + resolution: {integrity: sha512-F7B4JoY+OP3ImAfn+wWbBsKw39HaktI5Qi+DE8/KtgnXRTBpJ9XKmvyVEUYvSFysBuckprdHV7ZqVEmc5W24jA==} - '@welshman/editor@0.4.7': - resolution: {integrity: sha512-K6XCLG+vVvVeEYPx+m/+Lfx/JO+NtvvIemCds6gFjY7ac+WaQuEQDP3kFGYQu69XiFXp/UZrIa+t7SezEpnU8A==} + '@welshman/editor@0.5.0': + resolution: {integrity: sha512-rJWdjbidy+EtoqeDtUsVTeJGHQ9+HznVeLYNli0GfugvJoHlx2NB1pFTSlS2YCvm8QWDsI2ZCZglUeqPMcO2YA==} - '@welshman/feeds@0.4.7': - resolution: {integrity: sha512-aZQuTUD4aSkL0s2BkjwEpo5KTd9BKf/XiOssQrltLdc8NIsz8RIO0XLgCpFb0/dmqHRoJEqU/plIBy7AlleRCQ==} + '@welshman/feeds@0.5.0': + resolution: {integrity: sha512-bY8mjA+9lJoy6vU2lLpX+2sGp3LKpgyzFRf/oAQ6W/bQTlFLE1VXC4/W3yyVBDlGw8oJM9niaGd0/zCefCeTkg==} - '@welshman/lib@0.4.7': - resolution: {integrity: sha512-VP3WO2ROo5pf2vHwnrdt6lQVTc8Eo52Ie+1/9ZzfTrSxtLrreSSxW3H+1oPDbHl3FXbDnQWdFWbxys6OxzKZWw==} + '@welshman/lib@0.5.0': + resolution: {integrity: sha512-Sj4zNPpMC3QSi4hu8bjF/qzY+klYf1zlKOhwKKOJJDcSes1/9khT5QB9icJn7ki1a7J7mzYrJaa01hTE9hxkCw==} engines: {node: '>=12.0.0'} - '@welshman/net@0.4.7': - resolution: {integrity: sha512-S0dGVqNAfo5cvBxIuaI2oEX0JUs2FuzkOcYXkMNB1plWzi1mBcskNCBWfFf/zHJYaqUoYjZ/tDARy64lge9m/w==} + '@welshman/net@0.5.0': + resolution: {integrity: sha512-k/kRaNmP0TC/quoRJPJ+yYk6TSyjB4HX1NaXmFmWoDIOnIUskgbgH83hbklvHoO3TvAgR1r8ENzDtiTnUVryMg==} - '@welshman/relay@0.4.7': - resolution: {integrity: sha512-FkqYswNA3uT1NeJVHdEZ7p9jEPGCFMx4ci2y+h9o25rCDdxg3WUhqDSdc5d85sGTO0qG2pNnvNMfS/Du/nFlOA==} + '@welshman/relay@0.5.0': + resolution: {integrity: sha512-9sYPz6b8H+zlyAyZZ/zRGwnKN+lWGt/Tn7+pFLCSpdAAHTtRU0T68MaRgnLHJNcLNiMpwjG/He/CZj0PkYL5Bw==} - '@welshman/router@0.4.7': - resolution: {integrity: sha512-HnB1qrKGNxL8HtC6p47yHUnaDHevi+IKtqWEVCIFMRf17GwINBc3wp3+d3pu9KBBXItFZz1awABvZK+pNKQcgg==} + '@welshman/router@0.5.0': + resolution: {integrity: sha512-ue/8pzVweTfqE1naJ1TzdC+PCWt3Z3gRu1jesXFPXelWb6rhb9eb4Rk91I2TuZLKdJbX5PvaO+d/lbtnnBM6eA==} - '@welshman/signer@0.4.7': - resolution: {integrity: sha512-V5Jdmblb2kPO5bAv1CzVrodZiwKpYYotmS6MFXfWmyrKKp+9B5KoMWzXt6yfd4HWYbEKEjLhbm1gzbckupW8Nw==} + '@welshman/signer@0.5.0': + resolution: {integrity: sha512-EQhNfV+7nNEXYRc71XNvkgy74tu1IoWrtDZAkASyaCxTzigU8K2R7QfCFQEl3zxWlmu1PZ9Xr1teTeo3OX/IPw==} peerDependencies: nostr-signer-capacitor-plugin: ~0.0.4 - '@welshman/store@0.4.7': - resolution: {integrity: sha512-8PniW1AOOYFtLRYMuay62taumW7zgwtBmouwoMh08fBQjLb+c90V4g2cEGVWoyvKXSLzQkQppPlaqYzdSDqgwg==} + '@welshman/store@0.5.0': + resolution: {integrity: sha512-wdQgUvxWhvi6ybx20Ic9GZMDHmXDs3t9Y6Dp7CUu8xBZsiSIqflMGitjd8ITvH5LBVVMbhqdWAxIat/ihXP/rw==} - '@welshman/util@0.4.7': - resolution: {integrity: sha512-FlmBiZeKlAEAAwyhu7cWtlfAxU3CWX7WQGn0NkCZaAjgGV3n8LIDjT1u9m1PmXirBT0+qFNGLWas3p72IQMLgg==} + '@welshman/util@0.5.0': + resolution: {integrity: sha512-i/Pg6uy9x8KfOThbvYHpanPX171mkTLaw0JCNIB4ZZAxjYKhpRkczRKpoyw9GQ1yW/ROWzUhYFaPCwU7UUcioA==} '@xml-tools/parser@1.0.11': resolution: {integrity: sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==} @@ -3090,8 +3090,8 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - js-base64@3.7.7: - resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} + js-base64@3.7.8: + resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3196,8 +3196,8 @@ packages: linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - linkifyjs@4.3.1: - resolution: {integrity: sha512-DRSlB9DKVW04c4SUdGvKK5FR6be45lTU9M76JnngqPeeGDqPwYc0zdUErtsNVMtxPXgUWV4HbXbnC4sNyBxkYg==} + linkifyjs@4.3.2: + resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==} load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} @@ -3441,8 +3441,8 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - nostr-editor@1.0.0: - resolution: {integrity: sha512-+TL3G0m7WsXeEAitxzQhun7hyARxqRANjGIS2z9CBbniCGvT/Wz6YLgUnUysnBg3tmSgMZg5FWhaDPwfvdvbSw==} + nostr-editor@1.0.1: + resolution: {integrity: sha512-HXqXjxtIN0CcC7sLV5xYjEsQF0bFYLmNKxS75ya2yZGQ/z16U+uK6bb2Hd72QyqXlHXyWN0m24E5Gcws8/NhRQ==} engines: {node: '>=18.16.1'} peerDependencies: '@tiptap/core': ^2.6.6 @@ -5957,7 +5957,7 @@ snapshots: dependencies: '@noble/hashes': 1.3.2 - '@noble/curves@1.9.2': + '@noble/curves@1.9.7': dependencies: '@noble/hashes': 1.8.0 @@ -6255,58 +6255,58 @@ snapshots: dependencies: '@tiptap/pm': 2.12.0 - '@tiptap/extension-code-block@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': + '@tiptap/extension-code-block@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) '@tiptap/pm': 2.12.0 - '@tiptap/extension-code@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': + '@tiptap/extension-code@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) - '@tiptap/extension-document@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': + '@tiptap/extension-document@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) - '@tiptap/extension-dropcursor@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': + '@tiptap/extension-dropcursor@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) '@tiptap/pm': 2.12.0 - '@tiptap/extension-gapcursor@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': + '@tiptap/extension-gapcursor@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) '@tiptap/pm': 2.12.0 - '@tiptap/extension-hard-break@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': + '@tiptap/extension-hard-break@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) - '@tiptap/extension-history@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': + '@tiptap/extension-history@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@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-image@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': dependencies: '@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/extension-link@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) '@tiptap/pm': 2.12.0 - linkifyjs: 4.3.1 + linkifyjs: 4.3.2 - '@tiptap/extension-paragraph@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': + '@tiptap/extension-paragraph@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) - '@tiptap/extension-placeholder@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': + '@tiptap/extension-placeholder@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) '@tiptap/pm': 2.12.0 - '@tiptap/extension-text@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': + '@tiptap/extension-text@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) @@ -6331,7 +6331,7 @@ snapshots: prosemirror-transform: 1.10.4 prosemirror-view: 1.39.3 - '@tiptap/suggestion@2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': + '@tiptap/suggestion@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)': dependencies: '@tiptap/core': 2.12.0(@tiptap/pm@2.12.0) '@tiptap/pm': 2.12.0 @@ -6542,19 +6542,18 @@ snapshots: optionalDependencies: '@vite-pwa/assets-generator': 0.2.6 - '@welshman/app@0.4.7(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)': + '@welshman/app@0.5.0(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.7(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3) - '@welshman/lib': 0.4.7 - '@welshman/net': 0.4.7(typescript@5.8.3)(ws@8.18.3) - '@welshman/relay': 0.4.7(typescript@5.8.3) - '@welshman/router': 0.4.7(typescript@5.8.3) - '@welshman/signer': 0.4.7(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3) - '@welshman/store': 0.4.7(typescript@5.8.3) - '@welshman/util': 0.4.7(typescript@5.8.3) + '@welshman/feeds': 0.5.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3) + '@welshman/lib': 0.5.0 + '@welshman/net': 0.5.0(typescript@5.8.3)(ws@8.18.3) + '@welshman/relay': 0.5.0(typescript@5.8.3) + '@welshman/router': 0.5.0(typescript@5.8.3) + '@welshman/signer': 0.5.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3) + '@welshman/store': 0.5.0(typescript@5.8.3) + '@welshman/util': 0.5.0(typescript@5.8.3) fuse.js: 7.1.0 - idb: 8.0.2 svelte: 4.2.20 throttle-debounce: 5.0.2 transitivePeerDependencies: @@ -6562,31 +6561,31 @@ snapshots: - typescript - ws - '@welshman/content@0.4.7(typescript@5.8.3)': + '@welshman/content@0.5.0(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.7(@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.5.0(@tiptap/extension-image@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(linkifyjs@4.3.2)(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)) - '@tiptap/extension-code-block': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) - '@tiptap/extension-document': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)) - '@tiptap/extension-dropcursor': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) - '@tiptap/extension-gapcursor': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) - '@tiptap/extension-hard-break': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)) - '@tiptap/extension-history': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) - '@tiptap/extension-paragraph': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)) - '@tiptap/extension-placeholder': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) - '@tiptap/extension-text': 2.26.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)) + '@tiptap/extension-code': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)) + '@tiptap/extension-code-block': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) + '@tiptap/extension-document': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)) + '@tiptap/extension-dropcursor': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) + '@tiptap/extension-gapcursor': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) + '@tiptap/extension-hard-break': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)) + '@tiptap/extension-history': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) + '@tiptap/extension-paragraph': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)) + '@tiptap/extension-placeholder': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) + '@tiptap/extension-text': 2.26.2(@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.7 - '@welshman/util': 0.4.7(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))) + '@tiptap/suggestion': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) + '@welshman/lib': 0.5.0 + '@welshman/util': 0.5.0(typescript@5.8.3) + nostr-editor: 1.0.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/extension-image@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)(linkifyjs@4.3.2)(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 transitivePeerDependencies: @@ -6600,79 +6599,79 @@ snapshots: - tiptap-markdown - typescript - '@welshman/feeds@0.4.7(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)': + '@welshman/feeds@0.5.0(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.7 - '@welshman/net': 0.4.7(typescript@5.8.3)(ws@8.18.3) - '@welshman/relay': 0.4.7(typescript@5.8.3) - '@welshman/router': 0.4.7(typescript@5.8.3) - '@welshman/signer': 0.4.7(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3) - '@welshman/util': 0.4.7(typescript@5.8.3) + '@welshman/lib': 0.5.0 + '@welshman/net': 0.5.0(typescript@5.8.3)(ws@8.18.3) + '@welshman/relay': 0.5.0(typescript@5.8.3) + '@welshman/router': 0.5.0(typescript@5.8.3) + '@welshman/signer': 0.5.0(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3) + '@welshman/util': 0.5.0(typescript@5.8.3) trava: 1.2.1 transitivePeerDependencies: - nostr-signer-capacitor-plugin - typescript - ws - '@welshman/lib@0.4.7': + '@welshman/lib@0.5.0': dependencies: '@scure/base': 1.2.6 '@types/events': 3.0.3 events: 3.3.0 - '@welshman/net@0.4.7(typescript@5.8.3)(ws@8.18.3)': + '@welshman/net@0.5.0(typescript@5.8.3)(ws@8.18.3)': dependencies: - '@welshman/lib': 0.4.7 - '@welshman/relay': 0.4.7(typescript@5.8.3) - '@welshman/util': 0.4.7(typescript@5.8.3) + '@welshman/lib': 0.5.0 + '@welshman/relay': 0.5.0(typescript@5.8.3) + '@welshman/util': 0.5.0(typescript@5.8.3) events: 3.3.0 isomorphic-ws: 5.0.0(ws@8.18.3) transitivePeerDependencies: - typescript - ws - '@welshman/relay@0.4.7(typescript@5.8.3)': + '@welshman/relay@0.5.0(typescript@5.8.3)': dependencies: - '@welshman/lib': 0.4.7 - '@welshman/util': 0.4.7(typescript@5.8.3) + '@welshman/lib': 0.5.0 + '@welshman/util': 0.5.0(typescript@5.8.3) transitivePeerDependencies: - typescript - '@welshman/router@0.4.7(typescript@5.8.3)': + '@welshman/router@0.5.0(typescript@5.8.3)': dependencies: - '@welshman/lib': 0.4.7 - '@welshman/relay': 0.4.7(typescript@5.8.3) - '@welshman/util': 0.4.7(typescript@5.8.3) + '@welshman/lib': 0.5.0 + '@welshman/relay': 0.5.0(typescript@5.8.3) + '@welshman/util': 0.5.0(typescript@5.8.3) transitivePeerDependencies: - typescript - '@welshman/signer@0.4.7(nostr-signer-capacitor-plugin@0.0.4(@capacitor/core@7.2.0))(typescript@5.8.3)(ws@8.18.3)': + '@welshman/signer@0.5.0(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/curves': 1.9.7 '@noble/hashes': 1.8.0 - '@welshman/lib': 0.4.7 - '@welshman/net': 0.4.7(typescript@5.8.3)(ws@8.18.3) - '@welshman/util': 0.4.7(typescript@5.8.3) + '@welshman/lib': 0.5.0 + '@welshman/net': 0.5.0(typescript@5.8.3)(ws@8.18.3) + '@welshman/util': 0.5.0(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.7(typescript@5.8.3)': + '@welshman/store@0.5.0(typescript@5.8.3)': dependencies: - '@welshman/lib': 0.4.7 - '@welshman/relay': 0.4.7(typescript@5.8.3) - '@welshman/util': 0.4.7(typescript@5.8.3) + '@welshman/lib': 0.5.0 + '@welshman/relay': 0.5.0(typescript@5.8.3) + '@welshman/util': 0.5.0(typescript@5.8.3) svelte: 4.2.20 transitivePeerDependencies: - typescript - '@welshman/util@0.4.7(typescript@5.8.3)': + '@welshman/util@0.5.0(typescript@5.8.3)': dependencies: '@types/ws': 8.18.1 - '@welshman/lib': 0.4.7 - js-base64: 3.7.7 + '@welshman/lib': 0.5.0 + js-base64: 3.7.8 nostr-tools: 2.14.2(typescript@5.8.3) nostr-wasm: 0.1.0 transitivePeerDependencies: @@ -8192,7 +8191,7 @@ snapshots: jiti@1.21.7: {} - js-base64@3.7.7: {} + js-base64@3.7.8: {} js-tokens@4.0.0: {} @@ -8267,7 +8266,7 @@ snapshots: dependencies: uc.micro: 2.1.0 - linkifyjs@4.3.1: {} + linkifyjs@4.3.2: {} load-json-file@4.0.0: dependencies: @@ -8503,15 +8502,15 @@ snapshots: normalize-range@0.1.2: {} - 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-editor@1.0.1(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/extension-image@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)))(@tiptap/extension-link@2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0)(linkifyjs@4.3.2)(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))): dependencies: '@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/extension-image': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)) + '@tiptap/extension-link': 2.26.2(@tiptap/core@2.12.0(@tiptap/pm@2.12.0))(@tiptap/pm@2.12.0) '@tiptap/pm': 2.12.0 - js-base64: 3.7.7 + js-base64: 3.7.8 light-bolt11-decoder: 3.2.0 - linkifyjs: 4.3.1 + linkifyjs: 4.3.2 nostr-tools: 2.14.2(typescript@5.8.3) prosemirror-markdown: 1.13.2 prosemirror-model: 1.25.1 diff --git a/src/app/components/SpaceQuickLinks.svelte b/src/app/components/SpaceQuickLinks.svelte index 8c3fea88..f403d8a7 100644 --- a/src/app/components/SpaceQuickLinks.svelte +++ b/src/app/components/SpaceQuickLinks.svelte @@ -69,7 +69,7 @@ Goals {#if $notifications.has(goalsPath)}
{/if} @@ -81,7 +81,7 @@ Threads {#if $notifications.has(threadsPath)}
{/if} @@ -93,7 +93,7 @@ Calendar {#if $notifications.has(calendarPath)}
{/if} diff --git a/src/app/components/WalletAsReceivingAddress.svelte b/src/app/components/WalletAsReceivingAddress.svelte index a5f16fbf..2114e298 100644 --- a/src/app/components/WalletAsReceivingAddress.svelte +++ b/src/app/components/WalletAsReceivingAddress.svelte @@ -7,7 +7,7 @@ import {updateProfile} from "@app/core/commands" import {clearModals} from "@app/util/modal" import {userProfile, session} from "@welshman/app" - import {makeProfile, type NWCInfo} from "@welshman/util" + import {makeProfile} from "@welshman/util" const lud16 = getWalletAddress($session!.wallet!) diff --git a/src/app/components/WalletConnect.svelte b/src/app/components/WalletConnect.svelte index d6f97507..b8c6a38a 100644 --- a/src/app/components/WalletConnect.svelte +++ b/src/app/components/WalletConnect.svelte @@ -3,8 +3,7 @@ import {nwc} from "@getalby/sdk" import {sleep, assoc} from "@welshman/lib" import type {NWCInfo} from "@welshman/util" - import {pubkey, userProfile, updateSession, profilesByPubkey} from "@welshman/app" - import {makeProfile} from "@welshman/util" + import {pubkey, userProfile, updateSession} from "@welshman/app" import Link from "@lib/components/Link.svelte" import Cpu from "@assets/icons/cpu-bolt.svg?dataurl" import Lock from "@assets/icons/lock-keyhole.svg?dataurl" diff --git a/src/app/components/WalletUpdateReceivingAddress.svelte b/src/app/components/WalletUpdateReceivingAddress.svelte index f058eb12..0c941448 100644 --- a/src/app/components/WalletUpdateReceivingAddress.svelte +++ b/src/app/components/WalletUpdateReceivingAddress.svelte @@ -7,7 +7,6 @@ import ModalFooter from "@lib/components/ModalFooter.svelte" import Wallet from "@assets/icons/wallet.svg?dataurl" import CheckCircle from "@assets/icons/check-circle.svg?dataurl" - import CloseCircle from "@assets/icons/close-circle.svg?dataurl" import {updateProfile} from "@app/core/commands" import {pushToast} from "@app/util/toast" diff --git a/src/app/core/commands.ts b/src/app/core/commands.ts index 4ca68e4a..7dd5c9be 100644 --- a/src/app/core/commands.ts +++ b/src/app/core/commands.ts @@ -84,7 +84,6 @@ import { userInboxRelaySelections, nip44EncryptToSelf, loadRelay, - clearStorage, dropSession, tagEventForComment, tagEventForQuote, @@ -111,7 +110,7 @@ import { } from "@app/core/state" import {loadAlertStatuses} from "@app/core/requests" import {platform, platformName, getPushInfo} from "@app/util/push" -import {clearFileStorage, preferencesStorageProvider} from "@src/lib/storage" +import {preferencesStorageProvider, collectionStorageProvider} from "@src/lib/storage" // Utils @@ -154,12 +153,10 @@ export const logout = async () => { dropSession($pubkey) } - await clearStorage() - localStorage.clear() - await preferencesStorageProvider.clear() - await clearFileStorage() + await preferencesStorageProvider.clear() + await collectionStorageProvider.clear() } // Synchronization diff --git a/src/app/util/storage.ts b/src/app/util/storage.ts new file mode 100644 index 00000000..be74144d --- /dev/null +++ b/src/app/util/storage.ts @@ -0,0 +1,211 @@ +import {on, throttle, fromPairs, batch, sortBy, concat} from "@welshman/lib" +import {throttled, freshness} from "@welshman/store" +import { + PROFILE, + FOLLOWS, + MUTES, + RELAYS, + BLOSSOM_SERVERS, + INBOX_RELAYS, + ROOMS, + APP_DATA, + ALERT_STATUS, + ALERT_EMAIL, + ALERT_WEB, + ALERT_IOS, + ALERT_ANDROID, + EVENT_TIME, + THREAD, + MESSAGE, + DIRECT_MESSAGE, + DIRECT_MESSAGE_FILE, +} from "@welshman/util" +import type {Zapper, TrustedEvent} from "@welshman/util" +import type {RepositoryUpdate} from "@welshman/relay" +import type {Handle, Relay} from "@welshman/app" +import { + plaintext, + tracker, + relays, + repository, + handles, + zappers, + onZapper, + onHandle, +} from "@welshman/app" +import {collectionStorageProvider} from "@lib/storage" + +const syncEvents = async () => { + repository.load(await collectionStorageProvider.get("events")) + + const rankEvent = (event: TrustedEvent) => { + switch (event.kind) { + case PROFILE: + return 1 + case FOLLOWS: + return 1 + case MUTES: + return 1 + case RELAYS: + return 1 + case BLOSSOM_SERVERS: + return 1 + case INBOX_RELAYS: + return 1 + case ROOMS: + return 1 + case APP_DATA: + return 1 + case ALERT_STATUS: + return 1 + case ALERT_EMAIL: + return 1 + case ALERT_WEB: + return 1 + case ALERT_IOS: + return 1 + case ALERT_ANDROID: + return 1 + case EVENT_TIME: + return 0.9 + case THREAD: + return 0.9 + case MESSAGE: + return 0.9 + case DIRECT_MESSAGE: + return 0.9 + case DIRECT_MESSAGE_FILE: + return 0.9 + default: + return 0 + } + } + + return on( + repository, + "update", + batch(3000, async (updates: RepositoryUpdate[]) => { + let added: TrustedEvent[] = [] + const removed = new Set() + + for (const update of updates) { + for (const event of update.added) { + if (rankEvent(event) > 0) { + added.push(event) + removed.delete(event.id) + } + } + + for (const id of update.removed) { + added = added.filter(event => !update.removed.has(event.id)) + removed.add(id) + } + } + + if (added.length > 0) { + let events = concat(await collectionStorageProvider.get("events"), added) + + // If we're well above our retention limit, drop lowest-ranked events + if (events.length > 15_000) { + events = sortBy(e => -rankEvent(e), events).slice(10_000) + } + + await collectionStorageProvider.set("events", events) + } + }), + ) +} + +const syncTracker = async () => { + const relaysById = new Map>() + + for (const [id, relays] of await collectionStorageProvider.get<[string, string[]]>("tracker")) { + relaysById.set(id, new Set(relays)) + } + + tracker.load(relaysById) + + let p = Promise.resolve() + + const updateOne = batch(3000, (ids: string[]) => { + p = p.then(() => { + collectionStorageProvider.add( + "tracker", + ids.map(id => [id, Array.from(tracker.getRelays(id))]), + ) + }) + }) + + const updateAll = throttle(3000, () => { + p = p.then(() => { + collectionStorageProvider.set("tracker", Array.from(tracker.relaysById.entries())) + }) + }) + + tracker.on("add", updateOne) + tracker.on("remove", updateOne) + tracker.on("load", updateAll) + tracker.on("clear", updateAll) + + return () => { + tracker.off("add", updateOne) + tracker.off("remove", updateOne) + tracker.off("load", updateAll) + tracker.off("clear", updateAll) + } +} + +const syncRelays = async () => { + relays.set(await collectionStorageProvider.get("relays")) + + return throttled(3000, relays).subscribe($relays => { + collectionStorageProvider.set("relays", $relays) + }) +} + +const syncHandles = async () => { + handles.set(await collectionStorageProvider.get("handles")) + + return onHandle( + batch(3000, async $handles => { + await collectionStorageProvider.add("handles", $handles) + }), + ) +} + +const syncZappers = async () => { + zappers.set(await collectionStorageProvider.get("zappers")) + + return onZapper( + batch(3000, async $zappers => { + await collectionStorageProvider.add("zappers", $zappers) + }), + ) +} + +const syncFreshness = async () => { + freshness.set(fromPairs(await collectionStorageProvider.get<[string, number]>("freshness"))) + + return throttled(3000, freshness).subscribe($freshness => { + collectionStorageProvider.set("freshness", Object.entries($freshness)) + }) +} + +const syncPlaintext = async () => { + plaintext.set(fromPairs(await collectionStorageProvider.get<[string, string]>("plaintext"))) + + return throttled(3000, plaintext).subscribe($plaintext => { + collectionStorageProvider.set("plaintext", Object.entries($plaintext)) + }) +} + +export const syncDataStores = () => + Promise.all([ + syncEvents(), + syncTracker(), + syncRelays(), + syncHandles(), + syncZappers(), + syncFreshness(), + syncPlaintext(), + ]) diff --git a/src/lib/storage.ts b/src/lib/storage.ts index 02a879e2..b5abb458 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -1,17 +1,11 @@ +import {parseJson} from "@welshman/lib" import {type StorageProvider} from "@welshman/store" import {Preferences} from "@capacitor/preferences" -import type {Unsubscriber} from "svelte/store" -import {Encoding, Filesystem, type Directory} from "@capacitor/filesystem" -import {EventsStorageProvider} from "@lib/storage/events" -import {FreshnessStorageProvider} from "@lib/storage/freshness" -import {HandlesStorageProvider} from "@lib/storage/handles" -import {PlaintextStorageProvider} from "@lib/storage/plaintext" -import {RelaysStorageProvider} from "@lib/storage/relays" -import {TrackerStorageProvider} from "@lib/storage/tracker" -import {ZappersStorageProvider} from "@lib/storage/zappers" -import {repository, tracker, unsubscribers} from "@welshman/app" +import {Encoding, Filesystem, Directory} from "@capacitor/filesystem" export class PreferencesStorageProvider implements StorageProvider { + p = Promise.resolve() + get = async (key: string): Promise => { const result = await Preferences.get({key}) if (!result.value) return undefined @@ -22,74 +16,94 @@ export class PreferencesStorageProvider implements StorageProvider { } } - p = Promise.resolve() set = async (key: string, value: T): Promise => { - this.p = this.p.then(async () => await Preferences.set({key, value: JSON.stringify(value)})) + this.p = this.p.then(() => Preferences.set({key, value: JSON.stringify(value)})) + + await this.p + } + + clear = async () => { + this.p = this.p.then(() => Preferences.clear()) + + await this.p + } +} + +export const preferencesStorageProvider = new PreferencesStorageProvider() + +export class CollectionStorageProvider implements StorageProvider { + p = Promise.resolve() + + get = async (key: string): Promise => { + try { + const file = await Filesystem.readFile({ + path: key + ".json", + directory: Directory.Data, + encoding: Encoding.UTF8, + }) + + const items: T[] = [] + for (const line of file.data.toString().split("\n")) { + const item = parseJson(line) + + if (item) { + items.push(item) + } + } + + return items + } catch (err) { + // file doesn't exist, or isn't valid json + return [] + } + } + + set = async (key: string, value: T[]): Promise => { + this.p = this.p.then(async () => { + await Filesystem.writeFile({ + path: key + ".json", + directory: Directory.Data, + encoding: Encoding.UTF8, + data: value.map(v => JSON.stringify(v)).join("\n"), + }) + }) + + await this.p + } + + add = async (key: string, value: T[]): Promise => { + this.p = this.p.then(async () => { + await Filesystem.appendFile({ + path: key + ".json", + directory: Directory.Data, + encoding: Encoding.UTF8, + data: value.map(v => JSON.stringify(v)).join("\n"), + }) + }) + await this.p } clear = async (): Promise => { - await Preferences.clear() - this.p = Promise.resolve() + this.p = this.p.then(async () => { + try { + const res = await Filesystem.readdir({path: "./", directory: Directory.Data}) + + await Promise.all( + res.files.map(file => + Filesystem.deleteFile({ + path: file.name + ".json", + directory: Directory.Data, + }), + ), + ) + } catch (e) { + // Directory might not have been created + } + }) + + await this.p } } -// singleton instance of PreferencesStorageProvider -export const preferencesStorageProvider = new PreferencesStorageProvider() - -export interface FilesystemStorageProvider { - initializeState(): Promise - sync(): Unsubscriber -} - -export const getAllFromFile = async ( - filepath: string, - directory: Directory, - encoding: Encoding, -): Promise => { - try { - const contents = ( - await Filesystem.readFile({ - path: filepath, - directory, - encoding, - }) - ).data.toString() - - if (!contents || contents == "") { - return [] - } - - return JSON.parse(contents) - } catch (err) { - // file doesn't exist - return [] - } -} - -export const defaultStorageProviders = { - relays: new RelaysStorageProvider(), - handles: new RelaysStorageProvider(), - zappers: new ZappersStorageProvider(), - freshness: new FreshnessStorageProvider(), - plaintext: new PlaintextStorageProvider(), - tracker: new TrackerStorageProvider({tracker}), - events: new EventsStorageProvider({limit: 10_000, repository, rankEvent: () => 1}), -} - -export const initFileStorage = async (storageProviders: Record) => { - await Promise.all(Object.values(storageProviders).map(async provider => { - await provider.initializeState() - unsubscribers.push(provider.sync()) - })) -} - -export const clearFileStorage = async (): Promise => { - await EventsStorageProvider.clearStorage() - await FreshnessStorageProvider.clearStorage() - await HandlesStorageProvider.clearStorage() - await PlaintextStorageProvider.clearStorage() - await RelaysStorageProvider.clearStorage() - await TrackerStorageProvider.clearStorage() - await ZappersStorageProvider.clearStorage() -} \ No newline at end of file +export const collectionStorageProvider = new CollectionStorageProvider() diff --git a/src/lib/storage/events.ts b/src/lib/storage/events.ts deleted file mode 100644 index 9efbdfe1..00000000 --- a/src/lib/storage/events.ts +++ /dev/null @@ -1,102 +0,0 @@ -import {getAllFromFile, type FilesystemStorageProvider} from "@lib/storage" -import type {TrustedEvent} from "@welshman/util" -import type {Unsubscriber} from "svelte/store" -import {Filesystem, Directory, Encoding} from "@capacitor/filesystem" -import type {Repository, RepositoryUpdate} from "@welshman/relay" -import {on, sortBy} from "@welshman/lib" - -export class EventsStorageProvider implements FilesystemStorageProvider { - static filepath = "events.json" - static directory = Directory.Data - static encoding = Encoding.UTF8 - limit: number - repository: Repository - rankEvent: (event: TrustedEvent) => number - eventCount: number = 0 - isDeleting = false - - constructor({ - limit, - repository, - rankEvent, - }: { - limit: number - repository: Repository - rankEvent: (event: TrustedEvent) => number - }) { - this.limit = limit - this.repository = repository - this.rankEvent = rankEvent - } - - async initializeState(): Promise { - const events = await this.getAll() - this.eventCount = events.length - this.repository.load(events) - } - - sync(): Unsubscriber { - const onUpdate = async ({added, removed}: RepositoryUpdate) => { - // Only add events we want to keep - const keep = added.filter(e => this.rankEvent(e) > 0) - - // Add new events - if (keep.length > 0) { - await this.updateEvents(keep) - } - - // If we're well above our retention limit, drop lowest-ranked events - if (!this.isDeleting && this.eventCount > this.limit * 1.5) { - try { - this.isDeleting = true - - for (const event of sortBy(e => -this.rankEvent(e), await this.getAll()).slice( - this.limit, - )) { - removed.add(event.id) - } - - if (removed.size > 0) { - await this.deleteEvents(Array.from(removed)) - } - } finally { - this.isDeleting = false - } - } - - // Keep track of our total number of events. This isn't strictly accurate, but it's close enough - this.eventCount = this.eventCount + keep.length - removed.size - } - - return on(this.repository, "update", onUpdate) - } - - async getAll(): Promise { - return await getAllFromFile(EventsStorageProvider.filepath, EventsStorageProvider.directory, EventsStorageProvider.encoding) - } - - async writeAll(events: TrustedEvent[]) { - await Filesystem.writeFile({ - path: EventsStorageProvider.filepath, - directory: EventsStorageProvider.directory, - encoding: EventsStorageProvider.encoding, - data: JSON.stringify(events), - }) - } - - async updateEvents(events: TrustedEvent[]) { - const existing = await this.getAll() - const updated = existing.concat(events) - await this.writeAll(updated) - } - - async deleteEvents(ids: string[]) { - const existing = await this.getAll() - const updated = existing.filter(e => !ids.includes(e.id)) - await this.writeAll(updated) - } - - static async clearStorage(): Promise { - await Filesystem.deleteFile({path: EventsStorageProvider.filepath, directory: EventsStorageProvider.directory}) - } -} diff --git a/src/lib/storage/freshness.ts b/src/lib/storage/freshness.ts deleted file mode 100644 index ad8cb913..00000000 --- a/src/lib/storage/freshness.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {getAllFromFile, type FilesystemStorageProvider} from "@lib/storage" -import type {Unsubscriber} from "svelte/store" -import {Filesystem, Directory, Encoding} from "@capacitor/filesystem" -import {fromPairs} from "@welshman/lib" -import {freshness} from "@welshman/store" - -type KV = {key: string; value: any} - -export class FreshnessStorageProvider implements FilesystemStorageProvider { - static filepath = "freshness.json" - static directory = Directory.Data - static encoding = Encoding.UTF8 - - async initializeState(): Promise { - const items = await this.getAll() - freshness.set(fromPairs(items.map(item => [item.key, item.value]))) - } - - sync(): Unsubscriber { - const interval = setInterval(() => { - this.writeAll(freshness.get()) - }, 10_000) - - return () => clearInterval(interval) - } - - async getAll(): Promise { - return await getAllFromFile(FreshnessStorageProvider.filepath, FreshnessStorageProvider.directory, FreshnessStorageProvider.encoding) - } - - async writeAll(items: Record) { - const kvs = Object.entries(items).map(([key, value]) => ({key, value})) - - await Filesystem.writeFile({ - path: FreshnessStorageProvider.filepath, - directory: FreshnessStorageProvider.directory, - encoding: FreshnessStorageProvider.encoding, - data: JSON.stringify(kvs), - }) - } - - static async clearStorage(): Promise { - await Filesystem.deleteFile({path: FreshnessStorageProvider.filepath, directory: FreshnessStorageProvider.directory}) - } -} diff --git a/src/lib/storage/handles.ts b/src/lib/storage/handles.ts deleted file mode 100644 index 41ad520a..00000000 --- a/src/lib/storage/handles.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {getAllFromFile, type FilesystemStorageProvider} from "@lib/storage" -import {get, type Unsubscriber} from "svelte/store" -import {Filesystem, Directory, Encoding} from "@capacitor/filesystem" -import {batch} from "@welshman/lib" -import {handles, onHandle, type Handle} from "@welshman/app" - -export class HandlesStorageProvider implements FilesystemStorageProvider { - static filepath = "handles.json" - static directory = Directory.Data - static encoding = Encoding.UTF8 - - async initializeState(): Promise { - handles.set(await this.getAll()) - } - - sync(): Unsubscriber { - return onHandle(batch(300, () => this.saveState())) - } - - async getAll(): Promise { - return await getAllFromFile(HandlesStorageProvider.filepath, HandlesStorageProvider.directory, HandlesStorageProvider.encoding) - } - - async writeAll(handles: Handle[]) { - await Filesystem.writeFile({ - path: HandlesStorageProvider.filepath, - directory: HandlesStorageProvider.directory, - encoding: HandlesStorageProvider.encoding, - data: JSON.stringify(handles), - }) - } - - async saveState() { - await this.writeAll(get(handles)) - } - - static async clearStorage(): Promise { - await Filesystem.deleteFile({path: HandlesStorageProvider.filepath, directory: HandlesStorageProvider.directory}) - } -} diff --git a/src/lib/storage/plaintext.ts b/src/lib/storage/plaintext.ts deleted file mode 100644 index 01df9055..00000000 --- a/src/lib/storage/plaintext.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {getAllFromFile, type FilesystemStorageProvider} from "@lib/storage" -import type {Unsubscriber} from "svelte/store" -import {Filesystem, Directory, Encoding} from "@capacitor/filesystem" -import {fromPairs} from "@welshman/lib" -import {plaintext} from "@welshman/app" - -type KV = {key: string; value: any} - -export class PlaintextStorageProvider implements FilesystemStorageProvider { - static filepath = "plaintext.json" - static directory = Directory.Data - static encoding = Encoding.UTF8 - - async initializeState(): Promise { - const items = await this.getAll() - plaintext.set(fromPairs(items.map(item => [item.key, item.value]))) - } - - sync(): Unsubscriber { - const interval = setInterval(() => { - this.writeAll(plaintext.get()) - }, 10_000) - - return () => clearInterval(interval) - } - - async getAll(): Promise { - return await getAllFromFile(PlaintextStorageProvider.filepath, PlaintextStorageProvider.directory, PlaintextStorageProvider.encoding) - } - - async writeAll(items: Record) { - const kvs = Object.entries(items).map(([key, value]) => ({key, value})) - - await Filesystem.writeFile({ - path: PlaintextStorageProvider.filepath, - directory: PlaintextStorageProvider.directory, - encoding: PlaintextStorageProvider.encoding, - data: JSON.stringify(kvs), - }) - } - - static async clearStorage(): Promise { - await Filesystem.deleteFile({path: PlaintextStorageProvider.filepath, directory: PlaintextStorageProvider.directory}) - } -} diff --git a/src/lib/storage/relays.ts b/src/lib/storage/relays.ts deleted file mode 100644 index c4feb885..00000000 --- a/src/lib/storage/relays.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {getAllFromFile, type FilesystemStorageProvider} from "@lib/storage" -import type {Unsubscriber} from "svelte/store" -import {Filesystem, Directory, Encoding} from "@capacitor/filesystem" -import {relays, type Relay} from "@welshman/app" -import {throttled} from "@welshman/store" - -export class RelaysStorageProvider implements FilesystemStorageProvider { - static filepath = "relays.json" - static directory = Directory.Data - static encoding = Encoding.UTF8 - - async initializeState(): Promise { - relays.set(await this.getAll()) - } - - sync(): Unsubscriber { - return throttled(3000, relays).subscribe(() => this.saveState()) - } - - async getAll(): Promise { - return await getAllFromFile(RelaysStorageProvider.filepath, RelaysStorageProvider.directory, RelaysStorageProvider.encoding) - } - - async writeAll(relays: Relay[]) { - await Filesystem.writeFile({ - path: RelaysStorageProvider.filepath, - directory: RelaysStorageProvider.directory, - encoding: RelaysStorageProvider.encoding, - data: JSON.stringify(relays), - }) - } - - async saveState() { - await this.writeAll(relays.get()) - } - - static async clearStorage(): Promise { - await Filesystem.deleteFile({path: RelaysStorageProvider.filepath, directory: RelaysStorageProvider.directory}) - } -} diff --git a/src/lib/storage/tracker.ts b/src/lib/storage/tracker.ts deleted file mode 100644 index 59b46899..00000000 --- a/src/lib/storage/tracker.ts +++ /dev/null @@ -1,80 +0,0 @@ -import {getAllFromFile, type FilesystemStorageProvider} from "@lib/storage" -import type {Unsubscriber} from "svelte/store" -import {Filesystem, Directory, Encoding} from "@capacitor/filesystem" -import type {Tracker} from "@welshman/net" -import {call, on} from "@welshman/lib" - -type Entry = {id: string; relays: string[]} - -export class TrackerStorageProvider implements FilesystemStorageProvider { - static filepath = "tracker.json" - static directory = Directory.Data - static encoding = Encoding.UTF8 - tracker: Tracker - - constructor({tracker}: {tracker: Tracker}) { - this.tracker = tracker - } - - async initializeState(): Promise { - const relaysByid = new Map>() - - for (const {id, relays} of await this.getAll()) { - relaysByid.set(id, new Set(relays)) - } - - this.tracker.load(relaysByid) - } - - sync(): Unsubscriber { - const updateOne = async (id: string, relay: string) => { - const relays = new Set(await this.getAll()) - relays.add({id, relays: Array.from(this.tracker.getRelays(id))}) - await this.writeAll([...relays]) - } - - const updateAll = async () => { - await this.writeAll(Array.from(this.tracker.relaysById.entries()).map(([id, relays]) => ({ - id, - relays: Array.from(relays), - }))) - } - - const unsubscribers = [ - on(this.tracker, "add", updateOne), - on(this.tracker, "remove", updateOne), - on(this.tracker, "load", updateAll), - on(this.tracker, "clear", updateAll), - ] - - return () => { - unsubscribers.forEach(call) - } - } - - async getAll(): Promise { - return await getAllFromFile(TrackerStorageProvider.filepath, TrackerStorageProvider.directory, TrackerStorageProvider.encoding) - } - - async writeAll(relays: Entry[]) { - await Filesystem.writeFile({ - path: TrackerStorageProvider.filepath, - directory: TrackerStorageProvider.directory, - encoding: TrackerStorageProvider.encoding, - data: JSON.stringify(relays), - }) - } - - async saveState() { - await this.writeAll( - Array.from(this.tracker.relaysById.entries()).map(([id, relays]) => ({ - id, - relays: Array.from(relays), - })), - ) - } - - static async clearStorage(): Promise { - await Filesystem.deleteFile({path: TrackerStorageProvider.filepath, directory: TrackerStorageProvider.directory}) - } -} diff --git a/src/lib/storage/zappers.ts b/src/lib/storage/zappers.ts deleted file mode 100644 index 463b41ec..00000000 --- a/src/lib/storage/zappers.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {getAllFromFile, type FilesystemStorageProvider} from "@lib/storage" -import {get, type Unsubscriber} from "svelte/store" -import {Filesystem, Directory, Encoding} from "@capacitor/filesystem" -import {onZapper, zappers} from "@welshman/app" -import {batch} from "@welshman/lib" -import type {Zapper} from "@welshman/util" - -export class ZappersStorageProvider implements FilesystemStorageProvider { - static filepath = "zappers.json" - static directory = Directory.Data - static encoding = Encoding.UTF8 - - async initializeState(): Promise { - zappers.set(await this.getAll()) - } - - sync(): Unsubscriber { - return onZapper(batch(300, () => this.saveState())) - } - - async getAll(): Promise { - return await getAllFromFile(ZappersStorageProvider.filepath, ZappersStorageProvider.directory, ZappersStorageProvider.encoding) - } - - async writeAll(zappers: Zapper[]) { - await Filesystem.writeFile({ - path: ZappersStorageProvider.filepath, - directory: ZappersStorageProvider.directory, - encoding: ZappersStorageProvider.encoding, - data: JSON.stringify(zappers), - }) - } - - async saveState() { - await this.writeAll(get(zappers)) - } - - static async clearStorage(): Promise { - await Filesystem.deleteFile({path: ZappersStorageProvider.filepath, directory: ZappersStorageProvider.directory}) - } -} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 77af3367..d48063ec 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -24,27 +24,7 @@ WEEK, } from "@welshman/lib" import type {TrustedEvent, StampedEvent} from "@welshman/util" - import { - WRAP, - ALERT_STATUS, - ALERT_EMAIL, - ALERT_WEB, - ALERT_IOS, - ALERT_ANDROID, - EVENT_TIME, - APP_DATA, - THREAD, - MESSAGE, - INBOX_RELAYS, - DIRECT_MESSAGE, - DIRECT_MESSAGE_FILE, - MUTES, - FOLLOWS, - PROFILE, - RELAYS, - BLOSSOM_SERVERS, - ROOMS, - } from "@welshman/util" + import {WRAP} from "@welshman/util" import {Nip46Broker, makeSecret} from "@welshman/signer" import type {Socket, RelayMessage, ClientMessage} from "@welshman/net" import { @@ -61,7 +41,6 @@ } from "@welshman/net" import { loadRelay, - db, repository, pubkey, session, @@ -71,7 +50,6 @@ dropSession, loginWithNip01, loginWithNip46, - EventsStorageAdapter, loadRelaySelections, SignerLogEntryStatus, } from "@welshman/app" @@ -83,7 +61,7 @@ import * as net from "@welshman/net" import * as app from "@welshman/app" import {nsecDecode} from "@lib/util" - import {defaultStorageProviders, initFileStorage, preferencesStorageProvider} from "@lib/storage" + import {preferencesStorageProvider} from "@lib/storage" import AppContainer from "@app/components/AppContainer.svelte" import ModalContainer from "@app/components/ModalContainer.svelte" import {setupTracking} from "@app/util/tracking" @@ -105,11 +83,13 @@ import {initializePushNotifications} from "@app/util/push" import * as commands from "@app/core/commands" import * as requests from "@app/core/requests" - import * as notifications from "@app/util/notifications" import * as appState from "@app/core/state" - import {badgeCount, handleBadgeCountChanges} from "@app/util/notifications" + import * as notifications from "@app/util/notifications" + import * as storage from "@app/util/storage" import NewNotificationSound from "@src/app/components/NewNotificationSound.svelte" - import {EventsStorageProvider} from "@lib/storage/events" + + // Migration: delete old indexeddb database + indexedDB?.deleteDatabase('flotilla') // Migration: old nostrtalk instance used different sessions if ($session && !$signer) { @@ -123,6 +103,8 @@ const ready = $state(defer()) + let initialized = false + onMount(async () => { Object.assign(window, { get, @@ -240,7 +222,8 @@ document.documentElement.style["font-size"] = `${$userSettingsValues.font_size}rem` }) - if (!db) { + if (!initialized) { + initialized = true setupTracking() setupAnalytics() @@ -279,42 +262,10 @@ storage: preferencesStorageProvider, }) - await initFileStorage({...defaultStorageProviders, - events: new EventsStorageProvider({ - limit: 10_000, - repository, - rankEvent: (e: TrustedEvent) => { - if ( - [ - PROFILE, - FOLLOWS, - MUTES, - RELAYS, - BLOSSOM_SERVERS, - INBOX_RELAYS, - ROOMS, - APP_DATA, - ALERT_STATUS, - ALERT_EMAIL, - ALERT_WEB, - ALERT_IOS, - ALERT_ANDROID, - ].includes(e.kind) - ) { - return 1 - } - - if ( - [EVENT_TIME, THREAD, MESSAGE, DIRECT_MESSAGE, DIRECT_MESSAGE_FILE].includes(e.kind) - ) { - return 0.9 - } - - return 0 - }, - }), - }) + // Sync application data (relay, events, etc) + await storage.syncDataStores() + // Wait 300 ms for any throttled stores to finish sleep(300).then(() => ready.resolve()) defaultSocketPolicies.push( @@ -465,7 +416,7 @@ ) // subscribe to badge count for changes - badgeCount.subscribe(handleBadgeCountChanges) + notifications.badgeCount.subscribe(notifications.handleBadgeCountChanges) // Listen for signer errors, report to user via toast signerLog.subscribe(