cleanup rxjs observables

break a lot more stuff
This commit is contained in:
hzrd149 2025-02-13 11:36:35 -06:00
parent aef779e64b
commit a66a9fcd15
36 changed files with 497 additions and 458 deletions

View File

@ -28,7 +28,7 @@
"@chakra-ui/shared-utils": "^2.0.4",
"@chakra-ui/styled-system": "^2.12.0",
"@chakra-ui/theme-tools": "^2.2.6",
"@codemirror/autocomplete": "^6.18.5",
"@codemirror/autocomplete": "^6.18.6",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/language": "^6.10.8",
"@codemirror/view": "^6.36.2",
@ -93,7 +93,7 @@
"nostr-typedef": "^0.11.0",
"nostr-wasm": "^0.1.0",
"nuka-carousel": "^8.2.0",
"prettier": "^3.5.0",
"prettier": "^3.5.1",
"react": "^19.0.0",
"react-chartjs-2": "^5.3.0",
"react-diff-viewer-continued": "^3.4.0",

239
pnpm-lock.yaml generated
View File

@ -40,8 +40,8 @@ importers:
specifier: ^2.2.6
version: 2.2.6(@chakra-ui/styled-system@2.12.0(react@19.0.0))(react@19.0.0)
'@codemirror/autocomplete':
specifier: ^6.18.5
version: 6.18.5
specifier: ^6.18.6
version: 6.18.6
'@codemirror/lang-json':
specifier: ^6.0.1
version: 6.0.1
@ -95,7 +95,7 @@ importers:
version: 4.23.8(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)
'@uiw/react-codemirror':
specifier: ^4.23.8
version: 4.23.8(@babel/runtime@7.26.7)(@codemirror/autocomplete@6.18.5)(@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)
version: 4.23.8(@babel/runtime@7.26.7)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.9)(@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,25 +104,25 @@ importers:
version: 0.7.2
applesauce-accounts:
specifier: next
version: 0.0.0-next-20250212001252(typescript@5.7.3)
version: 0.0.0-next-20250213173056(typescript@5.7.3)
applesauce-content:
specifier: next
version: 0.0.0-next-20250212001252(typescript@5.7.3)
version: 0.0.0-next-20250213173056(typescript@5.7.3)
applesauce-core:
specifier: next
version: 0.0.0-next-20250212001252(typescript@5.7.3)
version: 0.0.0-next-20250213173056(typescript@5.7.3)
applesauce-factory:
specifier: next
version: 0.0.0-next-20250212001252(typescript@5.7.3)
version: 0.0.0-next-20250213173056(typescript@5.7.3)
applesauce-loaders:
specifier: next
version: 0.0.0-next-20250212001252(typescript@5.7.3)
version: 0.0.0-next-20250213173056(typescript@5.7.3)
applesauce-react:
specifier: next
version: 0.0.0-next-20250212001252(typescript@5.7.3)
version: 0.0.0-next-20250213173056(react-dom@19.0.0(react@19.0.0))(typescript@5.7.3)
applesauce-signers:
specifier: next
version: 0.0.0-next-20250212001252(typescript@5.7.3)
version: 0.0.0-next-20250213173056(typescript@5.7.3)
bech32:
specifier: ^2.0.0
version: 2.0.0
@ -235,8 +235,8 @@ importers:
specifier: ^8.2.0
version: 8.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
prettier:
specifier: ^3.5.0
version: 3.5.0
specifier: ^3.5.1
version: 3.5.1
react:
specifier: ^19.0.0
version: 19.0.0
@ -266,7 +266,7 @@ importers:
version: 9.0.3(@types/react@18.3.18)(react@19.0.0)
react-mosaic-component:
specifier: ^6.1.1
version: 6.1.1(@types/node@22.13.1)(@types/react@18.3.18)(dnd-core@16.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
version: 6.1.1(@types/node@22.13.2)(@types/react@18.3.18)(dnd-core@16.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-photo-album:
specifier: ^2.4.1
version: 2.4.1(react@19.0.0)
@ -354,7 +354,7 @@ importers:
version: 6.0.2(@capacitor/core@6.2.0)
'@capacitor/assets':
specifier: ^3.0.5
version: 3.0.5(@types/node@22.13.1)(typescript@5.7.3)
version: 3.0.5(@types/node@22.13.2)(typescript@5.7.3)
'@capacitor/cli':
specifier: ^6.2.0
version: 6.2.0
@ -423,7 +423,7 @@ importers:
version: 4.7.5
'@vitejs/plugin-react':
specifier: ^4.3.4
version: 4.3.4(vite@5.4.14(@types/node@22.13.1)(terser@5.38.2))
version: 4.3.4(vite@5.4.14(@types/node@22.13.2)(terser@5.39.0))
camelcase:
specifier: ^8.0.0
version: 8.0.0
@ -438,13 +438,13 @@ importers:
version: 5.7.3
vite:
specifier: ^5.4.14
version: 5.4.14(@types/node@22.13.1)(terser@5.38.2)
version: 5.4.14(@types/node@22.13.2)(terser@5.39.0)
vite-plugin-pwa:
specifier: ^0.21.1
version: 0.21.1(vite@5.4.14(@types/node@22.13.1)(terser@5.38.2))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0)
version: 0.21.1(vite@5.4.14(@types/node@22.13.2)(terser@5.39.0))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0)
vite-tsconfig-paths:
specifier: ^5.1.4
version: 5.1.4(typescript@5.7.3)(vite@5.4.14(@types/node@22.13.1)(terser@5.38.2))
version: 5.1.4(typescript@5.7.3)(vite@5.4.14(@types/node@22.13.2)(terser@5.39.0))
workbox-build:
specifier: ^7.3.0
version: 7.3.0(@types/babel__core@7.20.5)
@ -1220,8 +1220,8 @@ packages:
'@changesets/write@0.3.2':
resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==}
'@codemirror/autocomplete@6.18.5':
resolution: {integrity: sha512-1e0/GALpdgVEcM6jUIrFuhznnwaCzxJNbZ4yiOjVluS2dvz+D0r6k4nBjL+TJZeHRemnBpTUdnEnqd5f0/+D0w==}
'@codemirror/autocomplete@6.18.6':
resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==}
'@codemirror/commands@6.8.0':
resolution: {integrity: sha512-q8VPEFaEP4ikSlt6ZxjB3zW72+7osfAYW9i8Zu943uqbKuz6utc1+F170hyLUCUltXORjQXRyYQNfkckzA/bPQ==}
@ -1238,8 +1238,8 @@ packages:
'@codemirror/lint@6.8.4':
resolution: {integrity: sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==}
'@codemirror/search@6.5.8':
resolution: {integrity: sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==}
'@codemirror/search@6.5.9':
resolution: {integrity: sha512-7DdQ9aaZMMxuWB1u6IIFWWuK9NocVZwvo4nG8QjJTS6oZGvteoLSiXw3EbVZVlO08Ri2ltO89JVInMpfcJxhtg==}
'@codemirror/state@6.5.2':
resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==}
@ -2005,8 +2005,8 @@ packages:
'@types/node@12.20.55':
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
'@types/node@22.13.1':
resolution: {integrity: sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==}
'@types/node@22.13.2':
resolution: {integrity: sha512-Z+r8y3XL9ZpI2EY52YYygAFmo2/oWfNSj4BCpAXE2McAexDk8VcnBMGC9Djn9gTKt4d2T/hhXqmPzo4hfIXtTg==}
'@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
@ -2192,26 +2192,26 @@ packages:
engines: {node: '>=8.0.0'}
hasBin: true
applesauce-accounts@0.0.0-next-20250212001252:
resolution: {integrity: sha512-yFbgY2HTQ4iEecYUjHJkZYS0Rjzm8DGZxc3iFURZY9mZtqQLthtwrL1zHm5UhEY0gnd3B67NxabbOpjKQr7CPA==}
applesauce-accounts@0.0.0-next-20250213173056:
resolution: {integrity: sha512-5rzAdw6UPmNbCXSu4+WR86/Q075ieucSqrBmFqBFfk61uGX2j2ttfG7fHTmOyZ69b9er2V4zOOZRNl1aKYUayQ==}
applesauce-content@0.0.0-next-20250212001252:
resolution: {integrity: sha512-ysAABUtkJSsr25Qnr/el+gNG8Ed/T4m+Bx1YIBXWNjaSvRCkNh22kMyQo3a6S40TQYALHSTY4pkIIshTv+HSMg==}
applesauce-content@0.0.0-next-20250213173056:
resolution: {integrity: sha512-soxdFR4PO5sLzJ2szL2+uiaVTnPrNGDITaJMF2oqjB3jOVTO5GD+N7hNH7VONORKrmgYDO9z0o+tr1e3YCS9Xg==}
applesauce-core@0.0.0-next-20250212001252:
resolution: {integrity: sha512-Gv8qL2pH/k3gXJHumZDla5PQuKsvpTpZaoETTQwIHaEw5C/ZMaIExjhgb1nsPnVjOgHnnfhb3FGc4BIP4q/mXA==}
applesauce-core@0.0.0-next-20250213173056:
resolution: {integrity: sha512-d+j2m2ddDj174Eb9cdjCWazI30pjo9ZZa6VXSyXKoaH1oFdeLx6pgNHh3DVrgZq7hvnicsvXNsC/ZfAOgZJViQ==}
applesauce-factory@0.0.0-next-20250212001252:
resolution: {integrity: sha512-J6ZXdMkyuJuWx8tHjNI1wTCZsKbs70EHR7pAzvVowc3RsFGl/yMp4tBlv5dg239MzWJv1lhkS4NP9DSaev65Gg==}
applesauce-factory@0.0.0-next-20250213173056:
resolution: {integrity: sha512-5MfEgh+7d0eIUvhgLIu0YFWlIoMZmAH6i75KXACdWGm6eSduGyAzOg8qRJJjHgLElI1SVbCMSqAaNoRSRMOQgA==}
applesauce-loaders@0.0.0-next-20250212001252:
resolution: {integrity: sha512-OoMKYSb1UIXShYoQyXd7Zqg5TJdXZMRzhw4+Fm22WQe3UaouF5QjDuXSOTcWfejgltNuD+TuYFelcxZ2criD1A==}
applesauce-loaders@0.0.0-next-20250213173056:
resolution: {integrity: sha512-+9gh+hzqyRy/3u5wKa3zB6oOYJ7UMWrEvEwnmPmjcmwhPPV5Tbl7uU5yYs/YZx3ju4W8tbEsWOiJjix/w2WvJA==}
applesauce-react@0.0.0-next-20250212001252:
resolution: {integrity: sha512-2BkTFiJWePOgiJzo0VU+2VdcVma2453OjndHOeX6WOOzLLhAvUhTgymQZJA3xcraj7bbVVVVyCtWXZByDF5EqA==}
applesauce-react@0.0.0-next-20250213173056:
resolution: {integrity: sha512-vgThHghEd0kBDuWH/wOVtzwsg5PW6q/SLntK1pKDrGWUE93+cnQc7HpuAuVxCQf7VnJbvPTQvidD9qjT1h03fg==}
applesauce-signers@0.0.0-next-20250212001252:
resolution: {integrity: sha512-ZLH4PafDk6CbtFmxYt4qP9+y82wDxj5U/hrs+YikToHPTBvAB9pFwWyRwzhC3l9bDm90liqGzjlGj7QyBpIuMA==}
applesauce-signers@0.0.0-next-20250213173056:
resolution: {integrity: sha512-e7CWyYOJixIogepTOLcXiipNfZj3mXSp9tcQW+rDaSRaTi3ory1DwUbSl5/MB5gAQboqEdN1Q1PT778gSXoxvQ==}
arg@4.1.3:
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
@ -2420,8 +2420,8 @@ packages:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
call-bind-apply-helpers@1.0.1:
resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
call-bind@1.0.8:
@ -3027,8 +3027,8 @@ packages:
engines: {node: '>=0.10.0'}
hasBin: true
electron-to-chromium@1.5.97:
resolution: {integrity: sha512-HKLtaH02augM7ZOdYRuO19rWDeY+QSJ1VxnXFa/XDFLf07HvM90pALIJFgrO+UVaajI3+aJMMpojoUTLZyQ7JQ==}
electron-to-chromium@1.5.98:
resolution: {integrity: sha512-bI/LbtRBxU2GzK7KK5xxFd2y9Lf9XguHooPYbcXWy6wUoT8NMnffsvRhPmSeUHLSDKAEtKuTaEtK4Ms15zkIEA==}
elementtree@0.1.7:
resolution: {integrity: sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==}
@ -4462,6 +4462,13 @@ packages:
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
engines: {node: '>= 0.4'}
observable-hooks@4.2.4:
resolution: {integrity: sha512-FdTQgyw1h5bG/QHCBIqctdBSnv9VARJCEilgpV6L2qlw1yeLqFIwPm4U15dMtl5kDmNN0hSt+Nl6iYbLFwEcQA==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
rxjs: '>=6.0.0'
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
@ -4649,8 +4656,8 @@ packages:
engines: {node: '>=10.13.0'}
hasBin: true
prettier@3.5.0:
resolution: {integrity: sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==}
prettier@3.5.1:
resolution: {integrity: sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==}
engines: {node: '>=14'}
hasBin: true
@ -5500,8 +5507,8 @@ packages:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
engines: {node: '>=8'}
terser@5.38.2:
resolution: {integrity: sha512-w8CXxxbFA5zfNsR/i8HZq5bvn18AK0O9jj7hyo1YqkovLxEFa0uP0LCVGZRqiRaKRFxXhELBp8SteeAjEnfeJg==}
terser@5.39.0:
resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==}
engines: {node: '>=10'}
hasBin: true
@ -6844,12 +6851,12 @@ snapshots:
dependencies:
'@capacitor/core': 6.2.0
'@capacitor/assets@3.0.5(@types/node@22.13.1)(typescript@5.7.3)':
'@capacitor/assets@3.0.5(@types/node@22.13.2)(typescript@5.7.3)':
dependencies:
'@capacitor/cli': 5.7.8
'@ionic/utils-array': 2.1.6
'@ionic/utils-fs': 3.1.7
'@trapezedev/project': 7.1.3(@types/node@22.13.1)(typescript@5.7.3)
'@trapezedev/project': 7.1.3(@types/node@22.13.2)(typescript@5.7.3)
commander: 8.3.0
debug: 4.3.4
fs-extra: 10.1.0
@ -7284,7 +7291,7 @@ snapshots:
human-id: 1.0.2
prettier: 2.8.8
'@codemirror/autocomplete@6.18.5':
'@codemirror/autocomplete@6.18.6':
dependencies:
'@codemirror/language': 6.10.8
'@codemirror/state': 6.5.2
@ -7305,7 +7312,7 @@ snapshots:
'@codemirror/lang-yaml@6.1.2':
dependencies:
'@codemirror/autocomplete': 6.18.5
'@codemirror/autocomplete': 6.18.6
'@codemirror/language': 6.10.8
'@codemirror/state': 6.5.2
'@lezer/common': 1.2.3
@ -7329,7 +7336,7 @@ snapshots:
'@codemirror/view': 6.36.2
crelt: 1.0.6
'@codemirror/search@6.5.8':
'@codemirror/search@6.5.9':
dependencies:
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
@ -7842,7 +7849,7 @@ snapshots:
'@prettier/plugin-xml@2.2.0':
dependencies:
'@xml-tools/parser': 1.0.11
prettier: 3.5.0
prettier: 3.5.1
'@react-dnd/asap@5.0.2': {}
@ -7883,7 +7890,7 @@ snapshots:
dependencies:
serialize-javascript: 6.0.2
smob: 1.5.0
terser: 5.38.2
terser: 5.39.0
optionalDependencies:
rollup: 2.79.2
@ -8072,7 +8079,7 @@ snapshots:
'@trapezedev/gradle-parse@7.1.3': {}
'@trapezedev/project@7.1.3(@types/node@22.13.1)(typescript@5.7.3)':
'@trapezedev/project@7.1.3(@types/node@22.13.2)(typescript@5.7.3)':
dependencies:
'@ionic/utils-fs': 3.1.7
'@ionic/utils-subprocess': 2.1.14
@ -8094,7 +8101,7 @@ snapshots:
replace: 1.2.2
tempy: 1.0.1
tmp: 0.2.3
ts-node: 10.9.2(@types/node@22.13.1)(typescript@5.7.3)
ts-node: 10.9.2(@types/node@22.13.2)(typescript@5.7.3)
xcode: 3.0.1
xml-js: 1.6.11
xpath: 0.0.32
@ -8175,7 +8182,7 @@ snapshots:
'@types/fs-extra@8.1.5':
dependencies:
'@types/node': 22.13.1
'@types/node': 22.13.2
'@types/gensync@1.0.4': {}
@ -8229,7 +8236,7 @@ snapshots:
'@types/node@12.20.55': {}
'@types/node@22.13.1':
'@types/node@22.13.2':
dependencies:
undici-types: 6.20.0
@ -8283,13 +8290,13 @@ snapshots:
'@types/webxr@0.5.21': {}
'@uiw/codemirror-extensions-basic-setup@4.23.8(@codemirror/autocomplete@6.18.5)(@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)':
'@uiw/codemirror-extensions-basic-setup@4.23.8(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.9)(@codemirror/state@6.5.2)(@codemirror/view@6.36.2)':
dependencies:
'@codemirror/autocomplete': 6.18.5
'@codemirror/autocomplete': 6.18.6
'@codemirror/commands': 6.8.0
'@codemirror/language': 6.10.8
'@codemirror/lint': 6.8.4
'@codemirror/search': 6.5.8
'@codemirror/search': 6.5.9
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
@ -8307,14 +8314,14 @@ snapshots:
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
'@uiw/react-codemirror@4.23.8(@babel/runtime@7.26.7)(@codemirror/autocomplete@6.18.5)(@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)':
'@uiw/react-codemirror@4.23.8(@babel/runtime@7.26.7)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.9)(@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.2
'@codemirror/theme-one-dark': 6.1.2
'@codemirror/view': 6.36.2
'@uiw/codemirror-extensions-basic-setup': 4.23.8(@codemirror/autocomplete@6.18.5)(@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)
'@uiw/codemirror-extensions-basic-setup': 4.23.8(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.9)(@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)
@ -8326,14 +8333,14 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-react@4.3.4(vite@5.4.14(@types/node@22.13.1)(terser@5.38.2))':
'@vitejs/plugin-react@4.3.4(vite@5.4.14(@types/node@22.13.2)(terser@5.39.0))':
dependencies:
'@babel/core': 7.26.8
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.8)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.8)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
vite: 5.4.14(@types/node@22.13.1)(terser@5.38.2)
vite: 5.4.14(@types/node@22.13.2)(terser@5.39.0)
transitivePeerDependencies:
- supports-color
@ -8420,10 +8427,10 @@ snapshots:
dependencies:
entities: 2.2.0
applesauce-accounts@0.0.0-next-20250212001252(typescript@5.7.3):
applesauce-accounts@0.0.0-next-20250213173056(typescript@5.7.3):
dependencies:
'@noble/hashes': 1.7.1
applesauce-signers: 0.0.0-next-20250212001252(typescript@5.7.3)
applesauce-signers: 0.0.0-next-20250213173056(typescript@5.7.3)
nanoid: 5.0.9
nostr-tools: 2.10.4(typescript@5.7.3)
rxjs: 7.8.1
@ -8431,13 +8438,13 @@ snapshots:
- supports-color
- typescript
applesauce-content@0.0.0-next-20250212001252(typescript@5.7.3):
applesauce-content@0.0.0-next-20250213173056(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-20250212001252(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250213173056(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 +8455,7 @@ snapshots:
- supports-color
- typescript
applesauce-core@0.0.0-next-20250212001252(typescript@5.7.3):
applesauce-core@0.0.0-next-20250213173056(typescript@5.7.3):
dependencies:
'@scure/base': 1.2.4
debug: 4.4.0
@ -8462,19 +8469,19 @@ snapshots:
- supports-color
- typescript
applesauce-factory@0.0.0-next-20250212001252(typescript@5.7.3):
applesauce-factory@0.0.0-next-20250213173056(typescript@5.7.3):
dependencies:
applesauce-content: 0.0.0-next-20250212001252(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250212001252(typescript@5.7.3)
applesauce-content: 0.0.0-next-20250213173056(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250213173056(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-20250212001252(typescript@5.7.3):
applesauce-loaders@0.0.0-next-20250213173056(typescript@5.7.3):
dependencies:
applesauce-core: 0.0.0-next-20250212001252(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250213173056(typescript@5.7.3)
nanoid: 5.0.9
nostr-tools: 2.10.4(typescript@5.7.3)
rx-nostr: 3.5.0
@ -8483,25 +8490,27 @@ snapshots:
- supports-color
- typescript
applesauce-react@0.0.0-next-20250212001252(typescript@5.7.3):
applesauce-react@0.0.0-next-20250213173056(react-dom@19.0.0(react@19.0.0))(typescript@5.7.3):
dependencies:
applesauce-accounts: 0.0.0-next-20250212001252(typescript@5.7.3)
applesauce-content: 0.0.0-next-20250212001252(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250212001252(typescript@5.7.3)
applesauce-factory: 0.0.0-next-20250212001252(typescript@5.7.3)
applesauce-accounts: 0.0.0-next-20250213173056(typescript@5.7.3)
applesauce-content: 0.0.0-next-20250213173056(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250213173056(typescript@5.7.3)
applesauce-factory: 0.0.0-next-20250213173056(typescript@5.7.3)
nostr-tools: 2.10.4(typescript@5.7.3)
observable-hooks: 4.2.4(react-dom@19.0.0(react@19.0.0))(react@18.3.1)(rxjs@7.8.1)
react: 18.3.1
rxjs: 7.8.1
transitivePeerDependencies:
- react-dom
- supports-color
- typescript
applesauce-signers@0.0.0-next-20250212001252(typescript@5.7.3):
applesauce-signers@0.0.0-next-20250213173056(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-20250212001252(typescript@5.7.3)
applesauce-core: 0.0.0-next-20250213173056(typescript@5.7.3)
debug: 4.4.0
nanoid: 5.0.9
nostr-tools: 2.10.4(typescript@5.7.3)
@ -8726,7 +8735,7 @@ snapshots:
browserslist@4.24.4:
dependencies:
caniuse-lite: 1.0.30001699
electron-to-chromium: 1.5.97
electron-to-chromium: 1.5.98
node-releases: 2.0.19
update-browserslist-db: 1.1.2(browserslist@4.24.4)
@ -8746,21 +8755,21 @@ snapshots:
bytes@3.1.2: {}
call-bind-apply-helpers@1.0.1:
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
call-bind@1.0.8:
dependencies:
call-bind-apply-helpers: 1.0.1
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
get-intrinsic: 1.2.7
set-function-length: 1.2.2
call-bound@1.0.3:
dependencies:
call-bind-apply-helpers: 1.0.1
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.2.7
callsites@3.1.0: {}
@ -8890,7 +8899,7 @@ snapshots:
shiki: 1.29.2
yaml: 2.7.0
optionalDependencies:
'@codemirror/autocomplete': 6.18.5
'@codemirror/autocomplete': 6.18.6
'@codemirror/lang-json': 6.0.1
'@codemirror/lang-yaml': 6.1.2
codemirror-json5: 1.0.3
@ -8915,11 +8924,11 @@ snapshots:
codemirror@6.0.1:
dependencies:
'@codemirror/autocomplete': 6.18.5
'@codemirror/autocomplete': 6.18.6
'@codemirror/commands': 6.8.0
'@codemirror/language': 6.10.8
'@codemirror/lint': 6.8.4
'@codemirror/search': 6.5.8
'@codemirror/search': 6.5.9
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.2
@ -9403,7 +9412,7 @@ snapshots:
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.1
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
@ -9423,7 +9432,7 @@ snapshots:
dependencies:
jake: 10.9.2
electron-to-chromium@1.5.97: {}
electron-to-chromium@1.5.98: {}
elementtree@0.1.7:
dependencies:
@ -9838,7 +9847,7 @@ snapshots:
get-intrinsic@1.2.7:
dependencies:
call-bind-apply-helpers: 1.0.1
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
@ -11142,6 +11151,12 @@ snapshots:
has-symbols: 1.1.0
object-keys: 1.1.1
observable-hooks@4.2.4(react-dom@19.0.0(react@19.0.0))(react@18.3.1)(rxjs@7.8.1):
dependencies:
react: 18.3.1
react-dom: 19.0.0(react@19.0.0)
rxjs: 7.8.1
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
@ -11330,7 +11345,7 @@ snapshots:
prettier@2.8.8: {}
prettier@3.5.0: {}
prettier@3.5.1: {}
pretty-bytes@5.6.0: {}
@ -11445,26 +11460,26 @@ snapshots:
dependencies:
dnd-core: 16.0.1
react-dnd-multi-backend@8.1.2(dnd-core@16.0.1)(react-dnd@16.0.1(@types/node@22.13.1)(@types/react@18.3.18)(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
react-dnd-multi-backend@8.1.2(dnd-core@16.0.1)(react-dnd@16.0.1(@types/node@22.13.2)(@types/react@18.3.18)(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
dnd-core: 16.0.1
dnd-multi-backend: 8.1.2(dnd-core@16.0.1)
react: 19.0.0
react-dnd: 16.0.1(@types/node@22.13.1)(@types/react@18.3.18)(react@19.0.0)
react-dnd-preview: 8.1.2(react-dnd@16.0.1(@types/node@22.13.1)(@types/react@18.3.18)(react@19.0.0))(react@19.0.0)
react-dnd: 16.0.1(@types/node@22.13.2)(@types/react@18.3.18)(react@19.0.0)
react-dnd-preview: 8.1.2(react-dnd@16.0.1(@types/node@22.13.2)(@types/react@18.3.18)(react@19.0.0))(react@19.0.0)
react-dom: 19.0.0(react@19.0.0)
react-dnd-preview@8.1.2(react-dnd@16.0.1(@types/node@22.13.1)(@types/react@18.3.18)(react@19.0.0))(react@19.0.0):
react-dnd-preview@8.1.2(react-dnd@16.0.1(@types/node@22.13.2)(@types/react@18.3.18)(react@19.0.0))(react@19.0.0):
dependencies:
react: 19.0.0
react-dnd: 16.0.1(@types/node@22.13.1)(@types/react@18.3.18)(react@19.0.0)
react-dnd: 16.0.1(@types/node@22.13.2)(@types/react@18.3.18)(react@19.0.0)
react-dnd-touch-backend@16.0.1:
dependencies:
'@react-dnd/invariant': 4.0.2
dnd-core: 16.0.1
react-dnd@16.0.1(@types/node@22.13.1)(@types/react@18.3.18)(react@19.0.0):
react-dnd@16.0.1(@types/node@22.13.2)(@types/react@18.3.18)(react@19.0.0):
dependencies:
'@react-dnd/invariant': 4.0.2
'@react-dnd/shallowequal': 4.0.2
@ -11473,7 +11488,7 @@ snapshots:
hoist-non-react-statics: 3.3.2
react: 19.0.0
optionalDependencies:
'@types/node': 22.13.1
'@types/node': 22.13.2
'@types/react': 18.3.18
react-dom@19.0.0(react@19.0.0):
@ -11542,7 +11557,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
react-mosaic-component@6.1.1(@types/node@22.13.1)(@types/react@18.3.18)(dnd-core@16.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
react-mosaic-component@6.1.1(@types/node@22.13.2)(@types/react@18.3.18)(dnd-core@16.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
classnames: 2.5.1
immutability-helper: 3.1.1
@ -11550,9 +11565,9 @@ snapshots:
prop-types: 15.8.1
rdndmb-html5-to-touch: 8.1.2(dnd-core@16.0.1)
react: 19.0.0
react-dnd: 16.0.1(@types/node@22.13.1)(@types/react@18.3.18)(react@19.0.0)
react-dnd: 16.0.1(@types/node@22.13.2)(@types/react@18.3.18)(react@19.0.0)
react-dnd-html5-backend: 16.0.1
react-dnd-multi-backend: 8.1.2(dnd-core@16.0.1)(react-dnd@16.0.1(@types/node@22.13.1)(@types/react@18.3.18)(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-dnd-multi-backend: 8.1.2(dnd-core@16.0.1)(react-dnd@16.0.1(@types/node@22.13.2)(@types/react@18.3.18)(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-dnd-touch-backend: 16.0.1
uuid: 9.0.1
transitivePeerDependencies:
@ -12365,7 +12380,7 @@ snapshots:
term-size@2.2.1: {}
terser@5.38.2:
terser@5.39.0:
dependencies:
'@jridgewell/source-map': 0.3.6
acorn: 8.14.0
@ -12473,14 +12488,14 @@ snapshots:
ts-easing@0.2.0: {}
ts-node@10.9.2(@types/node@22.13.1)(typescript@5.7.3):
ts-node@10.9.2(@types/node@22.13.2)(typescript@5.7.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
'@types/node': 22.13.1
'@types/node': 22.13.2
acorn: 8.14.0
acorn-walk: 8.3.4
arg: 4.1.3
@ -12695,37 +12710,37 @@ snapshots:
vite-plugin-funding@0.1.0: {}
vite-plugin-pwa@0.21.1(vite@5.4.14(@types/node@22.13.1)(terser@5.38.2))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0):
vite-plugin-pwa@0.21.1(vite@5.4.14(@types/node@22.13.2)(terser@5.39.0))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0):
dependencies:
debug: 4.4.0
pretty-bytes: 6.1.1
tinyglobby: 0.2.10
vite: 5.4.14(@types/node@22.13.1)(terser@5.38.2)
vite: 5.4.14(@types/node@22.13.2)(terser@5.39.0)
workbox-build: 7.3.0(@types/babel__core@7.20.5)
workbox-window: 7.3.0
transitivePeerDependencies:
- supports-color
vite-tsconfig-paths@5.1.4(typescript@5.7.3)(vite@5.4.14(@types/node@22.13.1)(terser@5.38.2)):
vite-tsconfig-paths@5.1.4(typescript@5.7.3)(vite@5.4.14(@types/node@22.13.2)(terser@5.39.0)):
dependencies:
debug: 4.4.0
globrex: 0.1.2
tsconfck: 3.1.5(typescript@5.7.3)
optionalDependencies:
vite: 5.4.14(@types/node@22.13.1)(terser@5.38.2)
vite: 5.4.14(@types/node@22.13.2)(terser@5.39.0)
transitivePeerDependencies:
- supports-color
- typescript
vite@5.4.14(@types/node@22.13.1)(terser@5.38.2):
vite@5.4.14(@types/node@22.13.2)(terser@5.39.0):
dependencies:
esbuild: 0.21.5
postcss: 8.5.2
rollup: 4.34.6
optionalDependencies:
'@types/node': 22.13.1
'@types/node': 22.13.2
fsevents: 2.3.3
terser: 5.38.2
terser: 5.39.0
w3c-keyname@2.2.8: {}

View File

@ -1,6 +1,6 @@
import { lazy, Suspense } from "react";
import { Spinner } from "@chakra-ui/react";
import { createBrowserRouter, Outlet, RouterProvider, ScrollRestoration, Location } from "react-router-dom";
import { createBrowserRouter, Outlet, RouterProvider } from "react-router-dom";
import GlobalStyles from "./styles";
@ -60,7 +60,6 @@ const RootPage = () => {
return (
<RouteProviders>
<ScrollRestoration />
<AppLayout />
</RouteProviders>
);
@ -69,7 +68,6 @@ const RootPage = () => {
const NoLayoutPage = () => {
return (
<RouteProviders>
<ScrollRestoration />
<Outlet />
</RouteProviders>
);

View File

@ -30,7 +30,7 @@ export default class MemoryRelay implements SimpleRelay {
...options,
fire: () => {
if (stream) stream.unsubscribe();
stream = this.store.stream(filters).subscribe((event) => sub.onevent?.(event));
stream = this.store.filters(filters).subscribe((event) => sub.onevent?.(event));
if (sub.oneose) sub.oneose();
},
close: () => {

View File

@ -1,44 +0,0 @@
import { PropsWithChildren, ReactNode, Suspense } from "react";
import { Outlet, useMatch } from "react-router-dom";
import { Flex, Spinner } from "@chakra-ui/react";
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
import SimpleHeader from "./simple-header";
import { ErrorBoundary } from "../../error-boundary";
export default function ContainedParentView({
children,
path,
title,
width = "xs",
actions,
}: PropsWithChildren<{ path: string; title?: string; width?: "xs" | "sm" | "md"; actions?: ReactNode }>) {
const match = useMatch(path);
const isMobile = useBreakpointValue({ base: true, lg: false });
const showMenu = !isMobile || !!match;
return (
<Flex data-type="contained-view" h="100vh" overflow="hidden" direction={{ base: "column", lg: "row" }}>
{showMenu && (
<Flex
w={{ base: "full", lg: width }}
direction="column"
overflowY="auto"
overflowX="hidden"
h="full"
flexShrink={0}
>
{title && <SimpleHeader title={title}>{actions}</SimpleHeader>}
<Flex direction="column" p="2" gap="2">
{children}
</Flex>
</Flex>
)}
<Suspense fallback={<Spinner />}>
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
</Suspense>
</Flex>
);
}

View File

@ -1,6 +1,6 @@
import { PropsWithChildren, Suspense } from "react";
import { PropsWithChildren, ReactNode, Suspense } from "react";
import { Outlet, OutletProps, useMatch } from "react-router-dom";
import { Box, Flex, Spinner } from "@chakra-ui/react";
import { Flex, FlexProps, Spinner } from "@chakra-ui/react";
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
import SimpleHeader from "./simple-header";
@ -11,45 +11,53 @@ export default function SimpleParentView({
path,
title,
width = "xs",
actions,
padding = true,
scroll = true,
gap = 2,
context,
}: PropsWithChildren<{ path: string; title?: string; width?: "xs" | "sm" | "md"; context?: OutletProps["context"] }>) {
}: PropsWithChildren<{
path: string;
title?: string;
width?: "xs" | "sm" | "md";
actions?: ReactNode;
padding?: boolean;
scroll?: boolean;
gap?: FlexProps["gap"];
context?: OutletProps["context"];
}>) {
const match = useMatch(path);
const isMobile = useBreakpointValue({ base: true, lg: false });
const showMenu = !isMobile || !!match;
const floating = useBreakpointValue({ base: false, lg: true });
if (showMenu)
return (
<Flex data-type="parent-view" flex={1} direction={floating ? "row" : "column"}>
<Box w={floating ? width : 0} flexGrow={0} flexShrink={0} />
<Flex
w={{ base: "full", lg: width }}
direction="column"
position={floating ? "fixed" : "initial"}
top="var(--safe-top)"
bottom="var(--safe-bottom)"
>
{title && <SimpleHeader title={title} />}
<Flex direction="column" p="2" gap="2" overflowY="auto" overflowX="hidden">
{children}
</Flex>
return (
<Flex data-type="contained-view" h="full" overflow="hidden" direction={{ base: "column", lg: "row" }}>
{showMenu && (
<Flex w={{ base: "full", lg: width }} direction="column" overflow="hidden" h="full" flexShrink={0}>
{title && <SimpleHeader title={title}>{actions}</SimpleHeader>}
{scroll ? (
<Flex
direction="column"
p={padding ? "2" : undefined}
gap={gap}
overflowY={scroll ? "auto" : "hidden"}
overflowX="hidden"
flex={1}
>
{children}
</Flex>
) : (
<>{children}</>
)}
</Flex>
{!isMobile && (
<Suspense fallback={<Spinner />}>
<ErrorBoundary>
<Outlet context={context} />
</ErrorBoundary>
</Suspense>
)}
</Flex>
);
else
return (
<Suspense fallback={<Spinner />}>
<ErrorBoundary>
<Outlet context={context} />
</ErrorBoundary>
</Suspense>
);
)}
{(!isMobile || !showMenu) && (
<Suspense fallback={<Spinner />}>
<ErrorBoundary>
<Outlet context={context} />
</ErrorBoundary>
</Suspense>
)}
</Flex>
);
}

View File

@ -2,6 +2,7 @@ import { ReactNode } from "react";
import { Flex, FlexProps } from "@chakra-ui/react";
import SimpleHeader from "./simple-header";
import { UNSAFE_useScrollRestoration } from "react-router-dom";
export default function SimpleView({
children,
@ -12,25 +13,44 @@ export default function SimpleView({
gap,
maxW,
center,
scroll = true,
...props
}: Omit<FlexProps, "title"> & { flush?: boolean; actions?: ReactNode; title?: ReactNode; center?: boolean }) {
}: Omit<FlexProps, "title"> & {
flush?: boolean;
actions?: ReactNode;
title?: ReactNode;
center?: boolean;
scroll?: boolean;
}) {
const content = (
<Flex
direction="column"
px={flush ? 0 : "4"}
pt={flush ? 0 : "4"}
pb={flush ? 0 : "max(1rem, var(--safe-bottom))"}
gap={gap || "2"}
flexGrow={1}
maxW={maxW}
w={maxW ? "full" : "initial"}
mx={center ? "auto" : undefined}
>
{children}
</Flex>
);
UNSAFE_useScrollRestoration;
return (
<Flex as={as} flex={1} direction="column" pr="var(--safe-right)" pl="var(--safe-left)" {...props}>
<Flex as={as} flex={1} direction="column" pr="var(--safe-right)" pl="var(--safe-left)" overflow="hidden" {...props}>
<SimpleHeader title={title}>{actions}</SimpleHeader>
<Flex
direction="column"
px={flush ? 0 : "4"}
pt={flush ? 0 : "4"}
pb={flush ? 0 : "max(1rem, var(--safe-bottom))"}
gap={gap || "2"}
flexGrow={1}
maxW={maxW}
w={maxW ? "full" : "initial"}
mx={center ? "auto" : undefined}
>
{children}
</Flex>
{scroll ? (
<Flex flex={1} overflowY="auto" overflowX="hidden" direction="column">
{content}
</Flex>
) : (
content
)}
</Flex>
);
}

View File

@ -58,7 +58,7 @@ export type TimelineNoteProps = Omit<CardProps, "children"> & {
};
export function TimelineNote({
event,
variant = "outline",
variant = "unstyled",
showReplyButton,
showReplyLine = true,
hideDrawerButton,
@ -82,53 +82,60 @@ export function TimelineNote({
return (
<TrustProvider event={event}>
<ExpandProvider>
<Card
as={LinkBox}
variant={variant}
ref={registerIntersectionEntity ? ref : undefined}
data-event-id={event.id}
<Flex
direction="column"
borderWidth="0 2px 0 2px"
rounded="none"
borderColor="var(--chakra-colors-chakra-border-color)"
{...props}
>
{clickable && <HoverLinkOverlay as={RouterLink} to={`/n/${getSharableEventAddress(event)}`} />}
<CardHeader p="2">
<Flex flex="1" gap="2" alignItems="center">
<UserAvatarLink pubkey={event.pubkey} size="sm" />
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(event)}`}>
<Timestamp timestamp={event.created_at} />
</Link>
<POWIcon event={event} boxSize={5} />
<NotePublishedUsing event={event} />
<Flex grow={1} />
</Flex>
<NoteCommunityMetadata event={event} />
{showReplyLine && <ReplyContext event={event} />}
</CardHeader>
<CardBody as={ShowMoreContainer} p="0">
<NoteContentWithWarning event={event} />
</CardBody>
<CardFooter padding="2" display="flex" gap="2" flexDirection="column" alignItems="flex-start">
{!hideZapBubbles && <ZapBubbles event={event} w="full" />}
{showReactionsOnNewLine && reactionButtons}
<Flex gap="2" w="full" alignItems="center">
<ButtonGroup size="sm" variant="ghost" zIndex={1}>
{showReplyButton && (
<IconButton icon={<ReplyIcon />} aria-label="Reply" title="Reply" onClick={replyForm.onOpen} />
)}
<EventShareButton event={event} />
<EventQuoteButton event={event} />
<EventZapButton event={event} />
</ButtonGroup>
{!showReactionsOnNewLine && reactionButtons}
<Box flexGrow={1} />
<ButtonGroup size="sm" variant="ghost" zIndex={1}>
<NoteProxyLink event={event} />
<BookmarkEventButton event={event} aria-label="Bookmark note" />
<NoteMenu event={event} aria-label="More Options" />
</ButtonGroup>
</Flex>
</CardFooter>
</Card>
<Card
as={LinkBox}
variant={variant}
ref={registerIntersectionEntity ? ref : undefined}
data-event-id={event.id}
>
{clickable && <HoverLinkOverlay as={RouterLink} to={`/n/${getSharableEventAddress(event)}`} />}
<CardHeader p="2">
<Flex flex="1" gap="2" alignItems="center">
<UserAvatarLink pubkey={event.pubkey} size="sm" />
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(event)}`}>
<Timestamp timestamp={event.created_at} />
</Link>
<POWIcon event={event} boxSize={5} />
<NotePublishedUsing event={event} />
<Flex grow={1} />
</Flex>
<NoteCommunityMetadata event={event} />
{showReplyLine && <ReplyContext event={event} />}
</CardHeader>
<CardBody as={ShowMoreContainer} px="2">
<NoteContentWithWarning event={event} />
</CardBody>
<CardFooter p="2" display="flex" gap="2" flexDirection="column" alignItems="flex-start">
{!hideZapBubbles && <ZapBubbles event={event} w="full" />}
{showReactionsOnNewLine && reactionButtons}
</CardFooter>
</Card>
<Flex gap="2" w="full" alignItems="center" pt="2" px="2">
<ButtonGroup size="sm" variant="ghost" zIndex={1}>
{showReplyButton && (
<IconButton icon={<ReplyIcon />} aria-label="Reply" title="Reply" onClick={replyForm.onOpen} />
)}
<EventShareButton event={event} />
<EventQuoteButton event={event} />
<EventZapButton event={event} />
</ButtonGroup>
{!showReactionsOnNewLine && reactionButtons}
<Box flexGrow={1} />
<ButtonGroup size="sm" variant="ghost" zIndex={1}>
<NoteProxyLink event={event} />
<BookmarkEventButton event={event} aria-label="Bookmark note" />
<NoteMenu event={event} aria-label="More Options" />
</ButtonGroup>
</Flex>
</Flex>
</ExpandProvider>
{replyForm.isOpen && (
<ReplyForm

View File

@ -16,6 +16,6 @@ export default function NoteContentWithWarning({ event }: { event: NostrEvent })
return showContentWarning ? (
<ContentWarning description={typeof warning === "string" ? warning : undefined} />
) : (
<TextNoteContents px="2" event={event} />
<TextNoteContents event={event} />
);
}

View File

@ -43,7 +43,7 @@ function ShareEvent({ event }: { event: NostrEvent }) {
</Heading>
<UserDnsIdentity pubkey={event.pubkey} onlyIcon />
<Text as="span">Shared</Text>
<NoteMenu event={event} size="sm" variant="link" aria-label="note options" ml="auto" />
<NoteMenu event={event} size="sm" variant="ghost" aria-label="note options" ml="auto" />
</Flex>
{!note ? (
<LoadingNostrLink link={{ type: "nevent", data: pointer }} />

View File

@ -62,7 +62,7 @@ export default function TimelinePage({
const maxWidth = useMaxPageWidth("6xl");
return (
<IntersectionObserverProvider callback={callback}>
<VerticalPageLayout maxW={maxWidth} mx="auto" {...props}>
<VerticalPageLayout maxW={maxWidth} mx="auto" gap="4" {...props}>
{header}
{renderTimeline()}
<TimelineActionAndStatus loader={loader} />

View File

@ -1,10 +1,12 @@
import { ComponentWithAs, Flex, FlexProps } from "@chakra-ui/react";
import { Box, ComponentWithAs, Flex, FlexProps } from "@chakra-ui/react";
const VerticalPageLayout: ComponentWithAs<"div", FlexProps> = ({ children, ...props }: FlexProps) => {
return (
<Flex direction="column" pt="2" pb="12" gap="2" px="2" w="full" {...props}>
{children}
</Flex>
<Box overflowX="hidden" overflowY="auto" h="full" w="full">
<Flex direction="column" pt="2" pb="12" gap="2" px="2" w="full" {...props}>
{children}
</Flex>
</Box>
);
};

View File

@ -3,7 +3,7 @@ import { AbstractRelay } from "nostr-tools/abstract-relay";
import relayInfoService from "../services/relay-info";
export function useRelayInfo(relay?: string | AbstractRelay, alwaysFetch = false) {
export function useRelayInfo(relay?: string, alwaysFetch = false) {
const {
value: info,
loading,

View File

@ -24,7 +24,7 @@ export default function useReplaceableEvent(
relays: [...readRelays, ...(parsed.relays ?? [])],
force,
});
}, [parsed, readRelays.join("|"), force]);
}, [parsed?.kind, parsed?.pubkey, parsed?.identifier, readRelays.join("|"), force]);
return useStoreQuery(ReplaceableQuery, parsed ? [parsed.kind, parsed.pubkey, parsed.identifier] : undefined);
}

View File

@ -8,16 +8,16 @@ import { useRelayInfo } from "./use-relay-info";
import { AbstractRelay } from "nostr-tools/abstract-relay";
import WasmRelay from "../services/wasm-relay";
export function useCacheRelaySupportsSearch() {
export function useCacheRelaySupportsSearch(): boolean {
const cacheRelay = useCacheRelay();
const { info: cacheRelayInfo } = useRelayInfo(cacheRelay instanceof AbstractRelay ? cacheRelay : undefined, true);
const { info: cacheRelayInfo } = useRelayInfo(cacheRelay instanceof AbstractRelay ? cacheRelay.url : undefined, true);
return (
cacheRelay instanceof WasmRelay ||
(cacheRelay instanceof AbstractRelay && !!cacheRelayInfo?.supported_nips?.includes(50))
);
}
export default function useSearchRelays() {
export default function useSearchRelays(): string[] {
const account = useActiveAccount();
const searchRelayList = useUserSearchRelayList(account?.pubkey);
const searchRelays = searchRelayList ? getRelaysFromList(searchRelayList) : DEFAULT_SEARCH_RELAYS;

View File

@ -4,8 +4,8 @@ import { UserContactsQuery } from "applesauce-core/queries";
import useReplaceableEvent from "./use-replaceable-event";
export default function useUserContactList(pubkey?: string, additionalRelays?: Iterable<string>, force?: boolean) {
export default function useUserContacts(pubkey?: string, additionalRelays?: Iterable<string>, force?: boolean) {
useReplaceableEvent(pubkey && { kind: kinds.Contacts, pubkey }, additionalRelays, force);
return useStoreQuery(UserContactsQuery);
return useStoreQuery(UserContactsQuery, pubkey ? [pubkey] : undefined);
}

View File

@ -0,0 +1,11 @@
import { kinds } from "nostr-tools";
import { useStoreQuery } from "applesauce-react/hooks";
import { UserMuteQuery } from "applesauce-core/queries";
import useReplaceableEvent from "./use-replaceable-event";
export default function useUserMutes(pubkey?: string, additionalRelays?: Iterable<string>, force?: boolean) {
useReplaceableEvent(pubkey && { kind: kinds.Mutelist, pubkey }, additionalRelays, force);
return useStoreQuery(UserMuteQuery, pubkey ? [pubkey] : undefined);
}

View File

@ -73,13 +73,7 @@ export default function WebOfTrustProvider({ pubkey, children }: PropsWithChildr
// load the graph when it changes
useEffect(() => {
if (!graph) return;
if (import.meta.env.DEV) {
//@ts-expect-error debug
window.webOfTrust = graph;
}
loadSocialGraph(graph, kinds.Contacts, graph.root, undefined, 1);
// loadSocialGraph(graph, kinds.Contacts, graph.root, undefined, 1);
}, [graph]);
return <WebOfTrustContext.Provider value={graph}>{children}</WebOfTrustContext.Provider>;

View File

@ -6,7 +6,7 @@ export default function DVMResponsesQuery(request: NostrEvent): Query<Record<str
return {
key: request.id,
run: (events) =>
events.stream([{ kinds: [request.kind + 1000, 7000], "#e": [request.id] }]).pipe(
events.filters([{ kinds: [request.kind + 1000, 7000], "#e": [request.id] }]).pipe(
scan(
(byPubkey, event) => {
if (byPubkey[event.pubkey] && byPubkey[event.pubkey].created_at > event.created_at) return byPubkey;

View File

@ -8,6 +8,8 @@ import relayInfoService from "./relay-info";
import replaceableEventLoader from "./replaceable-loader";
import timelineCacheService from "./timeline-cache";
import { userSearchDirectory } from "./username-search";
import singleEventLoader from "./single-event-loader";
import userSetsLoader from "./user-sets-loader";
const noStrudel = {
rxNostr,
@ -28,6 +30,8 @@ const noStrudel = {
// other internal services
replaceableEventLoader,
singleEventLoader,
userSetsLoader,
userSearchDirectory,
readStatusService,
relayInfoService,

View File

@ -1,4 +1,3 @@
import { AbstractRelay } from "nostr-tools/abstract-relay";
import { Nip11Registry } from "rx-nostr";
import db from "./db";
@ -36,9 +35,7 @@ async function saveInfo() {
await tx.done;
}
async function getInfo(relay: string | AbstractRelay, alwaysFetch = false) {
relay = typeof relay === "string" ? relay : relay.url;
async function getInfo(relay: string, alwaysFetch = false) {
let info = Nip11Registry.get(relay);
if (!info || alwaysFetch) {

View File

@ -1,16 +1,14 @@
import { ReplaceableLoader } from "applesauce-loaders/loaders";
import { truncateId } from "../helpers/string";
import { eventStore } from "./event-store";
import rxNostr from "./rx-nostr";
import { COMMON_CONTACT_RELAYS } from "../const";
import { cacheRequest } from "./cache-relay";
export function getHumanReadableCoordinate(kind: number, pubkey: string, d?: string) {
return `${kind}:${truncateId(pubkey)}${d ? ":" + d : ""}`;
}
const replaceableEventLoader = new ReplaceableLoader(rxNostr, { cacheRequest, lookupRelays: COMMON_CONTACT_RELAYS });
const replaceableEventLoader = new ReplaceableLoader(rxNostr, {
cacheRequest,
lookupRelays: COMMON_CONTACT_RELAYS,
});
replaceableEventLoader.subscribe((packet) => eventStore.add(packet.event, packet.from));

View File

@ -20,7 +20,7 @@ const cache = db.getAll("userSearch").then((rows: { pubkey: string; names: strin
return rows.reduce<UserDirectory>((dir, row) => ({ ...dir, [row.pubkey]: row.names }), {});
});
const updates = eventStore.stream([{ kinds: [kinds.Metadata] }]).pipe(
const updates = eventStore.filters([{ kinds: [kinds.Metadata] }]).pipe(
filter((event) => !isFromCache(event)),
bufferTime(500),
concatMap(async (events) => {

View File

@ -2,17 +2,15 @@ html {
margin: 0;
height: 100%;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: none;
overflow: hidden;
}
body {
margin: 0;
width: 100%;
}
height: 100%;
overflow: hidden;
body {
--safe-top: env(safe-area-inset-top, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px);
--safe-right: env(safe-area-inset-right, 0px);
@ -29,4 +27,6 @@ body {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
overflow: hidden;
}

View File

@ -23,8 +23,8 @@ import ChannelMessageForm from "./components/send-message-form";
import useParamsEventPointer from "../../hooks/use-params-event-pointer";
import { useReadRelays } from "../../hooks/use-client-relays";
import { truncateId } from "../../helpers/string";
import ContainedSimpleView from "../../components/layout/presets/contained-simple-view";
import ChannelImage from "./components/channel-image";
import SimpleView from "../../components/layout/presets/simple-view";
const ChannelChatLog = memo(({ channel }: { channel: NostrEvent }) => {
const messages = useStoreQuery(ChannelMessagesQuery, [channel]) ?? [];
@ -80,8 +80,9 @@ function ChannelPage({ channel }: { channel: NostrEvent }) {
return (
<ThreadsProvider messages={timeline}>
<IntersectionObserverProvider callback={callback}>
<ContainedSimpleView
reverse
<SimpleView
scroll={false}
flush
title={
<Flex gap="2" alignItems="center">
<ChannelImage channel={channel} w="10" rounded="md" />
@ -95,11 +96,14 @@ function ChannelPage({ channel }: { channel: NostrEvent }) {
<ChannelMenu channel={channel} aria-label="More Options" />
</ButtonGroup>
}
bottom={<ChannelMessageForm channel={channel} p="2" />}
>
<ChannelChatLog channel={channel} />
<TimelineActionAndStatus loader={loader} />
</ContainedSimpleView>
<Flex direction="column-reverse" p="4" gap={2} flexGrow={1} h={0} overflowX="hidden" overflowY="auto">
<ChannelChatLog channel={channel} />
<TimelineActionAndStatus loader={loader} />
</Flex>
<ChannelMessageForm channel={channel} px="2" pb="2" />
</SimpleView>
{drawer.isOpen && <ChannelMetadataDrawer isOpen onClose={drawer.onClose} channel={channel} size="lg" />}
</IntersectionObserverProvider>
</ThreadsProvider>

View File

@ -24,7 +24,7 @@ export default function ChannelCard({
if (!channel || !metadata) return null;
return (
<Card as={LinkBox} flexDirection="row" gap="2" overflow="hidden" alignItems="flex-start" ref={ref} {...props}>
<Card as={LinkBox} flexDirection="row" gap="2" alignItems="flex-start" ref={ref} {...props}>
<ChannelImage channel={channel} w="5rem" flexShrink={0} />
<Flex direction="column" flex={1} overflow="hidden" h="full">
<CardHeader p="2" display="flex" gap="2" alignItems="center">

View File

@ -3,10 +3,10 @@ import { Alert, AlertDescription, AlertIcon, AlertTitle, Box, Button, Link } fro
import { ErrorBoundary } from "../../components/error-boundary";
import ChannelCard from "./components/channel-card";
import { useReadRelays } from "../../hooks/use-client-relays";
import ContainedParentView from "../../components/layout/presets/contained-parent-view";
import useUserChannelsList from "../../hooks/use-user-channels-list";
import useSingleEvents from "../../hooks/use-single-events";
import RouterLink from "../../components/router-link";
import SimpleParentView from "../../components/layout/presets/simple-parent-view";
export default function ChannelsHomeView() {
const relays = useReadRelays();
@ -14,7 +14,7 @@ export default function ChannelsHomeView() {
const channels = useSingleEvents(pointers.map((p) => p.id));
return (
<ContainedParentView
<SimpleParentView
title="Public channels"
path="/channels"
width="sm"
@ -24,7 +24,7 @@ export default function ChannelsHomeView() {
</Button>
}
>
<Alert status="info">
<Alert status="info" flexShrink={0}>
<AlertIcon />
<Box>
<AlertTitle>Deprecated</AlertTitle>
@ -60,6 +60,6 @@ export default function ChannelsHomeView() {
</Button>
</Alert>
)}
</ContainedParentView>
</SimpleParentView>
);
}

View File

@ -24,6 +24,7 @@ import {
import { ChevronLeftIcon } from "@chakra-ui/icons";
import dayjs from "dayjs";
import { useNavigate } from "react-router-dom";
import { getCoordinateFromAddressPointer } from "applesauce-core/helpers";
import {
DVM_CONTENT_DISCOVERY_JOB_KIND,
@ -47,7 +48,6 @@ import useParamsAddressPointer from "../../../hooks/use-params-address-pointer";
import DVMParams from "./components/dvm-params";
import { useUserOutbox } from "../../../hooks/use-user-mailboxes";
import { usePublishEvent } from "../../../providers/global/publish-provider";
import { getHumanReadableCoordinate } from "../../../services/replaceable-loader";
import Timestamp from "../../../components/timestamp";
function DVMFeedPage({ pointer }: { pointer: AddressPointer }) {
@ -59,16 +59,12 @@ function DVMFeedPage({ pointer }: { pointer: AddressPointer }) {
const dvmRelays = useUserOutbox(pointer.pubkey);
const readRelays = useReadRelays(dvmRelays);
const { loader, timeline } = useTimelineLoader(
`${getHumanReadableCoordinate(pointer.kind, pointer.pubkey, pointer.identifier)}-jobs`,
readRelays,
{
authors: [account.pubkey, pointer.pubkey],
"#p": [account.pubkey, pointer.pubkey],
kinds: [DVM_CONTENT_DISCOVERY_JOB_KIND, DVM_CONTENT_DISCOVERY_RESULT_KIND, DVM_STATUS_KIND],
since,
},
);
const { loader, timeline } = useTimelineLoader(`${getCoordinateFromAddressPointer(pointer)}-jobs`, readRelays, {
authors: [account.pubkey, pointer.pubkey],
"#p": [account.pubkey, pointer.pubkey],
kinds: [DVM_CONTENT_DISCOVERY_JOB_KIND, DVM_CONTENT_DISCOVERY_RESULT_KIND, DVM_STATUS_KIND],
since,
});
const jobs = groupEventsIntoJobs(timeline);
const pages = chainJobs(Array.from(Object.values(jobs)));

View File

@ -6,7 +6,6 @@ import { getEventUID } from "nostr-idb";
import { useThrottle } from "react-use";
import { createRxForwardReq } from "rx-nostr";
import BackButton from "../../../components/router/back-button";
import RelayList from "./components/relay-list";
import useRouteStateValue from "../../../hooks/use-route-state-value";
import RelayMap from "./components/relay-map";
@ -15,7 +14,7 @@ import { SelectedContext } from "./selected-context";
import CountyPicker from "../../../components/county-picker";
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
import rxNostr from "../../../services/rx-nostr";
import ContainedSimpleView from "../../../components/layout/presets/contained-simple-view";
import SimpleView from "../../../components/layout/presets/simple-view";
export default function RelayDiscoveryView() {
const showMap = useBreakpointValue({ base: false, lg: true });
@ -82,9 +81,10 @@ export default function RelayDiscoveryView() {
return (
<SelectedContext.Provider value={selected}>
<ContainedSimpleView
<SimpleView
title="Relays"
flush
scroll={false}
actions={
<>
<Select value={network} onChange={(e) => setNetwork(e.target.value)} w="auto">
@ -106,7 +106,7 @@ export default function RelayDiscoveryView() {
)}
{showMap && <RelayMap events={eventsThrottle} />}
</Flex>
</ContainedSimpleView>
</SimpleView>
</SelectedContext.Provider>
);
}

View File

@ -1,17 +1,16 @@
import { Alert, AlertDescription, AlertIcon, AlertTitle, Button } from "@chakra-ui/react";
import { ErrorBoundary } from "../../components/error-boundary";
import ContainedParentView from "../../components/layout/presets/contained-parent-view";
import RouterLink from "../../components/router-link";
import useUserGroupsList from "../../hooks/use-user-groups-list";
import { encodeGroupPointer } from "applesauce-core/helpers/groups";
import SimpleNavItem from "../../components/layout/presets/simple-nav-item";
import SimpleParentView from "../../components/layout/presets/simple-parent-view";
export default function ChannelsHomeView() {
const { pointers } = useUserGroupsList();
return (
<ContainedParentView
<SimpleParentView
title="Groups"
path="/groups"
width="sm"
@ -46,6 +45,6 @@ export default function ChannelsHomeView() {
</Button>
</Alert>
)}
</ContainedParentView>
</SimpleParentView>
);
}

View File

@ -26,6 +26,7 @@ import UserDnsIdentityIcon from "../../components/user/user-dns-identity-icon";
import UserAvatarLink from "../../components/user/user-avatar-link";
import ContainedSimpleView from "../../components/layout/presets/contained-simple-view";
import { mergeRelaySets } from "../../helpers/relay";
import SimpleView from "../../components/layout/presets/simple-view";
/** This is broken out from DirectMessageChatPage for performance reasons. Don't use outside of file */
const ChatLog = memo(({ messages }: { messages: NostrEvent[] }) => {
@ -114,8 +115,7 @@ function DirectMessageChatPage({ pubkey }: { pubkey: string }) {
return (
<ThreadsProvider messages={messages}>
<IntersectionObserverProvider callback={callback}>
<ContainedSimpleView
reverse
<SimpleView
title={
<Flex gap="2" alignItems="center">
<UserAvatarLink pubkey={pubkey} size="sm" />
@ -138,14 +138,20 @@ function DirectMessageChatPage({ pubkey }: { pubkey: string }) {
/>
</ButtonGroup>
}
bottom={<SendMessageForm flexShrink={0} pubkey={pubkey} p="2" />}
scroll={false}
flush
>
<ChatLog messages={messages} />
<TimelineActionAndStatus loader={loader} />
<Flex direction="column-reverse" p="2" gap="2" flexGrow={1} h={0} overflowX="hidden" overflowY="auto">
<ChatLog messages={messages} />
<TimelineActionAndStatus loader={loader} />
</Flex>
<SendMessageForm flexShrink={0} pubkey={pubkey} px="2" pb="2" />
{location.state?.thread && (
<ThreadDrawer isOpen onClose={closeDrawer} threadId={location.state.thread} pubkey={pubkey} />
)}
</ContainedSimpleView>
</SimpleView>
</IntersectionObserverProvider>
</ThreadsProvider>
);

View File

@ -1,29 +1,30 @@
import { useCallback, useMemo } from "react";
import { Card, CardBody, Flex, LinkBox, LinkOverlay, Text } from "@chakra-ui/react";
import { useCallback, useMemo, useState } from "react";
import { Button, ButtonGroup, Flex, LinkBox, LinkOverlay, Text } from "@chakra-ui/react";
import { Link as RouterLink, useLocation } from "react-router-dom";
import { kinds, nip19 } from "nostr-tools";
import { FixedSizeList, ListChildComponentProps } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import UserAvatar from "../../components/user/user-avatar";
import RequireActiveAccount from "../../components/router/require-active-account";
import Timestamp from "../../components/timestamp";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import { useActiveAccount } from "applesauce-react/hooks";
import { KnownConversation, groupIntoConversations, hasResponded, identifyConversation } from "../../helpers/nostr/dms";
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 UserName from "../../components/user/user-name";
import { NostrEvent } from "../../types/nostr-event";
import { CheckIcon } from "../../components/icons";
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";
import useUserContacts from "../../hooks/use-user-contacts";
import useUserMutes from "../../hooks/use-user-mutes";
import SimpleParentView from "../../components/layout/presets/simple-parent-view";
export function useDirectMessagesTimeline(pubkey?: string) {
const userMuteFilter = useClientSideMuteFilter();
@ -60,7 +61,9 @@ function MessagePreview({ message, pubkey }: { message: NostrEvent; pubkey: stri
);
}
function ConversationCard({ conversation }: { conversation: KnownConversation }) {
function ConversationCard({ index, style, data }: ListChildComponentProps<KnownConversation[]>) {
const conversation = data[index];
const location = useLocation();
const lastReceived = conversation.messages.find((m) => m.pubkey === conversation.correspondent);
const lastMessage = conversation.messages[0];
@ -68,62 +71,88 @@ function ConversationCard({ conversation }: { conversation: KnownConversation })
const ref = useEventIntersectionRef(lastMessage);
return (
<LinkBox as={Card} size="sm" ref={ref}>
<CardBody display="flex" gap="2" overflow="hidden">
<UserAvatar pubkey={conversation.correspondent} />
<Flex direction="column" gap="1" overflow="hidden" flex={1}>
<Flex gap="2" alignItems="center" overflow="hidden">
<UserName pubkey={conversation.correspondent} isTruncated />
<UserDnsIdentity onlyIcon pubkey={conversation.correspondent} />
<Timestamp flexShrink={0} timestamp={lastMessage.created_at} ml="auto" />
{hasResponded(conversation) && <CheckIcon boxSize={4} color="green.500" />}
</Flex>
{lastReceived && <MessagePreview message={lastReceived} pubkey={lastReceived.pubkey} />}
<LinkBox as={Flex} ref={ref} style={style} gap="2" overflow="hidden" px="2">
<UserAvatar pubkey={conversation.correspondent} />
<Flex direction="column" gap="1" overflow="hidden" flex={1}>
<Flex gap="2" alignItems="center" overflow="hidden">
<UserName pubkey={conversation.correspondent} isTruncated />
<UserDnsIdentity onlyIcon pubkey={conversation.correspondent} />
<Timestamp flexShrink={0} timestamp={lastMessage.created_at} ml="auto" />
{hasResponded(conversation) && <CheckIcon boxSize={4} color="green.500" />}
</Flex>
</CardBody>
{lastReceived && <MessagePreview message={lastReceived} pubkey={lastReceived.pubkey} />}
</Flex>
<LinkOverlay as={RouterLink} to={`/messages/${nip19.npubEncode(conversation.correspondent)}` + location.search} />
</LinkBox>
);
}
function MessagesHomePage() {
const { people } = usePeopleListContext();
const [filter, setFilter] = useState<"contacts" | "other" | "muted">("contacts");
const account = useActiveAccount()!;
const contacts = useUserContacts(account?.pubkey, undefined, true)?.map((p) => p.pubkey);
const mutes = useUserMutes(account.pubkey, undefined, true);
const { timeline: messages, loader } = useDirectMessagesTimeline(account.pubkey);
const conversations = useMemo(() => {
const conversations = groupIntoConversations(messages).map((c) => identifyConversation(c, account.pubkey));
const filtered = conversations.filter((conversation) =>
people ? people.some((p) => p.pubkey === conversation.correspondent) : true,
);
let filtered = conversations;
switch (filter) {
case "contacts":
filtered = conversations.filter((c) => contacts?.includes(c.correspondent));
break;
case "muted":
filtered = conversations.filter((c) => mutes?.pubkeys?.has(c.correspondent));
break;
}
return filtered.sort((a, b) => b.messages[0].created_at - a.messages[0].created_at);
}, [messages, people, account.pubkey]);
}, [messages, account.pubkey, contacts?.length, filter, mutes?.pubkeys]);
const callback = useTimelineCurserIntersectionCallback(loader);
return (
<ContainedParentView path="/messages" width="md">
<Flex gap="2">
<PeopleListSelection flexShrink={0} size="sm" />
</Flex>
<SimpleParentView path="/messages" width="md" title="Messages" scroll={false}>
<ButtonGroup p="2" size="sm" variant="outline">
<Button onClick={() => setFilter("contacts")} variant={filter === "contacts" ? "solid" : "outline"}>
Contacts
</Button>
<Button onClick={() => setFilter("other")} variant={filter === "other" ? "solid" : "outline"}>
Other
</Button>
<Button onClick={() => setFilter("muted")} variant={filter === "muted" ? "solid" : "outline"}>
Muted
</Button>
</ButtonGroup>
<IntersectionObserverProvider callback={callback}>
{conversations.map((conversation) => (
<ConversationCard key={conversation.pubkeys.join("-")} conversation={conversation} />
))}
<Flex flex={1} overflow="hidden" position="relative">
<AutoSizer>
{({ width, height }) => (
<FixedSizeList
height={height}
width={width}
itemData={conversations ?? []}
itemCount={conversations?.length ?? 0}
itemKey={(i, data) => data[i].myself + data[i].correspondent}
itemSize={64}
>
{ConversationCard}
</FixedSizeList>
)}
</AutoSizer>
</Flex>
</IntersectionObserverProvider>
<TimelineActionAndStatus loader={loader} />
</ContainedParentView>
</SimpleParentView>
);
}
export default function MessagesHomeView() {
return (
<RequireActiveAccount>
<PeopleListProvider initList="global">
<MessagesHomePage />
</PeopleListProvider>
<MessagesHomePage />
</RequireActiveAccount>
);
}

View File

@ -19,6 +19,7 @@ import { useReadRelays } from "../../hooks/use-client-relays";
import { AdditionalRelayProvider, useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
import useFavoriteStreams from "../../hooks/use-favorite-streams";
import { getStreamStatus, getStreamStreamingURLs } from "../../helpers/nostr/stream";
import SimpleView from "../../components/layout/presets/simple-view";
function StreamsPage() {
useAppTitle("Streams");
@ -61,13 +62,17 @@ function StreamsPage() {
const columns = { base: 1, md: 2, lg: 3, xl: 4, "2xl": 5 };
return (
<VerticalPageLayout>
<Flex gap="2" wrap="wrap" alignItems="center">
<PeopleListSelection />
<Switch isChecked={showEnded.isOpen} onChange={showEnded.onToggle}>
Show Ended
</Switch>
</Flex>
<SimpleView
title="Streams"
actions={
<Flex gap="2" wrap="wrap" alignItems="center">
<PeopleListSelection size="sm" />
<Switch isChecked={showEnded.isOpen} onChange={showEnded.onToggle}>
Show Ended
</Switch>
</Flex>
}
>
<IntersectionObserverProvider callback={callback}>
{favorites.length > 0 && (
<>
@ -103,7 +108,7 @@ function StreamsPage() {
)}
<TimelineActionAndStatus loader={loader} />
</IntersectionObserverProvider>
</VerticalPageLayout>
</SimpleView>
);
}
export default function StreamHomeView() {

View File

@ -68,7 +68,7 @@ export default function SupportView() {
// close the pay request when new zap is received
useEffect(() => {
if (request) {
const sub = eventStore.stream([{ kinds: [kinds.Zap], since: unixNow() }]).subscribe((event) => {
const sub = eventStore.filters([{ kinds: [kinds.Zap], since: unixNow() }]).subscribe((event) => {
try {
const bont11 = getTagValue(event, "bolt11");

View File

@ -134,15 +134,7 @@ function ThreadPost({ post, initShowReplies, focusId, level = -1 }: ThreadItemPr
return (
<>
<Flex
direction="column"
gap="2"
p="2"
borderRadius="md"
borderWidth=".1rem .1rem .1rem .35rem"
{...colorProps}
ref={ref}
>
<Flex direction="column" gap="2" px="2" py="0" borderWidth="0 1px 0 .35rem" {...colorProps} ref={ref}>
{header}
{expanded.isOpen && (
<>

View File

@ -38,6 +38,7 @@ import RequireActiveAccount from "../../../components/router/require-active-acco
import VariableEditor from "./components/variable-editor";
import EventTemplateEditor from "./components/event-template-editor";
import useRouteStateValue from "../../../hooks/use-route-state-value";
import SimpleView from "../../../components/layout/presets/simple-view";
function EventPublisherPage({ initDraft }: { initDraft?: LooseEventTemplate }) {
const toast = useToast();
@ -129,81 +130,78 @@ function EventPublisherPage({ initDraft }: { initDraft?: LooseEventTemplate }) {
};
return (
<>
<VerticalPageLayout>
<Flex gap="2" alignItems="center" wrap="wrap">
<BackButton />
<Heading size="md">Event Publisher</Heading>
<Switch size="sm" isChecked={customRelay.isOpen} onChange={customRelay.onToggle}>
Publish to Relay
</Switch>
{customRelay.isOpen && (
<RelayUrlInput
borderRadius="md"
w="xs"
value={customRelayURL}
onChange={(e) => setCustomRelayURL(e.target.value)}
/>
)}
<ButtonGroup ml="auto">
<Button colorScheme="primary" onClick={submitEvent} leftIcon={<Play />}>
Publish
</Button>
</ButtonGroup>
</Flex>
<SimpleView title="Event Publisher">
<Flex gap="2" alignItems="center" wrap="wrap">
<Switch size="sm" isChecked={customRelay.isOpen} onChange={customRelay.onToggle}>
Publish to Relay
</Switch>
{customRelay.isOpen && (
<RelayUrlInput
borderRadius="md"
w="xs"
value={customRelayURL}
onChange={(e) => setCustomRelayURL(e.target.value)}
/>
)}
<ButtonGroup ml="auto">
<Button colorScheme="primary" onClick={submitEvent} leftIcon={<Play />}>
Publish
</Button>
</ButtonGroup>
</Flex>
<Flex direction={{ base: "column", xl: "row" }} gap="2">
<Flex direction="column" gap="2" flex={1} overflow="hidden">
<Flex gap="2" alignItems="center">
<Text fontWeight="bold">Template</Text>
<Select onChange={(e) => selectTemplate(e.target.value)} w="auto">
{TEMPLATES.map((template) => (
<option key={template.name} value={template.name}>
{template.name}
</option>
))}
</Select>
<Spacer />
<ButtonGroup size="sm">
{editor.isOpen && (
<Button onClick={editRaw.onToggle} colorScheme={editRaw.isOpen ? "primary" : undefined}>
Raw
</Button>
)}
<Button
onClick={editor.onToggle}
colorScheme={editor.isOpen ? "primary" : undefined}
leftIcon={<EditIcon />}
>
Edit
</Button>
</ButtonGroup>
</Flex>
{editor.isOpen &&
(editRaw.isOpen ? (
<EventJsonEditor draft={draft} onChange={setDraft} onRun={submitEvent} />
) : (
<EventTemplateEditor draft={draft} onChange={setDraft} />
<Flex direction={{ base: "column", xl: "row" }} gap="2">
<Flex direction="column" gap="2" flex={1} overflow="hidden">
<Flex gap="2" alignItems="center">
<Text fontWeight="bold">Template</Text>
<Select onChange={(e) => selectTemplate(e.target.value)} w="auto">
{TEMPLATES.map((template) => (
<option key={template.name} value={template.name}>
{template.name}
</option>
))}
<Flex gap="2">
<Heading size="md" mt="4">
Variables
</Heading>
</Flex>
<VariableEditor variables={variables} onChange={(v) => setVariables(v)} />
</Select>
<Spacer />
<ButtonGroup size="sm">
{editor.isOpen && (
<Button onClick={editRaw.onToggle} colorScheme={editRaw.isOpen ? "primary" : undefined}>
Raw
</Button>
)}
<Button
onClick={editor.onToggle}
colorScheme={editor.isOpen ? "primary" : undefined}
leftIcon={<EditIcon />}
>
Edit
</Button>
</ButtonGroup>
</Flex>
<Divider hideFrom="xl" />
{editor.isOpen &&
(editRaw.isOpen ? (
<EventJsonEditor draft={draft} onChange={setDraft} onRun={submitEvent} />
) : (
<EventTemplateEditor draft={draft} onChange={setDraft} />
))}
<Flex flex={1} direction="column" gap="2" overflow="hidden">
<Code whiteSpace="pre" overflow="auto" p="2">
{JSON.stringify(processed, null, 2)}
</Code>
<Flex gap="2">
<Heading size="md" mt="4">
Variables
</Heading>
</Flex>
<VariableEditor variables={variables} onChange={(v) => setVariables(v)} />
</Flex>
</VerticalPageLayout>
<Divider hideFrom="xl" />
<Flex flex={1} direction="column" gap="2" overflow="hidden">
<Code whiteSpace="pre" overflow="auto" p="2">
{JSON.stringify(processed, null, 2)}
</Code>
</Flex>
</Flex>
{finalized && (
<Modal isOpen onClose={() => setFinalized(undefined)} size="2xl">
<ModalOverlay />
@ -253,7 +251,7 @@ function EventPublisherPage({ initDraft }: { initDraft?: LooseEventTemplate }) {
</ModalContent>
</Modal>
)}
</>
</SimpleView>
);
}