diff --git a/package.json b/package.json index 2dc4681f..9db8d687 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 665e47b2..5852dfab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: diff --git a/src/app.tsx b/src/app.tsx index 916f432c..81cf7faa 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -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'; diff --git a/src/app/browse/components/edge.tsx b/src/app/browse/components/edge.tsx new file mode 100644 index 00000000..296996b1 --- /dev/null +++ b/src/app/browse/components/edge.tsx @@ -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 ( + + ); +} diff --git a/src/app/browse/components/groupTitle.tsx b/src/app/browse/components/groupTitle.tsx new file mode 100644 index 00000000..a01cd075 --- /dev/null +++ b/src/app/browse/components/groupTitle.tsx @@ -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
; + } + + return ( +

{`${ + user.name || user.display_name + }'s network`}

+ ); +}); diff --git a/src/app/browse/components/line.tsx b/src/app/browse/components/line.tsx new file mode 100644 index 00000000..993667c7 --- /dev/null +++ b/src/app/browse/components/line.tsx @@ -0,0 +1,14 @@ +export function Line({ fromX, fromY, toX, toY }) { + return ( + + + + + ); +} diff --git a/src/app/browse/components/userDrawer.tsx b/src/app/browse/components/userDrawer.tsx deleted file mode 100644 index f4234a2a..00000000 --- a/src/app/browse/components/userDrawer.tsx +++ /dev/null @@ -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 ( - - - - - - -
- {status === 'loading' ? ( -
-

Loading...

-
- ) : ( -
- {pubkey} -
-
-
- {user?.displayName || user?.name || 'No name'} -
- {user?.nip05 ? ( - - ) : ( - - {displayNpub(pubkey, 16)} - - )} -
-
- {user.about ? : null} -
-
- {followed ? ( - - ) : ( - - )} - - Message - -
-
-
- )} -
-
-
-
- ); -}); diff --git a/src/app/browse/components/userDropable.tsx b/src/app/browse/components/userDropable.tsx deleted file mode 100644 index b642f5a2..00000000 --- a/src/app/browse/components/userDropable.tsx +++ /dev/null @@ -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 ( -
- -
- ); -} diff --git a/src/app/browse/components/userGroupNode.tsx b/src/app/browse/components/userGroupNode.tsx new file mode 100644 index 00000000..7fe58277 --- /dev/null +++ b/src/app/browse/components/userGroupNode.tsx @@ -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 ( + <> + +
+ {data.title ? ( +

{data.title}

+ ) : ( + + )} +
+ {data.list.map((user: string) => ( + + ))} +
+
+ + + ); +} diff --git a/src/app/browse/components/userLatestPosts.tsx b/src/app/browse/components/userLatestPosts.tsx new file mode 100644 index 00000000..0c728a7c --- /dev/null +++ b/src/app/browse/components/userLatestPosts.tsx @@ -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 ( + + + + ); + case NDKKind.Repost: + return ; + case 1063: + return ( + + + + ); + case NDKKind.Article: + return ( + + + + ); + default: + return ( + + + + ); + } + }, + [data] + ); + + return ( +
+

Latest post

+
+ {status === 'loading' ? ( +
+
+ + Loading latest posts... +
+
+ ) : data.length < 1 ? ( +
+
+ No posts from 24 hours ago +
+
+ ) : ( + data.map((event) => renderItem(event)) + )} +
+
+ ); +} diff --git a/src/app/browse/components/userNode.tsx b/src/app/browse/components/userNode.tsx new file mode 100644 index 00000000..0f5af72e --- /dev/null +++ b/src/app/browse/components/userNode.tsx @@ -0,0 +1,21 @@ +import { Handle, Position } from 'reactflow'; + +import { User } from '@shared/user'; + +export function UserNode({ data }) { + return ( + <> +
+ +
+ +
+
+ + + ); +} diff --git a/src/app/browse/components/userWithDrawer.tsx b/src/app/browse/components/userWithDrawer.tsx new file mode 100644 index 00000000..4a0563e1 --- /dev/null +++ b/src/app/browse/components/userWithDrawer.tsx @@ -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 ( + + + + + + +
+ {status === 'loading' ? ( +
+

Loading...

+
+ ) : ( + <> +
+ {pubkey} +
+
+
+ {user?.displayName || user?.name || 'No name'} +
+ {user?.nip05 ? ( + + ) : ( + + {displayNpub(pubkey, 16)} + + )} + {user.about ? : null} +
+
+ {followed ? ( + + ) : ( + + )} + + Message + +
+
+
+ + + )} +
+
+
+
+ ); +}); diff --git a/src/app/browse/index.tsx b/src/app/browse/index.tsx index 56db7b7d..3f095f29 100644 --- a/src/app/browse/index.tsx +++ b/src/app/browse/index.tsx @@ -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 ( -
-
-
-
- - 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 - - - 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 - + +
+
+
+
+ + 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 + + + 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 + +
+
+
+
+
-
-
- -
-
- -
-
+ ); } diff --git a/src/app/browse/users.tsx b/src/app/browse/users.tsx index 522b003e..c638c546 100644 --- a/src/app/browse/users.tsx +++ b/src/app/browse/users.tsx @@ -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 ( - -
-
-
-

Follows

-
- {data.map((user) => ( - - ))} -
-
-
- - -
-
-
-
+
+ + + +
); } diff --git a/src/app/chats/components/list.tsx b/src/app/chats/components/list.tsx index d4960fc2..86fbccf7 100644 --- a/src/app/chats/components/list.tsx +++ b/src/app/chats/components/list.tsx @@ -42,8 +42,8 @@ export function ChatsList() { return (
- {chats.follows.map((item) => renderItem(item))} - {chats.unknowns.length > 0 && } + {chats?.follows?.map((item) => renderItem(item))} + {chats?.unknowns?.length > 0 && }
); diff --git a/src/shared/navigation.tsx b/src/shared/navigation.tsx index 1af0df66..2844924f 100644 --- a/src/shared/navigation.tsx +++ b/src/shared/navigation.tsx @@ -24,7 +24,10 @@ export function Navigation() { return ( -
+
; + return ( +
+ ); } if (variant === 'mention') { diff --git a/src/stores/browse.ts b/src/stores/browse.ts new file mode 100644 index 00000000..197e87b9 --- /dev/null +++ b/src/stores/browse.ts @@ -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()( + persist( + (set) => ({ + data: [], + setData: (data) => { + set((state) => ({ data: [...state.data, data] })); + }, + }), + { + name: 'browseUsers', + storage: createJSONStorage(() => localStorage), + } + ) +); diff --git a/src/utils/hooks/useNostr.ts b/src/utils/hooks/useNostr.ts index c7197faa..1d09fc09 100644 --- a/src/utils/hooks/useNostr.ts +++ b/src/utils/hooks/useNostr.ts @@ -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, }; }