diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f0e3fb4..3ecd0e68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/ROADMAP.md b/ROADMAP.md index 34bfa254..629176a4 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -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 diff --git a/package-lock.json b/package-lock.json index 3f1faf18..e5b08a89 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/package.json b/package.json index 93126b0b..c6bd7b3e 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/App.svelte b/src/App.svelte index 5a17fdef..67c36600 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -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(':'))) diff --git a/src/agent/database.ts b/src/agent/database.ts index 7333411c..58e74f1b 100644 --- a/src/agent/database.ts +++ b/src/agent/database.ts @@ -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, } diff --git a/src/agent/helpers.ts b/src/agent/helpers.ts index c0b66f94..214fc0ee 100644 --- a/src/agent/helpers.ts +++ b/src/agent/helpers.ts @@ -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 diff --git a/src/agent/network.ts b/src/agent/network.ts index 2b445ac3..b1b705c4 100644 --- a/src/agent/network.ts +++ b/src/agent/network.ts @@ -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, } diff --git a/src/agent/pool.ts b/src/agent/pool.ts index f81d8e77..73bdde88 100644 --- a/src/agent/pool.ts +++ b/src/agent/pool.ts @@ -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 { @@ -220,7 +232,10 @@ const subscribe = async (relays, filters, {onEvent, onEose}: Record active, unsub: () => { log(`Closing subscription ${id}`) @@ -232,6 +247,7 @@ const subscribe = async (relays, filters, {onEvent, onEose}: Record { + // If we've already unsubscribed we're good + if (!agg.isActive()) { + return + } + const isComplete = eose.size === relays.length const isTimeout = Date.now() - now >= timeout diff --git a/src/app/index.ts b/src/app/index.ts index df5bdfab..f0670534 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -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()), ]) } diff --git a/src/app/messages.js b/src/app/messages.js index 1553a4e7..d35bdf2d 100644 --- a/src/app/messages.js +++ b/src/app/messages.js @@ -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 diff --git a/src/partials/RelayCard.svelte b/src/partials/RelayCard.svelte index f7d42040..cf41de24 100644 --- a/src/partials/RelayCard.svelte +++ b/src/partials/RelayCard.svelte @@ -31,8 +31,6 @@ quality = null message = "Not connected" } - - console.log(quality, message) }) }) diff --git a/src/views/notes/Network.svelte b/src/views/notes/Network.svelte index bc894dad..bfade329 100644 --- a/src/views/notes/Network.svelte +++ b/src/views/notes/Network.svelte @@ -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} diff --git a/src/views/notes/Popular.svelte b/src/views/notes/Popular.svelte index 21d0cc27..5ee6b159 100644 --- a/src/views/notes/Popular.svelte +++ b/src/views/notes/Popular.svelte @@ -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} diff --git a/src/views/notes/Top.svelte b/src/views/notes/Top.svelte index 21d0cc27..5ee6b159 100644 --- a/src/views/notes/Top.svelte +++ b/src/views/notes/Top.svelte @@ -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}