diff --git a/package.json b/package.json index 22196a3c..1e3cf888 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "next": "^13.2.1", "next-remove-imports": "^1.0.10", "nostr-react": "^0.6.4", + "nostr-relaypool": "^0.5.3", "nostr-tools": "^1.6.0", "qrcode.react": "^3.1.0", "react": "^18.2.0", @@ -37,7 +38,8 @@ "react-player": "^2.11.2", "react-virtuoso": "^4.1.0", "tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql", - "unique-names-generator": "^4.7.1" + "unique-names-generator": "^4.7.1", + "ws": "^8.12.1" }, "devDependencies": { "@tailwindcss/typography": "^0.5.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b13df7b0..7c7bb1c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,7 @@ specifiers: next: ^13.2.1 next-remove-imports: ^1.0.10 nostr-react: ^0.6.4 + nostr-relaypool: ^0.5.3 nostr-tools: ^1.6.0 postcss: ^8.4.21 prettier: ^2.8.4 @@ -50,6 +51,7 @@ specifiers: tauri-plugin-sql-api: github:tauri-apps/tauri-plugin-sql typescript: ^4.9.5 unique-names-generator: ^4.7.1 + ws: ^8.12.1 dependencies: '@nanostores/persistent': 0.7.0_nanostores@0.7.4 @@ -68,6 +70,7 @@ dependencies: next: 13.2.1_biqbaboplfbrettd7655fr4n2y next-remove-imports: 1.0.10 nostr-react: 0.6.4_react@18.2.0 + nostr-relaypool: 0.5.3_ws@8.12.1 nostr-tools: 1.6.0 qrcode.react: 3.1.0_react@18.2.0 react: 18.2.0 @@ -78,6 +81,7 @@ dependencies: react-virtuoso: 4.1.0_biqbaboplfbrettd7655fr4n2y tauri-plugin-sql-api: github.com/tauri-apps/tauri-plugin-sql/abd8759ef49e1ba441540a2260b453d43d86c7ee unique-names-generator: 4.7.1 + ws: 8.12.1 devDependencies: '@tailwindcss/typography': 0.5.9_tailwindcss@3.2.7 @@ -411,6 +415,15 @@ packages: resolution: { integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== } dev: true + /@jest/source-map/29.4.3: + resolution: { integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== } + engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + dependencies: + '@jridgewell/trace-mapping': 0.3.17 + callsites: 3.1.0 + graceful-fs: 4.2.10 + dev: false + /@jridgewell/gen-mapping/0.1.1: resolution: { integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== } engines: { node: '>=6.0.0' } @@ -1729,7 +1742,6 @@ packages: /callsites/3.1.0: resolution: { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== } engines: { node: '>=6' } - dev: true /camelcase-css/2.0.1: resolution: { integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== } @@ -2734,7 +2746,6 @@ packages: /graceful-fs/4.2.10: resolution: { integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== } - dev: true /grapheme-splitter/1.0.4: resolution: { integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== } @@ -3187,6 +3198,14 @@ packages: resolution: { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } dev: true + /isomorphic-ws/5.0.0_ws@8.12.1: + resolution: { integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== } + peerDependencies: + ws: '*' + dependencies: + ws: 8.12.1 + dev: false + /javascript-natural-sort/0.7.1: resolution: { integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== } dev: true @@ -3983,6 +4002,17 @@ packages: - jotai-zustand dev: false + /nostr-relaypool/0.5.3_ws@8.12.1: + resolution: { integrity: sha512-1INGKleOTuUTFUs3RnnZrew4+G/idLUewh44WBtmTTJ9g+kRiQtMMaBGTVUpf9621nBNleEVOB8p3XSNcaX3FQ== } + dependencies: + '@jest/source-map': 29.4.3 + isomorphic-ws: 5.0.0_ws@8.12.1 + nostr-tools: 1.6.0 + safe-stable-stringify: 2.4.2 + transitivePeerDependencies: + - ws + dev: false + /nostr-tools/1.6.0: resolution: { integrity: sha512-qjjJQ7YxJUMzgS24eVlxkZ87PKJtU6dlH04OzVuK6w+GSPL+VdUZkMe2lfSpnb7OkCrDIzmbFbtx+Q4LXdU2xw== } dependencies: @@ -4824,6 +4854,11 @@ packages: is-regex: 1.1.4 dev: true + /safe-stable-stringify/2.4.2: + resolution: { integrity: sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA== } + engines: { node: '>=10' } + dev: false + /scheduler/0.23.0: resolution: { integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== } dependencies: @@ -5447,6 +5482,19 @@ packages: resolution: { integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== } dev: true + /ws/8.12.1: + resolution: { integrity: sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== } + engines: { node: '>=10.0.0' } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + /xtend/4.0.2: resolution: { integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== } engines: { node: '>=0.4' } diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 7f2d2930..97c17e48 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import RelayProvider from '@stores/context'; import { relays } from '@stores/relays'; import { useStore } from '@nanostores/react'; import type { NextPage } from 'next'; import type { AppProps } from 'next/app'; -import { NostrProvider } from 'nostr-react'; -import type { ReactElement, ReactNode } from 'react'; +import { ReactElement, ReactNode } from 'react'; import '../App.css'; @@ -21,12 +21,8 @@ type AppPropsWithLayout = AppProps & { export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { // Use the layout defined at the page level, if available const getLayout = Component.getLayout ?? ((page) => page); - // Get relays + // Get all relays const $relays = useStore(relays); - return ( - - {getLayout()} - - ); + return {getLayout()}; } diff --git a/src/pages/feed/following.tsx b/src/pages/feed/following.tsx index 00766a4a..1ad966fc 100644 --- a/src/pages/feed/following.tsx +++ b/src/pages/feed/following.tsx @@ -1,54 +1,59 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import BaseLayout from '@layouts/baseLayout'; import NewsFeedLayout from '@layouts/newsfeedLayout'; -import { Placeholder } from '@components/note/placeholder'; -import { Thread } from '@components/thread'; - import { hoursAgo } from '@utils/getDate'; +import { RelayContext } from '@stores/context'; import { follows } from '@stores/follows'; +import { relays } from '@stores/relays'; import { useStore } from '@nanostores/react'; -import { dateToUnix, useNostrEvents } from 'nostr-react'; -import { - JSXElementConstructor, - ReactElement, - ReactFragment, - ReactPortal, - Suspense, - useRef, -} from 'react'; +import { dateToUnix } from 'nostr-react'; +import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useEffect, useRef, useState } from 'react'; export default function Page() { - const $follows = useStore(follows); + const relayPool: any = useContext(RelayContext); + + const [data, setData] = useState([]); const now = useRef(new Date()); - const { events } = useNostrEvents({ - filter: { - authors: $follows, - since: dateToUnix(hoursAgo(6, now.current)), - kinds: [1], - limit: 100, - }, - }); + const $follows = useStore(follows); + const $relays = useStore(relays); + + useEffect(() => { + const unsub = relayPool.subscribe( + [ + { + kinds: [1], + authors: $follows, + since: dateToUnix(hoursAgo(12, now.current)), + }, + ], + $relays, + (event: any) => { + setData((data) => [event, ...data]); + }, + undefined, + (events: any, relayURL: any) => { + console.log(events, relayURL); + } + ); + + return () => unsub(); + }, [$follows, $relays, relayPool]); return (
- }> - - + {data.map((item, index) => ( +

{item.id}

+ ))}
); } Page.getLayout = function getLayout( - page: - | string - | number - | boolean - | ReactElement> - | ReactFragment - | ReactPortal + page: string | number | boolean | ReactElement> | ReactFragment | ReactPortal ) { return ( diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 06993360..14a98545 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -40,7 +40,7 @@ export default function Page() { return result; }, []); - const getFollows = useCallback(async (account) => { + const getFollows = useCallback(async (account: { id: string }) => { const arr = []; const result: any = await db.select(`SELECT pubkey FROM follows WHERE account = "${account.id}"`); @@ -82,7 +82,7 @@ export default function Page() { }) .catch(console.error); }); - }, [getAccount, getFollows, requestNotification, router]); + }, [requestNotification, getAccount, getFollows, router]); return (
diff --git a/src/stores/context.tsx b/src/stores/context.tsx new file mode 100644 index 00000000..41b30048 --- /dev/null +++ b/src/stores/context.tsx @@ -0,0 +1,11 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { RelayPool } from 'nostr-relaypool'; +import { createContext, useMemo } from 'react'; + +export const RelayContext = createContext({}); + +export default function RelayProvider({ relays, children }: { relays: any; children: React.ReactNode }) { + const value = useMemo(() => new RelayPool(relays, { useEventCache: true }), [relays]); + + return {children}; +}