Fix nip 65, remove relays from people, pulling stats from routes table only

This commit is contained in:
Jonathan Staab 2023-02-16 13:25:01 -06:00
parent 12506c015b
commit 130773a90c
31 changed files with 156 additions and 195 deletions

View File

@ -7,10 +7,12 @@
- [ ] Separate user info and relays so we can still select/figure out relays for anons - [ ] Separate user info and relays so we can still select/figure out relays for anons
- [ ] Separate petnames out as well so anons can follow people - [ ] Separate petnames out as well so anons can follow people
- [ ] Initial user load doesn't have any relays, cache user or wait for people db to be loaded - [ ] Initial user load doesn't have any relays, cache user or wait for people db to be loaded
- nip07.getRelays, nip05, relay.nostr.band
- [ ] Fix bugs on bugsnag - [ ] Fix bugs on bugsnag
# Snacks # Snacks
- [ ] DM/chat read status in encrypted note
- [ ] Relay recommendations based on follows/followers - [ ] Relay recommendations based on follows/followers
- [ ] Pinned posts ala snort - [ ] Pinned posts ala snort
- [ ] Likes list on note detail. Maybe a sidebar or header for note detail page? - [ ] Likes list on note detail. Maybe a sidebar or header for note detail page?

View File

@ -13,7 +13,7 @@
import {displayPerson, isLike} from 'src/util/nostr' import {displayPerson, isLike} from 'src/util/nostr'
import {timedelta, shuffle, now, sleep} from 'src/util/misc' import {timedelta, shuffle, now, sleep} from 'src/util/misc'
import cmd from 'src/agent/cmd' import cmd from 'src/agent/cmd'
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getUserRelays} from 'src/agent/relays' import {getUserRelays} from 'src/agent/relays'
import database from 'src/agent/database' import database from 'src/agent/database'
import keys from 'src/agent/keys' import keys from 'src/agent/keys'

View File

@ -13,7 +13,17 @@ const updateUser = (relays, updates) =>
publishEvent(relays, 0, {content: JSON.stringify(updates)}) publishEvent(relays, 0, {content: JSON.stringify(updates)})
const setRelays = (relays, newRelays) => const setRelays = (relays, newRelays) =>
publishEvent(relays, 10001, {tags: newRelays.map(r => [r.url, r.read || "", r.write || ""])}) publishEvent(relays, 10002, {
tags: newRelays.map(r => {
const t = ["r", r.url]
if (!r.write) {
t.push('read')
}
return t
}),
})
const setPetnames = (relays, petnames) => const setPetnames = (relays, petnames) =>
publishEvent(relays, 3, {tags: petnames}) publishEvent(relays, 3, {tags: petnames})

View File

@ -1,47 +0,0 @@
import type {Person} from 'src/util/types'
import type {Readable} from 'svelte/store'
import {uniq, last} from 'ramda'
import {derived, get} from 'svelte/store'
import {Tags} from 'src/util/nostr'
import {now, timedelta} from 'src/util/misc'
import database from 'src/agent/database'
import keys from 'src/agent/keys'
export const user = derived(
[keys.pubkey, database.people as Readable<any>],
([pubkey, $people]) => {
if (!pubkey) {
return null
}
return ($people[pubkey] || {pubkey})
}
) as Readable<Person>
export const getMuffle = () => {
const $user = get(user) as Person
if (!$user?.muffle) {
return []
}
const shouldMuffle = t => Math.random() > parseFloat(last(t))
return Tags.wrap($user.muffle.filter(shouldMuffle)).values().all()
}
export const getFollows = pubkey =>
Tags.wrap(database.getPersonWithFallback(pubkey).petnames).type("p").values().all()
export const getNetwork = pubkey =>
uniq(getFollows(pubkey).flatMap(getFollows))
export const getStalePubkeys = pubkeys => {
// If we're not reloading, only get pubkeys we don't already know about
return uniq(pubkeys).filter(pubkey => {
const p = database.people.get(pubkey)
return !p || p.updated_at < now() - timedelta(1, 'days')
})
}

View File

@ -3,12 +3,12 @@
* *
* cmd * cmd
* -> network * -> network
* -> helpers, pool * -> user, pool
* -> keys * -> keys
* -> sync * -> sync
* -> database * -> database
* *
* In other words, command/network depend on utility functions and the network to * In other words, command/network depend on utility functions and the network to
* do their job. The database sits at the bottom since it's shared between helpers * do their job. The database sits at the bottom since it's shared between user
* which query the database, and network which both queries and updates it. * which query the database, and network which both queries and updates it.
*/ */

View File

@ -1,16 +1,25 @@
import {uniq, uniqBy, prop, map, propEq, indexBy, pluck} from 'ramda' import {uniq, uniqBy, prop, map, propEq, indexBy, pluck} from 'ramda'
import {personKinds, findReplyId} from 'src/util/nostr' import {personKinds, findReplyId} from 'src/util/nostr'
import {chunk} from 'hurdak/lib/hurdak' import {chunk} from 'hurdak/lib/hurdak'
import {batch} from 'src/util/misc' import {batch, timedelta, now} from 'src/util/misc'
import {getStalePubkeys} from 'src/agent/helpers'
import { import {
getRelaysForEventParent, getAllPubkeyWriteRelays, aggregateScores, getRelaysForEventParent, getAllPubkeyWriteRelays, aggregateScores,
getUserNetworkWriteRelays, getUserNetworkWriteRelays,
} from 'src/agent/relays' } from 'src/agent/relays'
import database from 'src/agent/database'
import pool from 'src/agent/pool' import pool from 'src/agent/pool'
import keys from 'src/agent/keys' import keys from 'src/agent/keys'
import sync from 'src/agent/sync' import sync from 'src/agent/sync'
const getStalePubkeys = pubkeys => {
// If we're not reloading, only get pubkeys we don't already know about
return uniq(pubkeys).filter(pubkey => {
const p = database.people.get(pubkey)
return !p || p.updated_at < now() - timedelta(1, 'days')
})
}
const publish = async (relays, event) => { const publish = async (relays, event) => {
const signedEvent = await keys.sign(event) const signedEvent = await keys.sign(event)

View File

@ -1,8 +1,9 @@
import {get} from 'svelte/store' import type {Relay} from 'src/util/types'
import {sortBy, uniq, uniqBy, prop, pluck} from 'ramda' import {writable, get} from 'svelte/store'
import {createMapOf, first} from 'hurdak/lib/hurdak' import {pick, map, assoc, sortBy, uniq, uniqBy, prop} from 'ramda'
import {first} from 'hurdak/lib/hurdak'
import {Tags} from 'src/util/nostr' import {Tags} from 'src/util/nostr'
import {getFollows} from 'src/agent/helpers' import {getFollows} from 'src/agent/social'
import database from 'src/agent/database' import database from 'src/agent/database'
import keys from 'src/agent/keys' import keys from 'src/agent/keys'
@ -18,25 +19,24 @@ import keys from 'src/agent/keys'
// doesn't need to see. // doesn't need to see.
// 5) Advertise relays — write and read back your own relay list // 5) Advertise relays — write and read back your own relay list
export const relays = writable([])
// Pubkey relays // Pubkey relays
export const getPubkeyRelays = pubkey => { export const getPubkeyRelays = (pubkey, mode = null) => {
const person = database.getPersonWithFallback(pubkey) const filter = mode ? {pubkey, mode} : {pubkey}
return scoreRelays(pubkey, person.relays || []) return sortByScore(map(pick(['url', 'score']), database.routes.all(filter)))
} }
export const getPubkeyReadRelays = pubkey => export const getPubkeyReadRelays = pubkey => getPubkeyRelays(pubkey, 'read')
getPubkeyRelays(pubkey).filter(r => r.read !== '!')
export const getPubkeyWriteRelays = pubkey => export const getPubkeyWriteRelays = pubkey => getPubkeyRelays(pubkey, 'write')
getPubkeyRelays(pubkey).filter(r => r.write !== '!')
// Multiple pubkeys // Multiple pubkeys
export const getAllPubkeyRelays = pubkeys => export const getAllPubkeyRelays = (pubkeys, mode = null) =>
aggregateScores(pubkeys.map(getPubkeyRelays)) aggregateScores(pubkeys.map(pubkey => getPubkeyRelays(pubkey, mode)))
export const getAllPubkeyReadRelays = pubkeys => export const getAllPubkeyReadRelays = pubkeys =>
aggregateScores(pubkeys.map(getPubkeyReadRelays)) aggregateScores(pubkeys.map(getPubkeyReadRelays))
@ -46,9 +46,9 @@ export const getAllPubkeyWriteRelays = pubkeys =>
// Current user // Current user
export const getUserRelays = () => getPubkeyRelays(get(keys.pubkey)) export const getUserRelays = (): Array<Relay> => get(relays).map(assoc('score', 1))
export const getUserReadRelays = () => getPubkeyReadRelays(get(keys.pubkey)) export const getUserReadRelays = () => getUserRelays().filter(prop('read'))
export const getUserWriteRelays = () => getPubkeyWriteRelays(get(keys.pubkey)) export const getUserWriteRelays = () => getUserRelays().filter(prop('write'))
// Network relays // Network relays
@ -111,13 +111,6 @@ export const getEventPublishRelays = event => {
const uniqByUrl = uniqBy(prop('url')) const uniqByUrl = uniqBy(prop('url'))
const sortByScore = sortBy(r => -r.score) const sortByScore = sortBy(r => -r.score)
const scoreRelays = (pubkey, relays) => {
const routes = database.routes.all({pubkey, url: pluck('url', relays)})
const scores = createMapOf('url', 'score', routes)
return uniqByUrl(sortByScore(relays.map(r => ({...r, score: scores[r.url] || 0}))))
}
export const aggregateScores = relayGroups => { export const aggregateScores = relayGroups => {
const scores = {} as Record<string, { const scores = {} as Record<string, {
score: number, score: number,

9
src/agent/social.ts Normal file
View File

@ -0,0 +1,9 @@
import {uniq} from 'ramda'
import {Tags} from 'src/util/nostr'
import database from 'src/agent/database'
export const getFollows = pubkey =>
Tags.wrap(database.getPersonWithFallback(pubkey).petnames).type("p").values().all()
export const getNetwork = pubkey =>
uniq(getFollows(pubkey).flatMap(getFollows))

View File

@ -41,49 +41,8 @@ const processProfileEvents = async events => {
return content return content
}) })
}, },
2: () => { 3: () => ({petnames: e.tags}),
if (e.created_at > (person.relays_updated_at || 0)) {
const {relays = []} = database.getPersonWithFallback(e.pubkey)
return {
relays: relays.concat({url: e.content}),
relays_updated_at: e.created_at,
}
}
},
3: () => {
const data = {petnames: e.tags}
if (e.created_at > (person.relays_updated_at || 0)) {
tryJson(() => {
Object.assign(data, {
relays_updated_at: e.created_at,
relays: Object.entries(JSON.parse(e.content))
.map(([url, conditions]) => {
const {write, read} = conditions as Record<string, boolean|string>
return {
url,
write: [false, '!'].includes(write) ? '!' : '',
read: [false, '!'].includes(read) ? '!' : '',
}
})
.filter(r => isRelay(r.url)),
})
})
}
return data
},
12165: () => ({muffle: e.tags}), 12165: () => ({muffle: e.tags}),
10001: () => {
if (e.created_at > (person.relays_updated_at || 0)) {
return {
relays: e.tags.map(([url, read, write]) => ({url, read, write})),
relays_updated_at: e.created_at,
}
}
},
default: () => { default: () => {
log(`Received unsupported event type ${e.kind}`) log(`Received unsupported event type ${e.kind}`)
}, },
@ -153,7 +112,7 @@ const processMessages = async events => {
const getWeight = type => { const getWeight = type => {
if (type === 'nip05') return 1 if (type === 'nip05') return 1
if (type === 'kind:10001') return 1 if (type === 'kind:10002') return 1
if (type === 'kind:3') return 0.8 if (type === 'kind:3') return 0.8
if (type === 'kind:2') return 0.5 if (type === 'kind:2') return 0.5
if (type === 'seen') return 0.2 if (type === 'seen') return 0.2
@ -216,19 +175,14 @@ const processRoutes = async events => {
}) })
}) })
}, },
10001: () => { 10002: () => {
e.tags e.tags
.forEach(([url, read, write]) => { .forEach(([url, read, mode]) => {
if (![false, '!'].includes(write)) { if (mode) {
updates.push( calculateRoute(e.pubkey, url, 'kind:10002', mode, e.created_at)
calculateRoute(e.pubkey, url, 'kind:100001', 'write', e.created_at) } else {
) calculateRoute(e.pubkey, url, 'kind:10002', 'read', e.created_at)
} calculateRoute(e.pubkey, url, 'kind:10002', 'write', e.created_at)
if (![false, '!'].includes(read)) {
updates.push(
calculateRoute(e.pubkey, url, 'kind:100001', 'read', e.created_at)
)
} }
}) })
}, },

16
src/agent/user.ts Normal file
View File

@ -0,0 +1,16 @@
import type {Person} from 'src/util/types'
import type {Readable} from 'svelte/store'
import {derived} from 'svelte/store'
import database from 'src/agent/database'
import keys from 'src/agent/keys'
export const user = derived(
[keys.pubkey, database.people as Readable<any>],
([pubkey, $people]) => {
if (!pubkey) {
return null
}
return ($people[pubkey] || {pubkey})
}
) as Readable<Person>

View File

@ -5,9 +5,9 @@ import {createMap, ellipsize} from 'hurdak/lib/hurdak'
import {get} from 'svelte/store' import {get} from 'svelte/store'
import {renderContent} from 'src/util/html' import {renderContent} from 'src/util/html'
import {Tags, displayPerson, findReplyId} from 'src/util/nostr' import {Tags, displayPerson, findReplyId} from 'src/util/nostr'
import {user, getNetwork} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getUserWriteRelays} from 'src/agent/relays' import {getNetwork} from 'src/agent/social'
import defaults from 'src/agent/defaults' import {relays} from 'src/agent/relays'
import database from 'src/agent/database' import database from 'src/agent/database'
import network from 'src/agent/network' import network from 'src/agent/network'
import keys from 'src/agent/keys' import keys from 'src/agent/keys'
@ -49,44 +49,50 @@ export const login = async ({privkey, pubkey}: {privkey?: string, pubkey?: strin
export const addRelay = async url => { export const addRelay = async url => {
const person = get(user) as Person const person = get(user) as Person
const modify = relays => relays.concat({url, write: '!'})
// Set to defaults to support anonymous usage relays.update($relays => {
defaults.relays = modify(defaults.relays) $relays.push({url, write: false, read: true})
if (person) { if (person) {
const relays = modify(person.relays || []) (async () => {
// Publish to the new set of relays // Publish to the new set of relays
await cmd.setRelays(relays, relays) await cmd.setRelays($relays, $relays)
// Reload alerts, messages, etc // Reload alerts, messages, etc
await loadAppData(person.pubkey) await loadAppData(person.pubkey)
})()
} }
return $relays
})
} }
export const removeRelay = async url => { export const removeRelay = async url => {
const person = get(user) as Person const person = get(user) as Person
const modify = relays => reject(whereEq({url}), relays)
// Set to defaults to support anonymous usage relays.update($relays => {
defaults.relays = modify(defaults.relays) $relays = reject(whereEq({url}), $relays)
if (person) { if (person && $relays.length > 0) {
await cmd.setRelays(getUserWriteRelays(), modify(person.relays || [])) cmd.setRelays($relays, $relays)
} }
return $relays
})
} }
export const setRelayWriteCondition = async (url, write) => { export const setRelayWriteCondition = async (url, write) => {
const person = get(user) as Person const person = get(user) as Person
const modify = relays => relays.map(when(whereEq({url}), assoc('write', write)))
// Set to defaults to support anonymous usage relays.update($relays => {
defaults.relays = modify(defaults.relays) $relays = $relays.map(when(whereEq({url}), assoc('write', write)))
if (person) { if (person && $relays.length > 0) {
await cmd.setRelays(getUserWriteRelays(), modify(person.relays || [])) cmd.setRelays($relays, $relays)
} }
return $relays
})
} }
export const renderNote = (note, {showEntire = false}) => { export const renderNote = (note, {showEntire = false}) => {

View File

@ -1,7 +1,7 @@
import {pluck, reject} from 'ramda' import {pluck, reject} from 'ramda'
import {get} from 'svelte/store' import {get} from 'svelte/store'
import {synced, now, timedelta} from 'src/util/misc' import {synced, now, timedelta} from 'src/util/misc'
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getUserReadRelays} from 'src/agent/relays' import {getUserReadRelays} from 'src/agent/relays'
import database from 'src/agent/database' import database from 'src/agent/database'
import network from 'src/agent/network' import network from 'src/agent/network'

View File

@ -8,7 +8,7 @@
import Badge from 'src/partials/Badge.svelte' import Badge from 'src/partials/Badge.svelte'
import Anchor from 'src/partials/Anchor.svelte' import Anchor from 'src/partials/Anchor.svelte'
import Spinner from 'src/partials/Spinner.svelte' import Spinner from 'src/partials/Spinner.svelte'
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import database from 'src/agent/database' import database from 'src/agent/database'
import {renderNote} from 'src/app' import {renderNote} from 'src/app'

View File

@ -16,7 +16,7 @@
import {formatTimestamp, stringToColor} from 'src/util/misc' import {formatTimestamp, stringToColor} from 'src/util/misc'
import Compose from "src/partials/Compose.svelte" import Compose from "src/partials/Compose.svelte"
import Card from "src/partials/Card.svelte" import Card from "src/partials/Card.svelte"
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getEventPublishRelays, getRelaysForEventParent} from 'src/agent/relays' import {getEventPublishRelays, getRelaysForEventParent} from 'src/agent/relays'
import database from 'src/agent/database' import database from 'src/agent/database'
import cmd from 'src/agent/cmd' import cmd from 'src/agent/cmd'
@ -217,7 +217,7 @@
{/if} {/if}
</Anchor> </Anchor>
<Anchor <Anchor
href={"/" + nip19.neventEncode({id: note.id, relays: [note.seen_on.url]})} href={"/" + nip19.neventEncode({id: note.id, relays: [note.seen_on]})}
class="text-sm text-light" class="text-sm text-light"
type="unstyled"> type="unstyled">
{formatTimestamp(note.created_at)} {formatTimestamp(note.created_at)}

View File

@ -1,13 +1,14 @@
<script lang="ts"> <script lang="ts">
import {onMount} from 'svelte' import {onMount} from 'svelte'
import {partition, propEq, always, mergeRight, uniqBy, sortBy, prop} from 'ramda' import {partition, last, propEq, always, mergeRight, uniqBy, sortBy, prop} from 'ramda'
import {slide} from 'svelte/transition' import {slide} from 'svelte/transition'
import {quantify} from 'hurdak/lib/hurdak' import {quantify} from 'hurdak/lib/hurdak'
import {createScroller, now, Cursor} from 'src/util/misc' import {createScroller, now, Cursor} from 'src/util/misc'
import {Tags} from 'src/util/nostr'
import Spinner from 'src/partials/Spinner.svelte' import Spinner from 'src/partials/Spinner.svelte'
import Content from 'src/partials/Content.svelte' import Content from 'src/partials/Content.svelte'
import Note from "src/partials/Note.svelte" import Note from "src/partials/Note.svelte"
import {getMuffle} from 'src/agent/helpers' import {user} from 'src/agent/user'
import network from 'src/agent/network' import network from 'src/agent/network'
import {modal, mergeParents} from "src/app" import {modal, mergeParents} from "src/app"
@ -20,8 +21,10 @@
const since = now() const since = now()
const maxNotes = 300 const maxNotes = 300
const muffle = getMuffle()
const cursor = new Cursor() const cursor = new Cursor()
const muffle = Tags
.wrap(($user?.muffle || []).filter(t => Math.random() > parseFloat(last(t))))
.values().all()
const processNewNotes = async newNotes => { const processNewNotes = async newNotes => {
// Remove people we're not interested in hearing about, sort by created date // Remove people we're not interested in hearing about, sort by created date

View File

@ -6,7 +6,7 @@
import {between} from 'hurdak/lib/hurdak' import {between} from 'hurdak/lib/hurdak'
import {fly} from 'svelte/transition' import {fly} from 'svelte/transition'
import Toggle from "src/partials/Toggle.svelte" import Toggle from "src/partials/Toggle.svelte"
import {user} from "src/agent/helpers" import {relays} from "src/agent/relays"
import pool from 'src/agent/pool' import pool from 'src/agent/pool'
import {addRelay, removeRelay, setRelayWriteCondition} from "src/app" import {addRelay, removeRelay, setRelayWriteCondition} from "src/app"
@ -19,7 +19,7 @@
let showStatus = false let showStatus = false
let joined = false let joined = false
$: joined = find(propEq('url', relay.url), $user?.relays || []) $: joined = find(propEq('url', relay.url), $relays)
onMount(() => { onMount(() => {
return poll(10_000, async () => { return poll(10_000, async () => {

View File

@ -4,7 +4,7 @@
import {nip19} from 'nostr-tools' import {nip19} from 'nostr-tools'
import {navigate} from "svelte-routing" import {navigate} from "svelte-routing"
import {fuzzy} from "src/util/misc" import {fuzzy} from "src/util/misc"
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import network from 'src/agent/network' import network from 'src/agent/network'
import database from 'src/agent/database' import database from 'src/agent/database'
import {getUserReadRelays} from 'src/agent/relays' import {getUserReadRelays} from 'src/agent/relays'

View File

@ -3,7 +3,7 @@
import {nip19} from 'nostr-tools' import {nip19} from 'nostr-tools'
import {now} from 'src/util/misc' import {now} from 'src/util/misc'
import Channel from 'src/partials/Channel.svelte' import Channel from 'src/partials/Channel.svelte'
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getRelaysForEventChildren} from 'src/agent/relays' import {getRelaysForEventChildren} from 'src/agent/relays'
import database from 'src/agent/database' import database from 'src/agent/database'
import network from 'src/agent/network' import network from 'src/agent/network'

View File

@ -4,7 +4,7 @@
import {personKinds} from 'src/util/nostr' import {personKinds} from 'src/util/nostr'
import {now} from 'src/util/misc' import {now} from 'src/util/misc'
import Channel from 'src/partials/Channel.svelte' import Channel from 'src/partials/Channel.svelte'
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getAllPubkeyRelays} from 'src/agent/relays' import {getAllPubkeyRelays} from 'src/agent/relays'
import database from 'src/agent/database' import database from 'src/agent/database'
import network from 'src/agent/network' import network from 'src/agent/network'

View File

@ -6,7 +6,7 @@
import Tabs from "src/partials/Tabs.svelte" import Tabs from "src/partials/Tabs.svelte"
import Network from "src/views/notes/Network.svelte" import Network from "src/views/notes/Network.svelte"
import Popular from "src/views/notes/Popular.svelte" import Popular from "src/views/notes/Popular.svelte"
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
export let activeTab export let activeTab

View File

@ -16,7 +16,7 @@
import Notes from "src/views/person/Notes.svelte" import Notes from "src/views/person/Notes.svelte"
import Likes from "src/views/person/Likes.svelte" import Likes from "src/views/person/Likes.svelte"
import Relays from "src/views/person/Relays.svelte" import Relays from "src/views/person/Relays.svelte"
import {user} from "src/agent/helpers" import {user} from "src/agent/user"
import {getPubkeyWriteRelays, getUserWriteRelays} from "src/agent/relays" import {getPubkeyWriteRelays, getUserWriteRelays} from "src/agent/relays"
import network from "src/agent/network" import network from "src/agent/network"
import keys from "src/agent/keys" import keys from "src/agent/keys"

View File

@ -11,7 +11,7 @@
import Button from "src/partials/Button.svelte" import Button from "src/partials/Button.svelte"
import Content from "src/partials/Content.svelte" import Content from "src/partials/Content.svelte"
import Heading from "src/partials/Heading.svelte" import Heading from "src/partials/Heading.svelte"
import {user} from "src/agent/helpers" import {user} from "src/agent/user"
import {getUserWriteRelays} from 'src/agent/relays' import {getUserWriteRelays} from 'src/agent/relays'
import cmd from "src/agent/cmd" import cmd from "src/agent/cmd"
import {toast} from "src/app" import {toast} from "src/app"

View File

@ -8,14 +8,14 @@
import Anchor from "src/partials/Anchor.svelte" import Anchor from "src/partials/Anchor.svelte"
import Content from "src/partials/Content.svelte" import Content from "src/partials/Content.svelte"
import RelayCard from "src/partials/RelayCard.svelte" import RelayCard from "src/partials/RelayCard.svelte"
import {user} from "src/agent/helpers" import {relays} from "src/agent/relays"
import database from 'src/agent/database' import database from 'src/agent/database'
import {modal, settings} from "src/app" import {modal, settings} from "src/app"
import defaults from "src/agent/defaults" import defaults from "src/agent/defaults"
let q = "" let q = ""
let search let search
let relays = [] let knownRelays = database.watch('relays', t => t.all())
fetch(get(settings).dufflepudUrl + '/relay') fetch(get(settings).dufflepudUrl + '/relay')
.then(async res => { .then(async res => {
@ -26,15 +26,13 @@
database.relays.bulkPatch(createMap('url', defaults.relays)) database.relays.bulkPatch(createMap('url', defaults.relays))
const knownRelays = database.watch('relays', relays => relays.all())
$: { $: {
relays = $user?.relays || [] const joined = new Set(pluck('url', $relays))
const joined = new Set(pluck('url', relays)) search = fuzzy(
const data = ($knownRelays || []).filter(r => !joined.has(r.url)) $knownRelays.filter(r => !joined.has(r.url)),
{keys: ["name", "description", "url"]}
search = fuzzy(data, {keys: ["name", "description", "url"]}) )
} }
</script> </script>
@ -52,11 +50,11 @@
Relays are hubs for your content and connections. At least one is required to Relays are hubs for your content and connections. At least one is required to
interact with the network, but you can join as many as you like. interact with the network, but you can join as many as you like.
</p> </p>
{#if relays.length === 0} {#if $relays.length === 0}
<div class="text-center">No relays connected</div> <div class="text-center">No relays connected</div>
{/if} {/if}
<div class="grid grid-cols-1 gap-4"> <div class="grid grid-cols-1 gap-4">
{#each relays as relay (relay.url)} {#each $relays as relay (relay.url)}
<RelayCard showControls {relay} /> <RelayCard showControls {relay} />
{/each} {/each}
</div> </div>
@ -79,8 +77,8 @@
<RelayCard {relay} /> <RelayCard {relay} />
{/each} {/each}
<small class="text-center"> <small class="text-center">
Showing {Math.min(($knownRelays || []).length - relays.length, 50)} Showing {Math.min(($knownRelays || []).length - $relays.length, 50)}
of {($knownRelays || []).length - relays.length} known relays of {($knownRelays || []).length - $relays.length} known relays
</small> </small>
</div> </div>
</Content> </Content>

View File

@ -7,7 +7,7 @@
import Button from "src/partials/Button.svelte" import Button from "src/partials/Button.svelte"
import Content from "src/partials/Content.svelte" import Content from "src/partials/Content.svelte"
import Heading from "src/partials/Heading.svelte" import Heading from "src/partials/Heading.svelte"
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {toast, settings} from "src/app" import {toast, settings} from "src/app"
let values = {...$settings} let values = {...$settings}

View File

@ -2,7 +2,7 @@ import {last, identity, objOf, prop, flatten, uniq} from 'ramda'
import {nip19} from 'nostr-tools' import {nip19} from 'nostr-tools'
import {ensurePlural, ellipsize, first} from 'hurdak/lib/hurdak' import {ensurePlural, ellipsize, first} from 'hurdak/lib/hurdak'
export const personKinds = [0, 2, 3, 10001, 12165] export const personKinds = [0, 2, 3, 10002, 12165]
export class Tags { export class Tags {
constructor(tags) { constructor(tags) {

View File

@ -2,6 +2,9 @@ import type {Event} from 'nostr-tools'
export type Relay = { export type Relay = {
url: string url: string
score?: number
write?: boolean
read?: boolean
} }
export type Person = { export type Person = {

View File

@ -2,7 +2,7 @@
import {onMount} from "svelte" import {onMount} from "svelte"
import {nip19} from 'nostr-tools' import {nip19} from 'nostr-tools'
import {quantify} from 'hurdak/lib/hurdak' import {quantify} from 'hurdak/lib/hurdak'
import {last, whereEq, find, reject, pluck, propEq} from 'ramda' import {last, reject, pluck, propEq} from 'ramda'
import {fly} from 'svelte/transition' import {fly} from 'svelte/transition'
import {navigate} from "svelte-routing" import {navigate} from "svelte-routing"
import {fuzzy} from "src/util/misc" import {fuzzy} from "src/util/misc"
@ -13,7 +13,7 @@
import Content from "src/partials/Content.svelte" import Content from "src/partials/Content.svelte"
import Modal from "src/partials/Modal.svelte" import Modal from "src/partials/Modal.svelte"
import Heading from 'src/partials/Heading.svelte' import Heading from 'src/partials/Heading.svelte'
import {user} from "src/agent/helpers" import {user} from "src/agent/user"
import {getUserWriteRelays} from 'src/agent/relays' import {getUserWriteRelays} from 'src/agent/relays'
import database from 'src/agent/database' import database from 'src/agent/database'
import cmd from "src/agent/cmd" import cmd from "src/agent/cmd"
@ -27,12 +27,15 @@
let q = '' let q = ''
let search let search
const knownRelays = database.watch('relays', relays => relays.all()) const knownRelays = database.watch('relays', t => t.all())
$: { $: {
const data = reject(({url}) => find(whereEq({url}), relays), $knownRelays || []) const joined = new Set(pluck('url', relays))
search = fuzzy(data, {keys: ["name", "description", "url"]}) search = fuzzy(
$knownRelays.filter(r => !joined.has(r.url)),
{keys: ["name", "description", "url"]}
)
} }
const onSubmit = async () => { const onSubmit = async () => {

View File

@ -5,7 +5,7 @@
import Button from "src/partials/Button.svelte" import Button from "src/partials/Button.svelte"
import Content from 'src/partials/Content.svelte' import Content from 'src/partials/Content.svelte'
import SelectButton from "src/partials/SelectButton.svelte" import SelectButton from "src/partials/SelectButton.svelte"
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getUserWriteRelays} from 'src/agent/relays' import {getUserWriteRelays} from 'src/agent/relays'
import cmd from 'src/agent/cmd' import cmd from 'src/agent/cmd'
import {modal} from 'src/app' import {modal} from 'src/app'

View File

@ -3,7 +3,7 @@
import {personKinds} from "src/util/nostr" import {personKinds} from "src/util/nostr"
import Input from "src/partials/Input.svelte" import Input from "src/partials/Input.svelte"
import PersonInfo from 'src/partials/PersonInfo.svelte' import PersonInfo from 'src/partials/PersonInfo.svelte'
import {user} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getUserReadRelays} from 'src/agent/relays' import {getUserReadRelays} from 'src/agent/relays'
import database from 'src/agent/database' import database from 'src/agent/database'
import network from 'src/agent/network' import network from 'src/agent/network'

View File

@ -1,7 +1,8 @@
<script> <script>
import {uniq} from 'ramda' import {uniq} from 'ramda'
import Notes from "src/partials/Notes.svelte" import Notes from "src/partials/Notes.svelte"
import {user, getFollows, getNetwork} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getFollows, getNetwork} from 'src/agent/social'
import {getAllPubkeyWriteRelays} from 'src/agent/relays' import {getAllPubkeyWriteRelays} from 'src/agent/relays'
// Get first- and second-order follows. shuffle and slice network so we're not // Get first- and second-order follows. shuffle and slice network so we're not

View File

@ -2,7 +2,8 @@
import {uniq} from 'ramda' import {uniq} from 'ramda'
import Notes from "src/partials/Notes.svelte" import Notes from "src/partials/Notes.svelte"
import {isLike} from 'src/util/nostr' import {isLike} from 'src/util/nostr'
import {user, getFollows, getNetwork} from 'src/agent/helpers' import {user} from 'src/agent/user'
import {getFollows, getNetwork} from 'src/agent/social'
import {getAllPubkeyWriteRelays} from 'src/agent/relays' import {getAllPubkeyWriteRelays} from 'src/agent/relays'
// Get first- and second-order follows. shuffle and slice network so we're not // Get first- and second-order follows. shuffle and slice network so we're not