Count followers using rbr.bio

This commit is contained in:
Jonathan Staab 2023-03-13 14:31:31 -05:00
parent 1939725ac6
commit 755d6cfa7f
5 changed files with 81 additions and 55 deletions

View File

@ -199,8 +199,8 @@ const applyContext = (notes, context) => {
}
export default {
listen,
load,
listen,
loadPeople,
loadParents,
streamContext,

View File

@ -38,7 +38,7 @@ class Connection {
url: string
promise?: Deferred<void>
queue: string[]
error: {code: string; occurredAt: number}
error: {code: string; message: string, occurredAt: number}
status: {code: string; message: string}
timeout?: number
stats: Record<string, number>
@ -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,
}

View File

@ -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 @@
<strong>{person.petnames.length}</strong> following
</button>
<button on:click={showFollowers}>
<strong>{$followersCount}</strong> followers
<strong>{numberFmt.format($followersCount)}</strong> followers
</button>
</div>
{/if}

View File

@ -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 = {

View File

@ -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)