diff --git a/src/Feed/EventPublisher.ts b/src/Feed/EventPublisher.ts index a3a8b5fa..bbd7b08b 100644 --- a/src/Feed/EventPublisher.ts +++ b/src/Feed/EventPublisher.ts @@ -90,22 +90,8 @@ export default function useEventPublisher() { return Promise.reject(); } - useEffect(() =>{ - const nip42AuthEvent = async (event: NIP42AuthChallenge) => { - if(event.challenge && event.relay) { - const signedEvent = await nip42Auth(event.challenge, event.relay); - const response = new NIP42AuthResponse(event.challenge, signedEvent); - window.dispatchEvent(response); - } - } - window.addEventListener("nip42auth", nip42AuthEvent) - - return () => { - window.removeEventListener("nip42auth", nip42AuthEvent) - } - }, []) - return { + nip42Auth: nip42Auth, broadcast: (ev: NEvent | undefined) => { if (ev) { console.debug("Sending event: ", ev); diff --git a/src/Nostr/Connection.ts b/src/Nostr/Connection.ts index f0b3dff0..3b2e645c 100644 --- a/src/Nostr/Connection.ts +++ b/src/Nostr/Connection.ts @@ -47,9 +47,9 @@ export default class Connection { LastState: Readonly; IsClosed: boolean; ReconnectTimer: ReturnType | null; - EventsCallback: Map void>; + EventsCallback: Map void>; + AwaitingAuth: Map; Authed: boolean; - AwaitingAuth: boolean; constructor(addr: string, options: RelaySettings) { this.Address = addr; @@ -74,8 +74,8 @@ export default class Connection { this.IsClosed = false; this.ReconnectTimer = null; this.EventsCallback = new Map(); + this.AwaitingAuth = new Map(); this.Authed = false; - this.AwaitingAuth = false; this.Connect(); } @@ -124,11 +124,10 @@ export default class Connection { this.ConnectTimeout = DefaultConnectTimeout; console.log(`[${this.Address}] Open!`); setTimeout(() => { - if(this.AwaitingAuth) { - return + if(this.Authed || this.AwaitingAuth.size === 0) { + this._InitSubscriptions(); } - this._InitSubscriptions(); - }, 500) + }, 150) } OnClose(e: CloseEvent) { @@ -152,7 +151,7 @@ export default class Connection { let tag = msg[0]; switch (tag) { case "AUTH": { - this._OnAuth(msg[1]) + this._OnAuthAsync(msg[1]) this.Stats.EventsReceived++; this._UpdateState(); break; @@ -174,7 +173,7 @@ export default class Connection { if (this.EventsCallback.has(id)) { let cb = this.EventsCallback.get(id)!; this.EventsCallback.delete(id); - cb(); + cb(msg); } break; } @@ -316,6 +315,11 @@ export default class Connection { } _SendSubscription(sub: Subscriptions) { + if(!this.Authed && this.AwaitingAuth.size > 0) { + this.Pending.push(sub); + return; + } + let req = ["REQ", sub.Id, sub.ToObject()]; if (sub.OrSubs.length > 0) { req = [ @@ -350,25 +354,33 @@ export default class Connection { } } - async _OnAuth(challenge: string, timeout: number = 5000):Promise { + async _OnAuthAsync(challenge: string) { const challengeEvent = new NIP42AuthChallenge(challenge, this.Address) + const authCleanup = () => { + this.AwaitingAuth.delete(challenge) + } + const authCallback = (e:NIP42AuthResponse):Promise => { - return new Promise((resolve,reject) => { + window.removeEventListener(`nip42response:${challenge}`, authCallback) + return new Promise((resolve,_) => { if(!e.event) { + authCleanup(); return Promise.reject('no event'); } let t = setTimeout(() => { - window.removeEventListener(`nip42response:${challenge}`, authCallback); - this.AwaitingAuth = false; - reject('timeout'); - }, timeout); + authCleanup(); + resolve(); + }, 10_000); - this.EventsCallback.set(e.event.Id, () => { + this.EventsCallback.set(e.event.Id, (msg:any[]) => { clearTimeout(t); - this.AwaitingAuth = false; - window.removeEventListener(`nip42response:${challenge}`, authCallback) + authCleanup(); + if(msg.length > 3 && msg[2] === true) { + this.Authed = true; + this._InitSubscriptions(); + } resolve(); }); @@ -379,7 +391,7 @@ export default class Connection { }) } - this.AwaitingAuth = true; + this.AwaitingAuth.set(challenge, true) window.addEventListener(`nip42response:${challenge}`, authCallback) window.dispatchEvent(challengeEvent) } diff --git a/src/Pages/Layout.tsx b/src/Pages/Layout.tsx index dffdc48a..2eb5b18f 100644 --- a/src/Pages/Layout.tsx +++ b/src/Pages/Layout.tsx @@ -6,13 +6,15 @@ import { faBell, faMessage } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { RootState } from "State/Store"; -import { init, setPreferences, UserPreferences } from "State/Login"; +import { init, UserPreferences } from "State/Login"; import { HexKey, RawEvent, TaggedRawEvent } from "Nostr"; import { RelaySettings } from "Nostr/Connection"; import { System } from "Nostr/System" import ProfileImage from "Element/ProfileImage"; import useLoginFeed from "Feed/LoginFeed"; import { totalUnread } from "Pages/MessagesPage"; +import useEventPublisher from "Feed/EventPublisher"; +import { NIP42AuthChallenge, NIP42AuthResponse } from "Nostr/Auth"; export default function Layout() { const dispatch = useDispatch(); @@ -24,10 +26,20 @@ export default function Layout() { const readNotifications = useSelector(s => s.login.readNotifications); const dms = useSelector(s => s.login.dms); const prefs = useSelector(s => s.login.preferences); + const pub = useEventPublisher(); useLoginFeed(); useEffect(() => { if (relays) { + const nip42AuthEvent = async (event: NIP42AuthChallenge) => { + if(event.challenge && event.relay) { + const signedEvent = await pub.nip42Auth(event.challenge, event.relay); + const response = new NIP42AuthResponse(event.challenge, signedEvent); + window.dispatchEvent(response); + } + } + window.addEventListener("nip42auth", nip42AuthEvent) + for (let [k, v] of Object.entries(relays)) { System.ConnectToRelay(k, v); } @@ -36,6 +48,10 @@ export default function Layout() { System.DisconnectRelay(k); } } + + return () => { + window.removeEventListener("nip42auth", nip42AuthEvent) + } } }, [relays]);