Remove old nip 65

This commit is contained in:
Jonathan Staab 2023-09-11 17:30:58 -07:00
parent ca10b3f962
commit ffc53f9210
24 changed files with 82 additions and 353 deletions

View File

@ -23,8 +23,9 @@
hasNewNip24Messages, hasNewNip24Messages,
hasNewNotfications, hasNewNotfications,
getUserRelayUrls, getUserRelayUrls,
searchableRelays,
} from "src/engine2" } from "src/engine2"
import engine, {Keys, Directory, Network, Nip65} from "src/app/engine" import engine, {Keys, Directory, Network} from "src/app/engine"
const {keyState, canUseGiftWrap} = Keys const {keyState, canUseGiftWrap} = Keys
const logoUrl = import.meta.env.VITE_LOGO_URL || "/images/logo.png" const logoUrl = import.meta.env.VITE_LOGO_URL || "/images/logo.png"
@ -71,7 +72,7 @@
// This allows us to populate results even if search isn't supported by forced urls // This allows us to populate results even if search isn't supported by forced urls
if (term.length > 2) { if (term.length > 2) {
Network.subscribe({ Network.subscribe({
relays: Nip65.getSearchRelays(), relays: $searchableRelays,
filter: [{kinds: [0], search, limit: 10}], filter: [{kinds: [0], search, limit: 10}],
timeout: 3000, timeout: 3000,
}) })

View File

@ -52,4 +52,3 @@ export const Nip04 = engine.Nip04
export const Nip05 = engine.Nip05 export const Nip05 = engine.Nip05
export const Nip28 = engine.Nip28 export const Nip28 = engine.Nip28
export const Nip44 = engine.Nip44 export const Nip44 = engine.Nip44
export const Nip65 = engine.Nip65

View File

@ -6,8 +6,8 @@
import PersonBadge from "src/app/shared/PersonBadge.svelte" import PersonBadge from "src/app/shared/PersonBadge.svelte"
import ContentEditable from "src/partials/ContentEditable.svelte" import ContentEditable from "src/partials/ContentEditable.svelte"
import Suggestions from "src/partials/Suggestions.svelte" import Suggestions from "src/partials/Suggestions.svelte"
import {isFollowing} from "src/engine2" import {isFollowing, searchableRelays, getPubkeyHints} from "src/engine2"
import {Nip65, Network, Directory} from "src/app/engine" import {Network, Directory} from "src/app/engine"
export let onSubmit export let onSubmit
@ -17,7 +17,7 @@
const pubkeyEncoder = { const pubkeyEncoder = {
encode: pubkey => { encode: pubkey => {
const relays = Nip65.getPubkeyHints(3, pubkey, "write") const relays = getPubkeyHints(3, pubkey, "write")
const nprofile = nip19.nprofileEncode({pubkey, relays}) const nprofile = nip19.nprofileEncode({pubkey, relays})
return "nostr:" + nprofile return "nostr:" + nprofile
@ -34,7 +34,7 @@
if (search.length > 2 && search.startsWith("@")) { if (search.length > 2 && search.startsWith("@")) {
Network.subscribe({ Network.subscribe({
timeout: 3000, timeout: 3000,
relays: Nip65.getSearchRelays(), relays: $searchableRelays,
filter: [{kinds: [0], search, limit: 10}], filter: [{kinds: [0], search, limit: 10}],
onEvent: debounce(100, () => applySearch(getInfo().word)), onEvent: debounce(100, () => applySearch(getInfo().word)),
}) })

View File

@ -13,8 +13,8 @@
import FeedControls from "src/app/shared/FeedControls.svelte" import FeedControls from "src/app/shared/FeedControls.svelte"
import RelayFeed from "src/app/shared/RelayFeed.svelte" import RelayFeed from "src/app/shared/RelayFeed.svelte"
import Note from "src/app/shared/Note.svelte" import Note from "src/app/shared/Note.svelte"
import {getSetting} from "src/engine2" import {getSetting, searchableRelays, mergeHints, getPubkeyHints} from "src/engine2"
import {Keys, Nip65} from "src/app/engine" import {Keys} from "src/app/engine"
import {compileFilter} from "src/app/state" import {compileFilter} from "src/app/state"
export let relays = [] export let relays = []
@ -53,14 +53,14 @@
// If we have a search term we need to use only relays that support search // If we have a search term we need to use only relays that support search
if (filter.search) { if (filter.search) {
return Nip65.getSearchRelays() return $searchableRelays
} }
const limit = getSetting("relay_limit") const limit = getSetting("relay_limit")
const authors = (compileFilter(filter).authors || []).concat(Keys.pubkey.get()) const authors = (compileFilter(filter).authors || []).concat(Keys.pubkey.get())
const hints = authors.map(pubkey => Nip65.getPubkeyHints(limit, pubkey, "write")) const hints = authors.map(pubkey => getPubkeyHints(limit, pubkey, "write"))
return Nip65.mergeHints(limit, hints) return mergeHints(limit, hints)
} }
const loadMore = () => feed.load(5) const loadMore = () => feed.load(5)

View File

@ -13,8 +13,8 @@
import NoteReply from "src/app/shared/NoteReply.svelte" import NoteReply from "src/app/shared/NoteReply.svelte"
import NoteActions from "src/app/shared/NoteActions.svelte" import NoteActions from "src/app/shared/NoteActions.svelte"
import Card from "src/partials/Card.svelte" import Card from "src/partials/Card.svelte"
import {getSetting, isMuted} from "src/engine2" import {getSetting, isMuted, getParentHints, getEventHints} from "src/engine2"
import {Directory, Nip65} from "src/app/engine" import {Directory} from "src/app/engine"
import NoteContent from "src/app/shared/NoteContent.svelte" import NoteContent from "src/app/shared/NoteContent.svelte"
export let note export let note
@ -61,13 +61,13 @@
} }
const goToParent = async () => { const goToParent = async () => {
const relays = Nip65.getParentHints(getSetting("relay_limit"), note) const relays = getParentHints(getSetting("relay_limit"), note)
goToNote({note: {id: findReplyId(note), replies: [note]}, relays}) goToNote({note: {id: findReplyId(note), replies: [note]}, relays})
} }
const goToThread = async () => { const goToThread = async () => {
const relays = Nip65.getEventHints(getSetting("relay_limit"), note) const relays = getEventHints(getSetting("relay_limit"), note)
modal.push({type: "thread/detail", anchorId: note.id, relays}) modal.push({type: "thread/detail", anchorId: note.id, relays})
} }

View File

@ -9,8 +9,8 @@
import Card from "src/partials/Card.svelte" import Card from "src/partials/Card.svelte"
import Spinner from "src/partials/Spinner.svelte" import Spinner from "src/partials/Spinner.svelte"
import PersonCircle from "src/app/shared/PersonCircle.svelte" import PersonCircle from "src/app/shared/PersonCircle.svelte"
import {getSetting, isEventMuted} from "src/engine2" import {getSetting, isEventMuted, getEventHints, mergeHints} from "src/engine2"
import {Directory, Nip65, Network} from "src/app/engine" import {Directory, Network} from "src/app/engine"
export let note export let note
export let value export let value
@ -23,10 +23,10 @@
const {id, identifier, kind, pubkey} = value const {id, identifier, kind, pubkey} = value
const relays = Nip65.mergeHints(getSetting("relay_limit"), [ const relays = mergeHints(getSetting("relay_limit"), [
// Agora social has a bug // Agora social has a bug
(value.relays || []).flatMap(r => r.split(",")).filter(isShareableRelay), (value.relays || []).flatMap(r => r.split(",")).filter(isShareableRelay),
Nip65.getEventHints(getSetting("relay_limit"), note), getEventHints(getSetting("relay_limit"), note),
]) ])
const filter = ( const filter = (

View File

@ -4,7 +4,8 @@
import {modal} from "src/partials/state" import {modal} from "src/partials/state"
import Anchor from "src/partials/Anchor.svelte" import Anchor from "src/partials/Anchor.svelte"
import Rating from "src/partials/Rating.svelte" import Rating from "src/partials/Rating.svelte"
import {Directory, Nip65} from "src/app/engine" import {displayRelay} from "src/engine2"
import {Directory} from "src/app/engine"
export let note, rating export let note, rating
@ -26,7 +27,7 @@
}) })
display = switcherFn(type, { display = switcherFn(type, {
r: () => Nip65.displayRelay({url: value}), r: () => displayRelay({url: value}),
p: () => Directory.displayProfile(value), p: () => Directory.displayProfile(value),
e: () => "a note", e: () => "a note",
default: () => "something", default: () => "something",

View File

@ -6,8 +6,8 @@
import Spinner from "src/partials/Spinner.svelte" import Spinner from "src/partials/Spinner.svelte"
import PersonSummary from "src/app/shared/PersonSummary.svelte" import PersonSummary from "src/app/shared/PersonSummary.svelte"
import {loadPubkeys} from "src/engine2" import {loadPubkeys} from "src/engine2"
import {getSetting, follows} from "src/engine2" import {getSetting, getPubkeyHints, follows} from "src/engine2"
import {Nip65, Network} from "src/app/engine" import {Network} from "src/app/engine"
export let type export let type
export let pubkey export let pubkey
@ -19,7 +19,7 @@
pubkeys = $follows pubkeys = $follows
} else { } else {
const sub = Network.subscribe({ const sub = Network.subscribe({
relays: Nip65.getPubkeyHints(getSetting("relay_limit"), pubkey, "read"), relays: getPubkeyHints(getSetting("relay_limit"), pubkey, "read"),
filter: {kinds: [3], "#p": [pubkey]}, filter: {kinds: [3], "#p": [pubkey]},
onEvent: batch(500, events => { onEvent: batch(500, events => {
const newPubkeys = pluck("pubkey", events) const newPubkeys = pluck("pubkey", events)

View File

@ -5,8 +5,8 @@
import {tweened} from "svelte/motion" import {tweened} from "svelte/motion"
import {numberFmt} from "src/util/misc" import {numberFmt} from "src/util/misc"
import {modal} from "src/partials/state" import {modal} from "src/partials/state"
import {people} from "src/engine2" import {people, getPubkeyHints} from "src/engine2"
import {Keys, Nip65, Network} from "src/app/engine" import {Keys, Network} from "src/app/engine"
export let pubkey export let pubkey
@ -35,7 +35,7 @@
sub = Network.subscribe({ sub = Network.subscribe({
timeout: 30_000, timeout: 30_000,
shouldProcess: false, shouldProcess: false,
relays: Nip65.getPubkeyHints(3, Keys.pubkey.get(), "read"), relays: getPubkeyHints(3, Keys.pubkey.get(), "read"),
filter: [{kinds: [3], "#p": [pubkey]}], filter: [{kinds: [3], "#p": [pubkey]}],
onEvent: batch(300, events => { onEvent: batch(300, events => {
for (const e of events) { for (const e of events) {

View File

@ -1,14 +1,14 @@
<script lang="ts"> <script lang="ts">
import {last} from "ramda" import {last, prop} from "ramda"
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 {relayUrls, addRelay, removeRelay, hasRelay} from "src/engine2" import {relays, relayPolicyUrls, addRelay, removeRelay, hasRelay} from "src/engine2"
import {Nip65, Keys} from "src/app/engine" import {Keys} from "src/app/engine"
import {addToList} from "src/app/state" import {addToList} from "src/app/state"
export let relay export let relay
const info = Nip65.getRelayInfo(relay.url) const info = relays.key(relay.url).derived(prop("info"))
const joined = hasRelay(relay.url) const joined = hasRelay(relay.url)
let actions = [] let actions = []
@ -22,7 +22,7 @@
label: "Join", label: "Join",
icon: "right-to-bracket", icon: "right-to-bracket",
}) })
} else if ($relayUrls.length > 1) { } else if ($relayPolicyUrls.length > 1) {
actions.push({ actions.push({
onClick: () => removeRelay(relay.url), onClick: () => removeRelay(relay.url),
label: "Leave", label: "Leave",
@ -44,9 +44,9 @@
}) })
} }
if (info.contact) { if ($info?.contact) {
actions.push({ actions.push({
onClick: () => window.open("mailto:" + last(info.contact.split(":"))), onClick: () => window.open("mailto:" + last($info.contact.split(":"))),
label: "Contact", label: "Contact",
icon: "envelope", icon: "envelope",
}) })

View File

@ -8,8 +8,8 @@
import Anchor from "src/partials/Anchor.svelte" import Anchor from "src/partials/Anchor.svelte"
import RelayStatus from "src/app/shared/RelayStatus.svelte" import RelayStatus from "src/app/shared/RelayStatus.svelte"
import RelayCardActions from "src/app/shared/RelayCardActions.svelte" import RelayCardActions from "src/app/shared/RelayCardActions.svelte"
import {getSetting, setRelayPolicy} from "src/engine2" import {getSetting, displayRelay, setRelayPolicy} from "src/engine2"
import {Nip65, Keys} from "src/app/engine" import {Keys} from "src/app/engine"
export let relay export let relay
export let rating = null export let rating = null
@ -31,7 +31,7 @@
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 text-xl"> <div class="flex items-center gap-2 text-xl">
<i class={relay.url.startsWith("ws://") ? "fa fa-unlock" : "fa fa-lock"} /> <i class={relay.url.startsWith("ws://") ? "fa fa-unlock" : "fa fa-lock"} />
<Anchor on:click={openModal}>{Nip65.displayRelay(relay)}</Anchor> <Anchor on:click={openModal}>{displayRelay(relay)}</Anchor>
{#if showStatus && !getSetting("multiplextr_url")} {#if showStatus && !getSetting("multiplextr_url")}
<RelayStatus {relay} /> <RelayStatus {relay} />
{/if} {/if}

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import {relayUrls, removeRelay, addRelay, hasRelay} from "src/engine2" import {relayPolicyUrls, removeRelay, addRelay, hasRelay} from "src/engine2"
export let relay export let relay
@ -10,7 +10,7 @@
<button class="flex items-center gap-3 text-gray-1" on:click={() => addRelay(relay.url)}> <button class="flex items-center gap-3 text-gray-1" on:click={() => addRelay(relay.url)}>
<i class="fa fa-right-to-bracket" /> Join <i class="fa fa-right-to-bracket" /> Join
</button> </button>
{:else if $relayUrls.length > 1} {:else if $relayPolicyUrls.length > 1}
<button class="flex items-center gap-3 text-gray-1" on:click={() => removeRelay(relay.url)}> <button class="flex items-center gap-3 text-gray-1" on:click={() => removeRelay(relay.url)}>
<i class="fa fa-right-from-bracket" /> Leave <i class="fa fa-right-from-bracket" /> Leave
</button> </button>

View File

@ -4,8 +4,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 RelayStatus from "src/app/shared/RelayStatus.svelte" import RelayStatus from "src/app/shared/RelayStatus.svelte"
import {getSetting} from "src/engine2" import {getSetting, displayRelay} from "src/engine2"
import {Nip65} from "src/app/engine"
export let relay export let relay
export let rating = null export let rating = null
@ -18,7 +17,7 @@
href={`/relays/${webSocketURLToPlainOrBase64(relay.url)}`} href={`/relays/${webSocketURLToPlainOrBase64(relay.url)}`}
class="border-b border-solid" class="border-b border-solid"
style={`border-color: ${hsl(stringToHue(relay.url))}`}> style={`border-color: ${hsl(stringToHue(relay.url))}`}>
{Nip65.displayRelay(relay)} {displayRelay(relay)}
</Anchor> </Anchor>
{#if !getSetting("multiplextr_url")} {#if !getSetting("multiplextr_url")}
<RelayStatus {relay} /> <RelayStatus {relay} />

View File

@ -7,7 +7,7 @@
import NoteDetail from "src/app/views/NoteDetail.svelte" import NoteDetail from "src/app/views/NoteDetail.svelte"
import NaddrDetail from "src/app/views/NaddrDetail.svelte" import NaddrDetail from "src/app/views/NaddrDetail.svelte"
import PersonDetail from "src/app/views/PersonDetail.svelte" import PersonDetail from "src/app/views/PersonDetail.svelte"
import {Nip65} from "src/app/engine" import {selectHints} from "src/engine2"
export let entity export let entity
@ -18,7 +18,7 @@
onMount(() => { onMount(() => {
try { try {
;({type, data} = nip19.decode(entity) as {type: string; data: any}) ;({type, data} = nip19.decode(entity) as {type: string; data: any})
relays = Nip65.selectHints(3, data.relays || []) relays = selectHints(3, data.relays || [])
} catch (e) { } catch (e) {
warn(e) warn(e)
} }

View File

@ -12,8 +12,8 @@
import Anchor from "src/partials/Anchor.svelte" import Anchor from "src/partials/Anchor.svelte"
import Modal from "src/partials/Modal.svelte" import Modal from "src/partials/Modal.svelte"
import RelayCard from "src/app/shared/RelayCard.svelte" import RelayCard from "src/app/shared/RelayCard.svelte"
import {loadPubkeys, getUserRelayUrls} from "src/engine2" import {relays, loadPubkeys, getUserRelayUrls} from "src/engine2"
import {Env, Nip65, Keys, Network} from "src/app/engine" import {Env, Keys, Network} from "src/app/engine"
import {loadAppData} from "src/app/state" import {loadAppData} from "src/app/state"
const pubkey = Keys.pubkey.get() const pubkey = Keys.pubkey.get()
@ -25,7 +25,7 @@
let attemptedRelays = new Set() let attemptedRelays = new Set()
let customRelays = [] let customRelays = []
let allRelays = [] let allRelays = []
let knownRelays = Nip65.relays.derived($relays => let knownRelays = relays.derived($relays =>
uniqBy( uniqBy(
prop("url"), prop("url"),
// Make sure our hardcoded urls are first, since they're more likely to find a match // Make sure our hardcoded urls are first, since they're more likely to find a match

View File

@ -16,8 +16,8 @@
import RelayCard from "src/app/shared/RelayCard.svelte" import RelayCard from "src/app/shared/RelayCard.svelte"
import NoteContent from "src/app/shared/NoteContent.svelte" import NoteContent from "src/app/shared/NoteContent.svelte"
import RelaySearch from "src/app/shared/RelaySearch.svelte" import RelaySearch from "src/app/shared/RelaySearch.svelte"
import {publishNote, getUserRelayUrls, mention} from "src/engine2" import {publishNote, displayRelay, getUserRelayUrls, mention} from "src/engine2"
import {Directory, Network, Nip65, Keys} from "src/app/engine" import {Directory, Network, Keys} from "src/app/engine"
import {modal} from "src/partials/state" import {modal} from "src/partials/state"
import {toastProgress} from "src/app/state" import {toastProgress} from "src/app/state"
@ -157,7 +157,7 @@
}}> }}>
<span> <span>
Publishing to {#if $relays?.length === 1} Publishing to {#if $relays?.length === 1}
{Nip65.displayRelay({url: $relays[0]})} {displayRelay({url: $relays[0]})}
{:else} {:else}
{$relays.length} relays {$relays.length} relays
{/if} {/if}
@ -184,7 +184,7 @@
type="button" type="button"
class="fa fa-times cursor-pointer" class="fa fa-times cursor-pointer"
on:click={() => removeRelay(url)} /> on:click={() => removeRelay(url)} />
{Nip65.displayRelay({url})} {displayRelay({url})}
</div> </div>
{/each} {/each}
</div> </div>

View File

@ -8,15 +8,13 @@
import Heading from "src/partials/Heading.svelte" import Heading from "src/partials/Heading.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 {Nip65} from "src/app/engine" import {relays as knownRelays} from "src/engine2"
export let relays: Relay[] export let relays: Relay[]
let q = "" let q = ""
let search let search
const {relays: known} = Nip65
const addRelay = relay => { const addRelay = relay => {
relays = relays.concat(relay) relays = relays.concat(relay)
} }
@ -28,7 +26,7 @@
$: joined = new Set(pluck("url", relays)) $: joined = new Set(pluck("url", relays))
$: search = fuzzy( $: search = fuzzy(
$known.filter(r => !joined.has(r.url)), $knownRelays.filter(r => !joined.has(r.url)),
{keys: ["name", "description", "url"]} {keys: ["name", "description", "url"]}
) )
</script> </script>
@ -91,7 +89,7 @@
</RelayCard> </RelayCard>
{/each} {/each}
<small class="text-center"> <small class="text-center">
Showing {Math.min($known.length - relays.length, 50)} Showing {Math.min($knownRelays.length - relays.length, 50)}
of {$known.length - relays.length} known relays of {$knownRelays.length - relays.length} known relays
</small> </small>
</Content> </Content>

View File

@ -13,8 +13,15 @@
import PersonRelays from "src/app/shared/PersonRelays.svelte" import PersonRelays from "src/app/shared/PersonRelays.svelte"
import PersonHandle from "src/app/shared/PersonHandle.svelte" import PersonHandle from "src/app/shared/PersonHandle.svelte"
import PersonName from "src/app/shared/PersonName.svelte" import PersonName from "src/app/shared/PersonName.svelte"
import {loadPubkeys, getSetting, imgproxy} from "src/engine2" import {
import {Env, Directory, Nip65} from "src/app/engine" loadPubkeys,
getSetting,
imgproxy,
getPubkeyRelays,
mergeHints,
getPubkeyHints,
} from "src/engine2"
import {Env, Directory} from "src/app/engine"
import PersonCircle from "src/app/shared/PersonCircle.svelte" import PersonCircle from "src/app/shared/PersonCircle.svelte"
import PersonAbout from "src/app/shared/PersonAbout.svelte" import PersonAbout from "src/app/shared/PersonAbout.svelte"
import PersonStats from "src/app/shared/PersonStats.svelte" import PersonStats from "src/app/shared/PersonStats.svelte"
@ -31,11 +38,8 @@
let activeTab = "notes" let activeTab = "notes"
let loading = true let loading = true
$: ownRelays = Nip65.getPubkeyRelays(pubkey) $: ownRelays = getPubkeyRelays(pubkey)
$: mergedRelays = Nip65.mergeHints(relayLimit, [ $: mergedRelays = mergeHints(relayLimit, [relays, getPubkeyHints(relayLimit, pubkey, "write")])
relays,
Nip65.getPubkeyHints(relayLimit, pubkey, "write"),
])
$: banner = imgproxy($profile.banner, {w: window.innerWidth}) $: banner = imgproxy($profile.banner, {w: window.innerWidth})
info("Person", npub, $profile) info("Person", npub, $profile)

View File

@ -4,13 +4,13 @@
import Content from "src/partials/Content.svelte" import Content from "src/partials/Content.svelte"
import CopyValue from "src/partials/CopyValue.svelte" import CopyValue from "src/partials/CopyValue.svelte"
import RelayCard from "src/app/shared/RelayCard.svelte" import RelayCard from "src/app/shared/RelayCard.svelte"
import {getSetting} from "src/engine2" import {getPubkeyHints} from "src/engine2"
import {Nip05, Nip65} from "src/app/engine" import {Nip05} from "src/app/engine"
export let pubkey export let pubkey
const handle = Nip05.getHandle(pubkey) const handle = Nip05.getHandle(pubkey)
const relays = Nip65.getPubkeyHints(getSetting("relay_limit"), pubkey, "write") const relays = getPubkeyHints(3, pubkey, "write")
const nprofile = nip19.nprofileEncode({pubkey, relays}) const nprofile = nip19.nprofileEncode({pubkey, relays})
</script> </script>

View File

@ -7,7 +7,7 @@
import Rating from "src/partials/Rating.svelte" import Rating from "src/partials/Rating.svelte"
import RelayTitle from "src/app/shared/RelayTitle.svelte" import RelayTitle from "src/app/shared/RelayTitle.svelte"
import RelayActions from "src/app/shared/RelayActions.svelte" import RelayActions from "src/app/shared/RelayActions.svelte"
import {Nip65} from "src/app/engine" import {relays, displayRelay} from "src/engine2"
export let url export let url
@ -18,7 +18,7 @@
$: rating = getAvgQuality("review/relay", reviews) $: rating = getAvgQuality("review/relay", reviews)
const relay = Nip65.getRelay(url) const relay = relays.key(url)
const tabs = ["notes", "reviews"] const tabs = ["notes", "reviews"]
const setActiveTab = tab => { const setActiveTab = tab => {
activeTab = tab activeTab = tab
@ -28,29 +28,29 @@
reviews = reviews.concat(chunk) reviews = reviews.concat(chunk)
}) })
document.title = Nip65.displayRelay(relay) document.title = displayRelay($relay)
</script> </script>
<Content> <Content>
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<RelayTitle {relay} /> <RelayTitle relay={$relay} />
<RelayActions {relay} /> <RelayActions relay={$relay} />
</div> </div>
{#if rating} {#if rating}
<div class="text-sm"> <div class="text-sm">
<Rating inert value={rating} /> <Rating inert value={rating} />
</div> </div>
{/if} {/if}
{#if relay.info?.description} {#if $relay.info?.description}
<p>{relay.info.description}</p> <p>{$relay.info.description}</p>
{/if} {/if}
<Tabs borderClass="border-gray-6" {tabs} {activeTab} {setActiveTab} /> <Tabs borderClass="border-gray-6" {tabs} {activeTab} {setActiveTab} />
{#if activeTab === "reviews"} {#if activeTab === "reviews"}
<Feed <Feed
invertColors invertColors
onEvent={onReview} onEvent={onReview}
filter={{kinds: [1985], "#l": ["review/relay"], "#r": [relay.url]}} /> filter={{kinds: [1985], "#l": ["review/relay"], "#r": [$relay.url]}} />
{:else} {:else}
<Feed invertColors relays={[relay.url]} filter={{kinds: [1]}} /> <Feed invertColors relays={[$relay.url]} filter={{kinds: [1]}} />
{/if} {/if}
</Content> </Content>

View File

@ -9,7 +9,6 @@ import {Nip04} from "./components/Nip04"
import {Nip05} from "./components/Nip05" import {Nip05} from "./components/Nip05"
import {Nip28} from "./components/Nip28" import {Nip28} from "./components/Nip28"
import {Nip44} from "./components/Nip44" import {Nip44} from "./components/Nip44"
import {Nip65} from "./components/Nip65"
import {Settings} from "./components/Settings" import {Settings} from "./components/Settings"
export class Engine { export class Engine {
@ -24,7 +23,6 @@ export class Engine {
Nip05 = new Nip05() Nip05 = new Nip05()
Nip28 = new Nip28() Nip28 = new Nip28()
Nip44 = new Nip44() Nip44 = new Nip44()
Nip65 = new Nip65()
Settings = new Settings() Settings = new Settings()
constructor(Env: Env) { constructor(Env: Env) {

View File

@ -1,270 +0,0 @@
import {sortBy, pluck, uniq, nth, uniqBy, prop, last, inc} from "ramda"
import {chain, Fetch} from "hurdak"
import {fuzzy, tryJson, now} from "src/util/misc"
import {warn} from "src/util/logger"
import {normalizeRelayUrl, findReplyId, findRootId, isShareableRelay, Tags} from "src/util/nostr"
import type {Engine} from "src/engine/Engine"
import type {Event, Relay, RelayInfo, RelayPolicy, RelayPolicyEntry} from "src/engine/types"
import {derived, collection} from "src/engine/util/store"
export enum Mode {
Read = "read",
Write = "write",
}
export class Nip65 {
engine: Engine
relays = collection<Relay>("url")
policies = collection<RelayPolicy>("pubkey")
addRelay = (url: string) => {
if (isShareableRelay(url)) {
const relay = this.relays.key(url).get()
this.relays.key(url).merge({
count: inc(relay?.count || 0),
first_seen: relay?.first_seen || now(),
info: {
last_checked: 0,
},
})
}
}
setPolicy = (
{pubkey, created_at}: {pubkey: string; created_at: number},
relays: RelayPolicyEntry[]
) => {
if (relays?.length > 0) {
if (created_at < this.policies.key(pubkey).get()?.created_at) {
return
}
this.policies.key(pubkey).merge({
created_at,
updated_at: now(),
relays: uniqBy(prop("url"), relays).map((relay: RelayPolicyEntry) => {
this.addRelay(relay.url)
return {read: true, write: true, ...relay}
}),
})
}
}
getRelay = (url: string): Relay => this.relays.key(url).get() || {url}
getRelayInfo = (url: string): RelayInfo => this.getRelay(url)?.info || {}
displayRelay = ({url}: Relay) => last(url.split("://"))
searchRelays = derived(this.relays, $relays => fuzzy($relays.values(), {keys: ["url"]}))
getSearchRelays = () => {
const searchableRelayUrls = this.relays
.get()
.filter(r => (r.info?.supported_nips || []).includes(50))
.map(prop("url"))
return uniq(this.engine.Env.SEARCH_RELAYS.concat(searchableRelayUrls)).slice(0, 8)
}
getPubkeyRelays = (pubkey: string, mode: string = null) => {
const relays = this.policies.key(pubkey).get()?.relays || []
return mode ? relays.filter(prop(mode)) : relays
}
getPubkeyRelayUrls = (pubkey: string, mode: string = null) =>
pluck("url", this.getPubkeyRelays(pubkey, mode))
getUserRelays = (mode: string = null) =>
this.getPubkeyRelays(this.engine.Keys.stateKey.get(), mode)
getUserRelayUrls = (mode: string = null) => pluck("url", this.getUserRelays(mode))
// Smart relay selection
//
// From Mike Dilger:
// 1) Other people's write relays — pull events from people you follow,
// including their contact lists
// 2) Other people's read relays — push events that tag them (replies or just tagging).
// However, these may be authenticated, use with caution
// 3) Your write relays —- write events you post to your microblog feed for the
// world to see. ALSO write your contact list. ALSO read back your own contact list.
// 4) Your read relays —- read events that tag you. ALSO both write and read
// client-private data like client configuration events or anything that the world
// doesn't need to see.
// 5) Advertise relays — write and read back your own relay list
selectHints = (limit: number | null, hints: Iterable<string>) => {
const seen = new Set()
const ok = []
const bad = []
for (const url of chain(
hints,
this.getUserRelayUrls(Mode.Read),
this.engine.Env.DEFAULT_RELAYS
)) {
if (seen.has(url)) {
continue
}
seen.add(url)
// Filter out relays that appear to be broken or slow
if (!isShareableRelay(url)) {
bad.push(url)
} else if (this.engine.Network.relayIsLowQuality(url)) {
bad.push(url)
} else {
ok.push(url)
}
if (limit && ok.length > limit) {
break
}
}
// If we don't have enough hints, use the broken ones
return ok.concat(bad).slice(0, limit || Infinity)
}
hintSelector =
(generateHints: (...args: any[]) => Iterable<string>) =>
(limit: number, ...args: any[]) =>
this.selectHints(limit, generateHints.call(this, ...args))
getPubkeyHints = this.hintSelector(function* (this: Nip65, pubkey: string, mode: Mode) {
yield* this.getPubkeyRelayUrls(pubkey, mode)
})
getEventHints = this.hintSelector(function* (this: Nip65, event: Event) {
yield* this.getPubkeyRelayUrls(event.pubkey, Mode.Write)
})
// If we're looking for an event's children, the read relays the author has
// advertised would be the most reliable option, since well-behaved clients
// will write replies there.
getReplyHints = this.hintSelector(function* (this: Nip65, event) {
yield* this.getPubkeyRelayUrls(event.pubkey, Mode.Read)
})
// If we're looking for an event's parent, tags are the most reliable hint,
// but we can also look at where the author of the note reads from
getParentHints = this.hintSelector(function* (this: Nip65, event) {
const parentId = findReplyId(event)
yield* Tags.from(event).equals(parentId).relays()
yield* this.getPubkeyRelayUrls(event.pubkey, Mode.Read)
})
getRootHints = this.hintSelector(function* (this: Nip65, event) {
const rootId = findRootId(event)
yield* Tags.from(event).equals(rootId).relays()
yield* this.getPubkeyRelayUrls(event.pubkey, Mode.Read)
})
// If we're replying or reacting to an event, we want the author to know, as well as
// anyone else who is tagged in the original event or the reply. Get everyone's read
// relays. Limit how many per pubkey we publish to though. We also want to advertise
// our content to our followers, so publish to our write relays as well.
getPublishHints = (limit: number, event: Event, extraRelays: string[] = []) => {
const pubkeys = Tags.from(event).type("p").values().all()
const hintGroups = pubkeys.map(pubkey => this.getPubkeyRelayUrls(pubkey, Mode.Read))
const authorRelays = this.getPubkeyRelayUrls(event.pubkey, Mode.Write)
return this.mergeHints(limit, hintGroups.concat([extraRelays, authorRelays]))
}
mergeHints = (limit: number, groups: string[][]) => {
const scores = {} as Record<string, any>
for (const hints of groups) {
hints.forEach((hint, i) => {
const score = 1 / (i + 1) / hints.length
if (!scores[hint]) {
scores[hint] = {score: 0, count: 0}
}
scores[hint].score += score
scores[hint].count += 1
})
}
// Use the log-sum-exp and a weighted sum
for (const score of Object.values(scores)) {
const weight = Math.log(groups.length / score.count)
score.score = weight + Math.log1p(Math.exp(score.score - score.count))
}
return sortBy(([hint, {score}]) => score, Object.entries(scores))
.map(nth(0))
.slice(0, limit)
}
async initialize(engine: Engine) {
this.engine = engine
engine.Events.addHandler(2, e => {
this.addRelay(normalizeRelayUrl(e.content))
})
engine.Events.addHandler(3, e => {
this.setPolicy(
e,
tryJson<RelayPolicyEntry[]>(() => {
return Object.entries(JSON.parse(e.content || ""))
.filter(([url]) => isShareableRelay(url))
.map(([url, conditions]) => {
// @ts-ignore
const write = ![false, "!"].includes(conditions.write)
// @ts-ignore
const read = ![false, "!"].includes(conditions.read)
return {url: normalizeRelayUrl(url), write, read}
})
}) as RelayPolicyEntry[]
)
})
engine.Events.addHandler(10002, e => {
this.setPolicy(
e,
Tags.from(e)
.type("r")
.all()
.map(([_, url, mode]) => {
const write = !mode || mode === Mode.Write
const read = !mode || mode === Mode.Read
if (!write && !read) {
warn(`Encountered unknown relay mode: ${mode}`)
}
return {url: normalizeRelayUrl(url), write, read}
})
)
})
const {DEFAULT_RELAYS, FORCE_RELAYS, DUFFLEPUD_URL} = engine.Env
// Throw some hardcoded defaults in there
DEFAULT_RELAYS.forEach(this.addRelay)
// Load relays from nostr.watch via dufflepud
if (FORCE_RELAYS.length === 0 && DUFFLEPUD_URL) {
try {
const json = await Fetch.fetchJson(DUFFLEPUD_URL + "/relay")
json.relays.forEach(this.addRelay)
} catch (e) {
warn("Failed to fetch relays list", e)
}
}
}
}

View File

@ -11,4 +11,3 @@ export {Nip04} from "./components/Nip04"
export {Nip05} from "./components/Nip05" export {Nip05} from "./components/Nip05"
export {Nip28} from "./components/Nip28" export {Nip28} from "./components/Nip28"
export {Nip44} from "./components/Nip44" export {Nip44} from "./components/Nip44"
export {Nip65} from "./components/Nip65"

View File

@ -9,9 +9,9 @@ import {stateKey, user} from "src/engine2/queries/session"
export const relayPolicies = user.derived($user => $user.relays || []) export const relayPolicies = user.derived($user => $user.relays || [])
export const relayUrls = relayPolicies.derived(pluck("url")) export const relayPolicyUrls = relayPolicies.derived(pluck("url"))
export const hasRelay = url => relayUrls.derived(urls => urls.includes(url)) export const hasRelay = url => relayPolicyUrls.derived(urls => urls.includes(url))
export const relayIsLowQuality = (url: string) => export const relayIsLowQuality = (url: string) =>
pool.get(url, {autoConnect: false})?.meta?.quality < 0.6 pool.get(url, {autoConnect: false})?.meta?.quality < 0.6
@ -25,7 +25,7 @@ export const searchableRelays = relays.derived($relays => {
.filter(r => (r.info?.supported_nips || []).includes(50)) .filter(r => (r.info?.supported_nips || []).includes(50))
.map(prop("url")) .map(prop("url"))
return uniq(env.get().SEARCH_RELAYS.concat(searchableRelayUrls)).slice(0, 8) return uniq(env.get().SEARCH_RELAYS.concat(searchableRelayUrls)).slice(0, 8) as string[]
}) })
export const getPubkeyRelays = (pubkey: string, mode: string = null) => { export const getPubkeyRelays = (pubkey: string, mode: string = null) => {