Add NostrContext to manage connection to Nostr relay

environments/review-ditto-auth-csh1vn/deployments/4454
Alex Gleason 7 months ago
parent fc0de1bc49
commit b9a0c1f0f6
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -1,23 +1,12 @@
import { type NostrEvent } from '@soapbox/nspec';
import { NiceRelay } from 'nostr-machina';
import { useEffect, useMemo } from 'react';
import { useEffect } from 'react';
import { signer } from 'soapbox/features/nostr/sign';
import { useInstance } from 'soapbox/hooks';
import { useNostr } from 'soapbox/contexts/nostr-context';
import { connectRequestSchema, nwcRequestSchema } from 'soapbox/schemas/nostr';
import { jsonSchema } from 'soapbox/schemas/utils';
function useSignerStream() {
const instance = useInstance();
const relayUrl = instance.nostr?.relay;
const pubkey = instance.nostr?.pubkey;
const relay = useMemo(() => {
if (relayUrl && signer) {
return new NiceRelay(relayUrl);
}
}, [relayUrl, !!signer]);
const { relay, pubkey, signer } = useNostr();
async function handleConnectEvent(event: NostrEvent) {
if (!relay || !pubkey || !signer) return;
@ -42,7 +31,7 @@ function useSignerStream() {
created_at: Math.floor(Date.now() / 1000),
});
relay.send(['EVENT', respEvent]);
relay.event(respEvent);
}
async function handleWalletEvent(event: NostrEvent) {
@ -61,28 +50,26 @@ function useSignerStream() {
await window.webln?.sendPayment(reqMsg.data.params.invoice);
}
async function handleEvent(event: NostrEvent) {
switch (event.kind) {
case 24133:
await handleConnectEvent(event);
break;
case 23194:
await handleWalletEvent(event);
break;
}
}
useEffect(() => {
if (!relay || !pubkey) return;
const sub = relay.req([{ kinds: [24133, 23194], authors: [pubkey], limit: 0 }]);
const readEvents = async () => {
for await (const event of sub) {
switch (event.kind) {
case 24133:
await handleConnectEvent(event);
break;
case 23194:
await handleWalletEvent(event);
break;
}
}
};
readEvents();
(async() => {
for await (const msg of relay.req([{ kinds: [24133, 23194], authors: [pubkey], limit: 0 }])) {
if (msg[0] === 'EVENT') handleEvent(msg[2]);
}
})();
return () => {
relay?.close();
};
}, [relay, pubkey]);
}

@ -0,0 +1,48 @@
import { NRelay, NRelay1, NostrSigner } from '@soapbox/nspec';
import React, { createContext, useContext, useState, useEffect } from 'react';
import { signer } from 'soapbox/features/nostr/sign';
import { useInstance } from 'soapbox/hooks/useInstance';
interface NostrContextType {
relay?: NRelay;
pubkey?: string;
signer?: NostrSigner;
}
const NostrContext = createContext<NostrContextType | undefined>(undefined);
interface NostrProviderProps {
children: React.ReactNode;
}
export const NostrProvider: React.FC<NostrProviderProps> = ({ children }) => {
const instance = useInstance();
const [relay, setRelay] = useState<NRelay1>();
const url = instance.nostr?.relay;
const pubkey = instance.nostr?.pubkey;
useEffect(() => {
if (url) {
setRelay(new NRelay1(url));
}
return () => {
relay?.close();
};
}, [url]);
return (
<NostrContext.Provider value={{ relay, pubkey, signer }}>
{children}
</NostrContext.Provider>
);
};
export const useNostr = () => {
const context = useContext(NostrContext);
if (context === undefined) {
throw new Error('useNostr must be used within a NostrProvider');
}
return context;
};

@ -1,5 +1,6 @@
import { NSchema as n, NostrSigner, NSecSigner } from '@soapbox/nspec';
import { getPublicKey, nip19 } from 'nostr-tools';
import { z } from 'zod';
import { lockStorageKey } from 'soapbox/utils/storage';
@ -34,7 +35,7 @@ export class NKeyStorage implements ReadonlyMap<string, NostrSigner> {
}
}
#dataSchema() {
#dataSchema(): z.ZodType<`nsec1${string}`[]> {
return n.json().pipe(n.bech32('nsec').array());
}

@ -2,6 +2,7 @@ import { QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import { Provider } from 'react-redux';
import { NostrProvider } from 'soapbox/contexts/nostr-context';
import { StatProvider } from 'soapbox/contexts/stat-context';
import { createGlobals } from 'soapbox/globals';
import { queryClient } from 'soapbox/queries/client';
@ -29,11 +30,13 @@ const Soapbox: React.FC = () => {
<Provider store={store}>
<QueryClientProvider client={queryClient}>
<StatProvider>
<SoapboxHead>
<SoapboxLoad>
<SoapboxMount />
</SoapboxLoad>
</SoapboxHead>
<NostrProvider>
<SoapboxHead>
<SoapboxLoad>
<SoapboxMount />
</SoapboxLoad>
</SoapboxHead>
</NostrProvider>
</StatProvider>
</QueryClientProvider>
</Provider>

Loading…
Cancel
Save