Upgrade nostr tools, fix initial user/network loading, fix unnecessary dm alerts

This commit is contained in:
Jonathan Staab 2023-02-15 15:22:06 -06:00
parent 9ad897adef
commit 7dbb69e54a
15 changed files with 111 additions and 67 deletions

View File

@ -11,6 +11,8 @@
- [x] Show relay status based on stats not current connection status
- [x] Auto-mention person when creating a note from their profile page
- [x] Make chat header overlap main header to save space
- [x] Strip formatting when pasting into Compose
- [x] Upgraded nostr-tools to 1.4.1
## 0.2.11

View File

@ -9,10 +9,10 @@
- [ ] Fix anon/new user experience
- [ ] Initial user load doesn't have any relays, cache user or wait for people db to be loaded
- [ ] Fix bugs on bugsnag
- [ ] Fix bugs on github
# Snacks
- [ ] Relay recommendations based on follows/followers
- [ ] Pinned posts ala snort
- [ ] Likes list on note detail. Maybe a sidebar or header for note detail page?
- [ ] Support key delegation
@ -54,7 +54,7 @@
# Maintenance
- [ ] If the latest message in a dm was the user, don't show notification
- [ ] Don't waste space caching rooms, load those lazily
- [ ] Normalize relay urls (lowercase, strip trailing slash)
- [ ] Use nip 56 for reporting
- https://github.com/nostr-protocol/nips/pull/205#issuecomment-1419234230

75
package-lock.json generated
View File

@ -19,7 +19,7 @@
"husky": "^8.0.3",
"localforage": "^1.10.0",
"localforage-memoryStorageDriver": "^0.9.2",
"nostr-tools": "^1.2.1",
"nostr-tools": "^1.4.1",
"npm-run-all": "^4.1.5",
"qr-scanner": "^1.4.2",
"qrcode": "^1.5.1",
@ -218,14 +218,14 @@
}
},
"node_modules/@noble/hashes": {
"version": "0.5.9",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-0.5.9.tgz",
"integrity": "sha512-7lN1Qh6d8DUGmfN36XRsbN/WcGIPNtTGhkw26vWId/DlCIGsYJJootTtPGghTLcn/AaXPx2Q0b3cacrwXa7OVw=="
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz",
"integrity": "sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg=="
},
"node_modules/@noble/secp256k1": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.0.tgz",
"integrity": "sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw==",
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
"integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==",
"funding": [
{
"type": "individual",
@ -3759,15 +3759,16 @@
}
},
"node_modules/nostr-tools": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.2.1.tgz",
"integrity": "sha512-SL0sst29mrQ7oUPGQn+NMH4yuTe69a8S4bliNpYB2IG0fDl3Cx+xSLnuCTb4nZiNalatYsA5l+751wQiDGA3+A==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.4.1.tgz",
"integrity": "sha512-pFAbVNtRMfW5ducUmk0f20IZv4a6pXYchBQKuA6D3x6sg83KoWipuiAie/gfOgrkoCsBLV14DmpWsVmo3N7WXQ==",
"dependencies": {
"@noble/hashes": "^0.5.7",
"@noble/secp256k1": "^1.7.0",
"@noble/hashes": "1.0.0",
"@noble/secp256k1": "^1.7.1",
"@scure/base": "^1.1.1",
"@scure/bip32": "^1.1.1",
"@scure/bip39": "^1.1.0"
"@scure/bip32": "^1.1.5",
"@scure/bip39": "^1.1.1",
"prettier": "^2.8.4"
}
},
"node_modules/npm-run-all": {
@ -4340,6 +4341,20 @@
"node": ">= 0.8.0"
}
},
"node_modules/prettier": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -5929,14 +5944,14 @@
}
},
"@noble/hashes": {
"version": "0.5.9",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-0.5.9.tgz",
"integrity": "sha512-7lN1Qh6d8DUGmfN36XRsbN/WcGIPNtTGhkw26vWId/DlCIGsYJJootTtPGghTLcn/AaXPx2Q0b3cacrwXa7OVw=="
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz",
"integrity": "sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg=="
},
"@noble/secp256k1": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.0.tgz",
"integrity": "sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw=="
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
"integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw=="
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
@ -8430,15 +8445,16 @@
"dev": true
},
"nostr-tools": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.2.1.tgz",
"integrity": "sha512-SL0sst29mrQ7oUPGQn+NMH4yuTe69a8S4bliNpYB2IG0fDl3Cx+xSLnuCTb4nZiNalatYsA5l+751wQiDGA3+A==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.4.1.tgz",
"integrity": "sha512-pFAbVNtRMfW5ducUmk0f20IZv4a6pXYchBQKuA6D3x6sg83KoWipuiAie/gfOgrkoCsBLV14DmpWsVmo3N7WXQ==",
"requires": {
"@noble/hashes": "^0.5.7",
"@noble/secp256k1": "^1.7.0",
"@noble/hashes": "1.0.0",
"@noble/secp256k1": "^1.7.1",
"@scure/base": "^1.1.1",
"@scure/bip32": "^1.1.1",
"@scure/bip39": "^1.1.0"
"@scure/bip32": "^1.1.5",
"@scure/bip39": "^1.1.1",
"prettier": "^2.8.4"
}
},
"npm-run-all": {
@ -8827,6 +8843,11 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
"prettier": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw=="
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",

View File

@ -38,7 +38,7 @@
"husky": "^8.0.3",
"localforage": "^1.10.0",
"localforage-memoryStorageDriver": "^0.9.2",
"nostr-tools": "^1.2.1",
"nostr-tools": "^1.4.1",
"npm-run-all": "^4.1.5",
"qr-scanner": "^1.4.2",
"qrcode": "^1.5.1",

View File

@ -3,7 +3,6 @@
import "@fortawesome/fontawesome-free/css/solid.css"
import {find, is, identity, nthArg, pluck} from 'ramda'
import {onMount} from "svelte"
import {createMap, first} from 'hurdak/lib/hurdak'
import {writable, get} from "svelte/store"
import {fly, fade} from "svelte/transition"
@ -88,13 +87,12 @@
))
}
onMount(() => {
database.onReady(() => {
if ($user) {
loadAppData($user.pubkey)
}
// Background work
const interval = setInterval(() => {
alertSlowConnections()
retrieveRelayMeta()
@ -168,6 +166,7 @@
}
})
// Log usage on navigate
const unsubHistory = globalHistory.listen(({location}) => {
if (!location.hash) {
// Remove identifying information, e.g. pubkeys, event ids, etc
@ -178,8 +177,8 @@
}
})
// Keep scroll position on body, but don't allow scrolling
const unsubModal = modal.subscribe($modal => {
// Keep scroll position on body, but don't allow scrolling
if ($modal) {
logUsage(btoa(['modal', $modal.type].join(':')))

View File

@ -304,8 +304,17 @@ const clearAll = () => Promise.all(Object.keys(registry).map(clear))
const ready = derived(pluck('ready', Object.values(registry)), all(identity))
const onReady = cb => {
const unsub = ready.subscribe($ready => {
if ($ready) {
cb()
unsub()
}
})
}
export default {
getItem, setItem, setItems, removeItem, removeItems, length, clear, keys,
dump, iterate, watch, getPersonWithFallback, clearAll, people, rooms, messages,
alerts, relays, routes, ready,
alerts, relays, routes, ready, onReady,
}

View File

@ -32,11 +32,8 @@ export const getMuffle = () => {
return Tags.wrap($user.muffle.filter(shouldMuffle)).values().all()
}
export const getFollows = pubkey => {
const person = database.getPersonWithFallback(pubkey)
return Tags.wrap(person.petnames || defaults.petnames).values().all()
}
export const getFollows = pubkey =>
database.getPersonWithFallback(pubkey).petnames || defaults.petnames
export const getPersonRelays = (person, mode = 'all') => {
const relays = isEmpty(person?.relays || []) ? defaults.relays : person.relays

View File

@ -2,7 +2,7 @@ import {uniq, uniqBy, prop, map, propEq, indexBy, pluck} from 'ramda'
import {findReply, personKinds, findReplyId, Tags} from 'src/util/nostr'
import {chunk} from 'hurdak/lib/hurdak'
import {batch} from 'src/util/misc'
import {getFollows, getStalePubkeys, getTopEventRelays} from 'src/agent/helpers'
import {getStalePubkeys, getTopEventRelays} from 'src/agent/helpers'
import pool from 'src/agent/pool'
import keys from 'src/agent/keys'
import sync from 'src/agent/sync'
@ -70,13 +70,6 @@ const loadPeople = (relays, pubkeys, {kinds = personKinds, force = false, ...opt
: Promise.resolve([])
}
const loadNetwork = async (relays, pubkey) => {
const tags = Tags.wrap(getFollows(pubkey))
// Use nip-2 recommended relays to load our user's second-order follows
await loadPeople(tags.relays(), tags.values().all())
}
const loadParents = (relays, notes) => {
const parentIds = new Set(Tags.wrap(notes.map(findReply)).values().all())
@ -128,7 +121,7 @@ const streamContext = ({relays, notes, updateNotes, depth = 0}) => {
}
export default {
publish, load, listen, listenUntilEose, loadNetwork, loadPeople, personKinds,
publish, load, listen, listenUntilEose, loadPeople, personKinds,
loadParents, streamContext,
}

View File

@ -3,7 +3,7 @@ import type {MyEvent} from 'src/util/types'
import {relayInit} from 'nostr-tools'
import {uniqBy, without, prop, find, is} from 'ramda'
import {ensurePlural} from 'hurdak/lib/hurdak'
import {warn, log} from 'src/util/logger'
import {warn, log, error} from 'src/util/logger'
import {isRelay} from 'src/util/nostr'
import {sleep} from 'src/util/misc'
import database from 'src/agent/database'
@ -51,6 +51,14 @@ class Connection {
if (shouldConnect) {
this.status = CONNECTION_STATUS.PENDING
this.promise = this.nostr.connect()
this.nostr.on('connect', () => {
this.status = CONNECTION_STATUS.READY
})
this.nostr.on('error', () => {
this.status = CONNECTION_STATUS.ERROR
})
}
if (this.status === CONNECTION_STATUS.PENDING) {
@ -170,7 +178,11 @@ const subscribe = async (relays, filters, {onEvent, onEose}: Record<string, (e:
const seen = new Set()
const eose = new Set()
log(`Starting subscription ${id} with ${relays.length} relays`, filters)
if (relays.length === 0) {
error(`Attempted to start subscription ${id} with zero relays`, filters)
} else {
log(`Starting subscription ${id} with ${relays.length} relays`, filters)
}
// Don't await before returning so we're not blocking on slow connects
const promises = relays.map(async relay => {
@ -220,7 +232,10 @@ const subscribe = async (relays, filters, {onEvent, onEose}: Record<string, (e:
return Object.assign(sub, {conn})
})
let active = true
return {
isActive: () => active,
unsub: () => {
log(`Closing subscription ${id}`)
@ -232,6 +247,7 @@ const subscribe = async (relays, filters, {onEvent, onEose}: Record<string, (e:
sub.unsub()
}
active = false
sub.conn.stats.activeSubsCount -= 1
}
})
@ -254,7 +270,14 @@ const subscribeUntilEose = async (
const now = Date.now()
const eose = new Set()
let closed = true
const attemptToComplete = () => {
// If we've already unsubscribed we're good
if (!agg.isActive()) {
return
}
const isComplete = eose.size === relays.length
const isTimeout = Date.now() - now >= timeout

View File

@ -5,7 +5,7 @@ import {createMap, ellipsize} from 'hurdak/lib/hurdak'
import {get} from 'svelte/store'
import {renderContent} from 'src/util/html'
import {Tags, displayPerson, findReplyId} from 'src/util/nostr'
import {user, getUserRelays} from 'src/agent/helpers'
import {user, getUserRelays, getFollows} from 'src/agent/helpers'
import defaults from 'src/agent/defaults'
import database from 'src/agent/database'
import network from 'src/agent/network'
@ -19,12 +19,13 @@ export {toast, modal, settings, alerts, messages, logUsage}
export const loadAppData = pubkey => {
const relays = getUserRelays('read')
const follows = Tags.wrap(getFollows(pubkey))
return Promise.all([
network.loadNetwork(relays, pubkey),
alerts.load(relays, pubkey),
alerts.listen(relays, pubkey),
messages.listen(relays, pubkey),
network.loadPeople(follows.relays(), follows.values().all()),
])
}

View File

@ -30,10 +30,10 @@ const listen = async (relays, pubkey) => {
await network.loadPeople(relays, pluck('pubkey', messages))
mostRecentByPubkey.update(o => {
for (const {pubkey, recipient, created_at} of messages) {
const k = pubkey === $user.pubkey ? recipient : pubkey
o[k] = Math.max(created_at, o[k] || 0)
for (const {pubkey, created_at} of messages) {
if (pubkey !== $user.pubkey) {
o[pubkey] = Math.max(created_at, o[pubkey] || 0)
}
}
return o

View File

@ -31,8 +31,6 @@
quality = null
message = "Not connected"
}
console.log(quality, message)
})
})
</script>

View File

@ -2,12 +2,13 @@
import {uniq} from 'ramda'
import Notes from "src/partials/Notes.svelte"
import {shuffle} from 'src/util/misc'
import {Tags} from 'src/util/nostr'
import {user, getTopRelays, getFollows} from 'src/agent/helpers'
// Get first- and second-order follows. shuffle and slice network so we're not
// sending too many pubkeys. This will also result in some variety.
const follows = shuffle(getFollows($user?.pubkey))
const others = shuffle(follows.flatMap(getFollows)).slice(0, 50)
const follows = shuffle(Tags.wrap(getFollows($user?.pubkey)).values().all())
const others = shuffle(Tags.wrap(follows.flatMap(getFollows)).values().all()).slice(0, 50)
const authors = uniq(follows.concat(others)).slice(0, 100)
const relays = getTopRelays(authors, 'write')
const filter = {kinds: [1, 7], authors}

View File

@ -2,13 +2,13 @@
import {uniq} from 'ramda'
import Notes from "src/partials/Notes.svelte"
import {shuffle} from 'src/util/misc'
import {isLike} from 'src/util/nostr'
import {isLike, Tags} from 'src/util/nostr'
import {user, getTopRelays, getFollows} from 'src/agent/helpers'
// Get first- and second-order follows. shuffle and slice network so we're not
// sending too many pubkeys. This will also result in some variety.
const follows = shuffle(getFollows($user?.pubkey))
const others = shuffle(follows.flatMap(getFollows)).slice(0, 50)
const follows = shuffle(Tags.wrap(getFollows($user?.pubkey)).values().all())
const others = shuffle(Tags.wrap(follows.flatMap(getFollows)).values().all()).slice(0, 50)
const authors = uniq(follows.concat(others)).slice(0, 100)
const relays = getTopRelays(authors, 'write')
const filter = {kinds: [1, 7], authors}

View File

@ -2,13 +2,13 @@
import {uniq} from 'ramda'
import Notes from "src/partials/Notes.svelte"
import {shuffle} from 'src/util/misc'
import {isLike} from 'src/util/nostr'
import {isLike, Tags} from 'src/util/nostr'
import {user, getTopRelays, getFollows} from 'src/agent/helpers'
// Get first- and second-order follows. shuffle and slice network so we're not
// sending too many pubkeys. This will also result in some variety.
const follows = shuffle(getFollows($user?.pubkey))
const others = shuffle(follows.flatMap(getFollows)).slice(0, 50)
const follows = shuffle(Tags.wrap(getFollows($user?.pubkey)).values().all())
const others = shuffle(Tags.wrap(follows.flatMap(getFollows)).values().all()).slice(0, 50)
const authors = uniq(follows.concat(others)).slice(0, 100)
const relays = getTopRelays(authors, 'write')
const filter = {kinds: [1, 7], authors}