Merge branch 'nostr-race' into 'main'
Wait for the signer before loading the UI (fix Ditto race condition) See merge request soapbox-pub/soapbox!3064
This commit is contained in:
commit
19f5440b8b
|
@ -8,7 +8,10 @@ const secretStorageKey = 'soapbox:nip46:secret';
|
||||||
sessionStorage.setItem(secretStorageKey, crypto.randomUUID());
|
sessionStorage.setItem(secretStorageKey, crypto.randomUUID());
|
||||||
|
|
||||||
function useSignerStream() {
|
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 [pubkey, setPubkey] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
const authStorageKey = `soapbox:nostr:auth:${pubkey}`;
|
const authStorageKey = `soapbox:nostr:auth:${pubkey}`;
|
||||||
|
@ -16,7 +19,7 @@ function useSignerStream() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isCancelled = false;
|
let isCancelled = false;
|
||||||
|
|
||||||
if (signer) {
|
if (signer && hasNostr) {
|
||||||
signer.getPublicKey().then((newPubkey) => {
|
signer.getPublicKey().then((newPubkey) => {
|
||||||
if (!isCancelled) {
|
if (!isCancelled) {
|
||||||
setPubkey(newPubkey);
|
setPubkey(newPubkey);
|
||||||
|
@ -27,7 +30,7 @@ function useSignerStream() {
|
||||||
return () => {
|
return () => {
|
||||||
isCancelled = true;
|
isCancelled = true;
|
||||||
};
|
};
|
||||||
}, [signer]);
|
}, [signer, hasNostr]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!relay || !signer || !pubkey) return;
|
if (!relay || !signer || !pubkey) return;
|
||||||
|
@ -39,6 +42,10 @@ function useSignerStream() {
|
||||||
localStorage.setItem(authStorageKey, authorizedPubkey);
|
localStorage.setItem(authStorageKey, authorizedPubkey);
|
||||||
sessionStorage.setItem(secretStorageKey, crypto.randomUUID());
|
sessionStorage.setItem(secretStorageKey, crypto.randomUUID());
|
||||||
},
|
},
|
||||||
|
onSubscribed() {
|
||||||
|
setIsSubscribed(true);
|
||||||
|
setIsSubscribing(false);
|
||||||
|
},
|
||||||
authorizedPubkey: localStorage.getItem(authStorageKey) ?? undefined,
|
authorizedPubkey: localStorage.getItem(authStorageKey) ?? undefined,
|
||||||
getSecret: () => sessionStorage.getItem(secretStorageKey)!,
|
getSecret: () => sessionStorage.getItem(secretStorageKey)!,
|
||||||
});
|
});
|
||||||
|
@ -47,6 +54,11 @@ function useSignerStream() {
|
||||||
connect.close();
|
connect.close();
|
||||||
};
|
};
|
||||||
}, [relay, signer, pubkey]);
|
}, [relay, signer, pubkey]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isSubscribed,
|
||||||
|
isSubscribing,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useSignerStream };
|
export { useSignerStream };
|
||||||
|
|
|
@ -8,6 +8,8 @@ import { useInstance } from 'soapbox/hooks/useInstance';
|
||||||
interface NostrContextType {
|
interface NostrContextType {
|
||||||
relay?: NRelay;
|
relay?: NRelay;
|
||||||
signer?: NostrSigner;
|
signer?: NostrSigner;
|
||||||
|
hasNostr: boolean;
|
||||||
|
isRelayOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NostrContext = createContext<NostrContextType | undefined>(undefined);
|
const NostrContext = createContext<NostrContextType | undefined>(undefined);
|
||||||
|
@ -18,7 +20,10 @@ interface NostrProviderProps {
|
||||||
|
|
||||||
export const NostrProvider: React.FC<NostrProviderProps> = ({ children }) => {
|
export const NostrProvider: React.FC<NostrProviderProps> = ({ children }) => {
|
||||||
const instance = useInstance();
|
const instance = useInstance();
|
||||||
|
const hasNostr = !!instance.nostr;
|
||||||
|
|
||||||
const [relay, setRelay] = useState<NRelay1>();
|
const [relay, setRelay] = useState<NRelay1>();
|
||||||
|
const [isRelayOpen, setIsRelayOpen] = useState(false);
|
||||||
|
|
||||||
const { account } = useOwnAccount();
|
const { account } = useOwnAccount();
|
||||||
|
|
||||||
|
@ -30,17 +35,24 @@ export const NostrProvider: React.FC<NostrProviderProps> = ({ children }) => {
|
||||||
[accountPubkey],
|
[accountPubkey],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleRelayOpen = () => {
|
||||||
|
setIsRelayOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (url) {
|
if (url) {
|
||||||
setRelay(new NRelay1(url));
|
const relay = new NRelay1(url);
|
||||||
|
relay.socket.underlyingWebsocket.addEventListener('open', handleRelayOpen);
|
||||||
|
setRelay(relay);
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
|
relay?.socket.underlyingWebsocket.removeEventListener('open', handleRelayOpen);
|
||||||
relay?.close();
|
relay?.close();
|
||||||
};
|
};
|
||||||
}, [url]);
|
}, [url]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NostrContext.Provider value={{ relay, signer }}>
|
<NostrContext.Provider value={{ relay, signer, isRelayOpen, hasNostr }}>
|
||||||
{children}
|
{children}
|
||||||
</NostrContext.Provider>
|
</NostrContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,6 +5,7 @@ interface NConnectOpts {
|
||||||
signer: NostrSigner;
|
signer: NostrSigner;
|
||||||
authorizedPubkey: string | undefined;
|
authorizedPubkey: string | undefined;
|
||||||
onAuthorize(pubkey: string): void;
|
onAuthorize(pubkey: string): void;
|
||||||
|
onSubscribed(): void;
|
||||||
getSecret(): string;
|
getSecret(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ export class NConnect {
|
||||||
private signer: NostrSigner;
|
private signer: NostrSigner;
|
||||||
private authorizedPubkey: string | undefined;
|
private authorizedPubkey: string | undefined;
|
||||||
private onAuthorize: (pubkey: string) => void;
|
private onAuthorize: (pubkey: string) => void;
|
||||||
|
private onSubscribed: () => void;
|
||||||
private getSecret: () => string;
|
private getSecret: () => string;
|
||||||
|
|
||||||
private controller = new AbortController();
|
private controller = new AbortController();
|
||||||
|
@ -23,6 +25,7 @@ export class NConnect {
|
||||||
this.signer = opts.signer;
|
this.signer = opts.signer;
|
||||||
this.authorizedPubkey = opts.authorizedPubkey;
|
this.authorizedPubkey = opts.authorizedPubkey;
|
||||||
this.onAuthorize = opts.onAuthorize;
|
this.onAuthorize = opts.onAuthorize;
|
||||||
|
this.onSubscribed = opts.onSubscribed;
|
||||||
this.getSecret = opts.getSecret;
|
this.getSecret = opts.getSecret;
|
||||||
|
|
||||||
this.open();
|
this.open();
|
||||||
|
@ -32,7 +35,10 @@ export class NConnect {
|
||||||
const pubkey = await this.signer.getPublicKey();
|
const pubkey = await this.signer.getPublicKey();
|
||||||
const signal = this.controller.signal;
|
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') {
|
if (msg[0] === 'EVENT') {
|
||||||
const event = msg[2];
|
const event = msg[2];
|
||||||
this.handleEvent(event);
|
this.handleEvent(event);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { fetchMe } from 'soapbox/actions/me';
|
||||||
import { loadSoapboxConfig } from 'soapbox/actions/soapbox';
|
import { loadSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||||
import { useSignerStream } from 'soapbox/api/hooks/nostr/useSignerStream';
|
import { useSignerStream } from 'soapbox/api/hooks/nostr/useSignerStream';
|
||||||
import LoadingScreen from 'soapbox/components/loading-screen';
|
import LoadingScreen from 'soapbox/components/loading-screen';
|
||||||
|
import { useNostr } from 'soapbox/contexts/nostr-context';
|
||||||
import {
|
import {
|
||||||
useAppSelector,
|
useAppSelector,
|
||||||
useAppDispatch,
|
useAppDispatch,
|
||||||
|
@ -44,7 +45,8 @@ const SoapboxLoad: React.FC<ISoapboxLoad> = ({ children }) => {
|
||||||
const [localeLoading, setLocaleLoading] = useState(true);
|
const [localeLoading, setLocaleLoading] = useState(true);
|
||||||
const [isLoaded, setIsLoaded] = useState(false);
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
|
|
||||||
useSignerStream();
|
const { hasNostr, isRelayOpen } = useNostr();
|
||||||
|
const { isSubscribed } = useSignerStream();
|
||||||
|
|
||||||
/** Whether to display a loading indicator. */
|
/** Whether to display a loading indicator. */
|
||||||
const showLoading = [
|
const showLoading = [
|
||||||
|
@ -53,6 +55,7 @@ const SoapboxLoad: React.FC<ISoapboxLoad> = ({ children }) => {
|
||||||
!isLoaded,
|
!isLoaded,
|
||||||
localeLoading,
|
localeLoading,
|
||||||
swUpdating,
|
swUpdating,
|
||||||
|
hasNostr && (!isRelayOpen || !isSubscribed),
|
||||||
].some(Boolean);
|
].some(Boolean);
|
||||||
|
|
||||||
// Load the user's locale
|
// Load the user's locale
|
||||||
|
|
Loading…
Reference in New Issue