mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Re-work relay selection
This commit is contained in:
parent
0fe2afb3a8
commit
5f1f9f9b69
@ -30,6 +30,7 @@ If you like Coracle and want to support its development, you can donate sats via
|
||||
- [ ] Attachments (a tag w/content type and url)
|
||||
- [ ] Linkify bech32 entities w/ NIP 21 https://github.com/nostr-protocol/nips/blob/master/21.md
|
||||
- [ ] Sign in as user with one click to view things from their pubkey's perspective - do this with multiple accounts
|
||||
- [ ] QR code generation/scanner to share nprofile https://cdn.jb55.com/s/d966a729777c2021.MP4
|
||||
|
||||
# Missions
|
||||
|
||||
@ -73,6 +74,7 @@ If you like Coracle and want to support its development, you can donate sats via
|
||||
|
||||
- [ ] Implement gossip model https://bountsr.org/code/2023/02/03/gossip-model.html
|
||||
- [ ] Add nip 05 to calculation
|
||||
- [ ] Add connection failures to calculation
|
||||
- [ ] Make feeds page customizable. This could potentially use the "lists" NIP
|
||||
- [ ] Show notification at top of feeds: "Showing notes from 3 relays". Click to customize.
|
||||
- [ ] Click through on relays page to view a feed for only that relay.
|
||||
|
@ -13,7 +13,7 @@
|
||||
import {displayPerson, isLike} from 'src/util/nostr'
|
||||
import {timedelta, shuffle, now, sleep} from 'src/util/misc'
|
||||
import cmd from 'src/agent/cmd'
|
||||
import {user, getRelays} from 'src/agent/helpers'
|
||||
import {user, getUserRelays} from 'src/agent/helpers'
|
||||
import database from 'src/agent/database'
|
||||
import keys from 'src/agent/keys'
|
||||
import network from 'src/agent/network'
|
||||
@ -65,6 +65,7 @@
|
||||
menuIsOpen.set(false)
|
||||
}
|
||||
|
||||
const {ready} = database
|
||||
const {lastCheckedAlerts, mostRecentAlert} = alerts
|
||||
const {lastCheckedByPubkey, mostRecentByPubkey} = messages
|
||||
|
||||
@ -97,7 +98,7 @@
|
||||
|
||||
const alertSlowConnections = () => {
|
||||
// Only notify about relays the user is actually subscribed to
|
||||
const relayUrls = pluck('url', getRelays())
|
||||
const relayUrls = pluck('url', getUserRelays('read'))
|
||||
|
||||
// Prune connections we haven't used in a while
|
||||
pool.getConnections()
|
||||
@ -201,6 +202,7 @@
|
||||
|
||||
<Router {url}>
|
||||
<div use:links class="h-full">
|
||||
{#if $ready}
|
||||
<div class="pt-16 text-white h-full lg:ml-56">
|
||||
<Route path="/alerts" component={Alerts} />
|
||||
<Route path="/search/:activeTab" component={Search} />
|
||||
@ -234,6 +236,7 @@
|
||||
</Route>
|
||||
<Route path="*" component={NotFound} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<ul
|
||||
class="py-20 w-56 bg-dark fixed top-0 bottom-0 left-0 transition-all shadow-xl
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {prop, pick, join, uniqBy, last} from 'ramda'
|
||||
import {get} from 'svelte/store'
|
||||
import {first} from "hurdak/lib/hurdak"
|
||||
import {Tags, isRelay, roomAttrs, displayPerson} from 'src/util/nostr'
|
||||
import {getWriteRelays} from 'src/agent/helpers'
|
||||
import {roomAttrs, displayPerson} from 'src/util/nostr'
|
||||
import {getBestRelay} from 'src/agent/helpers'
|
||||
import database from 'src/agent/database'
|
||||
import network from 'src/agent/network'
|
||||
import keys from 'src/agent/keys'
|
||||
@ -29,15 +29,14 @@ const createChatMessage = (relays, roomId, content) =>
|
||||
publishEvent(relays, 42, {content, tags: [["e", roomId, prop('url', first(relays)), "root"]]})
|
||||
|
||||
const createDirectMessage = (relays, pubkey, content) =>
|
||||
// todo, encrypt messages
|
||||
publishEvent(relays, 4, {content, tags: [["p", pubkey]]})
|
||||
|
||||
const createNote = (relays, content, mentions = [], topics = []) => {
|
||||
mentions = mentions.map(p => {
|
||||
const {url} = first(getWriteRelays(p))
|
||||
const name = displayPerson(database.getPersonWithFallback(p))
|
||||
mentions = mentions.map(pubkey => {
|
||||
const name = displayPerson(database.getPersonWithFallback(pubkey))
|
||||
const {url} = getBestRelay(pubkey, 'write')
|
||||
|
||||
return ["p", p, url, name]
|
||||
return ["p", pubkey, url, name]
|
||||
})
|
||||
|
||||
topics = topics.map(t => ["t", t])
|
||||
@ -46,7 +45,7 @@ const createNote = (relays, content, mentions = [], topics = []) => {
|
||||
}
|
||||
|
||||
const createReaction = (relays, note, content) => {
|
||||
const {url} = getBestRelay(relays, note)
|
||||
const {url} = getBestRelay(note.pubkey, 'write')
|
||||
const tags = uniqBy(
|
||||
join(':'),
|
||||
note.tags
|
||||
@ -59,10 +58,10 @@ const createReaction = (relays, note, content) => {
|
||||
}
|
||||
|
||||
const createReply = (relays, note, content, mentions = [], topics = []) => {
|
||||
mentions = mentions.map(p => ["p", p, prop('url', first(getWriteRelays(p)))])
|
||||
mentions = mentions.map(pubkey => ["p", pubkey, prop('url', getBestRelay(pubkey))])
|
||||
topics = topics.map(t => ["t", t])
|
||||
|
||||
const {url} = getBestRelay(relays, note)
|
||||
const {url} = getBestRelay(note.pubkey, 'write')
|
||||
const tags = uniqBy(
|
||||
join(':'),
|
||||
note.tags
|
||||
@ -80,24 +79,6 @@ const deleteEvent = (relays, ids) =>
|
||||
|
||||
// Utils
|
||||
|
||||
const getBestRelay = (relays, event) => {
|
||||
// Find the best relay, based on reply, root, or pubkey. Fall back to a
|
||||
// relay we're going to send the event to
|
||||
const tags = Tags.from(event).type("e")
|
||||
const reply = tags.mark("reply").values().first()
|
||||
const root = tags.mark("root").values().first()
|
||||
|
||||
if (isRelay(reply)) {
|
||||
return reply
|
||||
}
|
||||
|
||||
if (isRelay(root)) {
|
||||
return root
|
||||
}
|
||||
|
||||
return first(getWriteRelays(event.pubkey).concat(relays))
|
||||
}
|
||||
|
||||
const publishEvent = (relays, kind, {content = '', tags = []} = {}) => {
|
||||
if (relays.length === 0) {
|
||||
throw new Error("Unable to publish, no relays specified")
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {debounce} from 'throttle-debounce'
|
||||
import {is, prop, without} from 'ramda'
|
||||
import {writable} from 'svelte/store'
|
||||
import {switcherFn, createMap, ensurePlural, first} from 'hurdak/lib/hurdak'
|
||||
import {defer, asyncIterableToArray} from 'src/util/misc'
|
||||
import {is, prop, find, without, pluck, all, identity} from 'ramda'
|
||||
import {writable, derived} from 'svelte/store'
|
||||
import {switcherFn, createMap, ensurePlural} from 'hurdak/lib/hurdak'
|
||||
import {defer, where, asyncIterableToArray} from 'src/util/misc'
|
||||
|
||||
// Types
|
||||
|
||||
@ -18,8 +18,9 @@ type Table = {
|
||||
patch: (data: object) => void
|
||||
bulkPut: (data: object) => void
|
||||
bulkPatch: (data: object) => void
|
||||
all: (where?: object) => Promise<any>
|
||||
get: (key: string) => any
|
||||
iter: (spec?: object) => Promise<Array<Record<string, any>>>
|
||||
all: (spec?: object) => Array<Record<string, any>>
|
||||
get: (key: string) => Record<string, any>
|
||||
}
|
||||
|
||||
// Plumbing
|
||||
@ -142,6 +143,8 @@ const defineTable = (name: string, pk: string): Table => {
|
||||
let listeners = []
|
||||
let data = {}
|
||||
|
||||
const ready = writable(false)
|
||||
|
||||
const subscribe = f => {
|
||||
listeners.push(f)
|
||||
|
||||
@ -196,8 +199,10 @@ const defineTable = (name: string, pk: string): Table => {
|
||||
const put = item => bulkPut(createMap(pk, [item]))
|
||||
const patch = item => bulkPatch(createMap(pk, [item]))
|
||||
|
||||
const all = (where = {}) => asyncIterableToArray(iterate(name, where), prop('v'))
|
||||
const one = (where = {}) => first(all(where))
|
||||
const toArray = () => Object.values(data)
|
||||
const iter = (spec = {}) => asyncIterableToArray(iterate(name, spec), prop('v'))
|
||||
const all = (spec = {}) => toArray().filter(where(spec))
|
||||
const one = (spec = {}) => find(where(spec), toArray())
|
||||
const get = k => data[k]
|
||||
|
||||
// Sync from storage initially
|
||||
@ -208,9 +213,13 @@ const defineTable = (name: string, pk: string): Table => {
|
||||
}
|
||||
|
||||
setAndNotify(initialData)
|
||||
ready.set(true)
|
||||
})()
|
||||
|
||||
registry[name] = {name, subscribe, bulkPut, bulkPatch, put, patch, all, one, get}
|
||||
registry[name] = {
|
||||
name, subscribe, bulkPut, bulkPatch, put, patch, toArray, iter, all, one, get,
|
||||
ready,
|
||||
}
|
||||
|
||||
return registry[name]
|
||||
}
|
||||
@ -277,8 +286,10 @@ const getPersonWithFallback = pubkey => people.get(pubkey) || {pubkey}
|
||||
|
||||
const clearAll = () => Promise.all(Object.keys(registry).map(clear))
|
||||
|
||||
const ready = derived(pluck('ready', Object.values(registry)), all(identity))
|
||||
|
||||
export default {
|
||||
getItem, setItem, removeItem, length, clear, keys, iterate, watch,
|
||||
getPersonWithFallback, clearAll, people, rooms, messages, alerts, relays,
|
||||
routes,
|
||||
routes, ready,
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type {Person} from 'src/util/types'
|
||||
import type {Readable} from 'svelte/store'
|
||||
import {uniq, reject, last, propEq, uniqBy, prop} from 'ramda'
|
||||
import {isEmpty, pick, identity, sortBy, uniq, reject, groupBy, last, propEq, uniqBy, prop} from 'ramda'
|
||||
import {first} from 'hurdak/lib/hurdak'
|
||||
import {derived, get} from 'svelte/store'
|
||||
import {Tags} from 'src/util/nostr'
|
||||
import {now, timedelta} from 'src/util/misc'
|
||||
@ -37,27 +38,33 @@ export const getFollows = pubkey => {
|
||||
return Tags.wrap(person.petnames || defaults.petnames).values().all()
|
||||
}
|
||||
|
||||
export const getRelays = (pubkey?: string) => {
|
||||
let relays = database.getPersonWithFallback(pubkey).relays
|
||||
export const getPersonRelays = (person, mode = 'all') => {
|
||||
const relays = isEmpty(person?.relays || []) ? defaults.relays : person.relays
|
||||
|
||||
if (!relays?.length) {
|
||||
relays = database.getPersonWithFallback(get(keys.pubkey)).relays
|
||||
return reject(propEq(mode, '!'), relays)
|
||||
}
|
||||
|
||||
if (!relays?.length) {
|
||||
relays = defaults.relays
|
||||
export const getUserRelays = (mode = 'all') =>
|
||||
getPersonRelays(get(user), mode)
|
||||
|
||||
export const getPubkeyRelays = (pubkey, mode = 'all') =>
|
||||
getPersonRelays(database.people.get(pubkey), mode)
|
||||
|
||||
export const getTopRelays = (pubkeys, mode = 'all') => {
|
||||
const routes = database.routes.all({mode, pubkey: pubkeys})
|
||||
const routesByPubkey = groupBy(prop('pubkey'), routes)
|
||||
const selectRoute = k => first(sortBy(prop('score'), routesByPubkey[k] || []))
|
||||
|
||||
return uniqBy(prop('url'), pubkeys.map(selectRoute).filter(identity)).map(pick(['url']))
|
||||
}
|
||||
|
||||
return relays
|
||||
}
|
||||
|
||||
export const getWriteRelays = (...args) =>
|
||||
reject(propEq('write', '!'), getRelays(...args))
|
||||
export const getBestRelay = (pubkey, mode = 'all') =>
|
||||
first(getTopRelays([pubkey], mode).concat(getPubkeyRelays(pubkey, mode)))
|
||||
|
||||
export const getEventRelays = event => {
|
||||
return uniqBy(
|
||||
prop('url'),
|
||||
getRelays(event.pubkey)
|
||||
getPubkeyRelays(event.pubkey, 'write')
|
||||
.concat(Tags.from(event).relays())
|
||||
.concat({url: event.seen_on})
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import lf from 'localforage'
|
||||
import memoryStorageDriver from 'localforage-memoryStorageDriver'
|
||||
import {is, complement, equals, isNil, pipe, prop, identity, allPass} from 'ramda'
|
||||
import {switcherFn} from 'hurdak/lib/hurdak'
|
||||
import {where} from 'src/util/misc'
|
||||
|
||||
// Firefox private mode doesn't have access to any storage options
|
||||
lf.defineDriver(memoryStorageDriver)
|
||||
@ -28,41 +28,9 @@ addEventListener('message', async ({data: {topic, payload, channel}}) => {
|
||||
reply('localforage.return', result)
|
||||
},
|
||||
'localforage.iterate': async () => {
|
||||
const {storeName, where} = payload
|
||||
const matchesFilter = allPass(
|
||||
Object.entries(where)
|
||||
.map(([key, value]) => {
|
||||
let [field, operator = 'eq'] = key.split(':')
|
||||
let test, modifier = identity
|
||||
const matchesFilter = where(payload.where)
|
||||
|
||||
if (operator.startsWith('!')) {
|
||||
operator = operator.slice(1)
|
||||
modifier = complement
|
||||
}
|
||||
|
||||
if (operator === 'eq' && is(Array, value)) {
|
||||
test = v => value.includes(v)
|
||||
} else if (operator === 'eq') {
|
||||
test = equals(value)
|
||||
} else if (operator === 'lt') {
|
||||
test = v => (v || 0) < value
|
||||
} else if (operator === 'lte') {
|
||||
test = v => (v || 0) <= value
|
||||
} else if (operator === 'gt') {
|
||||
test = v => (v || 0) > value
|
||||
} else if (operator === 'gte') {
|
||||
test = v => (v || 0) >= value
|
||||
} else if (operator === 'nil') {
|
||||
test = isNil
|
||||
} else {
|
||||
throw new Error(`Invalid operator ${operator}`)
|
||||
}
|
||||
|
||||
return pipe(prop(field), modifier(test))
|
||||
})
|
||||
)
|
||||
|
||||
getStore(storeName).iterate(
|
||||
getStore(payload.storeName).iterate(
|
||||
(v, k, i) => {
|
||||
if (matchesFilter(v)) {
|
||||
reply('localforage.item', {v, k, i})
|
||||
|
@ -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, getRelays, getWriteRelays} from 'src/agent/helpers'
|
||||
import {user, getUserRelays} from 'src/agent/helpers'
|
||||
import defaults from 'src/agent/defaults'
|
||||
import database from 'src/agent/database'
|
||||
import network from 'src/agent/network'
|
||||
@ -18,11 +18,13 @@ import {toast, routes, modal, settings, logUsage} from 'src/app/ui'
|
||||
export {toast, modal, settings, alerts, messages, logUsage}
|
||||
|
||||
export const loadAppData = pubkey => {
|
||||
const relays = getUserRelays('read')
|
||||
|
||||
return Promise.all([
|
||||
network.loadNetwork(getRelays(), pubkey),
|
||||
alerts.load(getRelays(), pubkey),
|
||||
alerts.listen(getRelays(), pubkey),
|
||||
messages.listen(getRelays(), pubkey),
|
||||
network.loadNetwork(relays, pubkey),
|
||||
alerts.load(relays, pubkey),
|
||||
alerts.listen(relays, pubkey),
|
||||
messages.listen(relays, pubkey),
|
||||
])
|
||||
}
|
||||
|
||||
@ -39,7 +41,7 @@ export const login = async ({privkey, pubkey}: {privkey?: string, pubkey?: strin
|
||||
loadAppData(pubkey)
|
||||
|
||||
// Load our user so we can populate network and show profile info
|
||||
await network.loadPeople(getRelays(), [pubkey])
|
||||
await network.loadPeople(getUserRelays('read'), [pubkey])
|
||||
|
||||
// Not ideal, but the network tab depends on the user's social network being
|
||||
// loaded, so put them on global when they first log in so we're not slowing
|
||||
@ -73,7 +75,7 @@ export const removeRelay = async url => {
|
||||
defaults.relays = modify(defaults.relays)
|
||||
|
||||
if (person) {
|
||||
await cmd.setRelays(getWriteRelays(), modify(person.relays || []))
|
||||
await cmd.setRelays(getUserRelays('write'), modify(person.relays || []))
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +87,7 @@ export const setRelayWriteCondition = async (url, write) => {
|
||||
defaults.relays = modify(defaults.relays)
|
||||
|
||||
if (person) {
|
||||
await cmd.setRelays(getWriteRelays(), modify(person.relays || []))
|
||||
await cmd.setRelays(getUserRelays('write'), modify(person.relays || []))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
let prevContent = ''
|
||||
let search
|
||||
|
||||
database.people.all({'name:!nil': null}).then(people => {
|
||||
database.people.iter({'name:!nil': null}).then(people => {
|
||||
search = fuzzy(people, {keys: ["name", "pubkey"]})
|
||||
})
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
import {nip19} from 'nostr-tools'
|
||||
import {navigate} from "svelte-routing"
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import {getRelays, user} from 'src/agent/helpers'
|
||||
import {getUserRelays, getTopRelays, user} from 'src/agent/helpers'
|
||||
import network from 'src/agent/network'
|
||||
import database from 'src/agent/database'
|
||||
import {modal, messages} from 'src/app'
|
||||
@ -24,7 +24,7 @@
|
||||
const messages = await database.messages.all()
|
||||
const pubkeys = without([$user.pubkey], uniq(messages.flatMap(m => [m.pubkey, m.recipient])))
|
||||
|
||||
await network.loadPeople(getRelays(), pubkeys)
|
||||
await network.loadPeople(getTopRelays(pubkeys, 'write'), pubkeys)
|
||||
|
||||
return sortBy(k => -(mostRecentByPubkey[k] || 0), pubkeys)
|
||||
.map(k => ({type: 'npub', id: k, ...database.getPersonWithFallback(k)}))
|
||||
@ -58,7 +58,7 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const sub = network.listen(getRelays(), [{kinds: [40, 41]}])
|
||||
const sub = network.listen(getUserRelays('read'), [{kinds: [40, 41]}])
|
||||
|
||||
return () => {
|
||||
sub.then(s => {
|
||||
|
@ -3,7 +3,7 @@
|
||||
import {nip19} from 'nostr-tools'
|
||||
import {now, batch} from 'src/util/misc'
|
||||
import Channel from 'src/partials/Channel.svelte'
|
||||
import {getRelays, user} from 'src/agent/helpers'
|
||||
import {getEventRelays, user} from 'src/agent/helpers'
|
||||
import database from 'src/agent/database'
|
||||
import network from 'src/agent/network'
|
||||
import {modal} from 'src/app'
|
||||
@ -11,21 +11,11 @@
|
||||
|
||||
export let entity
|
||||
|
||||
let {data: roomId} = nip19.decode(entity) as {data: string}
|
||||
let room = database.watch('rooms', rooms => rooms.get(roomId))
|
||||
|
||||
const getRoomRelays = () => {
|
||||
let relays = getRelays()
|
||||
|
||||
if ($room) {
|
||||
relays = relays.concat(getRelays($room.pubkey))
|
||||
}
|
||||
|
||||
return relays
|
||||
}
|
||||
const {data: roomId} = nip19.decode(entity) as {data: string}
|
||||
const room = database.watch('rooms', rooms => rooms.get(roomId))
|
||||
|
||||
const listenForMessages = async cb => {
|
||||
const relays = getRoomRelays()
|
||||
const relays = getEventRelays($room)
|
||||
|
||||
return network.listen(
|
||||
relays,
|
||||
@ -43,7 +33,7 @@
|
||||
}
|
||||
|
||||
const loadMessages = async ({until, limit}) => {
|
||||
const relays = getRoomRelays()
|
||||
const relays = getEventRelays($room)
|
||||
const events = await network.load(relays, {kinds: [42], '#e': [roomId], until, limit})
|
||||
|
||||
if (events.length) {
|
||||
@ -58,7 +48,7 @@
|
||||
}
|
||||
|
||||
const sendMessage = content =>
|
||||
cmd.createChatMessage(getRoomRelays(), roomId, content)
|
||||
cmd.createChatMessage(getEventRelays($room), roomId, content)
|
||||
</script>
|
||||
|
||||
<Channel
|
||||
|
@ -4,7 +4,7 @@
|
||||
import {personKinds} from 'src/util/nostr'
|
||||
import {batch, now} from 'src/util/misc'
|
||||
import Channel from 'src/partials/Channel.svelte'
|
||||
import {getRelays, getWriteRelays, user} from 'src/agent/helpers'
|
||||
import {getUserRelays, getPubkeyRelays, user} from 'src/agent/helpers'
|
||||
import database from 'src/agent/database'
|
||||
import network from 'src/agent/network'
|
||||
import keys from 'src/agent/keys'
|
||||
@ -20,6 +20,8 @@
|
||||
|
||||
messages.lastCheckedByPubkey.update($obj => ({...$obj, [pubkey]: now()}))
|
||||
|
||||
const getRelays = () => getUserRelays('write').concat(getPubkeyRelays(pubkey, 'write'))
|
||||
|
||||
const decryptMessages = async events => {
|
||||
// Gotta do it in serial because of extension limitations
|
||||
for (const event of events) {
|
||||
@ -32,7 +34,7 @@
|
||||
}
|
||||
|
||||
const listenForMessages = cb => network.listen(
|
||||
getRelays().concat(getRelays(pubkey)),
|
||||
getRelays(),
|
||||
[{kinds: personKinds, authors: [pubkey]},
|
||||
{kinds: [4], authors: [$user.pubkey], '#p': [pubkey]},
|
||||
{kinds: [4], authors: [pubkey], '#p': [$user.pubkey]}],
|
||||
@ -55,9 +57,8 @@
|
||||
}
|
||||
|
||||
const sendMessage = async content => {
|
||||
const relays = getWriteRelays().concat(getRelays(pubkey))
|
||||
const cyphertext = await crypt.encrypt(pubkey, content)
|
||||
const event = await cmd.createDirectMessage(relays, pubkey, cyphertext)
|
||||
const event = await cmd.createDirectMessage(getRelays(), pubkey, cyphertext)
|
||||
|
||||
// Return unencrypted content so we can display it immediately
|
||||
return {...event, content}
|
||||
|
@ -2,7 +2,6 @@
|
||||
import {last, find, reject} from 'ramda'
|
||||
import {onMount, onDestroy} from 'svelte'
|
||||
import {nip19} from 'nostr-tools'
|
||||
import {first} from 'hurdak/lib/hurdak'
|
||||
import {fly} from 'svelte/transition'
|
||||
import {navigate} from 'svelte-routing'
|
||||
import {renderContent} from 'src/util/html'
|
||||
@ -14,7 +13,7 @@
|
||||
import Notes from "src/views/person/Notes.svelte"
|
||||
import Likes from "src/views/person/Likes.svelte"
|
||||
import Network from "src/views/person/Network.svelte"
|
||||
import {getRelays, getWriteRelays, user} from "src/agent/helpers"
|
||||
import {getPubkeyRelays, getUserRelays, user} from "src/agent/helpers"
|
||||
import network from "src/agent/network"
|
||||
import keys from "src/agent/keys"
|
||||
import database from "src/agent/database"
|
||||
@ -24,7 +23,7 @@
|
||||
|
||||
export let npub
|
||||
export let activeTab
|
||||
export let relays = null
|
||||
export let relays = []
|
||||
|
||||
let subs = []
|
||||
let pubkey = nip19.decode(npub).data as string
|
||||
@ -37,22 +36,27 @@
|
||||
$: following = find(t => t[1] === pubkey, $user?.petnames || [])
|
||||
|
||||
onMount(async () => {
|
||||
// Add all the relays we know the person uses
|
||||
relays = relays.concat(getPubkeyRelays(pubkey))
|
||||
|
||||
// Refresh our person if needed
|
||||
network.loadPeople(relays || getRelays(pubkey), [pubkey]).then(() => {
|
||||
network.loadPeople(relays, [pubkey]).then(() => {
|
||||
person = database.getPersonWithFallback(pubkey)
|
||||
loading = false
|
||||
})
|
||||
|
||||
// Get our followers count
|
||||
subs.push(await network.listen(
|
||||
relays || getRelays(pubkey),
|
||||
subs.push(
|
||||
await network.listen(
|
||||
relays,
|
||||
[{kinds: [3], '#p': [pubkey]}],
|
||||
e => {
|
||||
followers.add(e.pubkey)
|
||||
followersCount = followers.size
|
||||
},
|
||||
{shouldProcess: false},
|
||||
))
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
@ -74,17 +78,16 @@
|
||||
}
|
||||
|
||||
const follow = async () => {
|
||||
const relay = first(relays || getRelays(pubkey))
|
||||
const tag = ["p", pubkey, relay.url, person.name || ""]
|
||||
const tag = ["p", pubkey, relays[0].url, person.name || ""]
|
||||
const petnames = reject(t => t[1] === pubkey, $user.petnames).concat([tag])
|
||||
|
||||
cmd.setPetnames(getWriteRelays(), petnames)
|
||||
cmd.setPetnames(getUserRelays('write'), petnames)
|
||||
}
|
||||
|
||||
const unfollow = async () => {
|
||||
const petnames = reject(t => t[1] === pubkey, $user.petnames)
|
||||
|
||||
cmd.setPetnames(getWriteRelays(), petnames)
|
||||
cmd.setPetnames(getUserRelays('write'), petnames)
|
||||
}
|
||||
|
||||
const openAdvanced = () => {
|
||||
|
@ -10,7 +10,7 @@
|
||||
import Button from "src/partials/Button.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Heading from "src/partials/Heading.svelte"
|
||||
import {user, getWriteRelays} from "src/agent/helpers"
|
||||
import {user, getUserRelays} from "src/agent/helpers"
|
||||
import cmd from "src/agent/cmd"
|
||||
import {toast} from "src/app"
|
||||
import {routes} from "src/app/ui"
|
||||
@ -45,7 +45,7 @@
|
||||
const submit = async event => {
|
||||
event.preventDefault()
|
||||
|
||||
await cmd.updateUser(getWriteRelays(), values)
|
||||
await cmd.updateUser(getUserRelays('write'), values)
|
||||
|
||||
navigate(routes.person($user.pubkey, 'profile'))
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {debounce} from 'throttle-debounce'
|
||||
import {pluck, identity, sortBy} from "ramda"
|
||||
import {allPass, prop, pipe, isNil, complement, equals, is, pluck, sum, identity, sortBy} from "ramda"
|
||||
import Fuse from "fuse.js/dist/fuse.min.js"
|
||||
import {writable} from 'svelte/store'
|
||||
import {isObject} from 'hurdak/lib/hurdak'
|
||||
@ -145,7 +145,7 @@ export const getLastSync = (k, fallback = 0) => {
|
||||
export class Cursor {
|
||||
until: number
|
||||
limit: number
|
||||
constructor(limit = 10) {
|
||||
constructor(limit = 50) {
|
||||
this.until = now()
|
||||
this.limit = limit
|
||||
}
|
||||
@ -197,3 +197,40 @@ export const asyncIterableToArray = async (it, f = identity) => {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export const avg = xs => sum(xs) / xs.length
|
||||
|
||||
export const where = filters =>
|
||||
allPass(
|
||||
Object.entries(filters)
|
||||
.map(([key, value]) => {
|
||||
/* eslint prefer-const: 0 */
|
||||
let [field, operator = 'eq'] = key.split(':')
|
||||
let test, modifier = identity
|
||||
|
||||
if (operator.startsWith('!')) {
|
||||
operator = operator.slice(1)
|
||||
modifier = complement
|
||||
}
|
||||
|
||||
if (operator === 'eq' && is(Array, value)) {
|
||||
test = v => (value as Array<any>).includes(v)
|
||||
} else if (operator === 'eq') {
|
||||
test = equals(value)
|
||||
} else if (operator === 'lt') {
|
||||
test = v => (v || 0) < value
|
||||
} else if (operator === 'lte') {
|
||||
test = v => (v || 0) <= value
|
||||
} else if (operator === 'gt') {
|
||||
test = v => (v || 0) > value
|
||||
} else if (operator === 'gte') {
|
||||
test = v => (v || 0) >= value
|
||||
} else if (operator === 'nil') {
|
||||
test = isNil
|
||||
} else {
|
||||
throw new Error(`Invalid operator ${operator}`)
|
||||
}
|
||||
|
||||
return pipe(prop(field), modifier(test))
|
||||
})
|
||||
)
|
||||
|
@ -6,7 +6,7 @@
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Textarea from "src/partials/Textarea.svelte"
|
||||
import Button from "src/partials/Button.svelte"
|
||||
import {getWriteRelays} from 'src/agent/helpers'
|
||||
import {getUserRelays} from 'src/agent/helpers'
|
||||
import database from 'src/agent/database'
|
||||
import cmd from "src/agent/cmd"
|
||||
import {toast, modal} from "src/app"
|
||||
@ -36,8 +36,8 @@
|
||||
toast.show("error", "Please enter a name for your room.")
|
||||
} else {
|
||||
const event = room.id
|
||||
? await cmd.updateRoom(getWriteRelays(), room)
|
||||
: await cmd.createRoom(getWriteRelays(), room)
|
||||
? await cmd.updateRoom(getUserRelays('write'), room)
|
||||
: await cmd.createRoom(getUserRelays('write'), room)
|
||||
|
||||
await database.rooms.patch({id: room.id || event.id, joined: true})
|
||||
|
||||
|
@ -13,13 +13,13 @@
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Modal from "src/partials/Modal.svelte"
|
||||
import Heading from 'src/partials/Heading.svelte'
|
||||
import {user, getWriteRelays} from "src/agent/helpers"
|
||||
import {user, getUserRelays} from "src/agent/helpers"
|
||||
import database from 'src/agent/database'
|
||||
import cmd from "src/agent/cmd"
|
||||
import {toast, modal} from "src/app"
|
||||
|
||||
let input = null
|
||||
let relays = getWriteRelays()
|
||||
let relays = getUserRelays('write')
|
||||
let showSettings = false
|
||||
let q = ''
|
||||
let search
|
||||
|
@ -2,7 +2,7 @@
|
||||
import {onMount} from 'svelte'
|
||||
import {nip19} from 'nostr-tools'
|
||||
import {fly} from 'svelte/transition'
|
||||
import {getRelays} from 'src/agent/helpers'
|
||||
import {getEventRelays, getUserRelays} from 'src/agent/helpers'
|
||||
import network from 'src/agent/network'
|
||||
import {annotate} from 'src/app'
|
||||
import Note from 'src/partials/Note.svelte'
|
||||
@ -10,7 +10,7 @@
|
||||
import Spinner from 'src/partials/Spinner.svelte'
|
||||
|
||||
export let note
|
||||
export let relays = getRelays()
|
||||
export let relays = getUserRelays().concat(getEventRelays(note))
|
||||
|
||||
let loading = true
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
// Show the main note without waiting for context
|
||||
if (!note.pubkey) {
|
||||
note = annotate(found, [])
|
||||
relays = getEventRelays(note)
|
||||
}
|
||||
|
||||
const context = await network.loadContext(relays, found, {
|
||||
|
@ -1,15 +1,16 @@
|
||||
<script type="ts">
|
||||
import Content from 'src/partials/Content.svelte'
|
||||
import PersonInfo from 'src/partials/PersonInfo.svelte'
|
||||
import {getRelays} from 'src/agent/helpers'
|
||||
import {getUserRelays, getTopRelays} from 'src/agent/helpers'
|
||||
import database from 'src/agent/database'
|
||||
import network from 'src/agent/network'
|
||||
|
||||
export let pubkeys
|
||||
|
||||
const relays = getUserRelays('read').concat(getTopRelays(pubkeys, 'write'))
|
||||
const people = database.watch('people', people => people.all({pubkey: pubkeys}))
|
||||
|
||||
network.loadPeople(getRelays(), pubkeys)
|
||||
network.loadPeople(relays, pubkeys)
|
||||
</script>
|
||||
|
||||
<Content gap={2}>
|
||||
|
@ -5,7 +5,7 @@
|
||||
import Button from "src/partials/Button.svelte"
|
||||
import Content from 'src/partials/Content.svelte'
|
||||
import SelectButton from "src/partials/SelectButton.svelte"
|
||||
import {user, getWriteRelays} from 'src/agent/helpers'
|
||||
import {user, getUserRelays} from 'src/agent/helpers'
|
||||
import cmd from 'src/agent/cmd'
|
||||
import {modal} from 'src/app'
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
.concat([["p", $modal.person.pubkey, muffleValue.toString()]])
|
||||
.filter(t => last(t) !== "1")
|
||||
|
||||
cmd.muffle(getWriteRelays(), muffleTags)
|
||||
cmd.muffle(getUserRelays('write'), muffleTags)
|
||||
|
||||
history.back()
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
let search
|
||||
|
||||
database.people.all({'name:!nil': null}).then(people => {
|
||||
database.people.iter({'name:!nil': null}).then(people => {
|
||||
search = fuzzy(people, {keys: ["name", "about", "pubkey"]})
|
||||
})
|
||||
</script>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<script>
|
||||
import Notes from "src/partials/Notes.svelte"
|
||||
import {Cursor, now, batch} from 'src/util/misc'
|
||||
import {getRelays, getMuffle} from 'src/agent/helpers'
|
||||
import {getUserRelays, getMuffle} from 'src/agent/helpers'
|
||||
import network from 'src/agent/network'
|
||||
import {threadify} from 'src/app'
|
||||
|
||||
const relays = getRelays()
|
||||
const relays = getUserRelays('read')
|
||||
const filter = {kinds: [1, 5, 7]}
|
||||
const cursor = new Cursor()
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import {uniqBy, prop} from 'ramda'
|
||||
import {uniq} from 'ramda'
|
||||
import Notes from "src/partials/Notes.svelte"
|
||||
import {now, Cursor, shuffle, batch} from 'src/util/misc'
|
||||
import {user, getRelays, getFollows, getMuffle} from 'src/agent/helpers'
|
||||
import {user, getTopRelays, getFollows, getMuffle} from 'src/agent/helpers'
|
||||
import network from 'src/agent/network'
|
||||
import {threadify} from 'src/app'
|
||||
|
||||
@ -10,20 +10,23 @@
|
||||
// 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 authors = follows.concat(others).slice(0, 100)
|
||||
const authors = uniq(follows.concat(others)).slice(0, 100)
|
||||
const filter = {kinds: [1, 7], authors}
|
||||
const cursor = new Cursor()
|
||||
const relays = uniqBy(prop('url'), follows.flatMap(getRelays))
|
||||
|
||||
const listenForNotes = onNotes =>
|
||||
network.listen(relays, {...filter, since: now()}, batch(300, async notes => {
|
||||
const listenForNotes = async onNotes => {
|
||||
const relays = getTopRelays(authors, 'write')
|
||||
|
||||
return network.listen(relays, {...filter, since: now()}, batch(300, async notes => {
|
||||
const context = await network.loadContext(relays, notes)
|
||||
|
||||
onNotes(threadify(notes, context, {muffle: getMuffle(), showReplies: false}))
|
||||
}))
|
||||
}
|
||||
|
||||
const loadNotes = async () => {
|
||||
const {limit, until} = cursor
|
||||
const relays = getTopRelays(authors, 'write')
|
||||
const notes = await network.load(relays, {...filter, limit, until})
|
||||
const context = await network.loadContext(relays, notes)
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
<script>
|
||||
import Notes from "src/partials/Notes.svelte"
|
||||
import {now, batch, Cursor} from 'src/util/misc'
|
||||
import {getRelays, getMuffle} from 'src/agent/helpers'
|
||||
import {getPubkeyRelays, getMuffle} from 'src/agent/helpers'
|
||||
import network from 'src/agent/network'
|
||||
import {threadify} from 'src/app'
|
||||
|
||||
export let pubkey
|
||||
|
||||
const relays = getRelays(pubkey)
|
||||
const relays = getPubkeyRelays(pubkey, 'write')
|
||||
const filter = {kinds: [7], authors: [pubkey]}
|
||||
const cursor = new Cursor()
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
<script>
|
||||
import Notes from "src/partials/Notes.svelte"
|
||||
import {now, batch, Cursor} from 'src/util/misc'
|
||||
import {getRelays, getMuffle} from 'src/agent/helpers'
|
||||
import {getPubkeyRelays, getMuffle} from 'src/agent/helpers'
|
||||
import network from 'src/agent/network'
|
||||
import {threadify} from 'src/app'
|
||||
|
||||
export let pubkey
|
||||
|
||||
const relays = getRelays(pubkey)
|
||||
const relays = getPubkeyRelays(pubkey, 'write')
|
||||
const filter = {kinds: [1], authors: [pubkey]}
|
||||
const cursor = new Cursor()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user