Wait for the signer before loading the UI (fix Ditto race condition)

environments/review-main-yi2y9f/deployments/4697^2
Alex Gleason 3 months ago
parent bbdc224010
commit 0b1446c655
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -8,7 +8,10 @@ const secretStorageKey = 'soapbox:nip46:secret';
sessionStorage.setItem(secretStorageKey, crypto.randomUUID());
function useSignerStream() {
const { relay, signer } = useNostr();
const [isSubscribed, setIsSubscribed] = useState(false);
const [isSubscribing, setIsSubscribing] = useState(true);
const { relay, signer, hasNostr } = useNostr();
const [pubkey, setPubkey] = useState<string | undefined>(undefined);
const authStorageKey = `soapbox:nostr:auth:${pubkey}`;
@ -16,7 +19,7 @@ function useSignerStream() {
useEffect(() => {
let isCancelled = false;
if (signer) {
if (signer && hasNostr) {
signer.getPublicKey().then((newPubkey) => {
if (!isCancelled) {
setPubkey(newPubkey);
@ -27,7 +30,7 @@ function useSignerStream() {
return () => {
isCancelled = true;
};
}, [signer]);
}, [signer, hasNostr]);
useEffect(() => {
if (!relay || !signer || !pubkey) return;
@ -39,6 +42,10 @@ function useSignerStream() {
localStorage.setItem(authStorageKey, authorizedPubkey);
sessionStorage.setItem(secretStorageKey, crypto.randomUUID());
},
onSubscribed() {
setIsSubscribed(true);
setIsSubscribing(false);
},
authorizedPubkey: localStorage.getItem(authStorageKey) ?? undefined,
getSecret: () => sessionStorage.getItem(secretStorageKey)!,
});
@ -47,6 +54,11 @@ function useSignerStream() {
connect.close();
};
}, [relay, signer, pubkey]);
return {
isSubscribed,
isSubscribing,
};
}
export { useSignerStream };

@ -8,6 +8,8 @@ import { useInstance } from 'soapbox/hooks/useInstance';
interface NostrContextType {
relay?: NRelay;
signer?: NostrSigner;
hasNostr: boolean;
isRelayOpen: boolean;
}
const NostrContext = createContext<NostrContextType | undefined>(undefined);
@ -18,7 +20,10 @@ interface NostrProviderProps {
export const NostrProvider: React.FC<NostrProviderProps> = ({ children }) => {
const instance = useInstance();
const hasNostr = !!instance.nostr;
const [relay, setRelay] = useState<NRelay1>();
const [isRelayOpen, setIsRelayOpen] = useState(false);
const { account } = useOwnAccount();
@ -30,17 +35,24 @@ export const NostrProvider: React.FC<NostrProviderProps> = ({ children }) => {
[accountPubkey],
);
const handleRelayOpen = () => {
setIsRelayOpen(true);
};
useEffect(() => {
if (url) {
setRelay(new NRelay1(url));
const relay = new NRelay1(url);
relay.socket.underlyingWebsocket.addEventListener('open', handleRelayOpen);
setRelay(relay);
}
return () => {
relay?.socket.underlyingWebsocket.removeEventListener('open', handleRelayOpen);
relay?.close();
};
}, [url]);
return (
<NostrContext.Provider value={{ relay, signer }}>
<NostrContext.Provider value={{ relay, signer, isRelayOpen, hasNostr }}>
{children}
</NostrContext.Provider>
);

@ -5,6 +5,7 @@ interface NConnectOpts {
signer: NostrSigner;
authorizedPubkey: string | undefined;
onAuthorize(pubkey: string): void;
onSubscribed(): void;
getSecret(): string;
}
@ -14,6 +15,7 @@ export class NConnect {
private signer: NostrSigner;
private authorizedPubkey: string | undefined;
private onAuthorize: (pubkey: string) => void;
private onSubscribed: () => void;
private getSecret: () => string;
private controller = new AbortController();
@ -23,6 +25,7 @@ export class NConnect {
this.signer = opts.signer;
this.authorizedPubkey = opts.authorizedPubkey;
this.onAuthorize = opts.onAuthorize;
this.onSubscribed = opts.onSubscribed;
this.getSecret = opts.getSecret;
this.open();
@ -32,7 +35,10 @@ export class NConnect {
const pubkey = await this.signer.getPublicKey();
const signal = this.controller.signal;
for await (const msg of this.relay.req([{ kinds: [24133], '#p': [pubkey] }], { signal })) {
const sub = this.relay.req([{ kinds: [24133], '#p': [pubkey] }], { signal });
this.onSubscribed();
for await (const msg of sub) {
if (msg[0] === 'EVENT') {
const event = msg[2];
this.handleEvent(event);

@ -6,6 +6,7 @@ import { fetchMe } from 'soapbox/actions/me';
import { loadSoapboxConfig } from 'soapbox/actions/soapbox';
import { useSignerStream } from 'soapbox/api/hooks/nostr/useSignerStream';
import LoadingScreen from 'soapbox/components/loading-screen';
import { useNostr } from 'soapbox/contexts/nostr-context';
import {
useAppSelector,
useAppDispatch,
@ -44,7 +45,8 @@ const SoapboxLoad: React.FC<ISoapboxLoad> = ({ children }) => {
const [localeLoading, setLocaleLoading] = useState(true);
const [isLoaded, setIsLoaded] = useState(false);
useSignerStream();
const { hasNostr, isRelayOpen } = useNostr();
const { isSubscribed } = useSignerStream();
/** Whether to display a loading indicator. */
const showLoading = [
@ -53,6 +55,7 @@ const SoapboxLoad: React.FC<ISoapboxLoad> = ({ children }) => {
!isLoaded,
localeLoading,
swUpdating,
hasNostr && (!isRelayOpen || !isSubscribed),
].some(Boolean);
// Load the user's locale

Loading…
Cancel
Save