mirror of
https://github.com/lumehq/lume.git
synced 2025-03-18 05:41:53 +01:00
wip: finish browse users
This commit is contained in:
parent
9ff74599eb
commit
a66770989b
@ -18,7 +18,6 @@
|
||||
"**/*.{ts, tsx, css, md, html, json}": "prettier --cache --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.0.8",
|
||||
"@getalby/sdk": "^2.4.0",
|
||||
"@nostr-dev-kit/ndk": "^1.2.1",
|
||||
"@nostr-fetch/adapter-ndk": "^0.12.2",
|
||||
@ -59,6 +58,7 @@
|
||||
"react-textarea-autosize": "^8.5.3",
|
||||
"react-virtuoso": "^4.6.0",
|
||||
"react-zoom-pan-pinch": "^3.1.0",
|
||||
"reactflow": "^11.8.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
|
||||
"tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#v1",
|
||||
|
416
pnpm-lock.yaml
generated
416
pnpm-lock.yaml
generated
@ -5,9 +5,6 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@dnd-kit/core':
|
||||
specifier: ^6.0.8
|
||||
version: 6.0.8(react-dom@18.2.0)(react@18.2.0)
|
||||
'@getalby/sdk':
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0
|
||||
@ -128,6 +125,9 @@ dependencies:
|
||||
react-zoom-pan-pinch:
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0(react-dom@18.2.0)(react@18.2.0)
|
||||
reactflow:
|
||||
specifier: ^11.8.3
|
||||
version: 11.8.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
remark-gfm:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
@ -380,37 +380,6 @@ packages:
|
||||
'@babel/helper-validator-identifier': 7.22.20
|
||||
to-fast-properties: 2.0.0
|
||||
|
||||
/@dnd-kit/accessibility@3.0.1(react@18.2.0):
|
||||
resolution: {integrity: sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
|
||||
/@dnd-kit/core@6.0.8(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-lYaoP8yHTQSLlZe6Rr9qogouGUz9oRUj4AHhDQGQzq/hqaJRpFo65X+JKsdHf8oUFBzx5A+SJPUvxAwTF2OabA==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
dependencies:
|
||||
'@dnd-kit/accessibility': 3.0.1(react@18.2.0)
|
||||
'@dnd-kit/utilities': 3.2.1(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
|
||||
/@dnd-kit/utilities@3.2.1(react@18.2.0):
|
||||
resolution: {integrity: sha512-OOXqISfvBw/1REtkSK2N3Fi2EQiLMlWUlqnOK/UpOISqBZPWpE6TqL+jcPtMOkE8TqYGiURvRdPSI9hltNUjEA==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
|
||||
/@emotion/babel-plugin@11.11.0:
|
||||
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
|
||||
dependencies:
|
||||
@ -1545,6 +1514,114 @@ packages:
|
||||
'@babel/runtime': 7.22.15
|
||||
dev: false
|
||||
|
||||
/@reactflow/background@11.2.8(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-5o41N2LygiNC2/Pk8Ak2rIJjXbKHfQ23/Y9LFsnAlufqwdzFqKA8txExpsMoPVHHlbAdA/xpQaMuoChGPqmyDw==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.8.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
classcat: 5.0.4
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.4.1(@types/react@18.2.22)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/controls@11.1.19(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Vo0LFfAYjiSRMLEII/aeBo+1MT2a0Yc7iLVnkuRTLzChC0EX+A2Fa+JlzeOEYKxXlN4qcDxckRNGR7092v1HOQ==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.8.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
classcat: 5.0.4
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.4.1(@types/react@18.2.22)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/core@11.8.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-y6DN8Wy4V4KQBGHFqlj9zWRjLJU6CgdnVwWaEA/PdDg/YUkFBMpZnXqTs60czinoA2rAcvsz50syLTPsj5e+Wg==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@types/d3': 7.4.1
|
||||
'@types/d3-drag': 3.0.4
|
||||
'@types/d3-selection': 3.0.7
|
||||
'@types/d3-zoom': 3.0.5
|
||||
classcat: 5.0.4
|
||||
d3-drag: 3.0.0
|
||||
d3-selection: 3.0.0
|
||||
d3-zoom: 3.0.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.4.1(@types/react@18.2.22)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/minimap@11.6.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-PSA28dk09RnBHOA1zb45fjQXz3UozSJZmsIpgq49O3trfVFlSgRapxNdGsughWLs7/emg2M5jmi6Vc+ejcfjvQ==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.8.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/d3-selection': 3.0.7
|
||||
'@types/d3-zoom': 3.0.5
|
||||
classcat: 5.0.4
|
||||
d3-selection: 3.0.0
|
||||
d3-zoom: 3.0.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.4.1(@types/react@18.2.22)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/node-resizer@2.1.5(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-z/hJlsptd2vTx13wKouqvN/Kln08qbkA+YTJLohc2aJ6rx3oGn9yX4E4IqNxhA7zNqYEdrnc1JTEA//ifh9z3w==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.8.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
classcat: 5.0.4
|
||||
d3-drag: 3.0.0
|
||||
d3-selection: 3.0.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.4.1(@types/react@18.2.22)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@reactflow/node-toolbar@1.2.7(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-vs+Wg1tjy3SuD7eoeTqEtscBfE9RY+APqC28urVvftkrtsN7KlnoQjqDG6aE45jWP4z+8bvFizRWjAhxysNLkg==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/core': 11.8.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
classcat: 5.0.4
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
zustand: 4.4.1(@types/react@18.2.22)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/@remirror/core-constants@2.0.2:
|
||||
resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==}
|
||||
dev: false
|
||||
@ -2194,12 +2271,195 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@types/d3-array@3.0.8:
|
||||
resolution: {integrity: sha512-2xAVyAUgaXHX9fubjcCbGAUOqYfRJN1em1EKR2HfzWBpObZhwfnZKvofTN4TplMqJdFQao61I+NVSai/vnBvDQ==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-axis@3.0.4:
|
||||
resolution: {integrity: sha512-ySnjI/7qm+J602VjcejXcqs1hEuu5UBbGaJGp+Cn/yKVc1iS3JueLVpToGdQsS2sqta7tqA/kG4ore/+LH90UA==}
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.7
|
||||
dev: false
|
||||
|
||||
/@types/d3-brush@3.0.4:
|
||||
resolution: {integrity: sha512-Kg5uIsdJNMCs5lTqeZFsTKqj9lBvpiFRDkYN3j2CDlPhonNDg9/gXVpv1E/MKh3tEqArryIj9o6RBGE/MQe+6Q==}
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.7
|
||||
dev: false
|
||||
|
||||
/@types/d3-chord@3.0.4:
|
||||
resolution: {integrity: sha512-p4PvN1N+7GL3Y/NI9Ug1TKwowUV6h664kmxL79ctp1HRYCk1mhP0+SXhjRsoWXCdnJfbLLLmpV99rt8dMrHrzg==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-color@3.1.1:
|
||||
resolution: {integrity: sha512-CSAVrHAtM9wfuLJ2tpvvwCU/F22sm7rMHNN+yh9D6O6hyAms3+O0cgMpC1pm6UEUMOntuZC8bMt74PteiDUdCg==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-contour@3.0.4:
|
||||
resolution: {integrity: sha512-B0aeX8Xg3MNUglULxqDvlgY1SVXuN2xtEleYSAY0iMhl/SMVT7snzgAveejjwM3KaWuNXIoXEJ7dmXE8oPq/jA==}
|
||||
dependencies:
|
||||
'@types/d3-array': 3.0.8
|
||||
'@types/geojson': 7946.0.11
|
||||
dev: false
|
||||
|
||||
/@types/d3-delaunay@6.0.2:
|
||||
resolution: {integrity: sha512-WplUJ/OHU7eITneDqNnzK+2pgR+WDzUHG6XAUVo+oWHPQq74VcgUdw8a4ODweaZzF56OVYK+x9GxCyuq6hSu1A==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-dispatch@3.0.4:
|
||||
resolution: {integrity: sha512-NApHpGHRNxUy7e2Lfzl/cwOucmn4Xdx6FdmXzAoomo8T81LyGmlBjjko/vP0TVzawlvEFLDq8OCRLulW6DDzKw==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-drag@3.0.4:
|
||||
resolution: {integrity: sha512-/t53K1erTuUbP7WIX9SE0hlmytpTYRbIthlhbGkBHzCV5vPO++7yrk8OlisWPyIJO5TGowTmqCtGH2tokY5T/g==}
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.7
|
||||
dev: false
|
||||
|
||||
/@types/d3-dsv@3.0.4:
|
||||
resolution: {integrity: sha512-YxfUVJ55HxR8oq88136w09mBMPNhgH7PZjteq72onWXWOohGif/cLQnQv8V4A5lEGjXF04LhwSTpmzpY9wyVyA==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-ease@3.0.0:
|
||||
resolution: {integrity: sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-fetch@3.0.4:
|
||||
resolution: {integrity: sha512-RleYajubALkGjrvatxWhlygfvB1KNF0Uzz9guRUeeA+M/2B7l8rxObYdktaX9zU1st04lMCHjZWe4vbl+msH2Q==}
|
||||
dependencies:
|
||||
'@types/d3-dsv': 3.0.4
|
||||
dev: false
|
||||
|
||||
/@types/d3-force@3.0.6:
|
||||
resolution: {integrity: sha512-G9wbOvCxkNlLrppoHLZ6oFpbm3z7ibfkXwLD8g5/4Aa7iTEV0Z7TQ0OL8UxAtvdOhCa2VZcSuqn1NQqyCEqmiw==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-format@3.0.2:
|
||||
resolution: {integrity: sha512-9oQWvKk2qVBo49FQq8yD/et8Lx0W5Ac2FdGSOUecqOFKqh0wkpyHqf9Qc7A06ftTR+Lz13Pi3jHIQis0aCueOA==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-geo@3.0.5:
|
||||
resolution: {integrity: sha512-ysEEU93Wv9p2UZBxTK3kUP7veHgyhTA0qYtI7bxK5EMXb3JxGv0D4IH54PxprAF26n+uHci24McVmzwIdLgvgQ==}
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.11
|
||||
dev: false
|
||||
|
||||
/@types/d3-hierarchy@3.1.4:
|
||||
resolution: {integrity: sha512-wrvjpRFdmEu6yAqgjGy8MSud9ggxJj+I9XLuztLeSf/E0j0j6RQYtxH2J8U0Cfbgiw9ZDHyhpmaVuWhxscYaAQ==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-interpolate@3.0.2:
|
||||
resolution: {integrity: sha512-zAbCj9lTqW9J9PlF4FwnvEjXZUy75NQqPm7DMHZXuxCFTpuTrdK2NMYGQekf4hlasL78fCYOLu4EE3/tXElwow==}
|
||||
dependencies:
|
||||
'@types/d3-color': 3.1.1
|
||||
dev: false
|
||||
|
||||
/@types/d3-path@3.0.0:
|
||||
resolution: {integrity: sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-polygon@3.0.0:
|
||||
resolution: {integrity: sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-quadtree@3.0.3:
|
||||
resolution: {integrity: sha512-GDWaR+rGEk4ToLQSGugYnoh9AYYblsg/8kmdpa1KAJMwcdZ0v8rwgnldURxI5UrzxPlCPzF7by/Tjmv+Jn21Dg==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-random@3.0.1:
|
||||
resolution: {integrity: sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-scale-chromatic@3.0.0:
|
||||
resolution: {integrity: sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-scale@4.0.5:
|
||||
resolution: {integrity: sha512-w/C++3W394MHzcLKO2kdsIn5KKNTOqeQVzyPSGPLzQbkPw/jpeaGtSRlakcKevGgGsjJxGsbqS0fPrVFDbHrDA==}
|
||||
dependencies:
|
||||
'@types/d3-time': 3.0.1
|
||||
dev: false
|
||||
|
||||
/@types/d3-selection@3.0.7:
|
||||
resolution: {integrity: sha512-qoj2O7KjfqCobmtFOth8FMvjwMVPUAAmn6xiUbLl1ld7vQCPgffvyV5BBcEFfqWdilAUm+3zciU/3P3vZrUMlg==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-shape@3.1.3:
|
||||
resolution: {integrity: sha512-cHMdIq+rhF5IVwAV7t61pcEXfEHsEsrbBUPkFGBwTXuxtTAkBBrnrNA8++6OWm3jwVsXoZYQM8NEekg6CPJ3zw==}
|
||||
dependencies:
|
||||
'@types/d3-path': 3.0.0
|
||||
dev: false
|
||||
|
||||
/@types/d3-time-format@4.0.1:
|
||||
resolution: {integrity: sha512-Br6EFeu9B1Zrem7KaYbr800xCmEDyq8uE60kEU8rWhC/XpFYX6ocGMZuRJDQfFCq6SyakQxNHFqIfJbFLf4x6Q==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-time@3.0.1:
|
||||
resolution: {integrity: sha512-5j/AnefKAhCw4HpITmLDTPlf4vhi8o/dES+zbegfPb7LaGfNyqkLxBR6E+4yvTAgnJLmhe80EXFMzUs38fw4oA==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-timer@3.0.0:
|
||||
resolution: {integrity: sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==}
|
||||
dev: false
|
||||
|
||||
/@types/d3-transition@3.0.5:
|
||||
resolution: {integrity: sha512-dcfjP6prFxj3ziFOJrnt4W2P0oXNj/sGxsJXH8286sHtVZ4qWGbjuZj+RRCYx4YZ4C0izpeE8OqXVCtoWEtzYg==}
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.7
|
||||
dev: false
|
||||
|
||||
/@types/d3-zoom@3.0.5:
|
||||
resolution: {integrity: sha512-mIefdTLtxuWUWTbBupCUXPAXVPmi8/Uwrq41gQpRh0rD25GMU1ku+oTELqNY2NuuiI0F3wXC5e1liBQi7YS7XQ==}
|
||||
dependencies:
|
||||
'@types/d3-interpolate': 3.0.2
|
||||
'@types/d3-selection': 3.0.7
|
||||
dev: false
|
||||
|
||||
/@types/d3@7.4.1:
|
||||
resolution: {integrity: sha512-lBpYmbHTCtFKO1DB1R7E9dXp9/g1F3JXSGOF7iKPZ+wRmYg/Q6tCRHODGOc5Qk25fJRe2PI60EDRf2HLPUncMA==}
|
||||
dependencies:
|
||||
'@types/d3-array': 3.0.8
|
||||
'@types/d3-axis': 3.0.4
|
||||
'@types/d3-brush': 3.0.4
|
||||
'@types/d3-chord': 3.0.4
|
||||
'@types/d3-color': 3.1.1
|
||||
'@types/d3-contour': 3.0.4
|
||||
'@types/d3-delaunay': 6.0.2
|
||||
'@types/d3-dispatch': 3.0.4
|
||||
'@types/d3-drag': 3.0.4
|
||||
'@types/d3-dsv': 3.0.4
|
||||
'@types/d3-ease': 3.0.0
|
||||
'@types/d3-fetch': 3.0.4
|
||||
'@types/d3-force': 3.0.6
|
||||
'@types/d3-format': 3.0.2
|
||||
'@types/d3-geo': 3.0.5
|
||||
'@types/d3-hierarchy': 3.1.4
|
||||
'@types/d3-interpolate': 3.0.2
|
||||
'@types/d3-path': 3.0.0
|
||||
'@types/d3-polygon': 3.0.0
|
||||
'@types/d3-quadtree': 3.0.3
|
||||
'@types/d3-random': 3.0.1
|
||||
'@types/d3-scale': 4.0.5
|
||||
'@types/d3-scale-chromatic': 3.0.0
|
||||
'@types/d3-selection': 3.0.7
|
||||
'@types/d3-shape': 3.1.3
|
||||
'@types/d3-time': 3.0.1
|
||||
'@types/d3-time-format': 4.0.1
|
||||
'@types/d3-timer': 3.0.0
|
||||
'@types/d3-transition': 3.0.5
|
||||
'@types/d3-zoom': 3.0.5
|
||||
dev: false
|
||||
|
||||
/@types/debug@4.1.8:
|
||||
resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==}
|
||||
dependencies:
|
||||
'@types/ms': 0.7.31
|
||||
dev: false
|
||||
|
||||
/@types/geojson@7946.0.11:
|
||||
resolution: {integrity: sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==}
|
||||
dev: false
|
||||
|
||||
/@types/hast@2.3.6:
|
||||
resolution: {integrity: sha512-47rJE80oqPmFdVDCD7IheXBrVdwuBgsYwoczFvKmwfo2Mzsnt+V9OONsYauFmICb6lQPpCuXYJWejBNs4pDJRg==}
|
||||
dependencies:
|
||||
@ -2738,6 +2998,10 @@ packages:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/classcat@5.0.4:
|
||||
resolution: {integrity: sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==}
|
||||
dev: false
|
||||
|
||||
/cli-cursor@4.0.0:
|
||||
resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
@ -2848,6 +3112,71 @@ packages:
|
||||
/csstype@3.1.2:
|
||||
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
|
||||
|
||||
/d3-color@3.1.0:
|
||||
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-dispatch@3.0.1:
|
||||
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-drag@3.0.0:
|
||||
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
dev: false
|
||||
|
||||
/d3-ease@3.0.1:
|
||||
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-interpolate@3.0.1:
|
||||
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
dev: false
|
||||
|
||||
/d3-selection@3.0.0:
|
||||
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-timer@3.0.1:
|
||||
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-transition@3.0.1(d3-selection@3.0.0):
|
||||
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
d3-selection: 2 - 3
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
d3-dispatch: 3.0.1
|
||||
d3-ease: 3.0.1
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-timer: 3.0.1
|
||||
dev: false
|
||||
|
||||
/d3-zoom@3.0.0:
|
||||
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-drag: 3.0.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||
dev: false
|
||||
|
||||
/d@1.0.1:
|
||||
resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==}
|
||||
dependencies:
|
||||
@ -5462,6 +5791,25 @@ packages:
|
||||
loose-envify: 1.4.0
|
||||
dev: false
|
||||
|
||||
/reactflow@11.8.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-wuVxJOFqi1vhA4WAEJLK0JWx2TsTiWpxTXTRp/wvpqKInQgQcB49I2QNyNYsKJCQ6jjXektS7H+LXoaVK/pG4A==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
dependencies:
|
||||
'@reactflow/background': 11.2.8(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/controls': 11.1.19(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/core': 11.8.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/minimap': 11.6.3(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/node-resizer': 2.1.5(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reactflow/node-toolbar': 1.2.7(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
dev: false
|
||||
|
||||
/read-cache@1.0.0:
|
||||
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
||||
dependencies:
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { message } from '@tauri-apps/api/dialog';
|
||||
import { RouterProvider, createBrowserRouter, redirect } from 'react-router-dom';
|
||||
import 'reactflow/dist/style.css';
|
||||
|
||||
import { AuthCreateScreen } from '@app/auth/create';
|
||||
import { AuthImportScreen } from '@app/auth/import';
|
||||
|
29
src/app/browse/components/edge.tsx
Normal file
29
src/app/browse/components/edge.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { BaseEdge, EdgeProps, getBezierPath } from 'reactflow';
|
||||
|
||||
export function Edge({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
style = {},
|
||||
markerEnd,
|
||||
}: EdgeProps) {
|
||||
const [edgePath] = getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition,
|
||||
});
|
||||
|
||||
return (
|
||||
<BaseEdge
|
||||
path={edgePath}
|
||||
markerEnd={markerEnd}
|
||||
style={{ ...style, stroke: '#71717a' }}
|
||||
/>
|
||||
);
|
||||
}
|
17
src/app/browse/components/groupTitle.tsx
Normal file
17
src/app/browse/components/groupTitle.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import { useProfile } from '@utils/hooks/useProfile';
|
||||
|
||||
export const GroupTitle = memo(function GroupTitle({ pubkey }: { pubkey: string }) {
|
||||
const { status, user } = useProfile(pubkey);
|
||||
|
||||
if (status === 'loading') {
|
||||
return <div className="h-3 w-24 animate-pulse rounded bg-white/10" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<h3 className="text-sm font-semibold text-fuchsia-500">{`${
|
||||
user.name || user.display_name
|
||||
}'s network`}</h3>
|
||||
);
|
||||
});
|
14
src/app/browse/components/line.tsx
Normal file
14
src/app/browse/components/line.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
export function Line({ fromX, fromY, toX, toY }) {
|
||||
return (
|
||||
<g>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#f5d0fe"
|
||||
strokeWidth={1.5}
|
||||
className="animated"
|
||||
d={`M${fromX},${fromY} C ${fromX} ${toY} ${fromX} ${toY} ${toX},${toY}`}
|
||||
/>
|
||||
<circle cx={toX} cy={toY} fill="#fff" r={3} stroke="#f5d0fe" strokeWidth={1.5} />
|
||||
</g>
|
||||
);
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
import { useDraggable } from '@dnd-kit/core';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
import { Image } from '@shared/image';
|
||||
import { NIP05 } from '@shared/nip05';
|
||||
import { TextNote } from '@shared/notes';
|
||||
import { User } from '@shared/user';
|
||||
|
||||
import { useNostr } from '@utils/hooks/useNostr';
|
||||
import { useProfile } from '@utils/hooks/useProfile';
|
||||
import { displayNpub } from '@utils/shortenKey';
|
||||
|
||||
export const UserDrawer = memo(function UserDrawer({ pubkey }: { pubkey: string }) {
|
||||
const { db } = useStorage();
|
||||
const { status, user } = useProfile(pubkey);
|
||||
const { addContact, removeContact } = useNostr();
|
||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||
id: pubkey,
|
||||
});
|
||||
|
||||
const [followed, setFollowed] = useState(false);
|
||||
|
||||
const style = transform
|
||||
? {
|
||||
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
|
||||
zIndex: 20,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const followUser = (pubkey: string) => {
|
||||
try {
|
||||
addContact(pubkey);
|
||||
|
||||
// update state
|
||||
setFollowed(true);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const unfollowUser = (pubkey: string) => {
|
||||
try {
|
||||
removeContact(pubkey);
|
||||
|
||||
// update state
|
||||
setFollowed(false);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (db.account.follows.includes(pubkey)) {
|
||||
setFollowed(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
{...listeners}
|
||||
{...attributes}
|
||||
>
|
||||
<User pubkey={pubkey} variant="avatar" />
|
||||
</button>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="fixed right-0 top-0 z-50 flex h-full w-[400px] items-center justify-center px-4 pb-4 pt-16">
|
||||
<div className="h-full w-full overflow-y-auto rounded-lg border-t border-white/10 bg-white/20 px-3 py-3 backdrop-blur-3xl">
|
||||
{status === 'loading' ? (
|
||||
<div>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col">
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="h-14 w-14 rounded-lg"
|
||||
/>
|
||||
<div className="mt-2 flex flex-1 flex-col gap-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h5 className="text-lg font-semibold leading-none">
|
||||
{user?.displayName || user?.name || 'No name'}
|
||||
</h5>
|
||||
{user?.nip05 ? (
|
||||
<NIP05
|
||||
pubkey={pubkey}
|
||||
nip05={user?.nip05}
|
||||
className="max-w-[15rem] truncate text-sm leading-none text-white/50"
|
||||
/>
|
||||
) : (
|
||||
<span className="max-w-[15rem] truncate text-sm leading-none text-white/50">
|
||||
{displayNpub(pubkey, 16)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
{user.about ? <TextNote content={user.about} /> : null}
|
||||
</div>
|
||||
<div className="mt-4 inline-flex items-center gap-2">
|
||||
{followed ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => unfollowUser(pubkey)}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-white/10 text-sm font-medium backdrop-blur-xl hover:bg-fuchsia-500"
|
||||
>
|
||||
Unfollow
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => followUser(pubkey)}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-white/10 text-sm font-medium backdrop-blur-xl hover:bg-fuchsia-500"
|
||||
>
|
||||
Follow
|
||||
</button>
|
||||
)}
|
||||
<Link
|
||||
to={`/chats/${pubkey}`}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-white/10 text-sm font-medium backdrop-blur-xl hover:bg-fuchsia-500"
|
||||
>
|
||||
Message
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
);
|
||||
});
|
@ -1,22 +0,0 @@
|
||||
import { useDroppable } from '@dnd-kit/core';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
import { PlusIcon } from '@shared/icons';
|
||||
|
||||
export function UserDropable() {
|
||||
const { isOver, setNodeRef } = useDroppable({
|
||||
id: 'newBlock',
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
className={twMerge(
|
||||
'inline-flex h-12 w-12 items-center justify-center rounded-lg border-t border-white/10 backdrop-blur-xl',
|
||||
isOver ? 'bg-fuchsia-500' : 'bg-white/20 hover:bg-white/30'
|
||||
)}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4 text-white" />
|
||||
</div>
|
||||
);
|
||||
}
|
34
src/app/browse/components/userGroupNode.tsx
Normal file
34
src/app/browse/components/userGroupNode.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { Handle, Position } from 'reactflow';
|
||||
|
||||
import { UserWithDrawer } from '@app/browse/components/userWithDrawer';
|
||||
|
||||
import { GroupTitle } from './groupTitle';
|
||||
|
||||
export function UserGroupNode({ data }) {
|
||||
return (
|
||||
<>
|
||||
<Handle
|
||||
type="target"
|
||||
position={Position.Top}
|
||||
className="h-2 w-5 rounded-full border-none !bg-fuchsia-400"
|
||||
/>
|
||||
<div className="relative mx-3 my-3 flex flex-col gap-1">
|
||||
{data.title ? (
|
||||
<h3 className="text-sm font-semibold text-fuchsia-500">{data.title}</h3>
|
||||
) : (
|
||||
<GroupTitle pubkey={data.pubkey} />
|
||||
)}
|
||||
<div className="grid grid-cols-5 gap-6 rounded-lg border border-fuchsia-500/50 bg-fuchsia-500/10 p-4">
|
||||
{data.list.map((user: string) => (
|
||||
<UserWithDrawer key={user} pubkey={user} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Bottom}
|
||||
className="h-2 w-5 rounded-full border-none !bg-fuchsia-400"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
80
src/app/browse/components/userLatestPosts.tsx
Normal file
80
src/app/browse/components/userLatestPosts.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { LoaderIcon } from '@shared/icons';
|
||||
import {
|
||||
ArticleNote,
|
||||
FileNote,
|
||||
NoteWrapper,
|
||||
Repost,
|
||||
TextNote,
|
||||
UnknownNote,
|
||||
} from '@shared/notes';
|
||||
|
||||
import { useNostr } from '@utils/hooks/useNostr';
|
||||
|
||||
export function UserLatestPosts({ pubkey }: { pubkey: string }) {
|
||||
const { getEventsByPubkey } = useNostr();
|
||||
const { status, data } = useQuery(['user-posts', pubkey], async () => {
|
||||
return await getEventsByPubkey(pubkey);
|
||||
});
|
||||
|
||||
const renderItem = useCallback(
|
||||
(event: NDKEvent) => {
|
||||
switch (event.kind) {
|
||||
case NDKKind.Text:
|
||||
return (
|
||||
<NoteWrapper key={event.id} event={event}>
|
||||
<TextNote content={event.content} />
|
||||
</NoteWrapper>
|
||||
);
|
||||
case NDKKind.Repost:
|
||||
return <Repost key={event.id} event={event} />;
|
||||
case 1063:
|
||||
return (
|
||||
<NoteWrapper key={event.id} event={event}>
|
||||
<FileNote event={event} />
|
||||
</NoteWrapper>
|
||||
);
|
||||
case NDKKind.Article:
|
||||
return (
|
||||
<NoteWrapper key={event.id} event={event}>
|
||||
<ArticleNote event={event} />
|
||||
</NoteWrapper>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<NoteWrapper key={event.id} event={event}>
|
||||
<UnknownNote event={event} />
|
||||
</NoteWrapper>
|
||||
);
|
||||
}
|
||||
},
|
||||
[data]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mt-4 border-t border-white/5 pt-3">
|
||||
<h3 className="mb-4 px-3 font-semibold text-white">Latest post</h3>
|
||||
<div>
|
||||
{status === 'loading' ? (
|
||||
<div className="px-3">
|
||||
<div className="inline-flex h-16 w-full items-center justify-center gap-1.5 rounded-lg bg-white/10 text-sm font-medium text-white/70">
|
||||
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
|
||||
Loading latest posts...
|
||||
</div>
|
||||
</div>
|
||||
) : data.length < 1 ? (
|
||||
<div className="px-3">
|
||||
<div className="inline-flex h-16 w-full items-center justify-center rounded-lg bg-white/10 text-sm font-medium text-white/70">
|
||||
No posts from 24 hours ago
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
data.map((event) => renderItem(event))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
21
src/app/browse/components/userNode.tsx
Normal file
21
src/app/browse/components/userNode.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { Handle, Position } from 'reactflow';
|
||||
|
||||
import { User } from '@shared/user';
|
||||
|
||||
export function UserNode({ data }) {
|
||||
return (
|
||||
<>
|
||||
<div className="relative mx-3 my-3 inline-flex h-12 w-12 shrink-0 items-center justify-center">
|
||||
<span className="absolute inline-flex h-8 w-8 animate-ping rounded-lg bg-green-400 opacity-75"></span>
|
||||
<div className="relative z-10">
|
||||
<User pubkey={data.pubkey} variant="avatar" />
|
||||
</div>
|
||||
</div>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Bottom}
|
||||
className="h-2 w-2 rounded-full border-none !bg-white/20"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
130
src/app/browse/components/userWithDrawer.tsx
Normal file
130
src/app/browse/components/userWithDrawer.tsx
Normal file
@ -0,0 +1,130 @@
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
import { Image } from '@shared/image';
|
||||
import { NIP05 } from '@shared/nip05';
|
||||
import { TextNote } from '@shared/notes';
|
||||
import { User } from '@shared/user';
|
||||
|
||||
import { useNostr } from '@utils/hooks/useNostr';
|
||||
import { useProfile } from '@utils/hooks/useProfile';
|
||||
import { displayNpub } from '@utils/shortenKey';
|
||||
|
||||
import { UserLatestPosts } from './userLatestPosts';
|
||||
|
||||
export const UserWithDrawer = memo(function UserWithDrawer({
|
||||
pubkey,
|
||||
}: {
|
||||
pubkey: string;
|
||||
}) {
|
||||
const { addContact, removeContact } = useNostr();
|
||||
const { db } = useStorage();
|
||||
const { status, user } = useProfile(pubkey);
|
||||
|
||||
const [followed, setFollowed] = useState(false);
|
||||
|
||||
const followUser = (pubkey: string) => {
|
||||
try {
|
||||
addContact(pubkey);
|
||||
// update state
|
||||
setFollowed(true);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const unfollowUser = (pubkey: string) => {
|
||||
try {
|
||||
removeContact(pubkey);
|
||||
// update state
|
||||
setFollowed(false);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (db.account.follows.includes(pubkey)) {
|
||||
setFollowed(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger asChild>
|
||||
<button type="button">
|
||||
<User pubkey={pubkey} variant="avatar" />
|
||||
</button>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="fixed right-0 top-0 z-50 flex h-full w-[400px] items-center justify-center px-4 pb-4 pt-16">
|
||||
<div className="h-full w-full overflow-y-auto rounded-lg border-t border-white/10 bg-white/20 py-3 backdrop-blur-3xl">
|
||||
{status === 'loading' ? (
|
||||
<div>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex flex-col gap-3 px-3">
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="h-12 w-12 rounded-lg"
|
||||
/>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<h5 className="text-lg font-semibold leading-none">
|
||||
{user?.displayName || user?.name || 'No name'}
|
||||
</h5>
|
||||
{user?.nip05 ? (
|
||||
<NIP05
|
||||
pubkey={pubkey}
|
||||
nip05={user?.nip05}
|
||||
className="max-w-[15rem] truncate text-sm leading-none text-white/50"
|
||||
/>
|
||||
) : (
|
||||
<span className="max-w-[15rem] truncate text-sm leading-none text-white/50">
|
||||
{displayNpub(pubkey, 16)}
|
||||
</span>
|
||||
)}
|
||||
{user.about ? <TextNote content={user.about} /> : null}
|
||||
</div>
|
||||
<div className="mt-3 inline-flex items-center gap-2">
|
||||
{followed ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => unfollowUser(pubkey)}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-white/10 text-sm font-medium backdrop-blur-xl hover:bg-fuchsia-500"
|
||||
>
|
||||
Unfollow
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => followUser(pubkey)}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-white/10 text-sm font-medium backdrop-blur-xl hover:bg-fuchsia-500"
|
||||
>
|
||||
Follow
|
||||
</button>
|
||||
)}
|
||||
<Link
|
||||
to={`/chats/${pubkey}`}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-white/10 text-sm font-medium backdrop-blur-xl hover:bg-fuchsia-500"
|
||||
>
|
||||
Message
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<UserLatestPosts pubkey={pubkey} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
);
|
||||
});
|
@ -1,45 +1,43 @@
|
||||
import { NavLink, Outlet } from 'react-router-dom';
|
||||
import { ReactFlowProvider } from 'reactflow';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
import { DotsPattern } from '@shared/icons';
|
||||
|
||||
export function BrowseScreen() {
|
||||
return (
|
||||
<div className="relative h-full w-full">
|
||||
<div className="absolute left-0 right-0 top-4 z-30 flex w-full items-center justify-between px-3">
|
||||
<div className="w-10" />
|
||||
<div className="inline-flex gap-1 rounded-full border-t border-white/10 bg-white/20 p-1 backdrop-blur-xl">
|
||||
<NavLink
|
||||
to="/browse/"
|
||||
className={({ isActive }) =>
|
||||
twMerge(
|
||||
'inline-flex h-8 w-20 items-center justify-center rounded-full text-sm font-semibold',
|
||||
isActive ? 'bg-white/10 hover:bg-white/20' : ' hover:bg-white/5'
|
||||
)
|
||||
}
|
||||
>
|
||||
Users
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/browse/relays"
|
||||
className={({ isActive }) =>
|
||||
twMerge(
|
||||
'inline-flex h-8 w-20 items-center justify-center rounded-full text-sm font-semibold',
|
||||
isActive ? 'bg-white/10 hover:bg-white/20' : ' hover:bg-white/5'
|
||||
)
|
||||
}
|
||||
>
|
||||
Relays
|
||||
</NavLink>
|
||||
<ReactFlowProvider>
|
||||
<div className="relative h-full w-full">
|
||||
<div className="absolute left-0 right-0 top-4 z-30 flex w-full items-center justify-between px-3">
|
||||
<div className="w-10" />
|
||||
<div className="inline-flex gap-1 rounded-full border-t border-white/10 bg-white/20 p-1 backdrop-blur-xl">
|
||||
<NavLink
|
||||
to="/browse/"
|
||||
className={({ isActive }) =>
|
||||
twMerge(
|
||||
'inline-flex h-8 w-20 items-center justify-center rounded-full text-sm font-semibold',
|
||||
isActive ? 'bg-white/10 hover:bg-white/20' : ' hover:bg-white/5'
|
||||
)
|
||||
}
|
||||
>
|
||||
Users
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/browse/relays"
|
||||
className={({ isActive }) =>
|
||||
twMerge(
|
||||
'inline-flex h-8 w-20 items-center justify-center rounded-full text-sm font-semibold',
|
||||
isActive ? 'bg-white/10 hover:bg-white/20' : ' hover:bg-white/5'
|
||||
)
|
||||
}
|
||||
>
|
||||
Relays
|
||||
</NavLink>
|
||||
</div>
|
||||
<div className="w-10" />
|
||||
</div>
|
||||
<div className="relative z-20 h-full w-full">
|
||||
<Outlet />
|
||||
</div>
|
||||
<div className="w-10" />
|
||||
</div>
|
||||
<div className="absolute z-10 h-full w-full">
|
||||
<DotsPattern className="h-full w-full text-white/10" />
|
||||
</div>
|
||||
<div className="relative z-20 h-full w-full">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,42 +1,116 @@
|
||||
import { DndContext } from '@dnd-kit/core';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
ConnectionMode,
|
||||
addEdge,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
useReactFlow,
|
||||
} from 'reactflow';
|
||||
|
||||
import { UserDrawer } from '@app/browse/components/userDrawer';
|
||||
import { UserDropable } from '@app/browse/components/userDropable';
|
||||
import { Edge } from '@app/browse//components/edge';
|
||||
import { UserGroupNode } from '@app/browse//components/userGroupNode';
|
||||
import { Line } from '@app/browse/components/line';
|
||||
import { UserNode } from '@app/browse/components/userNode';
|
||||
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
import { User } from '@shared/user';
|
||||
|
||||
import { useNostr } from '@utils/hooks/useNostr';
|
||||
import { getMultipleRandom } from '@utils/transform';
|
||||
|
||||
let id = 2;
|
||||
const getId = () => `${id++}`;
|
||||
const nodeTypes = { user: UserNode, userGroup: UserGroupNode };
|
||||
const edgeTypes = { buttonedge: Edge };
|
||||
|
||||
export function BrowseUsersScreen() {
|
||||
const { db } = useStorage();
|
||||
const { getContactsByPubkey } = useNostr();
|
||||
const { project } = useReactFlow();
|
||||
|
||||
const data = useMemo(() => getMultipleRandom(db.account.follows, 10), []);
|
||||
const defaultContacts = useMemo(() => getMultipleRandom(db.account.follows, 10), []);
|
||||
const reactFlowWrapper = useRef(null);
|
||||
const connectingNodeId = useRef(null);
|
||||
|
||||
const handleDragEnd = (event) => {
|
||||
console.log(event.id);
|
||||
};
|
||||
const initialNodes = [
|
||||
{
|
||||
id: '0',
|
||||
type: 'user',
|
||||
position: { x: 141, y: 0 },
|
||||
data: { list: [], title: '', pubkey: db.account.pubkey },
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
type: 'userGroup',
|
||||
position: { x: 0, y: 200 },
|
||||
data: { list: defaultContacts, title: 'Starting Point', pubkey: '' },
|
||||
},
|
||||
];
|
||||
const initialEdges = [{ id: 'e0-1', type: 'buttonedge', source: '0', target: '1' }];
|
||||
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||
|
||||
const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), []);
|
||||
|
||||
const onConnectStart = useCallback((_, { nodeId }) => {
|
||||
connectingNodeId.current = nodeId;
|
||||
}, []);
|
||||
|
||||
const onConnectEnd = useCallback(
|
||||
async (event) => {
|
||||
const targetIsPane = event.target.classList.contains('react-flow__pane');
|
||||
|
||||
if (targetIsPane) {
|
||||
const { top, left } = reactFlowWrapper.current.getBoundingClientRect();
|
||||
|
||||
const id = getId();
|
||||
const prevData = nodes.slice(-1)[0];
|
||||
const randomPubkey = getMultipleRandom(prevData.data.list, 1)[0];
|
||||
|
||||
const newContactList = await getContactsByPubkey(randomPubkey);
|
||||
const newNode = {
|
||||
id,
|
||||
type: 'userGroup',
|
||||
position: project({ x: event.clientX - left, y: event.clientY - top }),
|
||||
data: { list: newContactList, title: null, pubkey: randomPubkey },
|
||||
};
|
||||
|
||||
setNodes((nds) => nds.concat(newNode));
|
||||
setEdges((eds) =>
|
||||
eds.concat({
|
||||
id,
|
||||
type: 'buttonedge',
|
||||
source: connectingNodeId.current,
|
||||
target: id,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
[project]
|
||||
);
|
||||
|
||||
return (
|
||||
<DndContext onDragEnd={handleDragEnd}>
|
||||
<div className="scrollbar-hide flex h-full w-full flex-col items-center justify-center overflow-x-auto overflow-y-auto">
|
||||
<div className="flex items-center gap-16">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="text-sm font-semibold text-fuchsia-500">Follows</h3>
|
||||
<div className="grid grid-cols-5 gap-6 rounded-lg border border-fuchsia-500/50 bg-fuchsia-500/10 p-4">
|
||||
{data.map((user) => (
|
||||
<UserDrawer key={user} pubkey={user} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-16">
|
||||
<User pubkey={db.account.pubkey} variant="avatar" />
|
||||
<UserDropable />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DndContext>
|
||||
<div className="h-full w-full" ref={reactFlowWrapper}>
|
||||
<ReactFlow
|
||||
proOptions={{ hideAttribution: true }}
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
connectionLineComponent={Line}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
onConnectStart={onConnectStart}
|
||||
onConnectEnd={onConnectEnd}
|
||||
connectionMode={ConnectionMode.Loose}
|
||||
minZoom={0.8}
|
||||
maxZoom={1.2}
|
||||
fitView
|
||||
>
|
||||
<Background color="#3f3f46" />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ export function ChatsList() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{chats.follows.map((item) => renderItem(item))}
|
||||
{chats.unknowns.length > 0 && <UnknownsModal data={chats.unknowns} />}
|
||||
{chats?.follows?.map((item) => renderItem(item))}
|
||||
{chats?.unknowns?.length > 0 && <UnknownsModal data={chats.unknowns} />}
|
||||
<NewMessageModal />
|
||||
</div>
|
||||
);
|
||||
|
@ -24,7 +24,10 @@ export function Navigation() {
|
||||
|
||||
return (
|
||||
<Frame className="relative flex h-full w-[232px] flex-col" lighter>
|
||||
<div className="inline-flex h-16 w-full items-center justify-end px-3">
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className="inline-flex h-16 w-full items-center justify-end px-3"
|
||||
>
|
||||
<ComposerModal />
|
||||
</div>
|
||||
<div
|
||||
|
@ -36,7 +36,9 @@ export const User = memo(function User({
|
||||
|
||||
if (status === 'loading') {
|
||||
if (variant === 'avatar') {
|
||||
<div className="h-12 w-12 animate-pulse overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />;
|
||||
return (
|
||||
<div className="h-12 w-12 animate-pulse overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />
|
||||
);
|
||||
}
|
||||
|
||||
if (variant === 'mention') {
|
||||
|
22
src/stores/browse.ts
Normal file
22
src/stores/browse.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { create } from 'zustand';
|
||||
import { createJSONStorage, persist } from 'zustand/middleware';
|
||||
|
||||
interface BrowseState {
|
||||
data: Array<{ title: string; data: string[] }>;
|
||||
setData: ({ title, data }: { title: string; data: string[] }) => void;
|
||||
}
|
||||
|
||||
export const useBrowse = create<BrowseState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
data: [],
|
||||
setData: (data) => {
|
||||
set((state) => ({ data: [...state.data, data] }));
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'browseUsers',
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
}
|
||||
)
|
||||
);
|
@ -19,6 +19,7 @@ import { useStronghold } from '@stores/stronghold';
|
||||
|
||||
import { createBlobFromFile } from '@utils/createBlobFromFile';
|
||||
import { nHoursAgo } from '@utils/date';
|
||||
import { getMultipleRandom } from '@utils/transform';
|
||||
import { NDKEventWithReplies, NostrBuildResponse } from '@utils/types';
|
||||
|
||||
export function useNostr() {
|
||||
@ -278,6 +279,22 @@ export function useNostr() {
|
||||
return events;
|
||||
};
|
||||
|
||||
const getContactsByPubkey = async (pubkey: string) => {
|
||||
const user = ndk.getUser({ hexpubkey: pubkey });
|
||||
const follows = [...(await user.follows())].map((user) => user.hexpubkey);
|
||||
return getMultipleRandom([...follows], 10);
|
||||
};
|
||||
|
||||
const getEventsByPubkey = async (pubkey: string) => {
|
||||
const events = await fetcher.fetchAllEvents(
|
||||
relayUrls,
|
||||
{ authors: [pubkey], kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Article] },
|
||||
{ since: nHoursAgo(24) },
|
||||
{ sort: true }
|
||||
);
|
||||
return events as unknown as NDKEvent[];
|
||||
};
|
||||
|
||||
const publish = async ({
|
||||
content,
|
||||
kind,
|
||||
@ -421,5 +438,7 @@ export function useNostr() {
|
||||
publish,
|
||||
createZap,
|
||||
upload,
|
||||
getContactsByPubkey,
|
||||
getEventsByPubkey,
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user