refactor channel
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react": "^0.23.1",
|
"@floating-ui/react": "^0.23.1",
|
||||||
"@headlessui/react": "^1.7.15",
|
"@headlessui/react": "^1.7.15",
|
||||||
"@nostr-dev-kit/ndk": "^0.5.2",
|
"@nostr-dev-kit/ndk": "^0.5.3",
|
||||||
"@tanstack/react-virtual": "3.0.0-beta.54",
|
"@tanstack/react-virtual": "3.0.0-beta.54",
|
||||||
"@tauri-apps/api": "^1.4.0",
|
"@tauri-apps/api": "^1.4.0",
|
||||||
"@vidstack/react": "^0.4.5",
|
"@vidstack/react": "^0.4.5",
|
||||||
|
|||||||
98
pnpm-lock.yaml
generated
98
pnpm-lock.yaml
generated
@@ -8,8 +8,8 @@ dependencies:
|
|||||||
specifier: ^1.7.15
|
specifier: ^1.7.15
|
||||||
version: 1.7.15(react-dom@18.2.0)(react@18.2.0)
|
version: 1.7.15(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@nostr-dev-kit/ndk':
|
'@nostr-dev-kit/ndk':
|
||||||
specifier: ^0.5.2
|
specifier: ^0.5.3
|
||||||
version: 0.5.2(typescript@4.9.5)
|
version: 0.5.3(typescript@4.9.5)
|
||||||
'@tanstack/react-virtual':
|
'@tanstack/react-virtual':
|
||||||
specifier: 3.0.0-beta.54
|
specifier: 3.0.0-beta.54
|
||||||
version: 3.0.0-beta.54(react@18.2.0)
|
version: 3.0.0-beta.54(react@18.2.0)
|
||||||
@@ -370,13 +370,13 @@ packages:
|
|||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@eslint-community/eslint-utils@4.4.0(eslint@8.42.0):
|
/@eslint-community/eslint-utils@4.4.0(eslint@8.43.0):
|
||||||
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
|
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.42.0
|
eslint: 8.43.0
|
||||||
eslint-visitor-keys: 3.4.1
|
eslint-visitor-keys: 3.4.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -402,8 +402,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@eslint/js@8.42.0:
|
/@eslint/js@8.43.0:
|
||||||
resolution: {integrity: sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==}
|
resolution: {integrity: sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -571,20 +571,20 @@ packages:
|
|||||||
'@nodelib/fs.scandir': 2.1.5
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
fastq: 1.15.0
|
fastq: 1.15.0
|
||||||
|
|
||||||
/@nostr-dev-kit/ndk@0.5.2(typescript@4.9.5):
|
/@nostr-dev-kit/ndk@0.5.3(typescript@4.9.5):
|
||||||
resolution: {integrity: sha512-ZTcEGHxe/Yoazr2SHB+6wQ/1ZkLvaJVdEFNOwg2Ll1lD6C7ttnP/rPD1ex17bQFaU//4yvsdgEREDJqo+mLedw==}
|
resolution: {integrity: sha512-GLmuAoor4oMxxKjFeZ4viHR9XEI61m0wBm78vTUzY1Ev+bBdDzorv6heBz7TWmQirtoJ32r/zIgWdzHsHC6h3A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.3.1
|
'@noble/hashes': 1.3.1
|
||||||
'@noble/secp256k1': 2.0.0
|
'@noble/secp256k1': 2.0.0
|
||||||
'@scure/base': 1.1.1
|
'@scure/base': 1.1.1
|
||||||
'@typescript-eslint/eslint-plugin': 5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.42.0)(typescript@4.9.5)
|
'@typescript-eslint/eslint-plugin': 5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.43.0)(typescript@4.9.5)
|
||||||
'@typescript-eslint/parser': 5.59.11(eslint@8.42.0)(typescript@4.9.5)
|
'@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@4.9.5)
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
esbuild: 0.17.19
|
esbuild: 0.17.19
|
||||||
esbuild-plugin-alias: 0.2.1
|
esbuild-plugin-alias: 0.2.1
|
||||||
eslint: 8.42.0
|
eslint: 8.43.0
|
||||||
eslint-config-prettier: 8.8.0(eslint@8.42.0)
|
eslint-config-prettier: 8.8.0(eslint@8.43.0)
|
||||||
eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.11)(eslint@8.42.0)
|
eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.11)(eslint@8.43.0)
|
||||||
esm-loader-typescript: 1.0.4
|
esm-loader-typescript: 1.0.4
|
||||||
eventemitter3: 5.0.1
|
eventemitter3: 5.0.1
|
||||||
light-bolt11-decoder: 3.0.0
|
light-bolt11-decoder: 3.0.0
|
||||||
@@ -1023,7 +1023,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-W8F4eoTIvzXeNrT3JroQPimZLXnlJA8smYygHZUKFPVoYwgs/OhJkA1VBhL3iSs57OQkuINqHlY4SmMT5wtnJg==}
|
resolution: {integrity: sha512-W8F4eoTIvzXeNrT3JroQPimZLXnlJA8smYygHZUKFPVoYwgs/OhJkA1VBhL3iSs57OQkuINqHlY4SmMT5wtnJg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/eslint-plugin@5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.42.0)(typescript@4.9.5):
|
/@typescript-eslint/eslint-plugin@5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.43.0)(typescript@4.9.5):
|
||||||
resolution: {integrity: sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==}
|
resolution: {integrity: sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1035,12 +1035,12 @@ packages:
|
|||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.5.1
|
'@eslint-community/regexpp': 4.5.1
|
||||||
'@typescript-eslint/parser': 5.59.11(eslint@8.42.0)(typescript@4.9.5)
|
'@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@4.9.5)
|
||||||
'@typescript-eslint/scope-manager': 5.59.11
|
'@typescript-eslint/scope-manager': 5.59.11
|
||||||
'@typescript-eslint/type-utils': 5.59.11(eslint@8.42.0)(typescript@4.9.5)
|
'@typescript-eslint/type-utils': 5.59.11(eslint@8.43.0)(typescript@4.9.5)
|
||||||
'@typescript-eslint/utils': 5.59.11(eslint@8.42.0)(typescript@4.9.5)
|
'@typescript-eslint/utils': 5.59.11(eslint@8.43.0)(typescript@4.9.5)
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.42.0
|
eslint: 8.43.0
|
||||||
grapheme-splitter: 1.0.4
|
grapheme-splitter: 1.0.4
|
||||||
ignore: 5.2.4
|
ignore: 5.2.4
|
||||||
natural-compare-lite: 1.4.0
|
natural-compare-lite: 1.4.0
|
||||||
@@ -1051,7 +1051,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@typescript-eslint/parser@5.59.11(eslint@8.42.0)(typescript@4.9.5):
|
/@typescript-eslint/parser@5.59.11(eslint@8.43.0)(typescript@4.9.5):
|
||||||
resolution: {integrity: sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==}
|
resolution: {integrity: sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1065,7 +1065,7 @@ packages:
|
|||||||
'@typescript-eslint/types': 5.59.11
|
'@typescript-eslint/types': 5.59.11
|
||||||
'@typescript-eslint/typescript-estree': 5.59.11(typescript@4.9.5)
|
'@typescript-eslint/typescript-estree': 5.59.11(typescript@4.9.5)
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.42.0
|
eslint: 8.43.0
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -1079,7 +1079,7 @@ packages:
|
|||||||
'@typescript-eslint/visitor-keys': 5.59.11
|
'@typescript-eslint/visitor-keys': 5.59.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@typescript-eslint/type-utils@5.59.11(eslint@8.42.0)(typescript@4.9.5):
|
/@typescript-eslint/type-utils@5.59.11(eslint@8.43.0)(typescript@4.9.5):
|
||||||
resolution: {integrity: sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==}
|
resolution: {integrity: sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1090,9 +1090,9 @@ packages:
|
|||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/typescript-estree': 5.59.11(typescript@4.9.5)
|
'@typescript-eslint/typescript-estree': 5.59.11(typescript@4.9.5)
|
||||||
'@typescript-eslint/utils': 5.59.11(eslint@8.42.0)(typescript@4.9.5)
|
'@typescript-eslint/utils': 5.59.11(eslint@8.43.0)(typescript@4.9.5)
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.42.0
|
eslint: 8.43.0
|
||||||
tsutils: 3.21.0(typescript@4.9.5)
|
tsutils: 3.21.0(typescript@4.9.5)
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -1125,19 +1125,19 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@typescript-eslint/utils@5.59.11(eslint@8.42.0)(typescript@4.9.5):
|
/@typescript-eslint/utils@5.59.11(eslint@8.43.0)(typescript@4.9.5):
|
||||||
resolution: {integrity: sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==}
|
resolution: {integrity: sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.42.0)
|
'@eslint-community/eslint-utils': 4.4.0(eslint@8.43.0)
|
||||||
'@types/json-schema': 7.0.12
|
'@types/json-schema': 7.0.12
|
||||||
'@types/semver': 7.5.0
|
'@types/semver': 7.5.0
|
||||||
'@typescript-eslint/scope-manager': 5.59.11
|
'@typescript-eslint/scope-manager': 5.59.11
|
||||||
'@typescript-eslint/types': 5.59.11
|
'@typescript-eslint/types': 5.59.11
|
||||||
'@typescript-eslint/typescript-estree': 5.59.11(typescript@4.9.5)
|
'@typescript-eslint/typescript-estree': 5.59.11(typescript@4.9.5)
|
||||||
eslint: 8.42.0
|
eslint: 8.43.0
|
||||||
eslint-scope: 5.1.1
|
eslint-scope: 5.1.1
|
||||||
semver: 7.5.2
|
semver: 7.5.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -1185,16 +1185,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/acorn-jsx@5.3.2(acorn@8.8.2):
|
/acorn-jsx@5.3.2(acorn@8.9.0):
|
||||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.8.2
|
acorn: 8.9.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/acorn@8.8.2:
|
/acorn@8.9.0:
|
||||||
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
|
resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -1415,7 +1415,7 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001503
|
caniuse-lite: 1.0.30001503
|
||||||
electron-to-chromium: 1.4.431
|
electron-to-chromium: 1.4.433
|
||||||
node-releases: 2.0.12
|
node-releases: 2.0.12
|
||||||
update-browserslist-db: 1.0.11(browserslist@4.21.9)
|
update-browserslist-db: 1.0.11(browserslist@4.21.9)
|
||||||
dev: true
|
dev: true
|
||||||
@@ -1780,8 +1780,8 @@ packages:
|
|||||||
/eastasianwidth@0.2.0:
|
/eastasianwidth@0.2.0:
|
||||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||||
|
|
||||||
/electron-to-chromium@1.4.431:
|
/electron-to-chromium@1.4.433:
|
||||||
resolution: {integrity: sha512-m232JTVmCawA2vG+1azVxhKZ9Sv1Q//xxNv5PkP5rWxGgQE8c3CiZFrh8Xnp+d1NmNxlu3QQrGIfdeW5TtXX5w==}
|
resolution: {integrity: sha512-MGO1k0w1RgrfdbLVwmXcDhHHuxCn2qRgR7dYsJvWFKDttvYPx6FNzCGG0c/fBBvzK2LDh3UV7Tt9awnHnvAAUQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/emoji-regex@8.0.0:
|
/emoji-regex@8.0.0:
|
||||||
@@ -1951,13 +1951,13 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/eslint-config-prettier@8.8.0(eslint@8.42.0):
|
/eslint-config-prettier@8.8.0(eslint@8.43.0):
|
||||||
resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==}
|
resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=7.0.0'
|
eslint: '>=7.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.42.0
|
eslint: 8.43.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/eslint-formatter-pretty@4.1.0:
|
/eslint-formatter-pretty@4.1.0:
|
||||||
@@ -1984,7 +1984,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-node@0.3.7)(eslint@8.42.0):
|
/eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-node@0.3.7)(eslint@8.43.0):
|
||||||
resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
|
resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2005,15 +2005,15 @@ packages:
|
|||||||
eslint-import-resolver-webpack:
|
eslint-import-resolver-webpack:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.59.11(eslint@8.42.0)(typescript@4.9.5)
|
'@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@4.9.5)
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
eslint: 8.42.0
|
eslint: 8.43.0
|
||||||
eslint-import-resolver-node: 0.3.7
|
eslint-import-resolver-node: 0.3.7
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.11)(eslint@8.42.0):
|
/eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.11)(eslint@8.43.0):
|
||||||
resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==}
|
resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2023,15 +2023,15 @@ packages:
|
|||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.59.11(eslint@8.42.0)(typescript@4.9.5)
|
'@typescript-eslint/parser': 5.59.11(eslint@8.43.0)(typescript@4.9.5)
|
||||||
array-includes: 3.1.6
|
array-includes: 3.1.6
|
||||||
array.prototype.flat: 1.3.1
|
array.prototype.flat: 1.3.1
|
||||||
array.prototype.flatmap: 1.3.1
|
array.prototype.flatmap: 1.3.1
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 8.42.0
|
eslint: 8.43.0
|
||||||
eslint-import-resolver-node: 0.3.7
|
eslint-import-resolver-node: 0.3.7
|
||||||
eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-node@0.3.7)(eslint@8.42.0)
|
eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-node@0.3.7)(eslint@8.43.0)
|
||||||
has: 1.0.3
|
has: 1.0.3
|
||||||
is-core-module: 2.12.1
|
is-core-module: 2.12.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -2071,15 +2071,15 @@ packages:
|
|||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/eslint@8.42.0:
|
/eslint@8.43.0:
|
||||||
resolution: {integrity: sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==}
|
resolution: {integrity: sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.42.0)
|
'@eslint-community/eslint-utils': 4.4.0(eslint@8.43.0)
|
||||||
'@eslint-community/regexpp': 4.5.1
|
'@eslint-community/regexpp': 4.5.1
|
||||||
'@eslint/eslintrc': 2.0.3
|
'@eslint/eslintrc': 2.0.3
|
||||||
'@eslint/js': 8.42.0
|
'@eslint/js': 8.43.0
|
||||||
'@humanwhocodes/config-array': 0.11.10
|
'@humanwhocodes/config-array': 0.11.10
|
||||||
'@humanwhocodes/module-importer': 1.0.1
|
'@humanwhocodes/module-importer': 1.0.1
|
||||||
'@nodelib/fs.walk': 1.2.8
|
'@nodelib/fs.walk': 1.2.8
|
||||||
@@ -2132,8 +2132,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==}
|
resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.8.2
|
acorn: 8.9.0
|
||||||
acorn-jsx: 5.3.2(acorn@8.8.2)
|
acorn-jsx: 5.3.2(acorn@8.9.0)
|
||||||
eslint-visitor-keys: 3.4.1
|
eslint-visitor-keys: 3.4.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -4686,7 +4686,7 @@ packages:
|
|||||||
'@brillout/json-serializer': 0.5.3
|
'@brillout/json-serializer': 0.5.3
|
||||||
'@brillout/picocolors': 1.0.4
|
'@brillout/picocolors': 1.0.4
|
||||||
'@brillout/vite-plugin-import-build': 0.2.18
|
'@brillout/vite-plugin-import-build': 0.2.18
|
||||||
acorn: 8.8.2
|
acorn: 8.9.0
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
es-module-lexer: 0.10.5
|
es-module-lexer: 0.10.5
|
||||||
esbuild: 0.17.19
|
esbuild: 0.17.19
|
||||||
|
|||||||
@@ -18,25 +18,6 @@ VALUES
|
|||||||
1681898574
|
1681898574
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT
|
|
||||||
OR IGNORE INTO channels (
|
|
||||||
event_id,
|
|
||||||
pubkey,
|
|
||||||
name,
|
|
||||||
about,
|
|
||||||
picture,
|
|
||||||
created_at
|
|
||||||
)
|
|
||||||
VALUES
|
|
||||||
(
|
|
||||||
"42224859763652914db53052103f0b744df79dfc4efef7e950fc0802fc3df3c5",
|
|
||||||
"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c",
|
|
||||||
"Amethyst Users",
|
|
||||||
"General discussion about the Amethyst Nostr client for Android",
|
|
||||||
"https://nostr.build/i/5970.png",
|
|
||||||
1674092111
|
|
||||||
);
|
|
||||||
|
|
||||||
INSERT
|
INSERT
|
||||||
OR IGNORE INTO channels (
|
OR IGNORE INTO channels (
|
||||||
event_id,
|
event_id,
|
||||||
|
|||||||
15
src-tauri/migrations/20230617003135_add_channel_messages.sql
Normal file
15
src-tauri/migrations/20230617003135_add_channel_messages.sql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-- Add migration script here
|
||||||
|
CREATE TABLE
|
||||||
|
channel_messages (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
channel_id TEXT NOT NULL,
|
||||||
|
event_id TEXT NOT NULL UNIQUE,
|
||||||
|
pubkey TEXT NOT NULL,
|
||||||
|
kind INTEGER NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
tags JSON,
|
||||||
|
mute BOOLEAN DEFAULT 0,
|
||||||
|
hide BOOLEAN DEFAULT 0,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (channel_id) REFERENCES channels (event_id)
|
||||||
|
);
|
||||||
@@ -93,6 +93,12 @@ fn main() {
|
|||||||
sql: include_str!("../migrations/20230521092300_add_block_model.sql"),
|
sql: include_str!("../migrations/20230521092300_add_block_model.sql"),
|
||||||
kind: MigrationKind::Up,
|
kind: MigrationKind::Up,
|
||||||
},
|
},
|
||||||
|
Migration {
|
||||||
|
version: 20230617003135,
|
||||||
|
description: "add channel messages",
|
||||||
|
sql: include_str!("../migrations/20230617003135_add_channel_messages.sql"),
|
||||||
|
kind: MigrationKind::Up,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.build(),
|
.build(),
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
import { ChannelMessageItem } from "@app/channel/components/messages/item";
|
|
||||||
import { useChannelMessages } from "@stores/channels";
|
|
||||||
import { getHourAgo } from "@utils/date";
|
|
||||||
import { useCallback, useRef } from "react";
|
|
||||||
import { Virtuoso } from "react-virtuoso";
|
|
||||||
|
|
||||||
export function ChannelMessageList() {
|
|
||||||
const now = useRef(new Date());
|
|
||||||
const virtuosoRef = useRef(null);
|
|
||||||
|
|
||||||
const messages = useChannelMessages((state: any) => state.messages);
|
|
||||||
|
|
||||||
const itemContent: any = useCallback(
|
|
||||||
(index: string | number) => {
|
|
||||||
return <ChannelMessageItem data={messages[index]} />;
|
|
||||||
},
|
|
||||||
[messages],
|
|
||||||
);
|
|
||||||
|
|
||||||
const computeItemKey = useCallback(
|
|
||||||
(index: string | number) => {
|
|
||||||
return messages[index].id;
|
|
||||||
},
|
|
||||||
[messages],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-full w-full">
|
|
||||||
<Virtuoso
|
|
||||||
ref={virtuosoRef}
|
|
||||||
data={[]}
|
|
||||||
itemContent={itemContent}
|
|
||||||
components={{
|
|
||||||
Header: () => (
|
|
||||||
<div className="relative py-4">
|
|
||||||
<div className="absolute inset-0 flex items-center">
|
|
||||||
<div className="w-full border-t border-zinc-800" />
|
|
||||||
</div>
|
|
||||||
<div className="relative flex justify-center">
|
|
||||||
<div className="inline-flex items-center gap-x-1.5 rounded-full bg-zinc-900 px-3 py-1.5 text-base font-medium text-zinc-400 shadow-sm ring-1 ring-inset ring-zinc-800">
|
|
||||||
{getHourAgo(24, now.current).toLocaleDateString("en-US", {
|
|
||||||
weekday: "long",
|
|
||||||
year: "numeric",
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
EmptyPlaceholder: () => (
|
|
||||||
<div className="flex flex-col gap-1 text-center">
|
|
||||||
<h3 className="text-base font-semibold leading-none text-white">
|
|
||||||
Nothing to see here yet
|
|
||||||
</h3>
|
|
||||||
<p className="text-base leading-none text-zinc-400">
|
|
||||||
Be the first to share a message in this channel.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
computeItemKey={computeItemKey}
|
|
||||||
initialTopMostItemIndex={messages.length - 1}
|
|
||||||
alignToBottom={true}
|
|
||||||
followOutput={true}
|
|
||||||
overscan={50}
|
|
||||||
increaseViewportBy={{ top: 200, bottom: 200 }}
|
|
||||||
className="scrollbar-hide h-full w-full overflow-y-auto"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { UserReply } from "@app/channel/components/messages/userReply";
|
import { UserReply } from "@app/channel/components/messages/userReply";
|
||||||
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
||||||
import { CancelIcon } from "@shared/icons";
|
import { CancelIcon, EnterIcon } from "@shared/icons";
|
||||||
|
import { MediaUploader } from "@shared/mediaUploader";
|
||||||
import { RelayContext } from "@shared/relayProvider";
|
import { RelayContext } from "@shared/relayProvider";
|
||||||
import { useActiveAccount } from "@stores/accounts";
|
import { useActiveAccount } from "@stores/accounts";
|
||||||
import { useChannelMessages } from "@stores/channels";
|
import { useChannelMessages } from "@stores/channels";
|
||||||
@@ -17,7 +18,7 @@ export function ChannelMessageForm({ channelID }: { channelID: string }) {
|
|||||||
state.closeReply,
|
state.closeReply,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const submitEvent = () => {
|
const submit = () => {
|
||||||
let tags: string[][];
|
let tags: string[][];
|
||||||
|
|
||||||
if (replyTo.id !== null) {
|
if (replyTo.id !== null) {
|
||||||
@@ -51,7 +52,7 @@ export function ChannelMessageForm({ channelID }: { channelID: string }) {
|
|||||||
const handleEnterPress = (e) => {
|
const handleEnterPress = (e) => {
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
submitEvent();
|
submit();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,11 +61,7 @@ export function ChannelMessageForm({ channelID }: { channelID: string }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={`relative w-full ${replyTo.id ? "h-36" : "h-24"}`}>
|
||||||
className={`relative ${
|
|
||||||
replyTo.id ? "h-36" : "h-24"
|
|
||||||
} w-full overflow-hidden before:pointer-events-none before:absolute before:-inset-1 before:rounded-[6px] before:border before:border-fuchsia-500 before:opacity-0 before:ring-2 before:ring-fuchsia-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-fuchsia-500/100 dark:focus-within:after:shadow-fuchsia-500/20`}
|
|
||||||
>
|
|
||||||
{replyTo.id && (
|
{replyTo.id && (
|
||||||
<div className="absolute left-0 top-0 z-10 h-16 w-full p-[2px]">
|
<div className="absolute left-0 top-0 z-10 h-16 w-full p-[2px]">
|
||||||
<div className="flex h-full w-full items-center justify-between rounded-t-md border-b border-zinc-700/70 bg-zinc-900 px-3">
|
<div className="flex h-full w-full items-center justify-between rounded-t-md border-b border-zinc-700/70 bg-zinc-900 px-3">
|
||||||
@@ -92,25 +89,21 @@ export function ChannelMessageForm({ channelID }: { channelID: string }) {
|
|||||||
placeholder="Message"
|
placeholder="Message"
|
||||||
className={`relative ${
|
className={`relative ${
|
||||||
replyTo.id ? "h-36 pt-16" : "h-24 pt-3"
|
replyTo.id ? "h-36 pt-16" : "h-24 pt-3"
|
||||||
} w-full resize-none rounded-lg border border-black/5 px-3.5 pb-3 text-base shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-white dark:shadow-black/10 dark:placeholder:text-zinc-500`}
|
} w-full resize-none rounded-md px-5 !outline-none bg-zinc-800 placeholder:text-zinc-500`}
|
||||||
/>
|
/>
|
||||||
<div className="absolute bottom-2 w-full px-2">
|
<div className="absolute right-2 bottom-0 h-11">
|
||||||
<div className="flex w-full items-center justify-between bg-zinc-800">
|
<div className="h-full flex gap-3 items-center justify-end text-zinc-500">
|
||||||
<div className="flex items-center gap-2 divide-x divide-zinc-700">
|
<MediaUploader setState={setValue} />
|
||||||
<div className="flex items-center gap-2 pl-2" />
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => submitEvent()}
|
onClick={submit}
|
||||||
disabled={value.length === 0 ? true : false}
|
className="inline-flex items-center gap-1 text-sm leading-none"
|
||||||
className="inline-flex h-8 w-16 items-center justify-center rounded-md bg-fuchsia-500 px-4 text-base font-medium shadow-button hover:bg-fuchsia-600 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
|
<EnterIcon width={14} height={14} className="" />
|
||||||
Send
|
Send
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export function MessageHideButton({ id }: { id: string }) {
|
|||||||
onClick={openModal}
|
onClick={openModal}
|
||||||
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-800"
|
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-800"
|
||||||
>
|
>
|
||||||
<HideIcon width={16} height={16} className="text-white" />
|
<HideIcon width={16} height={16} className="text-zinc-200" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
|
|||||||
@@ -2,22 +2,22 @@ import { MessageHideButton } from "@app/channel/components/messages/hideButton";
|
|||||||
import { MessageMuteButton } from "@app/channel/components/messages/muteButton";
|
import { MessageMuteButton } from "@app/channel/components/messages/muteButton";
|
||||||
import { MessageReplyButton } from "@app/channel/components/messages/replyButton";
|
import { MessageReplyButton } from "@app/channel/components/messages/replyButton";
|
||||||
import { ChannelMessageUser } from "@app/channel/components/messages/user";
|
import { ChannelMessageUser } from "@app/channel/components/messages/user";
|
||||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
||||||
import { MentionNote } from "@shared/notes/mentions/note";
|
import { MentionNote } from "@shared/notes/mentions/note";
|
||||||
import { ImagePreview } from "@shared/notes/preview/image";
|
import { ImagePreview } from "@shared/notes/preview/image";
|
||||||
|
import { LinkPreview } from "@shared/notes/preview/link";
|
||||||
import { VideoPreview } from "@shared/notes/preview/video";
|
import { VideoPreview } from "@shared/notes/preview/video";
|
||||||
import { parser } from "@utils/parser";
|
import { parser } from "@utils/parser";
|
||||||
import { useMemo } from "react";
|
import { LumeEvent } from "@utils/types";
|
||||||
|
|
||||||
export function ChannelMessageItem({ data }: { data: NDKEvent }) {
|
export function ChannelMessageItem({ data }: { data: LumeEvent }) {
|
||||||
const content = useMemo(() => parser(data), [data]);
|
const content = parser(data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group relative flex h-min min-h-min w-full select-text flex-col px-5 py-3 hover:bg-black/20">
|
<div className="group relative flex h-min min-h-min w-full select-text flex-col px-5 py-3 hover:bg-black/20">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<ChannelMessageUser pubkey={data.pubkey} time={data.created_at} />
|
<ChannelMessageUser pubkey={data.pubkey} time={data.created_at} />
|
||||||
<div className="-mt-[20px] pl-[49px]">
|
<div className="-mt-[20px] pl-[49px]">
|
||||||
<p className="whitespace-pre-line break-words text-base leading-tight">
|
<p className="select-text whitespace-pre-line break-words text-base text-zinc-100">
|
||||||
{content.parsed}
|
{content.parsed}
|
||||||
</p>
|
</p>
|
||||||
{Array.isArray(content.images) && content.images.length ? (
|
{Array.isArray(content.images) && content.images.length ? (
|
||||||
@@ -30,6 +30,11 @@ export function ChannelMessageItem({ data }: { data: NDKEvent }) {
|
|||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
{Array.isArray(content.links) && content.links.length ? (
|
||||||
|
<LinkPreview urls={content.links} />
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
{Array.isArray(content.notes) && content.notes.length ? (
|
{Array.isArray(content.notes) && content.notes.length ? (
|
||||||
content.notes.map((note: string) => (
|
content.notes.map((note: string) => (
|
||||||
<MentionNote key={note} id={note} />
|
<MentionNote key={note} id={note} />
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export function MessageMuteButton({ pubkey }: { pubkey: string }) {
|
|||||||
onClick={() => openModal()}
|
onClick={() => openModal()}
|
||||||
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-800"
|
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-800"
|
||||||
>
|
>
|
||||||
<MuteIcon width={16} height={16} className="text-white" />
|
<MuteIcon width={16} height={16} className="text-zinc-200" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function MessageReplyButton({
|
|||||||
onClick={() => createReply()}
|
onClick={() => createReply()}
|
||||||
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-800"
|
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-800"
|
||||||
>
|
>
|
||||||
<ReplyMessageIcon width={16} height={16} className="text-white" />
|
<ReplyMessageIcon width={16} height={16} className="text-zinc-200" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,95 +4,73 @@ import { ChannelMessageForm } from "@app/channel/components/messages/form";
|
|||||||
import { ChannelMetadata } from "@app/channel/components/metadata";
|
import { ChannelMetadata } from "@app/channel/components/metadata";
|
||||||
import { RelayContext } from "@shared/relayProvider";
|
import { RelayContext } from "@shared/relayProvider";
|
||||||
import { useChannelMessages } from "@stores/channels";
|
import { useChannelMessages } from "@stores/channels";
|
||||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
import { dateToUnix } from "@utils/date";
|
||||||
import { dateToUnix, getHourAgo } from "@utils/date";
|
|
||||||
import { usePageContext } from "@utils/hooks/usePageContext";
|
import { usePageContext } from "@utils/hooks/usePageContext";
|
||||||
|
import { LumeEvent } from "@utils/types";
|
||||||
import { useCallback, useContext, useEffect, useRef } from "react";
|
import { useCallback, useContext, useEffect, useRef } from "react";
|
||||||
|
import { Virtuoso } from "react-virtuoso";
|
||||||
import useSWRSubscription from "swr/subscription";
|
import useSWRSubscription from "swr/subscription";
|
||||||
|
|
||||||
const now = new Date();
|
|
||||||
const since = dateToUnix(getHourAgo(24, now));
|
|
||||||
|
|
||||||
export function Page() {
|
export function Page() {
|
||||||
const ndk = useContext(RelayContext);
|
const ndk = useContext(RelayContext);
|
||||||
const pageContext = usePageContext();
|
const pageContext = usePageContext();
|
||||||
|
const virtuosoRef = useRef(null);
|
||||||
|
|
||||||
const searchParams: any = pageContext.urlParsed.search;
|
const searchParams: any = pageContext.urlParsed.search;
|
||||||
const channelID = searchParams.id;
|
const channelID = searchParams.id;
|
||||||
|
|
||||||
const [messages, addMessage, fetchMessages, clearMessages]: any =
|
const [messages, fetchMessages, addMessage, clearMessages] =
|
||||||
useChannelMessages((state: any) => [
|
useChannelMessages((state: any) => [
|
||||||
state.messages,
|
state.messages,
|
||||||
state.addMessage,
|
|
||||||
state.fetch,
|
state.fetch,
|
||||||
|
state.add,
|
||||||
state.clear,
|
state.clear,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useSWRSubscription(["channelMessagesSubscribe", channelID], () => {
|
useSWRSubscription(
|
||||||
|
channelID ? ["channelMessagesSubscribe", channelID] : null,
|
||||||
|
() => {
|
||||||
// subscribe to channel
|
// subscribe to channel
|
||||||
const sub = ndk.subscribe({
|
const sub = ndk.subscribe(
|
||||||
|
{
|
||||||
"#e": [channelID],
|
"#e": [channelID],
|
||||||
kinds: [42],
|
kinds: [42],
|
||||||
since: dateToUnix(),
|
since: dateToUnix(),
|
||||||
});
|
},
|
||||||
|
{ closeOnEose: false },
|
||||||
|
);
|
||||||
|
|
||||||
sub.addListener("event", (event) => {
|
sub.addListener("event", (event: LumeEvent) => {
|
||||||
addMessage(event);
|
addMessage(channelID, event);
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
sub.stop();
|
sub.stop();
|
||||||
};
|
};
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchMessages(ndk, channelID, since);
|
fetchMessages(channelID);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearMessages();
|
clearMessages();
|
||||||
};
|
};
|
||||||
}, [fetchMessages]);
|
}, [fetchMessages]);
|
||||||
|
|
||||||
const count = messages.length;
|
const itemContent: any = useCallback(
|
||||||
const reverseIndex = useCallback((index) => count - 1 - index, [count]);
|
(index: string | number) => {
|
||||||
const parentRef = useRef();
|
return <ChannelMessageItem data={messages[index]} />;
|
||||||
const virtualizerRef = useRef(null);
|
},
|
||||||
|
[messages],
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
const computeItemKey = useCallback(
|
||||||
virtualizerRef.current &&
|
(index: string | number) => {
|
||||||
count !== virtualizerRef.current.options.count
|
return messages[index].event_id;
|
||||||
) {
|
},
|
||||||
const delta = count - virtualizerRef.current.options.count;
|
[messages],
|
||||||
const nextOffset = virtualizerRef.current.scrollOffset + delta * 200;
|
);
|
||||||
|
|
||||||
virtualizerRef.current.scrollOffset = nextOffset;
|
|
||||||
virtualizerRef.current.scrollToOffset(nextOffset, { align: "start" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const virtualizer = useVirtualizer({
|
|
||||||
count,
|
|
||||||
getScrollElement: () => parentRef.current,
|
|
||||||
estimateSize: () => 200,
|
|
||||||
getItemKey: useCallback(
|
|
||||||
(index) => messages[reverseIndex(index)].id,
|
|
||||||
[messages, reverseIndex],
|
|
||||||
),
|
|
||||||
overscan: 5,
|
|
||||||
scrollMargin: 50,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
virtualizerRef.current = virtualizer;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const items = virtualizer.getVirtualItems();
|
|
||||||
|
|
||||||
const [paddingTop, paddingBottom] =
|
|
||||||
items.length > 0
|
|
||||||
? [
|
|
||||||
Math.max(0, items[0].start - virtualizer.options.scrollMargin),
|
|
||||||
Math.max(0, virtualizer.getTotalSize() - items[items.length - 1].end),
|
|
||||||
]
|
|
||||||
: [0, 0];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full grid grid-cols-3">
|
<div className="h-full w-full grid grid-cols-3">
|
||||||
@@ -105,40 +83,23 @@ export function Page() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-full flex-1 p-3">
|
<div className="w-full flex-1 p-3">
|
||||||
<div className="flex h-full flex-col justify-between rounded-md bg-zinc-900">
|
<div className="flex h-full flex-col justify-between rounded-md bg-zinc-900">
|
||||||
<div
|
|
||||||
ref={parentRef}
|
|
||||||
className="scrollbar-hide overflow-y-auto h-full w-full"
|
|
||||||
style={{ contain: "strict" }}
|
|
||||||
>
|
|
||||||
{!messages ? (
|
{!messages ? (
|
||||||
<p>Loading...</p>
|
<p>Loading...</p>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<Virtuoso
|
||||||
style={{
|
ref={virtuosoRef}
|
||||||
overflowAnchor: "none",
|
data={messages}
|
||||||
paddingTop,
|
itemContent={itemContent}
|
||||||
paddingBottom,
|
computeItemKey={computeItemKey}
|
||||||
}}
|
initialTopMostItemIndex={messages.length - 1}
|
||||||
>
|
alignToBottom={true}
|
||||||
{items.map((item) => {
|
followOutput={true}
|
||||||
const index = reverseIndex(item.index);
|
overscan={50}
|
||||||
const message = messages[index];
|
increaseViewportBy={{ top: 200, bottom: 200 }}
|
||||||
|
className="scrollbar-hide overflow-y-auto h-full w-full"
|
||||||
return (
|
/>
|
||||||
<div
|
|
||||||
key={item.key}
|
|
||||||
data-index={item.index}
|
|
||||||
data-reverse-index={index}
|
|
||||||
ref={virtualizer.measureElement}
|
|
||||||
>
|
|
||||||
<ChannelMessageItem data={message} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
<div className="w-full inline-flex shrink-0 px-5 py-3 border-t border-zinc-800">
|
||||||
<div className="w-full inline-flex shrink-0 border-t border-zinc-800">
|
|
||||||
<ChannelMessageForm channelID={channelID} />
|
<ChannelMessageForm channelID={channelID} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ export function Page() {
|
|||||||
const searchParams: any = pageContext.urlParsed.search;
|
const searchParams: any = pageContext.urlParsed.search;
|
||||||
const pubkey = searchParams.pubkey;
|
const pubkey = searchParams.pubkey;
|
||||||
|
|
||||||
const [fetchMessages, clear] = useChatMessages((state: any) => [
|
const [add, fetchMessages, clear] = useChatMessages((state: any) => [
|
||||||
|
state.add,
|
||||||
state.fetch,
|
state.fetch,
|
||||||
state.clear,
|
state.clear,
|
||||||
]);
|
]);
|
||||||
const add = useChatMessages((state: any) => state.add);
|
|
||||||
|
|
||||||
useSWRSubscription(account !== pubkey ? ["chat", pubkey] : null, () => {
|
useSWRSubscription(account !== pubkey ? ["chat", pubkey] : null, () => {
|
||||||
const sub = ndk.subscribe({
|
const sub = ndk.subscribe({
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { prefetchEvents } from "@libs/ndk";
|
import { prefetchEvents } from "@libs/ndk";
|
||||||
import { countTotalNotes, createChat, createNote } from "@libs/storage";
|
import {
|
||||||
|
countTotalNotes,
|
||||||
|
createChannelMessage,
|
||||||
|
createChat,
|
||||||
|
createNote,
|
||||||
|
getChannels,
|
||||||
|
} from "@libs/storage";
|
||||||
import { NDKFilter } from "@nostr-dev-kit/ndk";
|
import { NDKFilter } from "@nostr-dev-kit/ndk";
|
||||||
import { LumeIcon } from "@shared/icons";
|
import { LumeIcon } from "@shared/icons";
|
||||||
import { RelayContext } from "@shared/relayProvider";
|
import { RelayContext } from "@shared/relayProvider";
|
||||||
@@ -98,12 +104,52 @@ export function Page() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchChannelMessages() {
|
||||||
|
try {
|
||||||
|
const ids = [];
|
||||||
|
const channels: any = await getChannels(10, 0);
|
||||||
|
channels.forEach((channel) => {
|
||||||
|
ids.push(channel.event_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const since =
|
||||||
|
lastLogin === 0 ? dateToUnix(getHourAgo(48, now.current)) : lastLogin;
|
||||||
|
|
||||||
|
const filter: NDKFilter = {
|
||||||
|
"#e": ids,
|
||||||
|
kinds: [42],
|
||||||
|
since: since,
|
||||||
|
};
|
||||||
|
|
||||||
|
const events = await prefetchEvents(ndk, filter);
|
||||||
|
events.forEach((event) => {
|
||||||
|
const channel_id = event.tags[0][1];
|
||||||
|
if (channel_id) {
|
||||||
|
createChannelMessage(
|
||||||
|
channel_id,
|
||||||
|
event.id,
|
||||||
|
event.pubkey,
|
||||||
|
event.kind,
|
||||||
|
event.content,
|
||||||
|
event.tags,
|
||||||
|
event.created_at,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function prefetch() {
|
async function prefetch() {
|
||||||
const notes = await fetchNotes();
|
const notes = await fetchNotes();
|
||||||
if (notes) {
|
if (notes) {
|
||||||
const chats = await fetchChats();
|
const chats = await fetchChats();
|
||||||
if (chats) {
|
const channels = await fetchChannelMessages();
|
||||||
|
if (chats && channels) {
|
||||||
navigate("/app/space", { overwriteLastHistoryEntry: true });
|
navigate("/app/space", { overwriteLastHistoryEntry: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export async function prefetchEvents(
|
|||||||
});
|
});
|
||||||
|
|
||||||
relaySetSubscription.on("eose", () => {
|
relaySetSubscription.on("eose", () => {
|
||||||
setTimeout(() => resolve(new Set(events.values())), 3000);
|
setTimeout(() => resolve(new Set(events.values())), 5000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,6 +303,31 @@ export async function updateChannelMetadata(event_id: string, value: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create channel messages
|
||||||
|
export async function createChannelMessage(
|
||||||
|
channel_id: string,
|
||||||
|
event_id: string,
|
||||||
|
pubkey: string,
|
||||||
|
kind: number,
|
||||||
|
content: string,
|
||||||
|
tags: string[][],
|
||||||
|
created_at: number,
|
||||||
|
) {
|
||||||
|
const db = await connect();
|
||||||
|
return await db.execute(
|
||||||
|
"INSERT OR IGNORE INTO channel_messages (channel_id, event_id, pubkey, kind, content, tags, created_at) VALUES (?, ?, ?, ?, ?, ?, ?);",
|
||||||
|
[channel_id, event_id, pubkey, kind, content, tags, created_at],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get channel messages by channel id
|
||||||
|
export async function getChannelMessages(channel_id: string) {
|
||||||
|
const db = await connect();
|
||||||
|
return await db.select(
|
||||||
|
`SELECT * FROM channel_messages WHERE channel_id = "${channel_id}" ORDER BY created_at ASC;`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// get all chats by pubkey
|
// get all chats by pubkey
|
||||||
export async function getChatsByPubkey(pubkey: string) {
|
export async function getChatsByPubkey(pubkey: string) {
|
||||||
const db = await connect();
|
const db = await connect();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Image } from "@shared/image";
|
|||||||
|
|
||||||
export function ImagePreview({ urls }: { urls: string[] }) {
|
export function ImagePreview({ urls }: { urls: string[] }) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-3 overflow-hidden">
|
<div className="mt-3 max-w-[420px] overflow-hidden">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{urls.map((url) => (
|
{urls.map((url) => (
|
||||||
<div key={url} className="min-w-0 grow-0 shrink-0 basis-full">
|
<div key={url} className="min-w-0 grow-0 shrink-0 basis-full">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export function LinkPreview({ urls }: { urls: string[] }) {
|
|||||||
const { data, error, isLoading } = useOpenGraph(urls[0]);
|
const { data, error, isLoading } = useOpenGraph(urls[0]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-3 overflow-hidden rounded-lg bg-zinc-800">
|
<div className="mt-3 max-w-[420px] overflow-hidden rounded-lg bg-zinc-800">
|
||||||
{error && <p>failed to load</p>}
|
{error && <p>failed to load</p>}
|
||||||
{isLoading || !data ? (
|
{isLoading || !data ? (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export function VideoPreview({ urls }: { urls: string[] }) {
|
|||||||
<div
|
<div
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
onKeyDown={(e) => e.stopPropagation()}
|
onKeyDown={(e) => e.stopPropagation()}
|
||||||
className="relative mt-2 flex w-full flex-col overflow-hidden rounded-lg bg-zinc-950"
|
className="relative mt-3 max-w-[420px] flex w-full flex-col overflow-hidden rounded-lg bg-zinc-950"
|
||||||
>
|
>
|
||||||
<MediaPlayer src={urls[0]} poster="" controls>
|
<MediaPlayer src={urls[0]} poster="" controls>
|
||||||
<MediaOutlet />
|
<MediaOutlet />
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { getChannels } from "@libs/storage";
|
import {
|
||||||
import NDK, { NDKFilter } from "@nostr-dev-kit/ndk";
|
createChannelMessage,
|
||||||
|
getChannelMessages,
|
||||||
|
getChannels,
|
||||||
|
} from "@libs/storage";
|
||||||
|
import { LumeEvent } from "@utils/types";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { immer } from "zustand/middleware/immer";
|
import { immer } from "zustand/middleware/immer";
|
||||||
|
|
||||||
@@ -38,19 +42,28 @@ export const useChannelMessages = create(
|
|||||||
immer((set) => ({
|
immer((set) => ({
|
||||||
messages: [],
|
messages: [],
|
||||||
replyTo: { id: null, pubkey: null, content: null },
|
replyTo: { id: null, pubkey: null, content: null },
|
||||||
fetch: async (ndk: NDK, id: string, since: number) => {
|
fetch: async (id: string) => {
|
||||||
const filter: NDKFilter = {
|
const events = await getChannelMessages(id);
|
||||||
"#e": [id],
|
set({ messages: events });
|
||||||
kinds: [42],
|
|
||||||
since: since,
|
|
||||||
};
|
|
||||||
const events = await ndk.fetchEvents(filter);
|
|
||||||
const array = [...events];
|
|
||||||
set({ messages: array });
|
|
||||||
},
|
},
|
||||||
add: (message: any) => {
|
add: (id, event: LumeEvent) => {
|
||||||
set((state: any) => {
|
set((state: any) => {
|
||||||
state.messages.push(message);
|
createChannelMessage(
|
||||||
|
id,
|
||||||
|
event.id,
|
||||||
|
event.pubkey,
|
||||||
|
event.kind,
|
||||||
|
event.content,
|
||||||
|
event.tags,
|
||||||
|
event.created_at,
|
||||||
|
);
|
||||||
|
state.messages.push({
|
||||||
|
event_id: event.id,
|
||||||
|
channel_id: id,
|
||||||
|
hide: 0,
|
||||||
|
mute: 0,
|
||||||
|
...event,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
openReply: (id: string, pubkey: string, content: string) => {
|
openReply: (id: string, pubkey: string, content: string) => {
|
||||||
|
|||||||
@@ -4,8 +4,19 @@ import getUrls from "get-urls";
|
|||||||
import { parseReferences } from "nostr-tools";
|
import { parseReferences } from "nostr-tools";
|
||||||
import reactStringReplace from "react-string-replace";
|
import reactStringReplace from "react-string-replace";
|
||||||
|
|
||||||
|
function isJsonString(str) {
|
||||||
|
try {
|
||||||
|
JSON.parse(str);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
export function parser(event: any) {
|
export function parser(event: any) {
|
||||||
event.tags = destr(event.tags);
|
if (isJsonString(event.tags)) {
|
||||||
|
event["tags"] = destr(event.tags);
|
||||||
|
}
|
||||||
|
|
||||||
const references = parseReferences(event);
|
const references = parseReferences(event);
|
||||||
const urls = getUrls(event.content);
|
const urls = getUrls(event.content);
|
||||||
|
|||||||
Reference in New Issue
Block a user