From 9242781670e2d3ef5ae0ab21dfafff5226bad2e3 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 3 Aug 2023 14:55:52 -0400 Subject: [PATCH] Adds an active relay information to the screen. --- index.html | 10 +++- js/nostr-broadcast.js | 36 ++++++++------ js/nostr-utils.js | 106 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 122 insertions(+), 30 deletions(-) diff --git a/index.html b/index.html index b2fbcf1..27c39d5 100644 --- a/index.html +++ b/index.html @@ -108,11 +108,17 @@ id="fetching-progress" name="fetching-progress" min="0" - max="300" + max="180" value="0" style="visibility: hidden" />

+
+

+
+
+

+

@@ -125,7 +131,7 @@ id="broadcasting-progress" name="broadcasting-progress" min="0" - max="300" + max="180" value="0" style="visibility: hidden" />

diff --git a/js/nostr-broadcast.js b/js/nostr-broadcast.js index fe5d2df..f4707ba 100644 --- a/js/nostr-broadcast.js +++ b/js/nostr-broadcast.js @@ -16,6 +16,7 @@ const fetchAndBroadcast = async () => { fetching: 'Fetching from relays... ', download: `Downloading Backup file... ${checkMark}`, } + $('#checking-relays-header').text("Waiting for Relays: ") // parse pubkey ('npub' or hexa) const pubkey = parsePubkey($('#pubkey').val()) if (!pubkey) return @@ -25,18 +26,22 @@ const fetchAndBroadcast = async () => { $('#fetching-status').text(txt.fetching) // show and update fetching progress bar $('#fetching-progress').css('visibility', 'visible') - const fetchInterval = setInterval(() => { - // update fetching progress bar - const currValue = parseInt($('#fetching-progress').val()) - $('#fetching-progress').val(currValue + 1) - }, 1000) + $('#fetching-progress').prop('max', relays.length) + + $('#checking-relays-header-box').css('display', 'flex') + $('#checking-relays-box').css('display', 'flex') + $('#checking-relays-header').text("Waiting for Relays:") + // get all events from relays const filters =[{ authors: [pubkey] }, { "#p": [pubkey] }] const data = await getEvents(filters) + + $('#checking-relays-header-box').css('display', 'none') + $('#checking-relays-box').css('display', 'none') + // inform user fetching is done $('#fetching-status').html(txt.fetching + checkMark) - clearInterval(fetchInterval) - $('#fetching-progress').val(300) + $('#fetching-progress').val(relays.length) // inform user that backup file (js format) is being downloaded $('#file-download').html(txt.download) downloadFile(data, 'nostr-backup.js') @@ -44,16 +49,19 @@ const fetchAndBroadcast = async () => { $('#broadcasting-status').html(txt.broadcasting) // show and update broadcasting progress bar $('#broadcasting-progress').css('visibility', 'visible') - const broadcastInterval = setInterval(() => { - // update fetching progress bar - const currValue = parseInt($('#broadcasting-progress').val()) - $('#broadcasting-progress').val(currValue + 1) - }, 1000) + $('#broadcasting-progress').prop('max', relays.length) + + $('#checking-relays-header-box').css('display', 'flex') + $('#checking-relays-box').css('display', 'flex') + $('#checking-relays-header').text("Waiting for Relays:") + await broadcastEvents(data) + + $('#checking-relays-header-box').css('display', 'none') + $('#checking-relays-box').css('display', 'none') // inform user that broadcasting is done $('#broadcasting-status').html(txt.broadcasting + checkMark) - clearInterval(broadcastInterval) - $('#broadcasting-progress').val(300) + $('#broadcasting-progress').val(relays.length) // re-enable broadcast button $('#fetch-and-broadcast').prop('disabled', false) } diff --git a/js/nostr-utils.js b/js/nostr-utils.js index b50a996..d0d670a 100644 --- a/js/nostr-utils.js +++ b/js/nostr-utils.js @@ -46,25 +46,46 @@ function hexToBytes(hex) { tempLink.setAttribute('download', fileName) tempLink.click() } - + + const updateRelayStatus = (relayStatus) => { + if (Object.keys(relayStatus).length > 0) { + let newText = Object.keys(relayStatus).map( + it => it.replace("wss://", "").replace("ws://", "") + ": " + relayStatus[it] + ).join("
") + $('#checking-relays').html(newText) + } else { + $('#checking-relays-header').html("") + $('#checking-relays').html("") + } + } + // fetch events from relay, returns a promise - const fetchFromRelay = async (relay, filters, events) => + const fetchFromRelay = async (relay, filters, events, relayStatus) => new Promise((resolve, reject) => { try { - + relayStatus[relay] = "Starting" + updateRelayStatus(relayStatus) // open websocket const ws = new WebSocket(relay) // prevent hanging forever - setTimeout(() => { + let myTimeout = setTimeout(() => { ws.close() reject('timeout') - }, 300_000) + }, 10_000) + // subscription id const subsId = 'my-sub' // subscribe to events filtered by author ws.onopen = () => { + clearTimeout(myTimeout) + myTimeout = setTimeout(() => { + ws.close() + reject('timeout') + }, 10_000) + relayStatus[relay] = "Downloading" + updateRelayStatus(relayStatus) ws.send(JSON.stringify(['REQ', subsId].concat(filters))) } @@ -73,6 +94,12 @@ function hexToBytes(hex) { const [msgType, subscriptionId, data] = JSON.parse(event.data) // event messages if (msgType === 'EVENT' && subscriptionId === subsId) { + clearTimeout(myTimeout) + myTimeout = setTimeout(() => { + ws.close() + reject('timeout') + }, 5_000) + const { id } = data // prevent duplicated events if (events[id]) return @@ -82,18 +109,27 @@ function hexToBytes(hex) { } // end of subscription messages if (msgType === 'EOSE' && subscriptionId === subsId) { + relayStatus[relay] = "Done" + updateRelayStatus(relayStatus) ws.close() resolve() } } ws.onerror = (err) => { + relayStatus[relay] = "Done" + updateRelayStatus(relayStatus) ws.close() reject(err) } ws.onclose = (socket, event) => { + relayStatus[relay] = "Done" + updateRelayStatus(relayStatus) resolve() } } catch (exception) { + console.log(exception) + relayStatus[relay] = "Error" + updateRelayStatus(relayStatus) try { ws.close() } catch (exception) { @@ -107,45 +143,87 @@ function hexToBytes(hex) { const getEvents = async (filters) => { // events hash const events = {} - // wait for all relays to finish - await Promise.allSettled( - relays.map((relay) => fetchFromRelay(relay, filters, events)) - ) + + // batch processing of 10 relays + let fetchFunctions = [...relays] + while (fetchFunctions.length) { + let relaysForThisRound = fetchFunctions.splice(0, 10) + let relayStatus = {} + $('#fetching-progress').val(relays.length - fetchFunctions.length) + await Promise.allSettled( relaysForThisRound.map((relay) => fetchFromRelay(relay, filters, events, relayStatus)) ) + } + updateRelayStatus({}) + // return data as an array of events return Object.keys(events).map((id) => events[id]) } // send events to a relay, returns a promisse - const sendToRelay = async (relay, data) => + const sendToRelay = async (relay, data, relayStatus) => new Promise((resolve, reject) => { try { const ws = new WebSocket(relay) + relayStatus[relay] = "Starting" + updateRelayStatus(relayStatus) + // prevent hanging forever - setTimeout(() => { + let myTimeout = setTimeout(() => { ws.close() reject('timeout') - }, 300_000) + }, 10_000) // fetch events from relay ws.onopen = () => { - console.log("sending ", data.length, "events to ", relay) + relayStatus[relay] = "Sending" + updateRelayStatus(relayStatus) for (evnt of data) { + clearTimeout(myTimeout) + myTimeout = setTimeout(() => { + ws.close() + reject('timeout') + }, 5_000) + ws.send(JSON.stringify(['EVENT', evnt])) } + relayStatus[relay] = "Done" + updateRelayStatus(relayStatus) ws.close() resolve(`done for ${relay}`) } ws.onerror = (err) => { + relayStatus[relay] = "Error" + updateRelayStatus(relayStatus) console.log("Error", err) + ws.close() reject(err) } + ws.onclose = (socket, event) => { + relayStatus[relay] = "Done" + updateRelayStatus(relayStatus) + resolve() + } } catch (exception) { + relayStatus[relay] = "Error" + updateRelayStatus(relayStatus) + try { + ws.close() + } catch (exception) { + } reject(exception) } }) // broadcast events to list of relays const broadcastEvents = async (data) => { - await Promise.allSettled(relays.map((relay) => sendToRelay(relay, data))) + // batch processing of 10 relays + let broadcastFunctions = [...relays] + while (broadcastFunctions.length) { + let relaysForThisRound = broadcastFunctions.splice(0, 10) + let relayStatus = {} + $('#broadcasting-progress').val(relays.length - broadcastFunctions.length) + await Promise.allSettled( relaysForThisRound.map((relay) => sendToRelay(relay, data, relayStatus)) ) + } + + updateRelayStatus({}) } \ No newline at end of file