diff --git a/src/Feed/EventPublisher.ts b/src/Feed/EventPublisher.ts index 2847ae5f..e670a579 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 a67949eb..9f810562 100644 --- a/src/Nostr/Connection.ts +++ b/src/Nostr/Connection.ts @@ -50,9 +50,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.Id = uuid(); @@ -78,8 +78,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(); } @@ -128,11 +128,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) { @@ -156,7 +155,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; @@ -178,7 +177,7 @@ export default class Connection { if (this.EventsCallback.has(id)) { let cb = this.EventsCallback.get(id)!; this.EventsCallback.delete(id); - cb(); + cb(msg); } break; } @@ -333,6 +332,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 = [ @@ -367,25 +371,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(); }); @@ -396,7 +408,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 b173c94a..b08d4e89 100644 --- a/src/Pages/Layout.tsx +++ b/src/Pages/Layout.tsx @@ -1,12 +1,12 @@ import "./Layout.css"; -import { useEffect, useState } from "react" +import { useEffect } from "react" import { useDispatch, useSelector } from "react-redux"; import { Outlet, useNavigate } from "react-router-dom"; import { faBell, faMessage, faSearch } 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" @@ -14,6 +14,8 @@ import ProfileImage from "Element/ProfileImage"; import useLoginFeed from "Feed/LoginFeed"; import { totalUnread } from "Pages/MessagesPage"; import { SearchRelays } from 'Const'; +import useEventPublisher from "Feed/EventPublisher"; +import { NIP42AuthChallenge, NIP42AuthResponse } from "Nostr/Auth"; export default function Layout() { const dispatch = useDispatch(); @@ -25,13 +27,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 [keyword, setKeyword] = useState(''); - + 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); } @@ -40,6 +49,10 @@ export default function Layout() { System.DisconnectRelay(k); } } + + return () => { + window.removeEventListener("nip42auth", nip42AuthEvent) + } } }, [relays]);