From 755d6cfa7f82a4f6991ed3176ae21b8c336e6894 Mon Sep 17 00:00:00 2001 From: Jonathan Staab Date: Mon, 13 Mar 2023 14:31:31 -0500 Subject: [PATCH] Count followers using rbr.bio --- src/agent/network.ts | 2 +- src/agent/pool.ts | 48 +++++++++++++++++++++++++++------- src/routes/PersonDetail.svelte | 41 ++++++++++++++--------------- src/util/misc.ts | 12 ++++----- vite.config.js | 33 ++++++++++++----------- 5 files changed, 81 insertions(+), 55 deletions(-) diff --git a/src/agent/network.ts b/src/agent/network.ts index 16aa9946..0079cf47 100644 --- a/src/agent/network.ts +++ b/src/agent/network.ts @@ -199,8 +199,8 @@ const applyContext = (notes, context) => { } export default { - listen, load, + listen, loadPeople, loadParents, streamContext, diff --git a/src/agent/pool.ts b/src/agent/pool.ts index eca970dc..18607217 100644 --- a/src/agent/pool.ts +++ b/src/agent/pool.ts @@ -38,7 +38,7 @@ class Connection { url: string promise?: Deferred queue: string[] - error: {code: string; occurredAt: number} + error: {code: string; message: string, occurredAt: number} status: {code: string; message: string} timeout?: number stats: Record @@ -72,8 +72,8 @@ class Connection { setStatus(code, message) { this.status = {code, message} } - setError(code) { - this.error = {code, occurredAt: Date.now()} + setError(code, message) { + this.error = {code, message, occurredAt: Date.now()} } connect() { if (this.ws) { @@ -100,11 +100,11 @@ class Connection { }) this.ws.addEventListener("error", e => { - log(`Error on connection to ${this.url}`, e) + log(`Error on connection to ${this.url}`) this.disconnect() this.promise.reject() - this.setError(ERROR.CONNECTION) + this.setError(ERROR.CONNECTION, "Disconnected") this.setStatus(STATUS.CLOSED, "Closed") }) @@ -172,7 +172,7 @@ class Connection { conn: this, unsub: () => { if (this.status.code === STATUS.READY) { - this.send("CLOSE", id, ...filters) + this.send("CLOSE", id) } this.bus.off("EVENT", eventChannel) @@ -196,12 +196,23 @@ class Connection { this.send("EVENT", event) } + count(filter, id, {onCount}) { + const channel = this.bus.on("COUNT", (subid, ...payload) => { + if (subid === id) { + onCount(...payload) + + this.bus.off("COUNT", channel) + } + }) + + this.send("COUNT", id, ...filter) + } listenForAuth() { // Propagate auth to global handler this.bus.on("AUTH", challenge => { if (!this.error) { this.setStatus(STATUS.ERROR, "Logging in") - this.setError(ERROR.UNAUTHORIZED) + this.setError(ERROR.UNAUTHORIZED, "Logging in") eventBus.handle("AUTH", challenge, this) } @@ -214,7 +225,7 @@ class Connection { this.setStatus(STATUS.READY, "Connected") } else { this.disconnect() - this.setError(ERROR.FORBIDDEN) + this.setError(ERROR.FORBIDDEN, "Access restricted") } this.bus.off("OK", channel) @@ -225,8 +236,8 @@ class Connection { return this.error && Date.now() - 30_000 < this.error.occurredAt } getQuality() { - if (this.status.code === STATUS.ERROR) { - return [0, this.status.message] + if (this.error) { + return [0, this.error.message] } const {timeouts, subsCount, eoseTimer, eoseCount} = this.stats @@ -474,6 +485,22 @@ const subscribe = async ({relays, filter, onEvent, onEose, onError}: SubscribeOp } } +const count = async filter => { + const conn = await connect("wss://rbr.bio") + + if (!conn || conn.status.code !== STATUS.READY) { + return null + } + + filter = ensurePlural(filter) + + return new Promise(resolve => { + conn.count(filter, createFilterId(filter), { + onCount: res => resolve(res?.count), + }) + }) +} + // Utils const createFilterId = filters => @@ -504,4 +531,5 @@ export default { disconnect, publish, subscribe, + count, } diff --git a/src/routes/PersonDetail.svelte b/src/routes/PersonDetail.svelte index 5e4efee1..da91703c 100644 --- a/src/routes/PersonDetail.svelte +++ b/src/routes/PersonDetail.svelte @@ -6,6 +6,7 @@ import {navigate} from "svelte-routing" import {log} from "src/util/logger" import {renderContent, parseHex} from "src/util/html" + import {numberFmt} from "src/util/misc" import {displayPerson, Tags, toHex} from "src/util/nostr" import Tabs from "src/partials/Tabs.svelte" import Content from "src/partials/Content.svelte" @@ -19,7 +20,7 @@ import pool from "src/agent/pool" import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays" import network from "src/agent/network" - import {getPersonWithFallback, people} from "src/agent/tables" + import {getPersonWithFallback} from "src/agent/tables" import {routes, modal, theme, getThemeColor} from "src/app/ui" import PersonCircle from "src/partials/PersonCircle.svelte" @@ -104,27 +105,25 @@ loading = false }) - // Prime our followers count - people.all().forEach(p => { - if (Tags.wrap(p.petnames).type("p").values().all().includes(pubkey)) { - followers.add(p.pubkey) - followersCount.set(followers.size) - } - }) + // Get our followers count + const count = await pool.count({kinds: [3], "#p": [pubkey]}) - // Round out our followers count - await network.load({ - shouldProcess: false, - relays: getRelays(), - filter: [{kinds: [3], "#p": [pubkey]}], - onChunk: events => { - for (const e of events) { - followers.add(e.pubkey) - } + if (count) { + followersCount.set(count) + } else { + await network.load({ + shouldProcess: false, + relays: getRelays(), + filter: [{kinds: [3], "#p": [pubkey]}], + onChunk: events => { + for (const e of events) { + followers.add(e.pubkey) + } - followersCount.set(followers.size) - }, - }) + followersCount.set(followers.size) + }, + }) + } }) const toggleActions = () => { @@ -231,7 +230,7 @@ {person.petnames.length} following {/if} diff --git a/src/util/misc.ts b/src/util/misc.ts index 12a68c72..d5a2eecb 100644 --- a/src/util/misc.ts +++ b/src/util/misc.ts @@ -388,13 +388,13 @@ export const hexToBech32 = (prefix, url) => export const bech32ToHex = b32 => utf8.encode(bech32.fromWords(bech32.decode(b32, false).words)) -export const formatSats = sats => { - const formatter = new Intl.NumberFormat() +export const numberFmt = new Intl.NumberFormat - if (sats < 1_000) return formatter.format(sats) - if (sats < 1_000_000) return formatter.format(round(1, sats / 1000)) + "K" - if (sats < 100_000_000) return formatter.format(round(1, sats / 1_000_000)) + "MM" - return formatter.format(round(2, sats / 100_000_000)) + "BTC" +export const formatSats = sats => { + if (sats < 1_000) return numberFmt.format(sats) + if (sats < 1_000_000) return numberFmt.format(round(1, sats / 1000)) + "K" + if (sats < 100_000_000) return numberFmt.format(round(1, sats / 1_000_000)) + "MM" + return numberFmt.format(round(2, sats / 100_000_000)) + "BTC" } type EventBusListener = { diff --git a/vite.config.js b/vite.config.js index ab763103..9562d75b 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,10 +1,10 @@ -import * as path from 'path' -import {defineConfig} from 'vite' -import {VitePWA} from 'vite-plugin-pwa' -import mkcert from 'vite-plugin-mkcert' -import sveltePreprocess from 'svelte-preprocess' -import {svelte} from '@sveltejs/vite-plugin-svelte' -import {nodePolyfills} from 'vite-plugin-node-polyfills' +import * as path from "path" +import {defineConfig} from "vite" +import {VitePWA} from "vite-plugin-pwa" +import mkcert from "vite-plugin-mkcert" +import sveltePreprocess from "svelte-preprocess" +import {svelte} from "@sveltejs/vite-plugin-svelte" +import {nodePolyfills} from "vite-plugin-node-polyfills" export default defineConfig({ server: { @@ -15,8 +15,8 @@ export default defineConfig({ }, resolve: { alias: { - src: path.resolve(__dirname, 'src'), - } + src: path.resolve(__dirname, "src"), + }, }, plugins: [ mkcert(), @@ -24,13 +24,13 @@ export default defineConfig({ protocolImports: true, }), VitePWA({ - registerType: 'autoUpdate', - injectRegister: 'auto', + registerType: "autoUpdate", + injectRegister: "auto", manifest: { - name: 'Coracle', - short_name: 'Coracle', - description: 'Nostr, your way.', - theme_color: '#EB5E28', + name: "Coracle", + short_name: "Coracle", + description: "Nostr, your way.", + theme_color: "#EB5E28", icons: [ {type: "image/png", sizes: "192x192", src: "/images/favicon/android-icon-192x192.png"}, {type: "image/png", sizes: "512x512", src: "/images/favicon/android-icon-512x512.png"}, @@ -40,8 +40,7 @@ export default defineConfig({ svelte({ preprocess: sveltePreprocess(), onwarn: (warning, handler) => { - - if (warning.code.startsWith('a11y-')) return + if (warning.code.startsWith("a11y-")) return if (warning.filename.includes("node_modules")) return handler(warning)