use timeline loader from applesauce packages

This commit is contained in:
hzrd149 2025-02-03 12:51:37 -06:00
parent 9257b06335
commit 1167dbae54
49 changed files with 438 additions and 1018 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Use TimelineLoader from applesauce packages

View File

@ -19,7 +19,7 @@
"cap-sync-version": "pnpm dlx capacitor-set-version . -v $(jq -r .version package.json) -b 1"
},
"dependencies": {
"@cashu/cashu-ts": "^2.1.0",
"@cashu/cashu-ts": "^2.2.0",
"@chakra-ui/anatomy": "^2.3.4",
"@chakra-ui/breakpoint-utils": "^2.0.8",
"@chakra-ui/icons": "^2.2.4",
@ -73,6 +73,7 @@
"framer-motion": "^10.18.0",
"gif-picker-react": "^1.4.0",
"handlebars": "^4.7.8",
"hash-sum": "^2.0.0",
"hls.js": "^1.5.20",
"i18n-iso-countries": "^7.13.0",
"idb": "^8.0.2",
@ -120,7 +121,6 @@
"three": "^0.170.0",
"three-spritetext": "^1.9.4",
"three-stdlib": "^2.35.13",
"tiny-lru": "^11.2.11",
"unified": "^11.0.5",
"uuid": "^11.0.5",
"vite-plugin-funding": "^0.1.0",
@ -146,6 +146,7 @@
"@types/debug": "^4.1.12",
"@types/dom-serial": "^1.0.6",
"@types/file-saver": "^2.0.7",
"@types/hash-sum": "^1.0.2",
"@types/identicon.js": "^2.3.5",
"@types/json-schema": "^7.0.15",
"@types/leaflet": "^1.9.16",

339
pnpm-lock.yaml generated
View File

@ -13,8 +13,8 @@ importers:
.:
dependencies:
'@cashu/cashu-ts':
specifier: ^2.1.0
version: 2.1.0
specifier: ^2.2.0
version: 2.2.0
'@chakra-ui/anatomy':
specifier: ^2.3.4
version: 2.3.4
@ -92,10 +92,10 @@ importers:
version: 1.3.1
'@uiw/codemirror-theme-github':
specifier: ^4.23.7
version: 4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
version: 4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)
'@uiw/react-codemirror':
specifier: ^4.23.7
version: 4.23.7(@babel/runtime@7.26.7)(@codemirror/autocomplete@6.18.4)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
version: 4.23.7(@babel/runtime@7.26.7)(@codemirror/autocomplete@6.18.4)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@webscopeio/react-textarea-autocomplete':
specifier: ^4.9.2
version: 4.9.2(prop-types@15.8.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -104,28 +104,28 @@ importers:
version: 0.7.2
applesauce-accounts:
specifier: next
version: 0.0.0-next-20250131214402(typescript@5.7.3)
version: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-content:
specifier: next
version: 0.0.0-next-20250131214402(typescript@5.7.3)
version: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-core:
specifier: next
version: 0.0.0-next-20250131214402(typescript@5.7.3)
version: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-factory:
specifier: next
version: 0.0.0-next-20250131214402(typescript@5.7.3)
version: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-loaders:
specifier: next
version: 0.0.0-next-20250131214402(typescript@5.7.3)
version: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-net:
specifier: ^0.10.0
version: 0.10.0(typescript@5.7.3)
applesauce-react:
specifier: next
version: 0.0.0-next-20250131214402(typescript@5.7.3)
version: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-signers:
specifier: next
version: 0.0.0-next-20250131214402(typescript@5.7.3)
version: 0.0.0-next-20250203180810(typescript@5.7.3)
bech32:
specifier: ^2.0.0
version: 2.0.0
@ -149,7 +149,7 @@ importers:
version: 6.0.1
codemirror-json-schema:
specifier: ^0.7.9
version: 0.7.9(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)(@lezer/common@1.2.3)
version: 0.7.9(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)(@lezer/common@1.2.3)
dayjs:
specifier: ^1.11.13
version: 1.11.13
@ -174,6 +174,9 @@ importers:
handlebars:
specifier: ^4.7.8
version: 4.7.8
hash-sum:
specifier: ^2.0.0
version: 2.0.0
hls.js:
specifier: ^1.5.20
version: 1.5.20
@ -315,9 +318,6 @@ importers:
three-stdlib:
specifier: ^2.35.13
version: 2.35.13(three@0.170.0)
tiny-lru:
specifier: ^11.2.11
version: 11.2.11
unified:
specifier: ^11.0.5
version: 11.0.5
@ -388,6 +388,9 @@ importers:
'@types/file-saver':
specifier: ^2.0.7
version: 2.0.7
'@types/hash-sum':
specifier: ^1.0.2
version: 1.0.2
'@types/identicon.js':
specifier: ^2.3.5
version: 2.3.5
@ -1032,8 +1035,8 @@ packages:
'@cashu/cashu-ts@2.0.0-rc1':
resolution: {integrity: sha512-39459l7x/fUMEgOsCdGLLl6rMekO4nbv+wEuavmyElh8hgN8t66wcb29AJvdFTb6K3lPACKF2rs/jAlPYrN7Ng==}
'@cashu/cashu-ts@2.1.0':
resolution: {integrity: sha512-qFfFz1dx9keJxumjk5FyTvI1j0Yp/P5LXDy0cGO4Xlp3WYKOI1nykNOTPd+bTY9vSkvIM+xuXRer9BtQxqHtwA==}
'@cashu/cashu-ts@2.2.0':
resolution: {integrity: sha512-7b6pGyjjpm3uAJvmOL+ztpRxqp1qnmzGpydp+Pu30pOjxj93EhejPTJVrZMDJ0P35y6u5+5jIjHF4k0fpovvmg==}
'@cashu/crypto@0.2.7':
resolution: {integrity: sha512-1aaDfUjiHNXoJqg8nW+341TLWV9W28DsVNXJUKcHL0yAmwLs5+56SSnb8LLDJzPamLVoYL0U0bda91klAzptig==}
@ -1241,8 +1244,8 @@ packages:
'@codemirror/search@6.5.8':
resolution: {integrity: sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==}
'@codemirror/state@6.5.1':
resolution: {integrity: sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==}
'@codemirror/state@6.5.2':
resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==}
'@codemirror/theme-one-dark@6.1.2':
resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==}
@ -1717,98 +1720,98 @@ packages:
rollup:
optional: true
'@rollup/rollup-android-arm-eabi@4.32.1':
resolution: {integrity: sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==}
'@rollup/rollup-android-arm-eabi@4.34.1':
resolution: {integrity: sha512-kwctwVlswSEsr4ljpmxKrRKp1eG1v2NAhlzFzDf1x1OdYaMjBYjDCbHkzWm57ZXzTwqn8stMXgROrnMw8dJK3w==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.32.1':
resolution: {integrity: sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==}
'@rollup/rollup-android-arm64@4.34.1':
resolution: {integrity: sha512-4H5ZtZitBPlbPsTv6HBB8zh1g5d0T8TzCmpndQdqq20Ugle/nroOyDMf9p7f88Gsu8vBLU78/cuh8FYHZqdXxw==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.32.1':
resolution: {integrity: sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==}
'@rollup/rollup-darwin-arm64@4.34.1':
resolution: {integrity: sha512-f2AJ7Qwx9z25hikXvg+asco8Sfuc5NCLg8rmqQBIOUoWys5sb/ZX9RkMZDPdnnDevXAMJA5AWLnRBmgdXGEUiA==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.32.1':
resolution: {integrity: sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==}
'@rollup/rollup-darwin-x64@4.34.1':
resolution: {integrity: sha512-+/2JBrRfISCsWE4aEFXxd+7k9nWGXA8+wh7ZUHn/u8UDXOU9LN+QYKKhd57sIn6WRcorOnlqPMYFIwie/OHXWw==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.32.1':
resolution: {integrity: sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==}
'@rollup/rollup-freebsd-arm64@4.34.1':
resolution: {integrity: sha512-SUeB0pYjIXwT2vfAMQ7E4ERPq9VGRrPR7Z+S4AMssah5EHIilYqjWQoTn5dkDtuIJUSTs8H+C9dwoEcg3b0sCA==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.32.1':
resolution: {integrity: sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==}
'@rollup/rollup-freebsd-x64@4.34.1':
resolution: {integrity: sha512-L3T66wAZiB/ooiPbxz0s6JEX6Sr2+HfgPSK+LMuZkaGZFAFCQAHiP3dbyqovYdNaiUXcl9TlgnIbcsIicAnOZg==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.32.1':
resolution: {integrity: sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==}
'@rollup/rollup-linux-arm-gnueabihf@4.34.1':
resolution: {integrity: sha512-UBXdQ4+ATARuFgsFrQ+tAsKvBi/Hly99aSVdeCUiHV9dRTTpMU7OrM3WXGys1l40wKVNiOl0QYY6cZQJ2xhKlQ==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.32.1':
resolution: {integrity: sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==}
'@rollup/rollup-linux-arm-musleabihf@4.34.1':
resolution: {integrity: sha512-m/yfZ25HGdcCSwmopEJm00GP7xAUyVcBPjttGLRAqZ60X/bB4Qn6gP7XTwCIU6bITeKmIhhwZ4AMh2XLro+4+w==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.32.1':
resolution: {integrity: sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==}
'@rollup/rollup-linux-arm64-gnu@4.34.1':
resolution: {integrity: sha512-Wy+cUmFuvziNL9qWRRzboNprqSQ/n38orbjRvd6byYWridp5TJ3CD+0+HUsbcWVSNz9bxkDUkyASGP0zS7GAvg==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.32.1':
resolution: {integrity: sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==}
'@rollup/rollup-linux-arm64-musl@4.34.1':
resolution: {integrity: sha512-CQ3MAGgiFmQW5XJX5W3wnxOBxKwFlUAgSXFA2SwgVRjrIiVt5LHfcQLeNSHKq5OEZwv+VCBwlD1+YKCjDG8cpg==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-loongarch64-gnu@4.32.1':
resolution: {integrity: sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==}
'@rollup/rollup-linux-loongarch64-gnu@4.34.1':
resolution: {integrity: sha512-rSzb1TsY4lSwH811cYC3OC2O2mzNMhM13vcnA7/0T6Mtreqr3/qs6WMDriMRs8yvHDI54qxHgOk8EV5YRAHFbw==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-powerpc64le-gnu@4.32.1':
resolution: {integrity: sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==}
'@rollup/rollup-linux-powerpc64le-gnu@4.34.1':
resolution: {integrity: sha512-fwr0n6NS0pG3QxxlqVYpfiY64Fd1Dqd8Cecje4ILAV01ROMp4aEdCj5ssHjRY3UwU7RJmeWd5fi89DBqMaTawg==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.32.1':
resolution: {integrity: sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==}
'@rollup/rollup-linux-riscv64-gnu@4.34.1':
resolution: {integrity: sha512-4uJb9qz7+Z/yUp5RPxDGGGUcoh0PnKF33QyWgEZ3X/GocpWb6Mb+skDh59FEt5d8+Skxqs9mng6Swa6B2AmQZg==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.32.1':
resolution: {integrity: sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==}
'@rollup/rollup-linux-s390x-gnu@4.34.1':
resolution: {integrity: sha512-QlIo8ndocWBEnfmkYqj8vVtIUpIqJjfqKggjy7IdUncnt8BGixte1wDON7NJEvLg3Kzvqxtbo8tk+U1acYEBlw==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.32.1':
resolution: {integrity: sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==}
'@rollup/rollup-linux-x64-gnu@4.34.1':
resolution: {integrity: sha512-hzpleiKtq14GWjz3ahWvJXgU1DQC9DteiwcsY4HgqUJUGxZThlL66MotdUEK9zEo0PK/2ADeZGM9LIondE302A==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.32.1':
resolution: {integrity: sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==}
'@rollup/rollup-linux-x64-musl@4.34.1':
resolution: {integrity: sha512-jqtKrO715hDlvUcEsPn55tZt2TEiBvBtCMkUuU0R6fO/WPT7lO9AONjPbd8II7/asSiNVQHCMn4OLGigSuxVQA==}
cpu: [x64]
os: [linux]
'@rollup/rollup-win32-arm64-msvc@4.32.1':
resolution: {integrity: sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==}
'@rollup/rollup-win32-arm64-msvc@4.34.1':
resolution: {integrity: sha512-RnHy7yFf2Wz8Jj1+h8klB93N0NHNHXFhNwAmiy9zJdpY7DE01VbEVtPdrK1kkILeIbHGRJjvfBDBhnxBr8kD4g==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.32.1':
resolution: {integrity: sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==}
'@rollup/rollup-win32-ia32-msvc@4.34.1':
resolution: {integrity: sha512-i7aT5HdiZIcd7quhzvwQ2oAuX7zPYrYfkrd1QFfs28Po/i0q6kas/oRrzGlDhAEyug+1UfUtkWdmoVlLJj5x9Q==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.32.1':
resolution: {integrity: sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==}
'@rollup/rollup-win32-x64-msvc@4.34.1':
resolution: {integrity: sha512-k3MVFD9Oq+laHkw2N2v7ILgoa9017ZMF/inTtHzyTVZjYs9cSH18sdyAf6spBAJIGwJ5UaC7et2ZH1WCdlhkMw==}
cpu: [x64]
os: [win32]
@ -1951,6 +1954,9 @@ packages:
'@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
'@types/hash-sum@1.0.2':
resolution: {integrity: sha512-UP28RddqY8xcU0SCEp9YKutQICXpaAq9N8U2klqF5hegGha7KzTOL8EdhIIV3bOSGBzjEpN9bU/d+nNZBdJYVw==}
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
@ -2186,32 +2192,32 @@ packages:
engines: {node: '>=8.0.0'}
hasBin: true
applesauce-accounts@0.0.0-next-20250131214402:
resolution: {integrity: sha512-5BUtFswVFg82Oq9MG6VdkEa5VeOAuvoPMi7Fr/I4IxqJwpZDSFIU/rkuDOMtHcj+V6TCEYeTO670f2p7B4/yMg==}
applesauce-accounts@0.0.0-next-20250203180810:
resolution: {integrity: sha512-yymm0dQgBJGgf3nlv1DC+AyxK+cvHLZczmRLyYW4k4MCNM7mO7qwnFGbMJJftDmVw5l8f9xBu0T/8r3IWsOxJw==}
applesauce-content@0.0.0-next-20250131214402:
resolution: {integrity: sha512-+cDqt4IRIjQ0Ncht0WLCcMueDaZRtsuAXBw3k4/ND37qUHsnt5Qj+VfnWr5Dfmhpk0HD3ghMqbMXZBwkybN6NA==}
applesauce-content@0.0.0-next-20250203180810:
resolution: {integrity: sha512-7TpIqocvhn5g4M/Biua7AP3pWo4YtDa1C21uci/ElkbuqMNhVT37ASwttGM8GPcfLTxQ7uBkpPjOBTXm74D8Rw==}
applesauce-core@0.0.0-next-20250131214402:
resolution: {integrity: sha512-5+KRKnV2d+6z9QFzSHUGKHxbtoByRkx0MQpjOePNBcl/DCbVmC7Zf4Bo3VrouNeBG5deiCMIy5PGp0A4uuGXRg==}
applesauce-core@0.0.0-next-20250203180810:
resolution: {integrity: sha512-BNXzw8ydbp4KiPfn7X5iCMaeaNlAW5tr3o6dWz9UwV7ItWieQLd17ySh0KPLdVbE+e42msBN7ByE7iNC0ItJOQ==}
applesauce-core@0.10.0:
resolution: {integrity: sha512-QMhUh4FIARcqY5soCB4Z8DIu+py0rYb28IgWT4gP9DLBGpDrY8lStXk7W1/46TLjEH97y0hbiXFK7kMCZ31oOQ==}
applesauce-factory@0.0.0-next-20250131214402:
resolution: {integrity: sha512-94yTHDr+N+/IyDD88ruHzToG4craEyJ/u5nfU2TP85o6snR8Ngw5qmX5d8sD6RHJm9UjMpzfTcFjxU9ZCnbwjg==}
applesauce-factory@0.0.0-next-20250203180810:
resolution: {integrity: sha512-O4xMkwaOTX7t6JYaS78WTKzIRekCipCBnDeZpyYmLK+h4rQ+8aKd21VonV4hfh9A8dTeS28QNFsdDabbH+22Nw==}
applesauce-loaders@0.0.0-next-20250131214402:
resolution: {integrity: sha512-ycKuHqqT951jwRpvMNjrhQq1YRhFHLYh57NIFJJTZFHOHzfi3OJjpagueJr5H7brFGOFdYtaOgJew3sxptucKw==}
applesauce-loaders@0.0.0-next-20250203180810:
resolution: {integrity: sha512-ZWU23UtUQPQdM26NFLUralB8M8IuxrJqYqCwA5j5pozi5EG49OxxdPaZuXn3u37VRX8mibQYRVQqR+rhPWEC6g==}
applesauce-net@0.10.0:
resolution: {integrity: sha512-ZsAs/MkeGHiPZ2/a8lwP8lx/Eh+5Dot0qG4BLTAqjg4emP/RsiqW+hyc6v6QcVbdvuR0+hP1gka3+wWtiy/cTA==}
applesauce-react@0.0.0-next-20250131214402:
resolution: {integrity: sha512-wqlVTecq3Aj+YY5gi71VOqKWNMwsK4nUFYuoY9ZFh1ueJG0anKqYLwZSrMvswsbt3JyhG4xtLrPc7FGdIsdmqQ==}
applesauce-react@0.0.0-next-20250203180810:
resolution: {integrity: sha512-/LloC27jD9BieXmoTZs59SVbHw/vzjh/mh1jt98TQT8NcSh9zoPD1quR4a4BNFXUmeDDAxOi4VcrTOCVXAvweA==}
applesauce-signers@0.0.0-next-20250131214402:
resolution: {integrity: sha512-rWlDZrPPGmt2MWQ02WWRfUtApTDNJhu7Sze0hNZ4qaVSwIezq7l2f1N4vdB34A3gsagi9aXhhoipF72PtkbFHg==}
applesauce-signers@0.0.0-next-20250203180810:
resolution: {integrity: sha512-PJBPVmLW+lDYQ2TYflJGr/ddmvDu0v19l6I9XBhHfmlS1ypAej9kk4c4X56gRzROeq2zgIs/SSi1+Jm454AqRw==}
arg@4.1.3:
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
@ -2312,8 +2318,8 @@ packages:
bare-path@3.0.0:
resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==}
bare-stream@2.6.4:
resolution: {integrity: sha512-G6i3A74FjNq4nVrrSTUz5h3vgXzBJnjmWAVlBWaZETkgu+LgKd7AiyOml3EDJY1AHlIbBHKDXE+TUT53Ff8OaA==}
bare-stream@2.6.5:
resolution: {integrity: sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==}
peerDependencies:
bare-buffer: '*'
bare-events: '*'
@ -3575,8 +3581,8 @@ packages:
immutability-helper@3.1.1:
resolution: {integrity: sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ==}
import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
indent-string@4.0.0:
@ -5129,8 +5135,8 @@ packages:
engines: {node: '>=10.0.0'}
hasBin: true
rollup@4.32.1:
resolution: {integrity: sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==}
rollup@4.34.1:
resolution: {integrity: sha512-iYZ/+PcdLYSGfH3S+dGahlW/RWmsqDhLgj1BT9DH/xXJ0ggZN7xkdP9wipPNjjNLczI+fmMLmTB9pye+d2r4GQ==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@ -5557,10 +5563,6 @@ packages:
tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
tiny-lru@11.2.11:
resolution: {integrity: sha512-27BIW0dIWTYYoWNnqSmoNMKe5WIbkXsc0xaCQHd3/3xT2XMuMJrzHdrO9QBFR14emBz1Bu0dOAs2sCBBrvgPQA==}
engines: {node: '>=12'}
tinycolor2@1.6.0:
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
@ -6946,12 +6948,11 @@ snapshots:
'@scure/bip32': 1.6.2
buffer: 6.0.3
'@cashu/cashu-ts@2.1.0':
'@cashu/cashu-ts@2.2.0':
dependencies:
'@cashu/crypto': 0.3.4
'@noble/curves': 1.8.1
'@noble/hashes': 1.7.1
'@scure/bip32': 1.6.2
buffer: 6.0.3
'@cashu/crypto@0.2.7':
@ -7291,14 +7292,14 @@ snapshots:
'@codemirror/autocomplete@6.18.4':
dependencies:
'@codemirror/language': 6.10.8
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
'@lezer/common': 1.2.3
'@codemirror/commands@6.8.0':
dependencies:
'@codemirror/language': 6.10.8
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
'@lezer/common': 1.2.3
@ -7311,7 +7312,7 @@ snapshots:
dependencies:
'@codemirror/autocomplete': 6.18.4
'@codemirror/language': 6.10.8
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
@ -7320,7 +7321,7 @@ snapshots:
'@codemirror/language@6.10.8':
dependencies:
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
@ -7329,30 +7330,30 @@ snapshots:
'@codemirror/lint@6.8.4':
dependencies:
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
crelt: 1.0.6
'@codemirror/search@6.5.8':
dependencies:
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
crelt: 1.0.6
'@codemirror/state@6.5.1':
'@codemirror/state@6.5.2':
dependencies:
'@marijn/find-cluster-break': 1.0.2
'@codemirror/theme-one-dark@6.1.2':
dependencies:
'@codemirror/language': 6.10.8
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
'@lezer/highlight': 1.2.1
'@codemirror/view@6.36.2':
dependencies:
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
style-mod: 4.1.2
w3c-keyname: 2.2.8
@ -7906,61 +7907,61 @@ snapshots:
optionalDependencies:
rollup: 2.79.2
'@rollup/rollup-android-arm-eabi@4.32.1':
'@rollup/rollup-android-arm-eabi@4.34.1':
optional: true
'@rollup/rollup-android-arm64@4.32.1':
'@rollup/rollup-android-arm64@4.34.1':
optional: true
'@rollup/rollup-darwin-arm64@4.32.1':
'@rollup/rollup-darwin-arm64@4.34.1':
optional: true
'@rollup/rollup-darwin-x64@4.32.1':
'@rollup/rollup-darwin-x64@4.34.1':
optional: true
'@rollup/rollup-freebsd-arm64@4.32.1':
'@rollup/rollup-freebsd-arm64@4.34.1':
optional: true
'@rollup/rollup-freebsd-x64@4.32.1':
'@rollup/rollup-freebsd-x64@4.34.1':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.32.1':
'@rollup/rollup-linux-arm-gnueabihf@4.34.1':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.32.1':
'@rollup/rollup-linux-arm-musleabihf@4.34.1':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.32.1':
'@rollup/rollup-linux-arm64-gnu@4.34.1':
optional: true
'@rollup/rollup-linux-arm64-musl@4.32.1':
'@rollup/rollup-linux-arm64-musl@4.34.1':
optional: true
'@rollup/rollup-linux-loongarch64-gnu@4.32.1':
'@rollup/rollup-linux-loongarch64-gnu@4.34.1':
optional: true
'@rollup/rollup-linux-powerpc64le-gnu@4.32.1':
'@rollup/rollup-linux-powerpc64le-gnu@4.34.1':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.32.1':
'@rollup/rollup-linux-riscv64-gnu@4.34.1':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.32.1':
'@rollup/rollup-linux-s390x-gnu@4.34.1':
optional: true
'@rollup/rollup-linux-x64-gnu@4.32.1':
'@rollup/rollup-linux-x64-gnu@4.34.1':
optional: true
'@rollup/rollup-linux-x64-musl@4.32.1':
'@rollup/rollup-linux-x64-musl@4.34.1':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.32.1':
'@rollup/rollup-win32-arm64-msvc@4.34.1':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.32.1':
'@rollup/rollup-win32-ia32-msvc@4.34.1':
optional: true
'@rollup/rollup-win32-x64-msvc@4.32.1':
'@rollup/rollup-win32-x64-msvc@4.34.1':
optional: true
'@sagold/json-pointer@5.1.2': {}
@ -8183,6 +8184,8 @@ snapshots:
'@types/geojson@7946.0.16': {}
'@types/hash-sum@1.0.2': {}
'@types/hast@3.0.4':
dependencies:
'@types/unist': 3.0.3
@ -8283,38 +8286,38 @@ snapshots:
'@types/webxr@0.5.21': {}
'@uiw/codemirror-extensions-basic-setup@4.23.7(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)':
'@uiw/codemirror-extensions-basic-setup@4.23.7(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)':
dependencies:
'@codemirror/autocomplete': 6.18.4
'@codemirror/commands': 6.8.0
'@codemirror/language': 6.10.8
'@codemirror/lint': 6.8.4
'@codemirror/search': 6.5.8
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
'@uiw/codemirror-theme-github@4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)':
'@uiw/codemirror-theme-github@4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)':
dependencies:
'@uiw/codemirror-themes': 4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
'@uiw/codemirror-themes': 4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)
transitivePeerDependencies:
- '@codemirror/language'
- '@codemirror/state'
- '@codemirror/view'
'@uiw/codemirror-themes@4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)':
'@uiw/codemirror-themes@4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)':
dependencies:
'@codemirror/language': 6.10.8
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
'@uiw/react-codemirror@4.23.7(@babel/runtime@7.26.7)(@codemirror/autocomplete@6.18.4)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
'@uiw/react-codemirror@4.23.7(@babel/runtime@7.26.7)(@codemirror/autocomplete@6.18.4)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.2)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@babel/runtime': 7.26.7
'@codemirror/commands': 6.8.0
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/theme-one-dark': 6.1.2
'@codemirror/view': 6.36.2
'@uiw/codemirror-extensions-basic-setup': 4.23.7(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
'@uiw/codemirror-extensions-basic-setup': 4.23.7(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)
codemirror: 6.0.1
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@ -8420,10 +8423,10 @@ snapshots:
dependencies:
entities: 2.2.0
applesauce-accounts@0.0.0-next-20250131214402(typescript@5.7.3):
applesauce-accounts@0.0.0-next-20250203180810(typescript@5.7.3):
dependencies:
'@noble/hashes': 1.7.1
applesauce-signers: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-signers: 0.0.0-next-20250203180810(typescript@5.7.3)
nanoid: 5.0.9
nostr-tools: 2.10.4(typescript@5.7.3)
rxjs: 7.8.1
@ -8431,13 +8434,13 @@ snapshots:
- supports-color
- typescript
applesauce-content@0.0.0-next-20250131214402(typescript@5.7.3):
applesauce-content@0.0.0-next-20250203180810(typescript@5.7.3):
dependencies:
'@cashu/cashu-ts': 2.0.0-rc1
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
'@types/unist': 3.0.3
applesauce-core: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250203180810(typescript@5.7.3)
mdast-util-find-and-replace: 3.0.2
nostr-tools: 2.10.4(typescript@5.7.3)
remark: 15.0.1
@ -8448,7 +8451,7 @@ snapshots:
- supports-color
- typescript
applesauce-core@0.0.0-next-20250131214402(typescript@5.7.3):
applesauce-core@0.0.0-next-20250203180810(typescript@5.7.3):
dependencies:
'@scure/base': 1.2.4
debug: 4.4.0
@ -8476,19 +8479,19 @@ snapshots:
- supports-color
- typescript
applesauce-factory@0.0.0-next-20250131214402(typescript@5.7.3):
applesauce-factory@0.0.0-next-20250203180810(typescript@5.7.3):
dependencies:
applesauce-content: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-content: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250203180810(typescript@5.7.3)
nanoid: 5.0.9
nostr-tools: 2.10.4(typescript@5.7.3)
transitivePeerDependencies:
- supports-color
- typescript
applesauce-loaders@0.0.0-next-20250131214402(typescript@5.7.3):
applesauce-loaders@0.0.0-next-20250203180810(typescript@5.7.3):
dependencies:
applesauce-core: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250203180810(typescript@5.7.3)
nanoid: 5.0.9
nostr-tools: 2.10.4(typescript@5.7.3)
rx-nostr: 3.5.0
@ -8507,12 +8510,12 @@ snapshots:
- supports-color
- typescript
applesauce-react@0.0.0-next-20250131214402(typescript@5.7.3):
applesauce-react@0.0.0-next-20250203180810(typescript@5.7.3):
dependencies:
applesauce-accounts: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-content: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-factory: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-accounts: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-content: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250203180810(typescript@5.7.3)
applesauce-factory: 0.0.0-next-20250203180810(typescript@5.7.3)
nostr-tools: 2.10.4(typescript@5.7.3)
react: 18.3.1
rxjs: 7.8.1
@ -8520,12 +8523,12 @@ snapshots:
- supports-color
- typescript
applesauce-signers@0.0.0-next-20250131214402(typescript@5.7.3):
applesauce-signers@0.0.0-next-20250203180810(typescript@5.7.3):
dependencies:
'@noble/hashes': 1.7.1
'@noble/secp256k1': 1.7.1
'@scure/base': 1.2.4
applesauce-core: 0.0.0-next-20250131214402(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250203180810(typescript@5.7.3)
debug: 4.4.0
nanoid: 5.0.9
nostr-tools: 2.10.4(typescript@5.7.3)
@ -8625,7 +8628,7 @@ snapshots:
dependencies:
bare-events: 2.5.4
bare-path: 3.0.0
bare-stream: 2.6.4(bare-events@2.5.4)
bare-stream: 2.6.5(bare-events@2.5.4)
transitivePeerDependencies:
- bare-buffer
optional: true
@ -8638,7 +8641,7 @@ snapshots:
bare-os: 3.4.0
optional: true
bare-stream@2.6.4(bare-events@2.5.4):
bare-stream@2.6.5(bare-events@2.5.4):
dependencies:
streamx: 2.22.0
optionalDependencies:
@ -8680,7 +8683,7 @@ snapshots:
blossom-client-sdk@0.0.0-next-20250124155258:
dependencies:
'@cashu/cashu-ts': 2.1.0
'@cashu/cashu-ts': 2.2.0
'@noble/hashes': 1.7.1
blossom-client-sdk@0.9.1:
@ -8897,11 +8900,11 @@ snapshots:
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
codemirror-json-schema@0.7.9(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)(@lezer/common@1.2.3):
codemirror-json-schema@0.7.9(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)(@lezer/common@1.2.3):
dependencies:
'@codemirror/language': 6.10.8
'@codemirror/lint': 6.8.4
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
'@lezer/common': 1.2.3
'@sagold/json-pointer': 5.1.2
@ -8923,7 +8926,7 @@ snapshots:
codemirror-json5@1.0.3:
dependencies:
'@codemirror/language': 6.10.8
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
@ -8944,7 +8947,7 @@ snapshots:
'@codemirror/language': 6.10.8
'@codemirror/lint': 6.8.4
'@codemirror/search': 6.5.8
'@codemirror/state': 6.5.1
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
color-convert@1.9.3:
@ -9119,7 +9122,7 @@ snapshots:
cosmiconfig@7.1.0:
dependencies:
'@types/parse-json': 4.0.2
import-fresh: 3.3.0
import-fresh: 3.3.1
parse-json: 5.2.0
path-type: 4.0.0
yaml: 1.10.2
@ -10110,7 +10113,7 @@ snapshots:
immutability-helper@3.1.1: {}
import-fresh@3.3.0:
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
resolve-from: 4.0.0
@ -11908,29 +11911,29 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
rollup@4.32.1:
rollup@4.34.1:
dependencies:
'@types/estree': 1.0.6
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.32.1
'@rollup/rollup-android-arm64': 4.32.1
'@rollup/rollup-darwin-arm64': 4.32.1
'@rollup/rollup-darwin-x64': 4.32.1
'@rollup/rollup-freebsd-arm64': 4.32.1
'@rollup/rollup-freebsd-x64': 4.32.1
'@rollup/rollup-linux-arm-gnueabihf': 4.32.1
'@rollup/rollup-linux-arm-musleabihf': 4.32.1
'@rollup/rollup-linux-arm64-gnu': 4.32.1
'@rollup/rollup-linux-arm64-musl': 4.32.1
'@rollup/rollup-linux-loongarch64-gnu': 4.32.1
'@rollup/rollup-linux-powerpc64le-gnu': 4.32.1
'@rollup/rollup-linux-riscv64-gnu': 4.32.1
'@rollup/rollup-linux-s390x-gnu': 4.32.1
'@rollup/rollup-linux-x64-gnu': 4.32.1
'@rollup/rollup-linux-x64-musl': 4.32.1
'@rollup/rollup-win32-arm64-msvc': 4.32.1
'@rollup/rollup-win32-ia32-msvc': 4.32.1
'@rollup/rollup-win32-x64-msvc': 4.32.1
'@rollup/rollup-android-arm-eabi': 4.34.1
'@rollup/rollup-android-arm64': 4.34.1
'@rollup/rollup-darwin-arm64': 4.34.1
'@rollup/rollup-darwin-x64': 4.34.1
'@rollup/rollup-freebsd-arm64': 4.34.1
'@rollup/rollup-freebsd-x64': 4.34.1
'@rollup/rollup-linux-arm-gnueabihf': 4.34.1
'@rollup/rollup-linux-arm-musleabihf': 4.34.1
'@rollup/rollup-linux-arm64-gnu': 4.34.1
'@rollup/rollup-linux-arm64-musl': 4.34.1
'@rollup/rollup-linux-loongarch64-gnu': 4.34.1
'@rollup/rollup-linux-powerpc64le-gnu': 4.34.1
'@rollup/rollup-linux-riscv64-gnu': 4.34.1
'@rollup/rollup-linux-s390x-gnu': 4.34.1
'@rollup/rollup-linux-x64-gnu': 4.34.1
'@rollup/rollup-linux-x64-musl': 4.34.1
'@rollup/rollup-win32-arm64-msvc': 4.34.1
'@rollup/rollup-win32-ia32-msvc': 4.34.1
'@rollup/rollup-win32-x64-msvc': 4.34.1
fsevents: 2.3.3
rtl-css-js@1.16.1:
@ -12458,8 +12461,6 @@ snapshots:
tiny-invariant@1.3.3: {}
tiny-lru@11.2.11: {}
tinycolor2@1.6.0: {}
tinyglobby@0.2.10:
@ -12747,7 +12748,7 @@ snapshots:
dependencies:
esbuild: 0.21.5
postcss: 8.5.1
rollup: 4.32.1
rollup: 4.34.1
optionalDependencies:
'@types/node': 22.13.0
fsevents: 2.3.3

View File

@ -1,128 +0,0 @@
import { Debugger } from "debug";
import { Filter, NostrEvent, matchFilters } from "nostr-tools";
import { AbstractRelay } from "nostr-tools/abstract-relay";
import { SimpleRelay } from "nostr-idb";
import _throttle from "lodash.throttle";
import { nanoid } from "nanoid";
import { Subject, Subscription } from "rxjs";
import { logger } from "../helpers/debug";
import EventStore from "./event-store";
import { mergeFilter } from "../helpers/nostr/filter";
import relayPoolService from "../services/relay-pool";
import Process from "./process";
import processManager from "../services/process-manager";
import LayersThree01 from "../components/icons/layers-three-01";
import { eventStore } from "../services/event-store";
const DEFAULT_CHUNK_SIZE = 100;
export type EventFilter = (event: NostrEvent) => boolean;
/** @deprecated this should be replaced with a rx-nostr based timeline loader */
export default class ChunkedRequest {
id: string;
process: Process;
relay: AbstractRelay;
filters: Filter[];
chunkSize = DEFAULT_CHUNK_SIZE;
private log: Debugger;
private subs: Subscription[] = [];
loading = false;
events: EventStore;
/** set to true when the next chunk produces 0 events */
complete = false;
private lastChunkIdx = 0;
onChunkFinish = new Subject<number>();
constructor(relay: SimpleRelay | AbstractRelay, filters: Filter[], log?: Debugger) {
this.id = nanoid(8);
this.process = new Process("ChunkedRequest", this, [relay]);
this.process.icon = LayersThree01;
this.relay = relay as AbstractRelay;
this.filters = filters;
this.log = log || logger.extend(relay.url);
this.events = new EventStore(relay.url);
processManager.registerProcess(this.process);
}
async loadNextChunk() {
if (this.loading) return;
// check if its possible to subscribe to this relay
if (!relayPoolService.canSubscribe(this.relay)) {
this.log("Cant subscribe to relay, aborting");
return;
}
this.loading = true;
if (!this.relay.connected) {
this.log("requesting relay connection");
relayPoolService.requestConnect(this.relay);
this.loading = false;
return;
}
let filters: Filter[] = mergeFilter(this.filters, { limit: this.chunkSize });
const oldestEvent = this.getLastEvent();
if (oldestEvent) {
filters = mergeFilter(filters, { until: oldestEvent.created_at - 1 });
}
let gotEvents = 0;
this.process.active = true;
await new Promise<number>((res) => {
const sub = this.relay.subscribe(filters, {
id: this.id + "-" + this.lastChunkIdx++,
onevent: (event) => {
this.handleEvent(event);
gotEvents++;
},
oneose: () => {
this.loading = false;
if (gotEvents === 0) {
this.complete = true;
this.log("Complete");
} else {
this.log(`Got ${gotEvents} events`);
}
this.onChunkFinish.next(gotEvents);
sub.close();
this.process.active = false;
res(gotEvents);
},
onclose: (reason) => {
relayPoolService.handleRelayNotice(this.relay, reason);
},
});
});
}
private handleEvent(event: NostrEvent) {
if (!matchFilters(this.filters, event)) return;
event = eventStore.add(event, this.relay.url);
return this.events.addEvent(event);
}
getFirstEvent(nth = 0, eventFilter?: EventFilter) {
return this.events.getFirstEvent(nth, eventFilter);
}
getLastEvent(nth = 0, eventFilter?: EventFilter) {
return this.events.getLastEvent(nth, eventFilter);
}
destroy() {
for (const sub of this.subs) sub.unsubscribe();
this.subs = [];
processManager.unregisterProcess(this.process);
}
}

View File

@ -1,111 +0,0 @@
import { NostrEvent } from "nostr-tools";
import { Subject, Subscription } from "rxjs";
import { nanoid } from "nanoid";
import { getEventUID, sortByDate } from "../helpers/nostr/event";
import SuperMap from "./super-map";
export type EventFilter = (event: NostrEvent) => boolean;
/**
* a class used to store and sort events
* @deprecated there shouldn't be a need for one-off event stores now that EventStore from applesauce is used
*/
export default class EventStore {
id = nanoid(8);
name?: string;
events = new Map<string, NostrEvent>();
customSort?: typeof sortByDate;
constructor(name?: string, customSort?: typeof sortByDate) {
this.name = name;
this.customSort = customSort;
}
getSortedEvents() {
return Array.from(this.events.values()).sort(this.customSort || sortByDate);
}
onEvent = new Subject<NostrEvent>();
onDelete = new Subject<string>();
onClear = new Subject();
private handleEvent(event: NostrEvent) {
const uid = getEventUID(event);
const existing = this.events.get(uid);
if (!existing || event.created_at > existing.created_at) {
this.events.set(uid, event);
this.onEvent.next(event);
}
}
addEvent(event: NostrEvent) {
this.handleEvent(event);
}
getEvent(id: string) {
return this.events.get(id);
}
deleteEvent(id: string) {
if (this.events.has(id)) {
this.events.delete(id);
this.onDelete.next(id);
}
}
clear() {
this.events.clear();
this.onClear.next(undefined);
}
private storeSubs = new SuperMap<EventStore, Subscription[]>(() => []);
connect(other: EventStore, fullSync = true) {
const subs = this.storeSubs.get(other);
subs.push(
other.onEvent.subscribe((e) => {
if (fullSync || this.events.has(getEventUID(e))) this.addEvent(e);
}),
);
subs.push(other.onDelete.subscribe(this.deleteEvent.bind(this)));
}
disconnect(other: EventStore) {
const subs = this.storeSubs.get(other);
for (const sub of subs) sub.unsubscribe();
this.storeSubs.delete(other);
}
cleanup() {
this.clear();
for (const [_, subs] of this.storeSubs) {
for (const sub of subs) sub.unsubscribe();
}
this.storeSubs.clear();
}
getFirstEvent(nth = 0, filter?: EventFilter) {
const events = this.getSortedEvents();
let i = 0;
while (true) {
const event = events.shift();
if (!event) return;
if (filter && !filter(event)) continue;
if (i === nth) return event;
i++;
}
}
getLastEvent(nth = 0, filter?: EventFilter) {
const events = this.getSortedEvents();
let i = 0;
while (true) {
const event = events.pop();
if (!event) return;
try {
if (filter && !filter(event)) continue;
} catch (error) {}
if (i === nth) return event;
i++;
}
}
}

View File

@ -1,284 +0,0 @@
import dayjs from "dayjs";
import { Debugger } from "debug";
import { Filter, NostrEvent } from "nostr-tools";
import { AbstractRelay } from "nostr-tools/abstract-relay";
import _throttle from "lodash.throttle";
import { BehaviorSubject, Observable, Subscription, map } from "rxjs";
import { isFilterEqual } from "applesauce-core/helpers";
import { shareLatestValue } from "applesauce-core/observable";
import { MultiSubscription } from "applesauce-net/subscription";
import { logger } from "../helpers/debug";
import { mergeFilter } from "../helpers/nostr/filter";
import SuperMap from "./super-map";
import ChunkedRequest from "./chunked-request";
import relayPoolService from "../services/relay-pool";
import Process from "./process";
import AlignHorizontalCentre02 from "../components/icons/align-horizontal-centre-02";
import processManager from "../services/process-manager";
import { eventStore, queryStore } from "../services/event-store";
import { getCacheRelay } from "../services/cache-relay";
const BLOCK_SIZE = 100;
export type EventFilter = (event: NostrEvent) => boolean;
export default class TimelineLoader {
cursor = dayjs().unix();
filters: Filter[] = [];
relays: AbstractRelay[] = [];
loading = new BehaviorSubject(false);
complete = new BehaviorSubject(false);
loadNextBlockBuffer = 2;
eventFilter?: EventFilter;
useCache = true;
name: string;
/** @deprecated */
timeline?: Observable<NostrEvent[]>;
process: Process;
private log: Debugger;
private subscription: MultiSubscription;
private cacheLoader: ChunkedRequest | null = null;
private loaders = new Map<string, ChunkedRequest>();
constructor(name: string) {
this.name = name;
this.process = new Process("TimelineLoader", this);
this.process.name = name;
this.process.icon = AlignHorizontalCentre02;
this.log = logger.extend("TimelineLoader:" + name);
this.subscription = new MultiSubscription(relayPoolService);
this.subscription.onEvent.subscribe(this.handleEvent.bind(this));
processManager.registerProcess(this.process);
}
private updateTimeline() {
const timeline = queryStore.timeline(this.filters);
if (this.eventFilter) {
// add filter
this.timeline = timeline.pipe(
map((events) =>
events.filter((e) => {
try {
return this.eventFilter!(e);
} catch (error) {}
return false;
}),
),
shareLatestValue(),
);
} else {
this.timeline = timeline.pipe(shareLatestValue());
}
}
private seenInCache = new Set<string>();
private handleEvent(event: NostrEvent, fromCache = false) {
event = eventStore.add(event);
if (fromCache) this.seenInCache.add(event.id);
}
private handleChunkFinished() {
this.updateLoading();
this.updateComplete();
}
private chunkLoaderSubs = new SuperMap<ChunkedRequest, Subscription[]>(() => []);
private connectToChunkLoader(loader: ChunkedRequest) {
this.process.addChild(loader.process);
const subs = this.chunkLoaderSubs.get(loader);
subs.push(loader.onChunkFinish.subscribe(this.handleChunkFinished.bind(this)));
}
private disconnectFromChunkLoader(loader: ChunkedRequest) {
loader.destroy();
const subs = this.chunkLoaderSubs.get(loader);
for (const sub of subs) sub.unsubscribe();
this.chunkLoaderSubs.delete(loader);
}
setFilters(filters: Filter[]) {
if (isFilterEqual(this.filters, filters)) return;
this.log("Set filters", filters);
// recreate all chunk loaders
for (const relay of this.relays) {
const loader = this.loaders.get(relay.url);
if (loader) {
this.disconnectFromChunkLoader(loader);
this.loaders.delete(relay.url);
}
const chunkLoader = new ChunkedRequest(
relayPoolService.requestRelay(relay.url),
filters,
this.log.extend(relay.url),
);
this.loaders.set(relay.url, chunkLoader);
this.connectToChunkLoader(chunkLoader);
}
// set filters
this.filters = filters;
// recreate cache chunk loader
if (this.cacheLoader) this.disconnectFromChunkLoader(this.cacheLoader);
const cacheRelay = getCacheRelay();
if (cacheRelay && this.useCache) {
this.cacheLoader = new ChunkedRequest(cacheRelay, this.filters, this.log.extend("cache-relay"));
this.connectToChunkLoader(this.cacheLoader);
}
// update the live subscription query map and add limit
this.subscription.setFilters(mergeFilter(filters, { limit: BLOCK_SIZE / 2 }));
// update timeline
this.updateTimeline();
}
setRelays(relays: Iterable<string | URL | AbstractRelay>) {
const newRelays = relayPoolService.getRelays(relays);
// remove chunk loaders
for (const relay of newRelays) {
const loader = this.loaders.get(relay.url);
if (!loader) continue;
if (!this.relays.includes(relay)) {
this.disconnectFromChunkLoader(loader);
this.loaders.delete(relay.url);
}
}
// create chunk loaders only if filters are set
if (this.filters.length > 0) {
for (const relay of newRelays) {
if (!this.loaders.has(relay.url)) {
const loader = new ChunkedRequest(relay, this.filters, this.log.extend(relay.url));
this.loaders.set(relay.url, loader);
this.connectToChunkLoader(loader);
}
}
}
this.relays = relayPoolService.getRelays(relays);
this.process.relays = new Set(this.relays);
// update live subscription
this.subscription.setRelays(this.relays);
}
setEventFilter(filter?: EventFilter) {
this.eventFilter = filter;
this.updateTimeline();
}
setCursor(cursor: number) {
this.cursor = cursor;
this.triggerChunkLoad();
}
private getAllLoaders() {
return this.cacheLoader ? [...this.loaders.values(), this.cacheLoader] : Array.from(this.loaders.values());
}
triggerChunkLoad() {
let triggeredLoad = false;
const loaders = this.getAllLoaders();
for (const loader of loaders) {
// skip loader if its already loading or complete
if (loader.complete || loader.loading) continue;
const event = loader.getLastEvent(this.loadNextBlockBuffer, this.eventFilter);
if (!event || event.created_at >= this.cursor) {
loader.loadNextChunk();
triggeredLoad = true;
}
}
if (triggeredLoad) this.updateLoading();
}
loadAllNextChunks() {
let triggeredLoad = false;
const loaders = this.getAllLoaders();
for (const loader of loaders) {
// skip loader if its already loading or complete
if (loader.complete || loader.loading) continue;
loader.loadNextChunk();
triggeredLoad = true;
}
if (triggeredLoad) this.updateLoading();
}
private updateLoading() {
const loaders = this.getAllLoaders();
for (const loader of loaders) {
if (loader.loading) {
if (!this.loading.value) {
this.loading.next(true);
return;
}
}
}
if (this.loading.value) this.loading.next(false);
}
private updateComplete() {
const loaders = this.getAllLoaders();
for (const loader of loaders) {
if (!loader.complete) {
this.complete.next(false);
return;
}
}
return this.complete.next(true);
}
open() {
this.process.active = true;
this.subscription.open();
}
close() {
this.process.active = false;
this.subscription.close();
}
forgetEvents() {
this.timeline = undefined;
}
reset() {
this.cursor = dayjs().unix();
const loaders = this.getAllLoaders();
for (const loader of loaders) this.disconnectFromChunkLoader(loader);
this.loaders.clear();
this.cacheLoader = null;
this.forgetEvents();
}
/** close the subscription and remove any event listeners for this timeline */
destroy() {
this.close();
const loaders = this.getAllLoaders();
for (const loader of loaders) this.disconnectFromChunkLoader(loader);
this.loaders.clear();
this.cacheLoader = null;
this.subscription.close();
this.process.remove();
processManager.unregisterProcess(this.process);
}
}

View File

@ -22,7 +22,7 @@ import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelays } from "../../hooks/use-client-relays";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import SearchRelayPicker, { useSearchRelay } from "../../views/search/components/search-relay-picker";
import SearchRelayPicker from "../../views/search/components/search-relay-picker";
import useEventIntersectionRef from "../../hooks/use-event-intersection-ref";
import { ListId, usePeopleListSelect } from "../../providers/local/people-list-provider";
@ -57,7 +57,7 @@ export default function GifPickerModal({ onClose, isOpen, onSelect, ...props }:
const [list, setList] = useState<ListId>("global");
const { selected, setSelected, filter, listId } = usePeopleListSelect(list, setList);
const searchRelay = useSearchRelay(searchRelayUrl);
// const searchRelay = useSearchRelay(searchRelayUrl);
const [debounceSearch, setDebounceSearch] = useState<string>();
useEffect(() => {
@ -75,8 +75,8 @@ export default function GifPickerModal({ onClose, isOpen, onSelect, ...props }:
const readRelays = useReadRelays();
const { loader, timeline } = useTimelineLoader(
[listId, "gifs", searchRelay?.url ?? "all", debounceSearch ?? "all"].join("-"),
searchRelay !== undefined ? [searchRelay] : readRelays,
[listId, "gifs", searchRelayUrl ?? "all", debounceSearch ?? "all"].join("-"),
searchRelayUrl !== undefined ? [searchRelayUrl] : readRelays,
debounceSearch !== undefined ? { ...baseFilter, search: debounceSearch } : baseFilter,
);

View File

@ -2,18 +2,17 @@ import { useCallback } from "react";
import { FlexProps } from "@chakra-ui/react";
import { useSearchParams } from "react-router-dom";
import { Expressions } from "applesauce-content/helpers";
import { TimelineLoader } from "applesauce-loaders";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import GenericNoteTimeline from "./generic-note-timeline";
import MediaTimeline from "./media-timeline";
import TimelineLoader from "../../classes/timeline-loader";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import TimelineActionAndStatus from "../timeline/timeline-action-and-status";
import { NostrEvent } from "../../types/nostr-event";
import TimelineHealth from "./timeline-health";
import useRouteSearchValue from "../../hooks/use-route-search-value";
import VerticalPageLayout from "../vertical-page-layout";
import useAppSettings from "../../hooks/use-user-app-settings";
import useMaxPageWidth from "../../hooks/use-max-page-width";
export function useTimelinePageEventFilter() {
@ -36,13 +35,12 @@ export default function TimelinePage({
timeline,
header,
...props
}: { loader: TimelineLoader; timeline: NostrEvent[]; header?: React.ReactNode } & Omit<
}: { loader?: TimelineLoader; timeline: NostrEvent[]; header?: React.ReactNode } & Omit<
FlexProps,
"children" | "direction" | "gap"
>) {
const callback = useTimelineCurserIntersectionCallback(loader);
const { maxPageWidth } = useAppSettings();
const viewParam = useRouteSearchValue("view", "timeline");
const mode = (viewParam.value as TimelineViewType) ?? "timeline";
@ -67,7 +65,7 @@ export default function TimelinePage({
<VerticalPageLayout maxW={maxWidth} mx="auto" {...props}>
{header}
{renderTimeline()}
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</VerticalPageLayout>
</IntersectionObserverProvider>
);

View File

@ -15,8 +15,8 @@ import {
useColorMode,
} from "@chakra-ui/react";
import { getSeenRelays } from "applesauce-core/helpers";
import { TimelineLoader } from "applesauce-loaders";
import TimelineLoader from "../../../classes/timeline-loader";
import { NostrEvent } from "../../../types/nostr-event";
import RelayFavicon from "../../relay-favicon";
import { NoteLink } from "../../note/note-link";
@ -70,8 +70,8 @@ function EventRow({
);
}
export default function TimelineHealth({ timeline, loader }: { loader: TimelineLoader; timeline: NostrEvent[] }) {
const relays = loader.relays.map((r) => r.url);
export default function TimelineHealth({ timeline, loader }: { loader?: TimelineLoader; timeline: NostrEvent[] }) {
const relays = loader && loader.requests ? Object.keys(loader.requests) : [];
return (
<>

View File

@ -1,11 +1,10 @@
import { Alert, AlertIcon, Button, Spinner } from "@chakra-ui/react";
import { TimelineLoader } from "applesauce-loaders";
import { useObservable } from "applesauce-react/hooks";
import TimelineLoader from "../../classes/timeline-loader";
export default function TimelineActionAndStatus({ timeline }: { timeline: TimelineLoader }) {
const loading = useObservable(timeline.loading);
const complete = useObservable(timeline.complete);
export default function TimelineActionAndStatus({ loader }: { loader?: TimelineLoader }) {
const loading = useObservable(loader?.loading$);
const complete = false;
if (complete) {
return (
@ -20,15 +19,10 @@ export default function TimelineActionAndStatus({ timeline }: { timeline: Timeli
return <Spinner ml="auto" mr="auto" mt="8" mb="8" flexShrink={0} />;
}
if (!loader) return null;
return (
<Button
onClick={() => timeline.loadAllNextChunks()}
flexShrink={0}
size="lg"
mx="auto"
colorScheme="primary"
my="4"
>
<Button onClick={() => loader?.next(-Infinity)} flexShrink={0} size="lg" mx="auto" colorScheme="primary" my="4">
Load More
</Button>
);

View File

@ -1,8 +1,8 @@
import { kinds } from "nostr-tools";
import { RelayMode } from "../../classes/relay";
import { DraftNostrEvent, NostrEvent, RTag, Tag, isRTag } from "../../types/nostr-event";
import { safeRelayUrl } from "../relay";
import { cloneEvent } from "./event";
import { RelayMode } from "../../services/app-relays";
/** fixes or removes any bad r tags */
export function cleanRTags(tags: Tag[]) {

View File

@ -1,5 +1,5 @@
import { useCallback } from "react";
import { LRU } from "tiny-lru";
import { LRU } from "applesauce-core/helpers";
const cache = new LRU<Map<string, number>>(10);

View File

@ -0,0 +1,46 @@
import { nanoid } from "nanoid";
import { Filter } from "nostr-tools";
import { useEffect, useMemo } from "react";
import { createRxForwardReq } from "rx-nostr";
import hash from "hash-sum";
import rxNostr from "../services/rx-nostr";
import { useEventStore } from "applesauce-react/hooks/use-event-store";
export default function useForwardSubscription(relays: string[], filters?: Filter | Filter[]) {
const eventStore = useEventStore();
const id = useMemo(() => nanoid(10), []);
const rxReq = useMemo(() => createRxForwardReq(id), [id]);
// load from cache
// useEffect(() => {
// const sub = cacheRequest(Array.isArray(filters) ? filters : [filters]).subscribe({
// next: (e) => eventStore.add(e),
// complete: () => sub.unsubscribe(),
// });
// }, [hash(filters)]);
// attach to rxNostr
const observable = useMemo(() => rxNostr.use(rxReq, { on: { relays } }), [rxReq, relays.join(",")]);
// subscribe
// NOTE: have to subscribe before emitting filter
useEffect(() => {
const sub = observable.subscribe((packet) => {
eventStore.add(packet.event, packet.from);
});
return () => sub.unsubscribe();
}, [observable, eventStore]);
// update filters
useEffect(() => {
if (filters) {
if (Array.isArray(filters)) {
if (filters.length > 0) rxReq.emit(filters);
} else rxReq.emit([filters]);
}
}, [rxReq, hash(filters)]);
return observable;
}

View File

@ -1,15 +0,0 @@
import { useEffect, useRef } from "react";
import { usePrevious } from "react-use";
export default function useRelaysChanged(relays: string[], cb: (relays: string[]) => void) {
const callback = useRef(cb);
callback.current = cb;
const prev = usePrevious(relays);
useEffect(() => {
if (!!prev && prev?.join(",") !== relays.join(",")) {
// always call the latest callback
callback.current(relays);
}
}, [relays.join(",")]);
}

View File

@ -1,35 +1,43 @@
import { useEffect, useRef } from "react";
import { useInterval } from "react-use";
import { NostrEvent } from "nostr-tools";
import { TimelineLoader } from "applesauce-loaders";
import TimelineLoader from "../classes/timeline-loader";
import { useCachedIntersectionMapCallback } from "../providers/local/intersection-observer";
import { eventStore } from "../services/event-store";
export function useTimelineCurserIntersectionCallback(timeline: TimelineLoader) {
export function useTimelineCurserIntersectionCallback(loader?: TimelineLoader) {
const oldest = useRef<NostrEvent | undefined>(undefined);
// Request next batch when components mounts
useEffect(() => {
if (loader)
setTimeout(() => {
loader?.next(-Infinity);
}, 100);
}, [loader]);
// if the cursor is set too far ahead and the last block did not overlap with the cursor
// we need to keep loading blocks until the timeline is complete or the blocks pass the cursor
useInterval(() => {
timeline.triggerChunkLoad();
if (oldest.current) loader?.next(oldest.current.created_at - 1);
else loader?.next(-Infinity);
}, 1000);
return useCachedIntersectionMapCallback(
(map) => {
// find oldest event that is visible
let oldestEvent: NostrEvent | undefined = undefined;
for (const [id, entry] of map) {
if (!entry.isIntersecting) continue;
const event = eventStore.getEvent(id);
if (!event) continue;
if (!oldestEvent || event.created_at < oldestEvent.created_at) {
oldestEvent = event;
if (!oldest.current || event.created_at < oldest.current.created_at - 1) {
oldest.current = event;
}
}
if (oldestEvent) {
timeline.setCursor(oldestEvent.created_at);
timeline.triggerChunkLoad();
}
if (oldest.current) loader?.next(oldest.current.created_at);
},
[timeline],
[loader],
);
}

View File

@ -1,67 +1,43 @@
import { useEffect, useMemo } from "react";
import { usePrevious, useUnmount } from "react-use";
import { AbstractRelay } from "nostr-tools/abstract-relay";
import { useStoreQuery } from "applesauce-react/hooks";
import { useEventStore } from "applesauce-react/hooks/use-event-store";
import { Queries } from "applesauce-core";
import { Filter } from "nostr-tools";
import { Filter, NostrEvent } from "nostr-tools";
import sum from "hash-sum";
import timelineCacheService from "../services/timeline-cache";
import { EventFilter } from "../classes/timeline-loader";
import useForwardSubscription from "./use-forward-subscription";
type Options = {
eventFilter?: EventFilter;
useCache?: boolean;
eventFilter?: (event: NostrEvent) => boolean;
};
export default function useTimelineLoader(
key: string,
relays: Iterable<string | AbstractRelay>,
relays: string[],
filters: Filter | Filter[] | undefined,
opts?: Options,
) {
const loader = useMemo(() => timelineCacheService.createTimeline(key), [key]);
// start a forward subscription while component is mounted
useForwardSubscription(relays, filters);
// set use cache
if (opts?.useCache !== undefined) loader.useCache = opts?.useCache;
const eventStore = useEventStore();
const loader = useMemo(() => {
if (filters) return timelineCacheService.createTimeline(key, relays, Array.isArray(filters) ? filters : [filters]);
}, [key, sum(filters), relays.join(",")]);
// update relays
// start and stop loader
useEffect(() => {
loader.setRelays(relays);
loader.triggerChunkLoad();
}, [
Array.from(relays)
.map((t) => (typeof t === "string" ? t : t.url))
.join("|"),
]);
const sub = loader?.subscribe((packet) => {
eventStore.add(packet.event, packet.from);
});
// update filters
useEffect(() => {
if (filters) {
loader.setFilters(Array.isArray(filters) ? filters : [filters]);
loader.open();
loader.triggerChunkLoad();
} else loader.close();
}, [loader, JSON.stringify(filters)]);
// update event filter
useEffect(() => {
loader.setEventFilter(opts?.eventFilter);
}, [loader, opts?.eventFilter]);
// close the old timeline when the key changes
const oldTimeline = usePrevious(loader);
useEffect(() => {
if (oldTimeline && oldTimeline !== loader) {
oldTimeline.close();
}
}, [loader, oldTimeline]);
// stop the loader when unmount
useUnmount(() => {
loader.close();
});
return () => sub?.unsubscribe();
}, [eventStore, loader]);
let timeline = useStoreQuery(Queries.TimelineQuery, filters && [filters]) ?? [];
// set event filter
if (opts?.eventFilter)
timeline = timeline.filter((e) => {
try {

View File

@ -1,53 +0,0 @@
import { PropsWithChildren, createContext, useCallback, useContext, useMemo } from "react";
import { kinds } from "nostr-tools";
import { useActiveAccount } from "applesauce-react/hooks";
import TimelineLoader from "../../classes/timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useUserInbox } from "../../hooks/use-user-mailboxes";
import { truncateId } from "../../helpers/string";
type DMTimelineContextType = {
timeline?: TimelineLoader;
};
const DMTimelineContext = createContext<DMTimelineContextType>({});
export function useDMTimeline() {
const context = useContext(DMTimelineContext);
if (!context?.timeline) throw new Error("No dm timeline");
return context.timeline;
}
export default function DMTimelineProvider({ children }: PropsWithChildren) {
const account = useActiveAccount();
const inbox = useUserInbox(account?.pubkey);
const userMuteFilter = useClientSideMuteFilter();
const eventFilter = useCallback(
(event: NostrEvent) => {
if (userMuteFilter(event)) return false;
return true;
},
[userMuteFilter],
);
const { loader } = useTimelineLoader(
`${truncateId(account?.pubkey ?? "anon")}-dms`,
inbox ?? [],
account?.pubkey
? [
{ authors: [account.pubkey], kinds: [kinds.EncryptedDirectMessage] },
{ "#p": [account.pubkey], kinds: [kinds.EncryptedDirectMessage] },
]
: undefined,
{ eventFilter },
);
const context = useMemo(() => ({ timeline: loader }), [loader]);
return <DMTimelineContext.Provider value={context}>{children}</DMTimelineContext.Provider>;
}

View File

@ -8,7 +8,6 @@ import useAppSettings from "../../hooks/use-user-app-settings";
import NotificationsProvider from "./notifications-provider";
import { UserEmojiProvider } from "./emoji-provider";
import BreakpointProvider from "./breakpoint-provider";
import DMTimelineProvider from "./dms-provider";
import PublishProvider from "./publish-provider";
import WebOfTrustProvider from "./web-of-trust-provider";
import { queryStore } from "../../services/event-store";
@ -35,13 +34,11 @@ export const GlobalProviders = ({ children }: { children: React.ReactNode }) =>
<SigningProvider>
<PublishProvider>
<NotificationsProvider>
<DMTimelineProvider>
<UserEmojiProvider>
<EventFactoryProvider>
<WebOfTrustProvider>{children}</WebOfTrustProvider>
</EventFactoryProvider>
</UserEmojiProvider>
</DMTimelineProvider>
<UserEmojiProvider>
<EventFactoryProvider>
<WebOfTrustProvider>{children}</WebOfTrustProvider>
</EventFactoryProvider>
</UserEmojiProvider>
</NotificationsProvider>
</PublishProvider>
</SigningProvider>

View File

@ -1,9 +1,9 @@
import { PropsWithChildren, createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
import { kinds } from "nostr-tools";
import { useActiveAccount } from "applesauce-react/hooks";
import { TimelineLoader } from "applesauce-loaders";
import { useReadRelays } from "../../hooks/use-client-relays";
import TimelineLoader from "../../classes/timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
import useTimelineLoader from "../../hooks/use-timeline-loader";
@ -13,7 +13,7 @@ import AccountNotifications from "../../classes/notifications";
import { truncateId } from "../../helpers/string";
type NotificationTimelineContextType = {
timeline: TimelineLoader;
timeline?: TimelineLoader;
notifications?: AccountNotifications;
};
const NotificationTimelineContext = createContext<NotificationTimelineContextType | null>(null);

View File

@ -1,8 +1,6 @@
import { PropsWithChildren, createContext, useCallback, useContext, useMemo } from "react";
import { NostrEvent } from "nostr-tools";
import { useObservable } from "applesauce-react/hooks";
import TimelineLoader from "../../classes/timeline-loader";
import { eventStore } from "../../services/event-store";
export type Thread = {
@ -25,9 +23,7 @@ export function useThreadsContext() {
return useContext(ThreadsContext);
}
export default function ThreadsProvider({ timeline, children }: { timeline: TimelineLoader } & PropsWithChildren) {
const messages = useObservable(timeline.timeline) ?? [];
export default function ThreadsProvider({ messages, children }: { messages: NostrEvent[] } & PropsWithChildren) {
const threads = useMemo(() => {
const grouped: Record<string, Thread> = {};
for (const message of messages) {

View File

@ -1,36 +1,25 @@
import TimelineLoader from "../classes/timeline-loader";
import { LRU } from "applesauce-core/helpers";
import { TimelessFilter, TimelineLoader } from "applesauce-loaders";
import rxNostr from "./rx-nostr";
import { logger } from "../helpers/debug";
const MAX_CACHE = 30;
const BATCH_LIMIT = 100;
class TimelineCacheService {
private timelines = new Map<string, TimelineLoader>();
private cacheQueue: string[] = [];
private log = logger.extend("TimelineCacheService");
protected timelines = new LRU<TimelineLoader>(MAX_CACHE);
protected log = logger.extend("TimelineCacheService");
createTimeline(key: string) {
createTimeline(key: string, relays: string[], filters: TimelessFilter[]) {
let timeline = this.timelines.get(key);
if (!timeline) {
if (!timeline && relays.length > 0 && filters.length > 0) {
this.log(`Creating ${key}`);
timeline = new TimelineLoader(key);
timeline = new TimelineLoader(rxNostr, TimelineLoader.simpleFilterMap(relays, filters), { limit: BATCH_LIMIT });
this.timelines.set(key, timeline);
}
// add or move the timelines key to the top of the queue
this.cacheQueue = this.cacheQueue.filter((p) => p !== key).concat(key);
// remove any timelines at the end of the queue
while (this.cacheQueue.length > MAX_CACHE) {
const deleteKey = this.cacheQueue.shift();
if (!deleteKey) break;
const deadTimeline = this.timelines.get(deleteKey);
if (deadTimeline) {
this.log(`Destroying ${deadTimeline.name}`);
this.timelines.delete(deleteKey);
deadTimeline.destroy();
}
}
return timeline;
}
}

View File

@ -61,7 +61,7 @@ function ArticlesHomePage() {
<ArticleCard article={article} />
</ErrorBoundary>
))}
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</IntersectionObserverProvider>
</VerticalPageLayout>
);

View File

@ -1,6 +1,5 @@
import { useNavigate } from "react-router-dom";
import { kinds } from "nostr-tools";
import { useObservable } from "applesauce-react/hooks";
import {
Button,
Flex,
@ -32,31 +31,22 @@ import UserLink from "../../components/user/user-link";
import Timestamp from "../../components/timestamp";
import VerticalPageLayout from "../../components/vertical-page-layout";
import BadgeAwardCard from "./components/badge-award-card";
import TimelineLoader from "../../classes/timeline-loader";
import { ErrorBoundary } from "../../components/error-boundary";
import useParamsAddressPointer from "../../hooks/use-params-address-pointer";
function BadgeActivityTab({ timeline }: { timeline: TimelineLoader }) {
const awards = useObservable(timeline.timeline);
const callback = useTimelineCurserIntersectionCallback(timeline);
function BadgeActivityTab({ awards }: { awards: NostrEvent[] }) {
return (
<Flex direction="column" gap="4">
<IntersectionObserverProvider callback={callback}>
{awards?.map((award) => (
<ErrorBoundary key={award.id}>
<BadgeAwardCard award={award} showImage={false} />
</ErrorBoundary>
))}
</IntersectionObserverProvider>
{awards?.map((award) => (
<ErrorBoundary key={award.id}>
<BadgeAwardCard award={award} showImage={false} />
</ErrorBoundary>
))}
</Flex>
);
}
function BadgeUsersTab({ timeline }: { timeline: TimelineLoader }) {
const awards = useObservable(timeline.timeline);
const callback = useTimelineCurserIntersectionCallback(timeline);
function BadgeUsersTab({ awards }: { awards: NostrEvent[] }) {
const pubkeys = new Set<string>();
if (awards) {
for (const award of awards) {
@ -68,14 +58,12 @@ function BadgeUsersTab({ timeline }: { timeline: TimelineLoader }) {
return (
<SimpleGrid spacing={4} columns={[1, 2, 2, 3, 4, 5, 6]}>
<IntersectionObserverProvider callback={callback}>
{Array.from(pubkeys).map((pubkey) => (
<Flex key={pubkey} gap="2" alignItems="center">
<UserAvatarLink pubkey={pubkey} size="md" />
<UserLink pubkey={pubkey} fontWeight="bold" isTruncated />
</Flex>
))}
</IntersectionObserverProvider>
{Array.from(pubkeys).map((pubkey) => (
<Flex key={pubkey} gap="2" alignItems="center">
<UserAvatarLink pubkey={pubkey} size="md" />
<UserLink pubkey={pubkey} fontWeight="bold" isTruncated />
</Flex>
))}
</SimpleGrid>
);
}
@ -88,68 +76,72 @@ function BadgeDetailsPage({ badge }: { badge: NostrEvent }) {
const readRelays = useReadRelays();
const coordinate = getEventCoordinate(badge);
const { loader } = useTimelineLoader(`${coordinate}-awards`, readRelays, {
const { loader, timeline } = useTimelineLoader(`${coordinate}-awards`, readRelays, {
"#a": [coordinate],
kinds: [kinds.BadgeAward],
});
const callback = useTimelineCurserIntersectionCallback(loader);
if (!badge) return <Spinner />;
return (
<VerticalPageLayout>
<Flex gap="2" alignItems="center" wrap="wrap">
<Button onClick={() => navigate(-1)} leftIcon={<ChevronLeftIcon />}>
Back
</Button>
<IntersectionObserverProvider callback={callback}>
<Flex gap="2" alignItems="center" wrap="wrap">
<Button onClick={() => navigate(-1)} leftIcon={<ChevronLeftIcon />}>
Back
</Button>
<UserAvatarLink pubkey={badge.pubkey} size="sm" />
<UserLink fontWeight="bold" pubkey={badge.pubkey} />
<Text>|</Text>
<Heading size="md">{getBadgeName(badge)}</Heading>
<Spacer />
<BadgeMenu aria-label="More options" badge={badge} />
</Flex>
<Flex direction={{ base: "column", lg: "row" }} gap="4">
{image && (
<Image src={image.src} maxW="3in" mr="2" mb="2" mx={{ base: "auto", lg: "initial" }} borderRadius="lg" />
)}
<Flex direction="column">
<UserAvatarLink pubkey={badge.pubkey} size="sm" />
<UserLink fontWeight="bold" pubkey={badge.pubkey} />
<Text>|</Text>
<Heading size="md">{getBadgeName(badge)}</Heading>
<Text>
Created by: <UserAvatarLink pubkey={badge.pubkey} size="xs" />{" "}
<UserLink fontWeight="bold" pubkey={badge.pubkey} />
</Text>
<Text>
Created: <Timestamp timestamp={badge.created_at} />
</Text>
{description && (
<>
<Heading size="md" mt="2">
Description
</Heading>
<Text pb="2">{description}</Text>
</>
)}
</Flex>
</Flex>
<Tabs colorScheme="primary" isLazy>
<TabList>
<Tab>Activity</Tab>
<Tab>Users</Tab>
</TabList>
<TabPanels>
<TabPanel px="0">
<BadgeActivityTab timeline={loader} />
</TabPanel>
<TabPanel>
<BadgeUsersTab timeline={loader} />
</TabPanel>
</TabPanels>
</Tabs>
<Spacer />
<BadgeMenu aria-label="More options" badge={badge} />
</Flex>
<Flex direction={{ base: "column", lg: "row" }} gap="4">
{image && (
<Image src={image.src} maxW="3in" mr="2" mb="2" mx={{ base: "auto", lg: "initial" }} borderRadius="lg" />
)}
<Flex direction="column">
<Heading size="md">{getBadgeName(badge)}</Heading>
<Text>
Created by: <UserAvatarLink pubkey={badge.pubkey} size="xs" />{" "}
<UserLink fontWeight="bold" pubkey={badge.pubkey} />
</Text>
<Text>
Created: <Timestamp timestamp={badge.created_at} />
</Text>
{description && (
<>
<Heading size="md" mt="2">
Description
</Heading>
<Text pb="2">{description}</Text>
</>
)}
</Flex>
</Flex>
<Tabs colorScheme="primary" isLazy>
<TabList>
<Tab>Activity</Tab>
<Tab>Users</Tab>
</TabList>
<TabPanels>
<TabPanel px="0">
<BadgeActivityTab awards={timeline} />
</TabPanel>
<TabPanel>
<BadgeUsersTab awards={timeline} />
</TabPanel>
</TabPanels>
</Tabs>
</IntersectionObserverProvider>
</VerticalPageLayout>
);
}

View File

@ -1,5 +1,4 @@
import { memo, useCallback, useMemo } from "react";
import { useNavigate } from "react-router-dom";
import { Button, ButtonGroup, Flex, Spinner, useDisclosure } from "@chakra-ui/react";
import { kinds } from "nostr-tools";
import { useStoreQuery } from "applesauce-react/hooks";
@ -17,7 +16,6 @@ import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import ThreadsProvider from "../../providers/local/thread-provider";
import TimelineLoader from "../../classes/timeline-loader";
import { groupMessages } from "../../helpers/nostr/dms";
import ChannelMessageBlock from "./components/channel-message-block";
import TimelineActionAndStatus from "../../components/timeline/timeline-action-and-status";
@ -28,7 +26,7 @@ import { truncateId } from "../../helpers/string";
import ContainedSimpleView from "../../components/layout/presets/contained-simple-view";
import ChannelImage from "./components/channel-image";
const ChannelChatLog = memo(({ timeline, channel }: { timeline: TimelineLoader; channel: NostrEvent }) => {
const ChannelChatLog = memo(({ channel }: { channel: NostrEvent }) => {
const messages = useStoreQuery(ChannelMessagesQuery, [channel]) ?? [];
const mutes = useStoreQuery(ChannelMutedQuery, [channel]);
const hidden = useStoreQuery(ChannelHiddenQuery, [channel]);
@ -55,7 +53,6 @@ const ChannelChatLog = memo(({ timeline, channel }: { timeline: TimelineLoader;
});
function ChannelPage({ channel }: { channel: NostrEvent }) {
const navigate = useNavigate();
const relays = useReadRelays();
const drawer = useDisclosure();
@ -81,7 +78,7 @@ function ChannelPage({ channel }: { channel: NostrEvent }) {
const callback = useTimelineCurserIntersectionCallback(loader);
return (
<ThreadsProvider timeline={loader}>
<ThreadsProvider messages={timeline}>
<IntersectionObserverProvider callback={callback}>
<ContainedSimpleView
reverse
@ -100,8 +97,8 @@ function ChannelPage({ channel }: { channel: NostrEvent }) {
}
bottom={<ChannelMessageForm channel={channel} p="2" />}
>
<ChannelChatLog timeline={loader} channel={channel} />
<TimelineActionAndStatus timeline={loader} />
<ChannelChatLog channel={channel} />
<TimelineActionAndStatus loader={loader} />
</ContainedSimpleView>
{drawer.isOpen && <ChannelMetadataDrawer isOpen onClose={drawer.onClose} channel={channel} size="lg" />}
</IntersectionObserverProvider>

View File

@ -41,7 +41,7 @@ function UserCard({ pubkey }: { pubkey: string }) {
</Card>
);
}
function ChannelMembers({ channel, relays }: { channel: NostrEvent; relays: Iterable<string> }) {
function ChannelMembers({ channel, relays }: { channel: NostrEvent; relays: string[] }) {
const { loader, timeline: userLists } = useTimelineLoader(`${channel.id}-members`, relays, {
kinds: [kinds.PublicChatsList],
"#e": [channel.id],

View File

@ -113,7 +113,7 @@ function FilesHomePage() {
</Table>
</TableContainer>
</IntersectionObserverProvider>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</VerticalPageLayout>
);
}

View File

@ -18,7 +18,6 @@ import useTimelineLoader from "../../hooks/use-timeline-loader";
import { isReply, isRepost } from "../../helpers/nostr/event";
import { CheckIcon, EditIcon } from "../../components/icons";
import { NostrEvent } from "../../types/nostr-event";
import useRelaysChanged from "../../hooks/use-relays-changed";
import TimelinePage, { useTimelinePageEventFilter } from "../../components/timeline-page";
import TimelineViewTypeButtons from "../../components/timeline-page/timeline-view-type";
import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
@ -74,8 +73,6 @@ function HashTagPage() {
{ eventFilter },
);
useRelaysChanged(readRelays, () => loader.reset());
const header = (
<Flex gap="2" alignItems="center" wrap="wrap">
<Editable

View File

@ -1,12 +1,10 @@
import { useMemo, useState } from "react";
import { Button, Card, CardBody, CardHeader, CardProps, Flex, Heading, Link, LinkBox, Text } from "@chakra-ui/react";
import { Link as RouterLink, useNavigate } from "react-router-dom";
import { useObservable } from "applesauce-react/hooks";
import { nip19 } from "nostr-tools";
import KeyboardShortcut from "../../../components/keyboard-shortcut";
import { useActiveAccount } from "applesauce-react/hooks";
import { useDMTimeline } from "../../../providers/global/dms-provider";
import {
KnownConversation,
groupIntoConversations,
@ -22,6 +20,7 @@ import UserDnsIdentity from "../../../components/user/user-dns-identity";
import Timestamp from "../../../components/timestamp";
import { useKind4Decrypt } from "../../../hooks/use-kind4-decryption";
import decryptionCacheService from "../../../services/decryption-cache";
import { useDirectMessagesTimeline } from "../../messages";
function MessagePreview({ message, pubkey }: { message: NostrEvent; pubkey: string }) {
const { plaintext } = useKind4Decrypt(message);
@ -52,9 +51,8 @@ export default function DMsCard({ ...props }: Omit<CardProps, "children">) {
const navigate = useNavigate();
const account = useActiveAccount()!;
const timeline = useDMTimeline();
const { timeline: messages } = useDirectMessagesTimeline(account.pubkey);
const messages = useObservable(timeline.timeline) ?? [];
const conversations = useMemo(() => {
const grouped = groupIntoConversations(messages)
.map((c) => identifyConversation(c, account.pubkey))

View File

@ -82,8 +82,7 @@ export default function MapView() {
setFocused(event.id);
}, []);
const events = useObservable(loader.timeline) ?? [];
useEventMarkers(events, map, handleMarkerClick);
useEventMarkers(timeline, map, handleMarkerClick);
return (
<Flex overflow={{ lg: "hidden" }} h={{ lg: "full" }} direction={{ base: "column-reverse", lg: "row" }}>
@ -98,8 +97,8 @@ export default function MapView() {
</Flex>
<Flex overflowY="auto" overflowX="hidden" gap="2" direction="column" h="full">
<MapTimeline timeline={loader} focused={focused} />
{cells.length > 0 && <TimelineActionAndStatus timeline={loader} />}
<MapTimeline timeline={timeline} focused={focused} />
{cells.length > 0 && <TimelineActionAndStatus loader={loader} />}
</Flex>
</Flex>

View File

@ -1,10 +1,8 @@
import React from "react";
import { kinds } from "nostr-tools";
import { useObservable } from "applesauce-react/hooks";
import { ErrorBoundary } from "../../components/error-boundary";
import StreamNote from "../../components/timeline-page/generic-note-timeline/stream-note";
import TimelineLoader from "../../classes/timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
import TimelineNote from "../../components/note/timeline-note";
@ -19,12 +17,10 @@ const RenderEvent = React.memo(({ event, focused }: { event: NostrEvent; focused
}
});
const MapTimeline = React.memo(({ timeline, focused }: { timeline: TimelineLoader; focused?: string }) => {
const events = useObservable(timeline.timeline);
const MapTimeline = React.memo(({ timeline, focused }: { timeline: NostrEvent[]; focused?: string }) => {
return (
<>
{events?.map((event) => (
{timeline?.map((event) => (
<ErrorBoundary key={event.id} event={event}>
<RenderEvent event={event} focused={focused === event.id} />
</ErrorBoundary>

View File

@ -112,7 +112,7 @@ function DirectMessageChatPage({ pubkey }: { pubkey: string }) {
const callback = useTimelineCurserIntersectionCallback(loader);
return (
<ThreadsProvider timeline={loader}>
<ThreadsProvider messages={messages}>
<IntersectionObserverProvider callback={callback}>
<ContainedSimpleView
reverse
@ -141,7 +141,7 @@ function DirectMessageChatPage({ pubkey }: { pubkey: string }) {
bottom={<SendMessageForm flexShrink={0} pubkey={pubkey} p="2" />}
>
<ChatLog messages={messages} />
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
{location.state?.thread && (
<ThreadDrawer isOpen onClose={closeDrawer} threadId={location.state.thread} pubkey={pubkey} />
)}

View File

@ -1,8 +1,7 @@
import { useMemo } from "react";
import { useCallback, useMemo } from "react";
import { Card, CardBody, Flex, LinkBox, LinkOverlay, Text } from "@chakra-ui/react";
import { Link as RouterLink, useLocation } from "react-router-dom";
import { useObservable } from "applesauce-react/hooks";
import { nip19 } from "nostr-tools";
import { kinds, nip19 } from "nostr-tools";
import UserAvatar from "../../components/user/user-avatar";
import RequireActiveAccount from "../../components/router/require-active-account";
@ -14,7 +13,6 @@ import { KnownConversation, groupIntoConversations, hasResponded, identifyConver
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import TimelineActionAndStatus from "../../components/timeline/timeline-action-and-status";
import { useDMTimeline } from "../../providers/global/dms-provider";
import UserName from "../../components/user/user-name";
import { NostrEvent } from "../../types/nostr-event";
import { CheckIcon } from "../../components/icons";
@ -22,6 +20,34 @@ import UserDnsIdentity from "../../components/user/user-dns-identity";
import useEventIntersectionRef from "../../hooks/use-event-intersection-ref";
import { useKind4Decrypt } from "../../hooks/use-kind4-decryption";
import ContainedParentView from "../../components/layout/presets/contained-parent-view";
import { truncateId } from "../../helpers/string";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import useUserMailboxes from "../../hooks/use-user-mailboxes";
import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
export function useDirectMessagesTimeline(pubkey?: string) {
const userMuteFilter = useClientSideMuteFilter();
const eventFilter = useCallback(
(event: NostrEvent) => {
if (userMuteFilter(event)) return false;
return true;
},
[userMuteFilter],
);
const mailboxes = useUserMailboxes(pubkey);
return useTimelineLoader(
`${truncateId(pubkey ?? "anon")}-dms`,
mailboxes?.inboxes ?? [],
pubkey
? [
{ authors: [pubkey], kinds: [kinds.EncryptedDirectMessage] },
{ "#p": [pubkey], kinds: [kinds.EncryptedDirectMessage] },
]
: undefined,
{ eventFilter },
);
}
function MessagePreview({ message, pubkey }: { message: NostrEvent; pubkey: string }) {
const ref = useEventIntersectionRef(message);
@ -64,9 +90,8 @@ function MessagesHomePage() {
const { people } = usePeopleListContext();
const account = useActiveAccount()!;
const timeline = useDMTimeline();
const { timeline: messages, loader } = useDirectMessagesTimeline(account.pubkey);
const messages = useObservable(timeline.timeline) ?? [];
const conversations = useMemo(() => {
const conversations = groupIntoConversations(messages).map((c) => identifyConversation(c, account.pubkey));
const filtered = conversations.filter((conversation) =>
@ -76,7 +101,7 @@ function MessagesHomePage() {
return filtered.sort((a, b) => b.messages[0].created_at - a.messages[0].created_at);
}, [messages, people, account.pubkey]);
const callback = useTimelineCurserIntersectionCallback(timeline);
const callback = useTimelineCurserIntersectionCallback(loader);
return (
<ContainedParentView path="/messages" width="md">
@ -88,7 +113,7 @@ function MessagesHomePage() {
<ConversationCard key={conversation.pubkeys.join("-")} conversation={conversation} />
))}
</IntersectionObserverProvider>
<TimelineActionAndStatus timeline={timeline} />
<TimelineActionAndStatus loader={loader} />
</ContainedParentView>
);
}

View File

@ -201,7 +201,7 @@ function NotificationsPage() {
</FocusedContext.Provider>
</IntersectionObserverProvider>
<TimelineActionAndStatus timeline={timeline} />
<TimelineActionAndStatus loader={timeline} />
</VerticalPageLayout>
);
}

View File

@ -53,7 +53,7 @@ function BrowseRelaySetsPage() {
<IntersectionObserverProvider callback={callback}>
{relaySets?.map((set) => <RelaySetCard key={getEventUID(set)} set={set} />)}
</IntersectionObserverProvider>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</VerticalPageLayout>
);
}

View File

@ -1,6 +1,7 @@
import { useCallback } from "react";
import { Flex, Spacer, useDisclosure } from "@chakra-ui/react";
import { kinds } from "nostr-tools";
import { getSeenRelays } from "applesauce-core/helpers";
import { isReply, isRepost } from "../../../helpers/nostr/event";
import { useAppTitle } from "../../../hooks/use-app-title";
@ -12,7 +13,6 @@ import PeopleListSelection from "../../../components/people-list-selection/peopl
import { usePeopleListContext } from "../../../providers/local/people-list-provider";
import useClientSideMuteFilter from "../../../hooks/use-client-side-mute-filter";
import NoteFilterTypeButtons from "../../../components/note-filter-type-buttons";
import { getSeenRelays } from "applesauce-core/helpers";
export default function RelayNotes({ relay }: { relay: string }) {
useAppTitle(`${relay} - Notes`);
@ -38,10 +38,7 @@ export default function RelayNotes({ relay }: { relay: string }) {
`${relay}-notes`,
[relay],
filter ? { ...filter, kinds: k } : undefined,
{
eventFilter,
useCache: false,
},
{ eventFilter },
);
const header = (
@ -53,5 +50,7 @@ export default function RelayNotes({ relay }: { relay: string }) {
</Flex>
);
if (!loader) return null;
return <TimelinePage loader={loader} timeline={timeline} header={header} />;
}

View File

@ -55,7 +55,7 @@ export default function RelayUsersTab({ relay }: { relay: string }) {
{lists?.map((list) => <UserCard key={getEventUID(list)} pubkey={list.pubkey} list={list} />)}
</SimpleGrid>
</IntersectionObserverProvider>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</Flex>
);
}

View File

@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react";
import { Filter, kinds, NostrEvent } from "nostr-tools";
import { AbstractRelay, Subscription, SubscriptionParams } from "nostr-tools/abstract-relay";
import { Alert, AlertDescription, AlertIcon, AlertTitle, Heading, Spinner, Text } from "@chakra-ui/react";
import { LRU } from "tiny-lru";
import { LRU } from "applesauce-core/helpers";
import relayPoolService from "../../../services/relay-pool";
import ProfileSearchResults from "./profile-results";

View File

@ -7,7 +7,6 @@ import useTimelineLoader from "../../hooks/use-timeline-loader";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import StreamCard from "./components/stream-card";
import useRelaysChanged from "../../hooks/use-relays-changed";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import TimelineActionAndStatus from "../../components/timeline/timeline-action-and-status";
@ -54,8 +53,6 @@ function StreamsPage() {
});
const callback = useTimelineCurserIntersectionCallback(loader);
useRelaysChanged(relays, () => loader.reset());
const { streams: favorites } = useFavoriteStreams();
const liveStreams = streams.filter((stream) => getStreamStatus(stream) === "live");
@ -104,7 +101,7 @@ function StreamsPage() {
</SimpleGrid>
</>
)}
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</IntersectionObserverProvider>
</VerticalPageLayout>
);

View File

@ -31,7 +31,7 @@ export default function UserArticlesTab() {
<ArticleCard article={article} />
</ErrorBoundary>
))}
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</IntersectionObserverProvider>
</SimpleView>
);

View File

@ -71,7 +71,7 @@ export default function UserFilesTab() {
</Tbody>
</Table>
</TableContainer>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</IntersectionObserverProvider>
</SimpleView>
);

View File

@ -49,7 +49,7 @@ export default function UserFollowersTab() {
<FollowerItem key={event.pubkey} event={event} />
))}
</SimpleGrid>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</IntersectionObserverProvider>
);
}

View File

@ -81,7 +81,7 @@ export default function UserMessagesTab() {
</Tbody>
</Table>
</TableContainer>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</VerticalPageLayout>
</IntersectionObserverProvider>
);

View File

@ -58,7 +58,7 @@ export default function UserReactionsTab() {
<VerticalPageLayout>
{reactions?.map((event) => <Reaction key={event.id} reaction={event} />)}
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</VerticalPageLayout>
</TrustProvider>
</IntersectionObserverProvider>

View File

@ -65,7 +65,7 @@ export default function UserReportsTab() {
<VerticalPageLayout>
{events?.map((report) => <ReportEvent key={report.id} report={report} />)}
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</VerticalPageLayout>
</IntersectionObserverProvider>
);

View File

@ -34,7 +34,7 @@ export default function UserStreamsTab() {
<StreamCard key={getEventUID(stream)} stream={stream} />
))}
</SimpleGrid>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</IntersectionObserverProvider>
</SimpleView>
);

View File

@ -48,7 +48,7 @@ export default function UserTorrentsTab() {
</Table>
</TableContainer>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</IntersectionObserverProvider>
</SimpleView>
);

View File

@ -132,7 +132,7 @@ const UserZapsTab = () => {
</ErrorBoundary>
))}
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</VerticalPageLayout>
</IntersectionObserverProvider>
);

View File

@ -39,7 +39,7 @@ function VideosPage() {
))}
</SimpleGrid>
</IntersectionObserverProvider>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</VerticalPageLayout>
);
}

View File

@ -78,7 +78,7 @@ export default function WikiHomeView() {
</LinkBox>
))}
</SimpleGrid>
<TimelineActionAndStatus timeline={loader} />
<TimelineActionAndStatus loader={loader} />
</VerticalPageLayout>
);
}