Move relay selections to system/routing

This commit is contained in:
Jonathan Staab 2023-07-03 11:09:42 -07:00
parent 8205f70027
commit 0405a073c0
14 changed files with 133 additions and 120 deletions

2
.env
View File

@ -1,6 +1,6 @@
VITE_THEME=transparent:transparent,black:#0f0f0e,white:#FFFFFF,accent:#EB5E28,accent-light:#FB652C,gray-dark:#8D8581,gray-light:#A8A5A4,danger:#ff0000,warning:#ebd112,success:#37ab51,input:#FAF6F1,input-hover:#F2EBE1 VITE_THEME=transparent:transparent,black:#0f0f0e,white:#FFFFFF,accent:#EB5E28,accent-light:#FB652C,gray-dark:#8D8581,gray-light:#A8A5A4,danger:#ff0000,warning:#ebd112,success:#37ab51,input:#FAF6F1,input-hover:#F2EBE1
VITE_DEFAULT_FOLLOWS=1739d937dc8c0c7370aa27585938c119e25c41f6c441a5d34c6d38503e3136ef,fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52,cc8d072efdcc676fcbac14f6cd6825edc3576e55eb786a2a975ee034a6a026cb,d91191e30e00444b942c0e82cad470b32af171764c2275bee0bd99377efd4075,3335d373e6c1b5bc669b4b1220c08728ea8ce622e5a7cfeeb4c0001d91ded1de,0b118e40d6f3dfabb17f21a94a647701f140d8b063a9e84fe6e483644edc09cb,b83a28b7e4e5d20bd960c5faeb6625f95529166b8bdb045d42634a2f35919450,958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c,a4cb51f4618cfcd16b2d3171c466179bed8e197c43b8598823b04de266cef110,e56e7b4326618f3d626c0e398f5082c3b16732e469e0a048b7ddb544c2be294a,011c1b374c12fbd3633e98957d3c46bed67983abecef50706c73a77c171d0d2c,b9e76546ba06456ed301d9e52bc49fa48e70a6bf2282be7a1ae72947612023dc,b708f7392f588406212c3882e7b3bc0d9b08d62f95fa170d099127ece2770e5e,5c508c34f58866ec7341aaf10cc1af52e9232bb9f859c8103ca5ecf2aa93bf78,baf27a4cc4da49913e7fdecc951fd3b971c9279959af62b02b761a043c33384c,2edbcea694d164629854a52583458fd6d965b161e3c48b57d3aff01940558884,0fecf65daa26faf3f668e8143325a4c199a040b6345ed40a08614d7dd85b1823,1bc70a0148b3f316da33fe3c89f23e3e71ac4ff998027ec712b905cd24f6a411,f783ba3b12b91e375aba6594015b90bd95f7e132b03cc8c4c52ce0a7c36aab52,3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24,82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2,3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d,ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49,97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322 VITE_DEFAULT_FOLLOWS=1739d937dc8c0c7370aa27585938c119e25c41f6c441a5d34c6d38503e3136ef,fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52,cc8d072efdcc676fcbac14f6cd6825edc3576e55eb786a2a975ee034a6a026cb,d91191e30e00444b942c0e82cad470b32af171764c2275bee0bd99377efd4075,3335d373e6c1b5bc669b4b1220c08728ea8ce622e5a7cfeeb4c0001d91ded1de,0b118e40d6f3dfabb17f21a94a647701f140d8b063a9e84fe6e483644edc09cb,b83a28b7e4e5d20bd960c5faeb6625f95529166b8bdb045d42634a2f35919450,958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c,a4cb51f4618cfcd16b2d3171c466179bed8e197c43b8598823b04de266cef110,e56e7b4326618f3d626c0e398f5082c3b16732e469e0a048b7ddb544c2be294a,011c1b374c12fbd3633e98957d3c46bed67983abecef50706c73a77c171d0d2c,b9e76546ba06456ed301d9e52bc49fa48e70a6bf2282be7a1ae72947612023dc,b708f7392f588406212c3882e7b3bc0d9b08d62f95fa170d099127ece2770e5e,5c508c34f58866ec7341aaf10cc1af52e9232bb9f859c8103ca5ecf2aa93bf78,baf27a4cc4da49913e7fdecc951fd3b971c9279959af62b02b761a043c33384c,2edbcea694d164629854a52583458fd6d965b161e3c48b57d3aff01940558884,0fecf65daa26faf3f668e8143325a4c199a040b6345ed40a08614d7dd85b1823,1bc70a0148b3f316da33fe3c89f23e3e71ac4ff998027ec712b905cd24f6a411,f783ba3b12b91e375aba6594015b90bd95f7e132b03cc8c4c52ce0a7c36aab52,3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24,82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2,3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d,ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49,97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322
VITE_DUFFLEPUD_URL=http://localhost:8000 VITE_DUFFLEPUD_URL=https://dufflepud.onrender.com
VITE_ENABLE_ZAPS=true VITE_ENABLE_ZAPS=true
VITE_FORCE_RELAYS= VITE_FORCE_RELAYS=
VITE_LOGO_URL= VITE_LOGO_URL=

View File

@ -6,7 +6,10 @@ import {Tags, isRelay, findReplyId} from "src/util/nostr"
import {shuffle} from "src/util/misc" import {shuffle} from "src/util/misc"
import {routes} from "src/agent/db" import {routes} from "src/agent/db"
import pool from "src/agent/pool" import pool from "src/agent/pool"
import user from "src/agent/user"
const ext = {routing: null}
export default {ext}
// From Mike Dilger: // From Mike Dilger:
// 1) Other people's write relays — pull events from people you follow, // 1) Other people's write relays — pull events from people you follow,
@ -63,8 +66,8 @@ export const getAllPubkeyWriteRelays = pubkeys => getAllPubkeyRelays(pubkeys, "w
// Current user // Current user
export const getUserRelays = () => export const getUserRelays = () =>
user ext.routing
.getRelays() .getUserRelays()
.filter(({url}) => isRelay(url)) .filter(({url}) => isRelay(url))
.map(assoc("score", 1)) .map(assoc("score", 1))

View File

@ -1,10 +1,8 @@
import type {Relay} from "src/util/types"
import type {Readable} from "svelte/store" import type {Readable} from "svelte/store"
import {uniqBy, without, reject, prop, assoc, whereEq, when, map} from "ramda" import {without, prop} from "ramda"
import {synced} from "src/util/misc" import {synced} from "src/util/misc"
import {derived, get} from "svelte/store" import {derived, get} from "svelte/store"
import keys from "src/system/keys" import keys from "src/system/keys"
import pool from "src/agent/pool"
const ext = {cmd: null} const ext = {cmd: null}
@ -14,14 +12,10 @@ const profile = synced("agent/user/profile", {
lnurl: null, lnurl: null,
rooms_joined: [], rooms_joined: [],
last_checked: {}, last_checked: {},
relays: pool.defaultRelays,
}) })
const roomsJoined = derived(profile, prop("rooms_joined")) as Readable<string> const roomsJoined = derived(profile, prop("rooms_joined")) as Readable<string>
const lastChecked = derived(profile, prop("last_checked")) as Readable<Record<string, number>> const lastChecked = derived(profile, prop("last_checked")) as Readable<Record<string, number>>
const relays = derived(profile, p =>
pool.forceRelays.length > 0 ? pool.forceRelays : p.relays
) as Readable<Array<Relay>>
// Keep a copy so we can avoid calling `get` all the time // Keep a copy so we can avoid calling `get` all the time
@ -68,27 +62,4 @@ export default {
leaveRoom(id) { leaveRoom(id) {
this.setAppData("rooms_joined/v1", without([id], profileCopy.rooms_joined)) this.setAppData("rooms_joined/v1", without([id], profileCopy.rooms_joined))
}, },
// Relays
relays,
getRelays: () => profileCopy.relays,
updateRelays(f) {
const $relays = uniqBy(prop("url"), f(profileCopy.relays))
profile.update(assoc("relays", $relays))
if (get(keys.canSign)) {
return ext.cmd.setRelays($relays).publish($relays)
}
},
addRelay(url) {
return this.updateRelays($relays => $relays.concat({url, write: true, read: true}))
},
removeRelay(url) {
return this.updateRelays(reject(whereEq({url})))
},
setRelayWriteCondition(url, write) {
return this.updateRelays(map(when(whereEq({url}), assoc("write", write))))
},
} }

View File

@ -3,7 +3,7 @@
import {modal} from "src/partials/state" import {modal} from "src/partials/state"
import OverflowMenu from "src/partials/OverflowMenu.svelte" import OverflowMenu from "src/partials/OverflowMenu.svelte"
import {keys, routing} from "src/system" import {keys, routing} from "src/system"
import user from "src/agent/user" import {watch} from "src/agent/db"
import {addToList} from "src/app/state" import {addToList} from "src/app/state"
export let relay export let relay
@ -11,23 +11,23 @@
relay = routing.getRelay(relay.url) relay = routing.getRelay(relay.url)
const {canSign} = keys const {canSign} = keys
const {relays: userRelays} = user const relays = watch(routing.policies, () => routing.getUserRelays())
let actions = [] let actions = []
$: joined = find(propEq("url", relay.url), $userRelays) $: joined = find(propEq("url", relay.url), $relays)
$: { $: {
actions = [] actions = []
if (!joined) { if (!joined) {
actions.push({ actions.push({
onClick: () => user.addRelay(relay.url), onClick: () => routing.addUserRelay(relay.url),
label: "Join", label: "Join",
icon: "right-to-bracket", icon: "right-to-bracket",
}) })
} else if ($userRelays.length > 1) { } else if ($relays.length > 1) {
actions.push({ actions.push({
onClick: () => user.removeRelay(relay.url), onClick: () => routing.removeUserRelay(relay.url),
label: "Leave", label: "Leave",
icon: "right-from-bracket", icon: "right-from-bracket",
}) })

View File

@ -10,6 +10,7 @@
import Rating from "src/partials/Rating.svelte" import Rating from "src/partials/Rating.svelte"
import Anchor from "src/partials/Anchor.svelte" import Anchor from "src/partials/Anchor.svelte"
import {keys, routing} from "src/system" import {keys, routing} from "src/system"
import {watch} from "src/agent/db"
import pool from "src/agent/pool" import pool from "src/agent/pool"
import user from "src/agent/user" import user from "src/agent/user"
import {loadAppData} from "src/app/state" import {loadAppData} from "src/app/state"
@ -22,18 +23,17 @@
export let showControls = false export let showControls = false
const {canSign} = keys const {canSign} = keys
const {relays} = user
let statusHover = false let statusHover = false
let quality = null let quality = null
let message = null let message = null
$: hasRelay = Boolean(find(propEq("url", relay.url), $relays)) const relays = watch(routing.policies, () => routing.getUserRelays())
const removeRelay = r => user.removeRelay(r.url) const removeRelay = r => routing.removeUserRelay(r.url)
const addRelay = r => { const addRelay = r => {
user.addRelay(r.url) routing.addUserRelay(r.url)
const pubkey = keys.getPubkey() const pubkey = keys.getPubkey()
const profile = user.getProfile() const profile = user.getProfile()
@ -47,6 +47,8 @@
modal.push({type: "relay/detail", url: relay.url}) modal.push({type: "relay/detail", url: relay.url})
} }
$: hasRelay = Boolean(find(propEq("url", relay.url), $relays))
onMount(() => { onMount(() => {
return poll(10_000, () => { return poll(10_000, () => {
;[quality, message] = pool.getQuality(relay.url) ;[quality, message] = pool.getQuality(relay.url)
@ -114,7 +116,7 @@
<span>Publish to this relay?</span> <span>Publish to this relay?</span>
<Toggle <Toggle
value={relay.write} value={relay.write}
on:change={() => user.setRelayWriteCondition(relay.url, !relay.write)} /> on:change={() => routing.setUserRelayPolicy(relay.url, {write: !relay.write})} />
</div> </div>
{/if} {/if}
</div> </div>

View File

@ -10,17 +10,16 @@
import {getUserReadRelays} from "src/agent/relays" import {getUserReadRelays} from "src/agent/relays"
import network from "src/agent/network" import network from "src/agent/network"
import {watch} from "src/agent/db" import {watch} from "src/agent/db"
import user from "src/agent/user"
export let q = "" export let q = ""
export let limit = 50 export let limit = 50
export let relays = user.relays
export let placeholder = "Search relays or add a custom url" export let placeholder = "Search relays or add a custom url"
export let hideIfEmpty = false export let hideIfEmpty = false
let search let search
let reviews = [] let reviews = []
const userRelays = watch(routing.policies, () => routing.getUserRelays())
const knownRelays = watch(routing.relays, () => routing.relays.all()) const knownRelays = watch(routing.relays, () => routing.relays.all())
$: ratings = mapValues( $: ratings = mapValues(
@ -29,7 +28,7 @@
) )
$: { $: {
const joined = new Set(pluck("url", $relays)) const joined = new Set(pluck("url", $userRelays))
search = fuzzy( search = fuzzy(
$knownRelays.filter(r => !joined.has(r.url)), $knownRelays.filter(r => !joined.has(r.url)),
@ -65,8 +64,8 @@
{/each} {/each}
<slot name="footer"> <slot name="footer">
<small class="text-center"> <small class="text-center">
Showing {Math.min(($knownRelays || []).length - $relays.length, 50)} Showing {Math.min(($knownRelays || []).length - $userRelays.length, 50)}
of {($knownRelays || []).length - $relays.length} known relays of {($knownRelays || []).length - $userRelays.length} known relays
</small> </small>
</slot> </slot>
</div> </div>

View File

@ -16,7 +16,6 @@
import {DEFAULT_RELAYS, routing, keys} from "src/system" import {DEFAULT_RELAYS, routing, keys} from "src/system"
import {watch} from "src/agent/db" import {watch} from "src/agent/db"
import network from "src/agent/network" import network from "src/agent/network"
import user from "src/agent/user"
import pool from "src/agent/pool" import pool from "src/agent/pool"
import {loadAppData} from "src/app/state" import {loadAppData} from "src/app/state"
@ -64,7 +63,7 @@
currentRelays[i] = null currentRelays[i] = null
if (searching && user.getRelays().length > 0) { if (searching && routing.getUserRelays().length > 0) {
searching = false searching = false
modal = "success" modal = "success"

View File

@ -9,11 +9,9 @@
import OnboardingRelays from "src/app/views/OnboardingRelays.svelte" import OnboardingRelays from "src/app/views/OnboardingRelays.svelte"
import OnboardingFollows from "src/app/views/OnboardingFollows.svelte" import OnboardingFollows from "src/app/views/OnboardingFollows.svelte"
import OnboardingNote from "src/app/views/OnboardingNote.svelte" import OnboardingNote from "src/app/views/OnboardingNote.svelte"
import {DEFAULT_FOLLOWS, social} from "src/system" import {DEFAULT_FOLLOWS, DEFAULT_RELAYS, social, routing} from "src/system"
import {getPubkeyWriteRelays, sampleRelays} from "src/agent/relays" import {getPubkeyWriteRelays, sampleRelays} from "src/agent/relays"
import network from "src/agent/network" import network from "src/agent/network"
import user from "src/agent/user"
import pool from "src/agent/pool"
import {keys, directory, cmd} from "src/system" import {keys, directory, cmd} from "src/system"
import {loadAppData} from "src/app/state" import {loadAppData} from "src/app/state"
import {modal} from "src/partials/state" import {modal} from "src/partials/state"
@ -22,25 +20,21 @@
const privkey = generatePrivateKey() const privkey = generatePrivateKey()
const profile = {} const profile = {}
const {relays} = user
if ($relays.length === 0) { if (routing.getUserRelays().length === 0) {
user.updateRelays(() => routing.setUserRelays(DEFAULT_RELAYS.map(url => ({url, read: true, write: true})))
(pool.forceUrls.length > 0 ? pool.forceUrls : pool.defaultUrls).map(url => ({
url,
write: true,
}))
)
} }
const signup = async note => { const signup = async note => {
const relays = routing.getUserRelays()
await keys.login("privkey", privkey) await keys.login("privkey", privkey)
// Re-save preferences now that we have a key // Re-save preferences now that we have a key
await Promise.all([ await Promise.all([
user.updateRelays(() => user.getRelays()), routing.setUserRelays(relays),
cmd.updateUser(profile).publish(user.getRelays()), cmd.updateUser(profile).publish(relays),
note && cmd.createNote(note).publish(user.getRelays()), note && cmd.createNote(note).publish(relays),
social.updatePetnames( social.updatePetnames(
social.getUserFollows().map(pubkey => { social.getUserFollows().map(pubkey => {
const [{url}] = sampleRelays(getPubkeyWriteRelays(pubkey)) const [{url}] = sampleRelays(getPubkeyWriteRelays(pubkey))
@ -60,7 +54,7 @@
onMount(() => { onMount(() => {
// Prime our database with some defaults // Prime our database with some defaults
network.loadPeople(DEFAULT_FOLLOWS, { network.loadPeople(DEFAULT_FOLLOWS, {
relays: sampleRelays(user.getRelays()), relays: sampleRelays(routing.getUserRelays()),
}) })
}) })
</script> </script>

View File

@ -9,20 +9,21 @@
import RelayCard from "src/app/shared/RelayCard.svelte" import RelayCard from "src/app/shared/RelayCard.svelte"
import {routing} from "src/system" import {routing} from "src/system"
import {watch} from "src/agent/db" import {watch} from "src/agent/db"
import user from "src/agent/user"
const {relays} = user
let q = "" let q = ""
let search let search
const userRelays = watch(routing.policies, () => routing.getUserRelays())
const knownRelays = watch(routing.relays, () => routing.relays.all()) const knownRelays = watch(routing.relays, () => routing.relays.all())
$: joined = new Set(pluck("url", $relays)) $: {
$: search = fuzzy( const joined = new Set(pluck("url", $userRelays))
$knownRelays.filter(r => !joined.has(r.url)),
{keys: ["name", "description", "url"]} search = fuzzy(
) $knownRelays.filter(r => !joined.has(r.url)),
{keys: ["name", "description", "url"]}
)
}
</script> </script>
<Content> <Content>
@ -44,14 +45,14 @@
<i class="fa fa-server fa-lg" /> <i class="fa fa-server fa-lg" />
<h2 class="staatliches text-2xl">Your relays</h2> <h2 class="staatliches text-2xl">Your relays</h2>
</div> </div>
{#if $relays.length === 0} {#if $userRelays.length === 0}
<div class="mt-8 flex items-center justify-center gap-2 text-center"> <div class="mt-8 flex items-center justify-center gap-2 text-center">
<i class="fa fa-triangle-exclamation" /> <i class="fa fa-triangle-exclamation" />
<span>No relays connected</span> <span>No relays connected</span>
</div> </div>
{:else} {:else}
<div class="grid grid-cols-1 gap-4"> <div class="grid grid-cols-1 gap-4">
{#each $relays as relay (relay.url)} {#each $userRelays as relay (relay.url)}
<RelayCard {relay} /> <RelayCard {relay} />
{/each} {/each}
</div> </div>
@ -67,7 +68,7 @@
<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 - $userRelays.length, 50)}
of {$knownRelays.length - $relays.length} known relays of {$knownRelays.length - $userRelays.length} known relays
</small> </small>
</Content> </Content>

View File

@ -3,10 +3,11 @@
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/app/shared/RelayCard.svelte" import RelayCard from "src/app/shared/RelayCard.svelte"
import user from "src/agent/user" import {routing} from "src/system"
import {watch} from "src/agent/db"
import {modal} from "src/partials/state" import {modal} from "src/partials/state"
const {relays} = user const relays = watch(routing.policies, () => routing.getUserRelays())
document.title = "Relays" document.title = "Relays"
</script> </script>

View File

@ -6,8 +6,7 @@
import Heading from "src/partials/Heading.svelte" import Heading from "src/partials/Heading.svelte"
import Compose from "src/partials/Compose.svelte" import Compose from "src/partials/Compose.svelte"
import Rating from "src/partials/Rating.svelte" import Rating from "src/partials/Rating.svelte"
import {cmd} from "src/system" import {cmd, routing} from "src/system"
import user from "src/agent/user"
export let url export let url
@ -27,7 +26,7 @@
["r", url], ["r", url],
], ],
}) })
.publish(user.getRelays()) .publish(routing.getUserRelays())
modal.pop() modal.pop()
} }

View File

@ -11,6 +11,7 @@ import initRouting from "src/system/routing"
import initCmd from "src/system/cmd" import initCmd from "src/system/cmd"
import {getUserWriteRelays} from "src/agent/relays" import {getUserWriteRelays} from "src/agent/relays"
import {default as agentSync} from "src/agent/sync" import {default as agentSync} from "src/agent/sync"
import relays from "src/agent/relays"
import pool from "src/agent/pool" import pool from "src/agent/pool"
import user from "src/agent/user" import user from "src/agent/user"
@ -27,7 +28,7 @@ const settings = initSettings({keys, sync, getCmd, getUserWriteRelays})
const directory = initDirectory({sync, sortByGraph: social.sortByGraph}) const directory = initDirectory({sync, sortByGraph: social.sortByGraph})
const nip05 = initNip05({sync, sortByGraph: social.sortByGraph}) const nip05 = initNip05({sync, sortByGraph: social.sortByGraph})
const nip57 = initNip57({sync, sortByGraph: social.sortByGraph}) const nip57 = initNip57({sync, sortByGraph: social.sortByGraph})
const routing = initRouting({sync, sortByGraph: social.sortByGraph}) const routing = initRouting({keys, sync, getCmd, sortByGraph: social.sortByGraph})
const content = initContent({keys, sync, getCmd, getUserWriteRelays}) const content = initContent({keys, sync, getCmd, getUserWriteRelays})
const cmd = initCmd({keys, sync, pool, displayPubkey: directory.displayPubkey}) const cmd = initCmd({keys, sync, pool, displayPubkey: directory.displayPubkey})
@ -41,6 +42,7 @@ settings.store.subscribe($settings => {
user.ext.cmd = cmd user.ext.cmd = cmd
pool.ext.routing = routing pool.ext.routing = routing
relays.ext.routing = routing
// =========================================================== // ===========================================================
// Initialization // Initialization

View File

@ -1,13 +1,14 @@
import {sortBy, last, inc} from "ramda" import {sortBy, uniqBy, reject, whereEq, when, prop, last, inc} from "ramda"
import {get} from "svelte/store"
import {fuzzy, tryJson, now, fetchJson} from "src/util/misc" import {fuzzy, tryJson, now, fetchJson} from "src/util/misc"
import {warn} from "src/util/logger" import {warn} from "src/util/logger"
import {normalizeRelayUrl, isShareableRelay} from "src/util/nostr" import {normalizeRelayUrl, isShareableRelay, Tags} from "src/util/nostr"
import {DUFFLEPUD_URL, DEFAULT_RELAYS, FORCE_RELAYS} from "src/system/env" import {DUFFLEPUD_URL, DEFAULT_RELAYS, FORCE_RELAYS} from "src/system/env"
import {Table, watch} from "src/agent/db" import {Table, watch} from "src/agent/db"
export default ({sync, sortByGraph}) => { export default ({keys, sync, getCmd, sortByGraph}) => {
const relays = new Table("routing/relays", "url", {sort: sortBy(e => -e.count)}) const relays = new Table("routing/relays", "url", {sort: sortBy(e => -e.count)})
const relaySelections = new Table("routing/relaySelections", "pubkey", {sort: sortByGraph}) const policies = new Table("routing/policies", "pubkey", {sort: sortByGraph})
const addRelay = url => { const addRelay = url => {
const relay = relays.get(url) const relay = relays.get(url)
@ -22,16 +23,21 @@ export default ({sync, sortByGraph}) => {
}) })
} }
const addPolicies = ({pubkey, created_at}, policies) => { const setPolicy = ({pubkey, created_at}, relays) => {
if (policies?.length > 0) { if (relays?.length > 0) {
const selection = relaySelections.get(pubkey) if (created_at < policies.get(pubkey)?.created_at) {
if (created_at < selection?.created_at) {
return return
} }
policies.forEach(({url}) => addRelay(url)) policies.patch({
relaySelections.patch({pubkey, created_at, policies}) pubkey,
created_at,
relays: uniqBy(prop("url"), relays).map(relay => {
addRelay(relay.url)
return {read: true, write: true, ...relay}
}),
})
} }
} }
@ -42,7 +48,7 @@ export default ({sync, sortByGraph}) => {
}) })
sync.addHandler(3, e => { sync.addHandler(3, e => {
addPolicies( setPolicy(
e, e,
tryJson(() => { tryJson(() => {
Object.entries(JSON.parse(e.content || "")) Object.entries(JSON.parse(e.content || ""))
@ -60,18 +66,21 @@ export default ({sync, sortByGraph}) => {
}) })
sync.addHandler(10002, e => { sync.addHandler(10002, e => {
addPolicies( setPolicy(
e, e,
e.tags.map(([_, url, mode = "read"]) => { Tags.from(e)
const write = mode === "write" .type("r")
const read = mode === "read" .all()
.map(([_, url, mode]) => {
const write = !mode || mode === "write"
const read = !mode || mode === "read"
if (!write && !read) { if (!write && !read) {
warn(`Encountered unknown relay mode: ${mode}`) warn(`Encountered unknown relay mode: ${mode}`)
} }
return {url: normalizeRelayUrl(url), write, read} return {url: normalizeRelayUrl(url), write, read}
}) })
) )
}) })
@ -83,6 +92,32 @@ export default ({sync, sortByGraph}) => {
const displayRelay = ({url}) => last(url.split("://")) const displayRelay = ({url}) => last(url.split("://"))
const getPubkeyRelays = (pubkey, mode = null) => {
const relays = policies.get(pubkey)?.relays || []
return mode ? relays.filter(prop(mode)) : relays
}
const getUserKey = () => keys.getPubkey() || "anonymous"
const getUserRelays = (...args) => getPubkeyRelays(getUserKey(), ...args)
const setUserRelays = relays => {
if (get(keys.canSign)) {
return getCmd().setRelays(relays).publish(relays)
} else {
setPolicy({pubkey: getUserKey(), created_at: now()}, relays)
}
}
const addUserRelay = url => setUserRelays(getUserRelays().concat({url}))
const removeUserRelay = url =>
setUserRelays(reject(whereEq({url: normalizeRelayUrl(url)}), getUserRelays()))
const setUserRelayPolicy = (url, policy) =>
setUserRelays(getUserRelays().map(when(whereEq({url}), p => ({...p, ...policy}))))
const initialize = async () => { const initialize = async () => {
// Throw some hardcoded defaults in there // Throw some hardcoded defaults in there
DEFAULT_RELAYS.forEach(addRelay) DEFAULT_RELAYS.forEach(addRelay)
@ -101,11 +136,18 @@ export default ({sync, sortByGraph}) => {
return { return {
relays, relays,
relaySelections, policies,
getRelay, getRelay,
getRelayMeta, getRelayMeta,
searchRelays, searchRelays,
displayRelay, displayRelay,
getPubkeyRelays,
getUserKey,
getUserRelays,
setUserRelays,
addUserRelay,
removeUserRelay,
setUserRelayPolicy,
initialize, initialize,
} }
} }

View File

@ -249,19 +249,7 @@ export const hsl = (hue, {saturation = 100, lightness = 50, opacity = 1} = {}) =
`hsl(${hue}, ${saturation}%, ${lightness}%, ${opacity})` `hsl(${hue}, ${saturation}%, ${lightness}%, ${opacity})`
export const tryFunc = (f, ignore = null) => { export const tryFunc = (f, ignore = null) => {
try { const onError = e => {
const r = f()
if (is(Promise, r)) {
return r.catch(e => {
if (!ignore || !e.toString().includes(ignore)) {
warn(e)
}
})
} else {
return r
}
} catch (e) {
if (ignore === false) { if (ignore === false) {
return return
} }
@ -272,6 +260,18 @@ export const tryFunc = (f, ignore = null) => {
warn(e) warn(e)
} }
try {
const r = f()
if (is(Promise, r)) {
return r.catch(onError)
} else {
return r
}
} catch (e) {
onError(e)
}
} }
export const tryJson = f => tryFunc(f, "JSON") export const tryJson = f => tryFunc(f, "JSON")