mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-29 16:31:04 +00:00
Move cmd over
This commit is contained in:
parent
9b9fe3e86d
commit
b34a0c51ea
197
src/agent/cmd.ts
197
src/agent/cmd.ts
@ -1,197 +0,0 @@
|
||||
import {identity, last, pick, uniqBy} from "ramda"
|
||||
import {get} from "svelte/store"
|
||||
import {doPipe} from "hurdak/lib/hurdak"
|
||||
import {Tags, roomAttrs, findRoot, findReply} from "src/util/nostr"
|
||||
import {parseContent} from "src/util/notes"
|
||||
import {getRelayForPersonHint, getRelayForEventHint} from "src/agent/relays"
|
||||
import pool from "src/agent/pool"
|
||||
import sync from "src/agent/sync"
|
||||
import keys from "src/system/keys"
|
||||
|
||||
const ext = {displayPubkey: identity}
|
||||
|
||||
const authenticate = (url, challenge) =>
|
||||
new PublishableEvent(22242, {
|
||||
tags: [
|
||||
["challenge", challenge],
|
||||
["relay", url],
|
||||
],
|
||||
})
|
||||
|
||||
const updateUser = updates => new PublishableEvent(0, {content: JSON.stringify(updates)})
|
||||
|
||||
const setRelays = newRelays =>
|
||||
new PublishableEvent(10002, {
|
||||
tags: newRelays.map(r => {
|
||||
const t = ["r", r.url]
|
||||
|
||||
if (!r.write) {
|
||||
t.push("read")
|
||||
}
|
||||
|
||||
return t
|
||||
}),
|
||||
})
|
||||
|
||||
const setAppData = (d, content) => new PublishableEvent(30078, {content, tags: [["d", d]]})
|
||||
|
||||
const setPetnames = petnames => new PublishableEvent(3, {tags: petnames})
|
||||
|
||||
const setMutes = mutes => new PublishableEvent(10000, {tags: mutes})
|
||||
|
||||
const createList = list => new PublishableEvent(30001, {tags: list})
|
||||
|
||||
const createRoom = room =>
|
||||
new PublishableEvent(40, {content: JSON.stringify(pick(roomAttrs, room))})
|
||||
|
||||
const updateRoom = ({id, ...room}) =>
|
||||
new PublishableEvent(41, {content: JSON.stringify(pick(roomAttrs, room)), tags: [["e", id]]})
|
||||
|
||||
const createChatMessage = (roomId, content, url) =>
|
||||
new PublishableEvent(42, {content, tags: [["e", roomId, url, "root"]]})
|
||||
|
||||
const createDirectMessage = (pubkey, content) =>
|
||||
new PublishableEvent(4, {content, tags: [["p", pubkey]]})
|
||||
|
||||
const createNote = (content, tags = []) =>
|
||||
new PublishableEvent(1, {content, tags: uniqTags(tagsFromContent(content, tags))})
|
||||
|
||||
const createReaction = (note, content) =>
|
||||
new PublishableEvent(7, {content, tags: getReplyTags(note)})
|
||||
|
||||
const createReply = (note, content, tags = []) =>
|
||||
new PublishableEvent(1, {
|
||||
content,
|
||||
tags: doPipe(tags, [
|
||||
tags => tags.concat(getReplyTags(note, true)),
|
||||
tags => tagsFromContent(content, tags),
|
||||
uniqTags,
|
||||
]),
|
||||
})
|
||||
|
||||
const requestZap = (relays, content, pubkey, eventId, amount, lnurl) => {
|
||||
const tags = [
|
||||
["relays", ...relays],
|
||||
["amount", amount.toString()],
|
||||
["lnurl", lnurl],
|
||||
["p", pubkey],
|
||||
]
|
||||
|
||||
if (eventId) {
|
||||
tags.push(["e", eventId])
|
||||
}
|
||||
|
||||
return new PublishableEvent(9734, {content, tags, tagClient: false})
|
||||
}
|
||||
|
||||
const deleteEvent = ids => new PublishableEvent(5, {tags: ids.map(id => ["e", id])})
|
||||
|
||||
const createLabel = payload => new PublishableEvent(1985, payload)
|
||||
|
||||
// Utils
|
||||
|
||||
const tagsFromContent = (content, tags) => {
|
||||
const seen = new Set(Tags.wrap(tags).values().all())
|
||||
|
||||
for (const {type, value} of parseContent({content})) {
|
||||
if (type === "topic") {
|
||||
tags = tags.concat([["t", value]])
|
||||
seen.add(value)
|
||||
}
|
||||
|
||||
if (type.match(/nostr:(note|nevent)/) && !seen.has(value.id)) {
|
||||
tags = tags.concat([["e", value.id, value.relays?.[0] || "", "mention"]])
|
||||
seen.add(value.id)
|
||||
}
|
||||
|
||||
if (type.match(/nostr:(nprofile|npub)/) && !seen.has(value.pubkey)) {
|
||||
tags = tags.concat([mention(value.pubkey)])
|
||||
seen.add(value.pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
const mention = pubkey => {
|
||||
const name = ext.displayPubkey(pubkey)
|
||||
const hint = getRelayForPersonHint(pubkey)
|
||||
|
||||
return ["p", pubkey, hint?.url || "", name]
|
||||
}
|
||||
|
||||
const getReplyTags = (n, inherit = false) => {
|
||||
const extra = inherit
|
||||
? Tags.from(n)
|
||||
.type("e")
|
||||
.reject(t => last(t) === "mention")
|
||||
.all()
|
||||
.map(t => t.slice(0, 3))
|
||||
: []
|
||||
const pHint = getRelayForPersonHint(n.pubkey)
|
||||
const eHint = getRelayForEventHint(n) || pHint
|
||||
const reply = ["e", n.id, eHint?.url || "", "reply"]
|
||||
const root = doPipe(findRoot(n) || findReply(n) || reply, [
|
||||
t => (t.length < 3 ? t.concat(eHint?.url || "") : t),
|
||||
t => t.slice(0, 3).concat("root"),
|
||||
])
|
||||
|
||||
return [mention(n.pubkey), root, ...extra, reply]
|
||||
}
|
||||
|
||||
const uniqTags = uniqBy(t => t.slice(0, 2).join(":"))
|
||||
|
||||
class PublishableEvent {
|
||||
event: Record<string, any>
|
||||
constructor(kind, {content = "", tags = [], tagClient = true}) {
|
||||
const pubkey = get(keys.pubkey)
|
||||
const createdAt = Math.round(new Date().valueOf() / 1000)
|
||||
|
||||
if (tagClient) {
|
||||
tags = tags.filter(t => t[0] !== "client").concat([["client", "coracle"]])
|
||||
}
|
||||
|
||||
this.event = {kind, content, tags, pubkey, created_at: createdAt}
|
||||
}
|
||||
getSignedEvent() {
|
||||
try {
|
||||
return keys.sign(this.event)
|
||||
} catch (e) {
|
||||
console.log(this.event)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
async publish(relays, onProgress = null, verb = "EVENT") {
|
||||
const event = await this.getSignedEvent()
|
||||
// return console.log(event)
|
||||
const promise = pool.publish({relays, event, onProgress, verb})
|
||||
|
||||
// Copy the event since loki mutates it to add metadata
|
||||
sync.processEvents({...event, seen_on: []})
|
||||
|
||||
return [event, promise]
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
ext,
|
||||
mention,
|
||||
authenticate,
|
||||
updateUser,
|
||||
setRelays,
|
||||
setAppData,
|
||||
setPetnames,
|
||||
setMutes,
|
||||
createList,
|
||||
createRoom,
|
||||
updateRoom,
|
||||
createChatMessage,
|
||||
createDirectMessage,
|
||||
createNote,
|
||||
createReaction,
|
||||
createReply,
|
||||
requestZap,
|
||||
deleteEvent,
|
||||
createLabel,
|
||||
PublishableEvent,
|
||||
}
|
@ -5,7 +5,6 @@ import {partition, uniqBy, sortBy, prop, always, pluck, without, is} from "ramda
|
||||
import {throttle} from "throttle-debounce"
|
||||
import {writable} from "svelte/store"
|
||||
import {ensurePlural, noop, createMap} from "hurdak/lib/hurdak"
|
||||
import {fuzzy} from "src/util/misc"
|
||||
|
||||
const Adapter = window.indexedDB ? IncrementalIndexedDBAdapter : Loki.LokiMemoryAdapter
|
||||
|
||||
@ -210,7 +209,6 @@ export const userEvents = new Table("userEvents", "id", {max: 2000, sort: sortBy
|
||||
export const notifications = new Table("notifications", "id", {sort: sortByCreatedAt})
|
||||
export const contacts = new Table("contacts", "pubkey")
|
||||
export const rooms = new Table("rooms", "id")
|
||||
export const relays = new Table("relays", "url")
|
||||
export const routes = new Table("routes", "id", {max: 10000, sort: sortByScore})
|
||||
|
||||
const ready = writable(false)
|
||||
|
@ -7,7 +7,10 @@ import {ensurePlural, switcher} from "hurdak/lib/hurdak"
|
||||
import {warn, log, error} from "src/util/logger"
|
||||
import {union, sleep, difference} from "src/util/misc"
|
||||
import {normalizeRelayUrl} from "src/util/nostr"
|
||||
import {relays} from "src/agent/db"
|
||||
|
||||
const ext = {
|
||||
routing: null,
|
||||
}
|
||||
|
||||
const Config = {
|
||||
multiplextrUrl: null,
|
||||
@ -218,8 +221,8 @@ async function getExecutor(urls, {bypassBoot = false} = {}) {
|
||||
// Eagerly connect and handle AUTH
|
||||
await Promise.all(
|
||||
ensurePlural(executor.target.sockets).map(async socket => {
|
||||
const relay = relays.get(socket.url)
|
||||
const waitForBoot = relay?.limitation?.payment_required || relay?.limitation?.auth_required
|
||||
const {limitation} = ext.routing.getRelayMeta(socket.url)
|
||||
const waitForBoot = limitation?.payment_required || limitation?.auth_required
|
||||
|
||||
if (socket.status === Socket.STATUS.NEW) {
|
||||
socket.booted = sleep(2000)
|
||||
@ -398,6 +401,7 @@ async function count(filter) {
|
||||
}
|
||||
|
||||
export default {
|
||||
ext,
|
||||
Config,
|
||||
Meta,
|
||||
forceUrls,
|
||||
|
@ -1,11 +1,10 @@
|
||||
import type {Relay} from "src/util/types"
|
||||
import LRUCache from "lru-cache"
|
||||
import {warn} from "src/util/logger"
|
||||
import {filter, pipe, pick, groupBy, objOf, map, assoc, sortBy, uniqBy, prop} from "ramda"
|
||||
import {first} from "hurdak/lib/hurdak"
|
||||
import {Tags, isRelay, findReplyId} from "src/util/nostr"
|
||||
import {shuffle, fetchJson} from "src/util/misc"
|
||||
import {relays, routes} from "src/agent/db"
|
||||
import {shuffle} from "src/util/misc"
|
||||
import {routes} from "src/agent/db"
|
||||
import pool from "src/agent/pool"
|
||||
import user from "src/agent/user"
|
||||
|
||||
@ -21,25 +20,6 @@ import user from "src/agent/user"
|
||||
// doesn't need to see.
|
||||
// 5) Advertise relays — write and read back your own relay list
|
||||
|
||||
// Initialize our database
|
||||
|
||||
export const initializeRelayList = async () => {
|
||||
// Throw some hardcoded defaults in there
|
||||
await relays.patch(pool.defaultRelays)
|
||||
|
||||
// Load relays from nostr.watch via dufflepud
|
||||
if (pool.forceUrls.length === 0) {
|
||||
try {
|
||||
const url = import.meta.env.VITE_DUFFLEPUD_URL + "/relay"
|
||||
const json = await fetchJson(url)
|
||||
|
||||
await relays.patch(json.relays.filter(isRelay).map(objOf("url")))
|
||||
} catch (e) {
|
||||
warn("Failed to fetch relays list", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pubkey relays
|
||||
|
||||
const _getPubkeyRelaysCache = new LRUCache({max: 1000})
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {partition, is, uniq, reject, pick, identity} from "ramda"
|
||||
import {partition, is, reject, pick, identity} from "ramda"
|
||||
import {ensurePlural, chunk} from "hurdak/lib/hurdak"
|
||||
import {now, sleep, tryJson, timedelta, hash} from "src/util/misc"
|
||||
import {Tags, roomAttrs, isRelay, isShareableRelay, normalizeRelayUrl} from "src/util/nostr"
|
||||
import {userEvents, rooms, routes} from "src/agent/db"
|
||||
import {sleep, tryJson} from "src/util/misc"
|
||||
import {Tags, roomAttrs, isRelay, normalizeRelayUrl} from "src/util/nostr"
|
||||
import {userEvents, rooms} from "src/agent/db"
|
||||
import {uniqByUrl} from "src/agent/relays"
|
||||
import keys from "src/system/keys"
|
||||
import user from "src/agent/user"
|
||||
|
@ -6,7 +6,8 @@ import {synced} from "src/util/misc"
|
||||
import {derived, get} from "svelte/store"
|
||||
import keys from "src/system/keys"
|
||||
import pool from "src/agent/pool"
|
||||
import cmd from "src/agent/cmd"
|
||||
|
||||
const ext = {cmd: null}
|
||||
|
||||
const profile = synced("agent/user/profile", {
|
||||
pubkey: null,
|
||||
@ -44,6 +45,8 @@ keys.pubkey.subscribe($pubkey => {
|
||||
})
|
||||
|
||||
export default {
|
||||
ext,
|
||||
|
||||
// Profile
|
||||
|
||||
profile,
|
||||
@ -59,7 +62,7 @@ export default {
|
||||
const d = `coracle/${key}`
|
||||
const v = await keys.encryptJson(content)
|
||||
|
||||
return cmd.setAppData(d, v).publish(profileCopy.relays)
|
||||
return ext.cmd.setAppData(d, v).publish(profileCopy.relays)
|
||||
}
|
||||
},
|
||||
setLastChecked(k, v) {
|
||||
@ -82,7 +85,7 @@ export default {
|
||||
profile.update(assoc("relays", $relays))
|
||||
|
||||
if (get(keys.canSign)) {
|
||||
return cmd.setRelays($relays).publish($relays)
|
||||
return ext.cmd.setRelays($relays).publish($relays)
|
||||
}
|
||||
},
|
||||
addRelay(url) {
|
||||
@ -112,7 +115,7 @@ export default {
|
||||
profile.update(assoc("mutes", $mutes))
|
||||
|
||||
if (get(keys.canSign)) {
|
||||
return cmd.setMutes($mutes.map(slice(0, 2))).publish(profileCopy.relays)
|
||||
return ext.cmd.setMutes($mutes.map(slice(0, 2))).publish(profileCopy.relays)
|
||||
}
|
||||
},
|
||||
addMute(type, value) {
|
||||
@ -135,12 +138,12 @@ export default {
|
||||
const tags = [["d", name]].concat(params).concat(relays)
|
||||
|
||||
if (id) {
|
||||
await cmd.deleteEvent([id]).publish(profileCopy.relays)
|
||||
await ext.cmd.deleteEvent([id]).publish(profileCopy.relays)
|
||||
}
|
||||
|
||||
await cmd.createList(tags).publish(profileCopy.relays)
|
||||
await ext.cmd.createList(tags).publish(profileCopy.relays)
|
||||
},
|
||||
removeList(id) {
|
||||
return cmd.deleteEvent([id]).publish(profileCopy.relays)
|
||||
return ext.cmd.deleteEvent([id]).publish(profileCopy.relays)
|
||||
},
|
||||
}
|
||||
|
@ -7,15 +7,21 @@
|
||||
import {get} from "svelte/store"
|
||||
import {Router, links} from "svelte-routing"
|
||||
import {globalHistory} from "svelte-routing/src/history"
|
||||
import {identity, isNil, last} from "ramda"
|
||||
import {isNil, last} from "ramda"
|
||||
import {first} from "hurdak/lib/hurdak"
|
||||
import {warn} from "src/util/logger"
|
||||
import {timedelta, hexToBech32, bech32ToHex, shuffle, now, tryFunc} from "src/util/misc"
|
||||
import cmd from "src/agent/cmd"
|
||||
import {onReady, relays} from "src/agent/db"
|
||||
import {
|
||||
timedelta,
|
||||
hexToBech32,
|
||||
bech32ToHex,
|
||||
shuffle,
|
||||
now,
|
||||
tryFunc,
|
||||
fetchJson,
|
||||
tryFetch,
|
||||
} from "src/util/misc"
|
||||
import {onReady} from "src/agent/db"
|
||||
import * as system from "src/system"
|
||||
import pool from "src/agent/pool"
|
||||
import {initializeRelayList} from "src/agent/relays"
|
||||
import user from "src/agent/user"
|
||||
import {loadAppData} from "src/app/state"
|
||||
import {theme, getThemeVariables, appName, modal} from "src/partials/state"
|
||||
@ -53,7 +59,7 @@
|
||||
if (get(system.keys.canSign) && !seenChallenges.has(challenge)) {
|
||||
seenChallenges.add(challenge)
|
||||
|
||||
const publishable = await cmd.authenticate(url, challenge)
|
||||
const publishable = await system.cmd.authenticate(url, challenge)
|
||||
|
||||
return first(publishable.publish([{url}], null, "AUTH"))
|
||||
}
|
||||
@ -116,7 +122,7 @@
|
||||
})
|
||||
|
||||
onReady(() => {
|
||||
initializeRelayList()
|
||||
system.initialize()
|
||||
|
||||
const pubkey = user.getPubkey()
|
||||
|
||||
@ -133,32 +139,28 @@
|
||||
|
||||
// Find relays with old/missing metadata and refresh them. Only pick a
|
||||
// few so we're not sending too many concurrent http requests
|
||||
const query = {refreshed_at: {$lt: now() - timedelta(7, "days")}}
|
||||
const staleRelays = shuffle(relays.all(query)).slice(0, 10)
|
||||
const query = {"meta.last_checked": {$lt: now() - timedelta(7, "days")}}
|
||||
const staleRelays = shuffle(system.routing.relays.all(query)).slice(0, 10)
|
||||
|
||||
const freshRelays = await Promise.all(
|
||||
staleRelays.map(async ({url}) => {
|
||||
try {
|
||||
const res = await fetch(dufflepudUrl + "/relay/info", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({url}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
system.routing.relays.patch(
|
||||
await Promise.all(
|
||||
staleRelays.map(relay =>
|
||||
tryFetch(async () => {
|
||||
const meta = await fetchJson(dufflepudUrl + "/relay/info", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({url: relay.url}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
|
||||
meta.last_checked = now()
|
||||
|
||||
return {...relay, meta}
|
||||
})
|
||||
|
||||
return {...(await res.json()), url, refreshed_at: now()}
|
||||
} catch (e) {
|
||||
if (!e.toString().includes("Failed to fetch")) {
|
||||
warn(e)
|
||||
}
|
||||
|
||||
return {url, refreshed_at: now()}
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
relays.patch(freshRelays.filter(identity))
|
||||
}, 30_000)
|
||||
|
||||
return () => {
|
||||
|
@ -4,7 +4,7 @@
|
||||
import {tweened} from "svelte/motion"
|
||||
import {find, reject, identity, propEq, pathEq, sum, pluck, sortBy} from "ramda"
|
||||
import {stringToHue, formatSats, hsl} from "src/util/misc"
|
||||
import {displayRelay, isLike} from "src/util/nostr"
|
||||
import {isLike} from "src/util/nostr"
|
||||
import {quantify, first} from "hurdak/lib/hurdak"
|
||||
import {modal} from "src/partials/state"
|
||||
import Popover from "src/partials/Popover.svelte"
|
||||
@ -14,11 +14,10 @@
|
||||
import CopyValue from "src/partials/CopyValue.svelte"
|
||||
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
||||
import RelayCard from "src/app/shared/RelayCard.svelte"
|
||||
import {ENABLE_ZAPS, keys, nip57} from "src/system"
|
||||
import {ENABLE_ZAPS, keys, nip57, cmd, routing} from "src/system"
|
||||
import {getEventPublishRelays} from "src/agent/relays"
|
||||
import pool from "src/agent/pool"
|
||||
import user from "src/agent/user"
|
||||
import cmd from "src/agent/cmd"
|
||||
|
||||
export let note
|
||||
export let reply
|
||||
@ -164,7 +163,7 @@
|
||||
style={`background: ${hsl(stringToHue(url))}`}
|
||||
on:click={() => setFeedRelay?.({url})} />
|
||||
</div>
|
||||
<div slot="tooltip">{displayRelay({url})}</div>
|
||||
<div slot="tooltip">{routing.displayRelay({url})}</div>
|
||||
</Popover>
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import {switcher, switcherFn} from "hurdak/lib/hurdak"
|
||||
import {displayRelay, Tags} from "src/util/nostr"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {modal} from "src/partials/state"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Rating from "src/partials/Rating.svelte"
|
||||
import {directory} from "src/system"
|
||||
import {directory, routing} from "src/system"
|
||||
|
||||
export let note, rating
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
})
|
||||
|
||||
const display = switcherFn(type, {
|
||||
r: () => displayRelay({url: value}),
|
||||
r: () => routing.displayRelay({url: value}),
|
||||
p: () => directory.displayProfile(value),
|
||||
e: () => "a note",
|
||||
default: "something",
|
||||
|
@ -9,10 +9,9 @@
|
||||
import Chip from "src/partials/Chip.svelte"
|
||||
import Media from "src/partials/Media.svelte"
|
||||
import Compose from "src/partials/Compose.svelte"
|
||||
import {directory} from "src/system"
|
||||
import {directory, cmd} from "src/system"
|
||||
import {getEventPublishRelays} from "src/agent/relays"
|
||||
import user from "src/agent/user"
|
||||
import cmd from "src/agent/cmd"
|
||||
import {publishWithToast} from "src/app/state"
|
||||
|
||||
export let note
|
||||
|
@ -47,9 +47,9 @@
|
||||
})
|
||||
}
|
||||
|
||||
if (relay.contact) {
|
||||
if (relay.meta.contact) {
|
||||
actions.push({
|
||||
onClick: () => window.open("mailto:" + last(relay.contact.split(":"))),
|
||||
onClick: () => window.open("mailto:" + last(relay.meta.contact.split(":"))),
|
||||
label: "Contact",
|
||||
icon: "envelope",
|
||||
})
|
||||
|
@ -5,12 +5,11 @@
|
||||
import {onMount} from "svelte"
|
||||
import {fly} from "src/util/transition"
|
||||
import {poll, stringToHue, hsl} from "src/util/misc"
|
||||
import {displayRelay} from "src/util/nostr"
|
||||
import {modal} from "src/partials/state"
|
||||
import Toggle from "src/partials/Toggle.svelte"
|
||||
import Rating from "src/partials/Rating.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import {keys} from "src/system"
|
||||
import {keys, routing} from "src/system"
|
||||
import pool from "src/agent/pool"
|
||||
import user from "src/agent/user"
|
||||
import {loadAppData} from "src/app/state"
|
||||
@ -65,7 +64,7 @@
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex items-center gap-2 text-xl">
|
||||
<i class={relay.url.startsWith("ws://") ? "fa fa-unlock" : "fa fa-lock"} />
|
||||
<Anchor on:click={openModal}>{displayRelay(relay)}</Anchor>
|
||||
<Anchor on:click={openModal}>{routing.displayRelay(relay)}</Anchor>
|
||||
{#if showStatus}
|
||||
<span
|
||||
on:mouseout={() => {
|
||||
|
@ -6,6 +6,7 @@
|
||||
import {normalizeRelayUrl, Tags, getAvgQuality} from "src/util/nostr"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import RelayCard from "src/app/shared/RelayCard.svelte"
|
||||
import {routing} from "src/system"
|
||||
import {getUserReadRelays} from "src/agent/relays"
|
||||
import network from "src/agent/network"
|
||||
import {watch} from "src/agent/db"
|
||||
@ -19,7 +20,8 @@
|
||||
|
||||
let search
|
||||
let reviews = []
|
||||
let knownRelays = watch("relays", t => t.all())
|
||||
|
||||
const knownRelays = watch(routing.relays, () => routing.relays.all())
|
||||
|
||||
$: ratings = mapValues(
|
||||
events => getAvgQuality("review/relay", events),
|
||||
|
@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {between} from "hurdak/lib/hurdak"
|
||||
import {displayRelay} from "src/util/nostr"
|
||||
import {webSocketURLToPlainOrBase64} from "src/util/misc"
|
||||
import {poll, stringToHue, hsl} from "src/util/misc"
|
||||
import Rating from "src/partials/Rating.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import {routing} from "src/system"
|
||||
import pool from "src/agent/pool"
|
||||
|
||||
export let relay
|
||||
@ -29,7 +29,7 @@
|
||||
href={`/relays/${webSocketURLToPlainOrBase64(relay.url)}`}
|
||||
class="border-b border-solid"
|
||||
style={`border-color: ${hsl(stringToHue(relay.url))}`}>
|
||||
{displayRelay(relay)}
|
||||
{routing.displayRelay(relay)}
|
||||
</Anchor>
|
||||
<span
|
||||
on:mouseout={() => {
|
||||
|
@ -6,11 +6,11 @@
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
||||
import NoteContent from "src/app/shared/NoteContent.svelte"
|
||||
import {cmd} from "src/system"
|
||||
import user from "src/agent/user"
|
||||
import {getRelaysForEventChildren, sampleRelays} from "src/agent/relays"
|
||||
import network from "src/agent/network"
|
||||
import {watch} from "src/agent/db"
|
||||
import cmd from "src/agent/cmd"
|
||||
|
||||
export let entity
|
||||
|
||||
|
@ -9,8 +9,8 @@
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import {toast, modal} from "src/partials/state"
|
||||
import {getUserWriteRelays} from "src/agent/relays"
|
||||
import {cmd} from "src/system"
|
||||
import user from "src/agent/user"
|
||||
import cmd from "src/agent/cmd"
|
||||
import {publishWithToast} from "src/app/state"
|
||||
|
||||
export let room = {name: null, id: null, about: null, picture: null}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import {pluck, find} from "ramda"
|
||||
import {Tags, displayRelay} from "src/util/nostr"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {modal, toast} from "src/partials/state"
|
||||
import Heading from "src/partials/Heading.svelte"
|
||||
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
||||
@ -87,7 +87,7 @@
|
||||
<strong>Relays</strong>
|
||||
<MultiSelect search={_searchRelays} bind:value={values.relays}>
|
||||
<div slot="item" let:item>
|
||||
{displayRelay({url: item[1]})}
|
||||
{routing.displayRelay({url: item[1]})}
|
||||
</div>
|
||||
</MultiSelect>
|
||||
<p class="text-sm text-gray-4">
|
||||
|
@ -13,6 +13,7 @@
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Modal from "src/partials/Modal.svelte"
|
||||
import RelayCard from "src/app/shared/RelayCard.svelte"
|
||||
import {DEFAULT_RELAYS, routing} from "src/system"
|
||||
import {watch} from "src/agent/db"
|
||||
import network from "src/agent/network"
|
||||
import user from "src/agent/user"
|
||||
@ -26,11 +27,11 @@
|
||||
let attemptedRelays = new Set()
|
||||
let customRelays = []
|
||||
let allRelays = []
|
||||
let knownRelays = watch("relays", table =>
|
||||
let knownRelays = watch(routing.relays, () =>
|
||||
uniqBy(
|
||||
prop("url"),
|
||||
// Make sure our hardcoded urls are first, since they're more likely to find a match
|
||||
pool.defaultUrls.map(objOf("url")).concat(shuffle(table.all()))
|
||||
DEFAULT_RELAYS.map(objOf("url")).concat(shuffle(routing.relays.all()))
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -9,9 +9,8 @@
|
||||
import {getAllPubkeyRelays, sampleRelays} from "src/agent/relays"
|
||||
import {watch} from "src/agent/db"
|
||||
import network from "src/agent/network"
|
||||
import {keys, directory} from "src/system"
|
||||
import {keys, directory, cmd} from "src/system"
|
||||
import user from "src/agent/user"
|
||||
import cmd from "src/agent/cmd"
|
||||
import {routes} from "src/app/state"
|
||||
import PersonCircle from "src/app/shared/PersonCircle.svelte"
|
||||
import PersonAbout from "src/app/shared/PersonAbout.svelte"
|
||||
|
@ -15,9 +15,8 @@
|
||||
import RelayCard from "src/app/shared/RelayCard.svelte"
|
||||
import NoteContent from "src/app/shared/NoteContent.svelte"
|
||||
import RelaySearch from "src/app/shared/RelaySearch.svelte"
|
||||
import {directory} from "src/system"
|
||||
import {directory, cmd} from "src/system"
|
||||
import {getUserWriteRelays, getRelayForPersonHint} from "src/agent/relays"
|
||||
import cmd from "src/agent/cmd"
|
||||
import user from "src/agent/user"
|
||||
import {toast, modal} from "src/partials/state"
|
||||
import {publishWithToast} from "src/app/state"
|
||||
|
@ -12,8 +12,7 @@
|
||||
import {directory} from "src/system"
|
||||
import {getEventPublishRelays} from "src/agent/relays"
|
||||
import network from "src/agent/network"
|
||||
import {keys, settings, nip57} from "src/system"
|
||||
import cmd from "src/agent/cmd"
|
||||
import {keys, cmd, settings, nip57} from "src/system"
|
||||
|
||||
export let note
|
||||
|
||||
|
@ -14,8 +14,7 @@
|
||||
import network from "src/agent/network"
|
||||
import user from "src/agent/user"
|
||||
import pool from "src/agent/pool"
|
||||
import {keys, directory} from "src/system"
|
||||
import cmd from "src/agent/cmd"
|
||||
import {keys, directory, cmd} from "src/system"
|
||||
import {loadAppData} from "src/app/state"
|
||||
import {modal} from "src/partials/state"
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
import Heading from "src/partials/Heading.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import RelayCard from "src/app/shared/RelayCard.svelte"
|
||||
import {routing} from "src/system"
|
||||
import {watch} from "src/agent/db"
|
||||
import user from "src/agent/user"
|
||||
|
||||
@ -15,7 +16,7 @@
|
||||
let q = ""
|
||||
let search
|
||||
|
||||
const knownRelays = watch("relays", t => t.all())
|
||||
const knownRelays = watch(routing.relays, () => routing.relays.all())
|
||||
|
||||
$: joined = new Set(pluck("url", $relays))
|
||||
$: search = fuzzy(
|
||||
|
@ -1,13 +1,13 @@
|
||||
<script lang="ts">
|
||||
import {batch, timedelta} from "src/util/misc"
|
||||
import {displayRelay, normalizeRelayUrl, getAvgQuality} from "src/util/nostr"
|
||||
import {normalizeRelayUrl, getAvgQuality} from "src/util/nostr"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Feed from "src/app/shared/Feed.svelte"
|
||||
import Tabs from "src/partials/Tabs.svelte"
|
||||
import Rating from "src/partials/Rating.svelte"
|
||||
import RelayTitle from "src/app/shared/RelayTitle.svelte"
|
||||
import RelayActions from "src/app/shared/RelayActions.svelte"
|
||||
import {relays} from "src/agent/db"
|
||||
import {routing} from "src/system"
|
||||
|
||||
export let url
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
$: rating = getAvgQuality("review/relay", reviews)
|
||||
|
||||
const relay = relays.get(url) || {url}
|
||||
const relay = routing.getRelay(url)
|
||||
const tabs = ["reviews", "notes"]
|
||||
const setActiveTab = tab => {
|
||||
activeTab = tab
|
||||
@ -28,7 +28,7 @@
|
||||
reviews = reviews.concat(chunk)
|
||||
})
|
||||
|
||||
document.title = displayRelay(relay)
|
||||
document.title = routing.displayRelay(relay)
|
||||
</script>
|
||||
|
||||
<Content>
|
||||
@ -41,8 +41,8 @@
|
||||
<Rating inert value={rating} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if relay.description}
|
||||
<p>{relay.description}</p>
|
||||
{#if relay.meta.description}
|
||||
<p>{relay.meta.description}</p>
|
||||
{/if}
|
||||
<Tabs borderClass="border-gray-6" {tabs} {activeTab} {setActiveTab} />
|
||||
{#if activeTab === "reviews"}
|
||||
|
@ -6,8 +6,8 @@
|
||||
import Heading from "src/partials/Heading.svelte"
|
||||
import Compose from "src/partials/Compose.svelte"
|
||||
import Rating from "src/partials/Rating.svelte"
|
||||
import {cmd} from "src/system"
|
||||
import user from "src/agent/user"
|
||||
import cmd from "src/agent/cmd"
|
||||
|
||||
export let url
|
||||
|
||||
|
@ -8,9 +8,9 @@
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Heading from "src/partials/Heading.svelte"
|
||||
import {cmd} from "src/system"
|
||||
import user from "src/agent/user"
|
||||
import {getUserWriteRelays} from "src/agent/relays"
|
||||
import cmd from "src/agent/cmd"
|
||||
import {routes} from "src/app/state"
|
||||
import {publishWithToast} from "src/app/state"
|
||||
|
||||
|
193
src/system/cmd.ts
Normal file
193
src/system/cmd.ts
Normal file
@ -0,0 +1,193 @@
|
||||
import {last, pick, uniqBy} from "ramda"
|
||||
import {get} from "svelte/store"
|
||||
import {doPipe} from "hurdak/lib/hurdak"
|
||||
import {Tags, roomAttrs, findRoot, findReply} from "src/util/nostr"
|
||||
import {parseContent} from "src/util/notes"
|
||||
import {getRelayForPersonHint, getRelayForEventHint} from "src/agent/relays"
|
||||
|
||||
export default ({keys, sync, pool, displayPubkey}) => {
|
||||
const authenticate = (url, challenge) =>
|
||||
new PublishableEvent(22242, {
|
||||
tags: [
|
||||
["challenge", challenge],
|
||||
["relay", url],
|
||||
],
|
||||
})
|
||||
|
||||
const updateUser = updates => new PublishableEvent(0, {content: JSON.stringify(updates)})
|
||||
|
||||
const setRelays = newRelays =>
|
||||
new PublishableEvent(10002, {
|
||||
tags: newRelays.map(r => {
|
||||
const t = ["r", r.url]
|
||||
|
||||
if (!r.write) {
|
||||
t.push("read")
|
||||
}
|
||||
|
||||
return t
|
||||
}),
|
||||
})
|
||||
|
||||
const setAppData = (d, content) => new PublishableEvent(30078, {content, tags: [["d", d]]})
|
||||
|
||||
const setPetnames = petnames => new PublishableEvent(3, {tags: petnames})
|
||||
|
||||
const setMutes = mutes => new PublishableEvent(10000, {tags: mutes})
|
||||
|
||||
const createList = list => new PublishableEvent(30001, {tags: list})
|
||||
|
||||
const createRoom = room =>
|
||||
new PublishableEvent(40, {content: JSON.stringify(pick(roomAttrs, room))})
|
||||
|
||||
const updateRoom = ({id, ...room}) =>
|
||||
new PublishableEvent(41, {content: JSON.stringify(pick(roomAttrs, room)), tags: [["e", id]]})
|
||||
|
||||
const createChatMessage = (roomId, content, url) =>
|
||||
new PublishableEvent(42, {content, tags: [["e", roomId, url, "root"]]})
|
||||
|
||||
const createDirectMessage = (pubkey, content) =>
|
||||
new PublishableEvent(4, {content, tags: [["p", pubkey]]})
|
||||
|
||||
const createNote = (content, tags = []) =>
|
||||
new PublishableEvent(1, {content, tags: uniqTags(tagsFromContent(content, tags))})
|
||||
|
||||
const createReaction = (note, content) =>
|
||||
new PublishableEvent(7, {content, tags: getReplyTags(note)})
|
||||
|
||||
const createReply = (note, content, tags = []) =>
|
||||
new PublishableEvent(1, {
|
||||
content,
|
||||
tags: doPipe(tags, [
|
||||
tags => tags.concat(getReplyTags(note, true)),
|
||||
tags => tagsFromContent(content, tags),
|
||||
uniqTags,
|
||||
]),
|
||||
})
|
||||
|
||||
const requestZap = (relays, content, pubkey, eventId, amount, lnurl) => {
|
||||
const tags = [
|
||||
["relays", ...relays],
|
||||
["amount", amount.toString()],
|
||||
["lnurl", lnurl],
|
||||
["p", pubkey],
|
||||
]
|
||||
|
||||
if (eventId) {
|
||||
tags.push(["e", eventId])
|
||||
}
|
||||
|
||||
return new PublishableEvent(9734, {content, tags, tagClient: false})
|
||||
}
|
||||
|
||||
const deleteEvent = ids => new PublishableEvent(5, {tags: ids.map(id => ["e", id])})
|
||||
|
||||
const createLabel = payload => new PublishableEvent(1985, payload)
|
||||
|
||||
// Utils
|
||||
|
||||
const tagsFromContent = (content, tags) => {
|
||||
const seen = new Set(Tags.wrap(tags).values().all())
|
||||
|
||||
for (const {type, value} of parseContent({content})) {
|
||||
if (type === "topic") {
|
||||
tags = tags.concat([["t", value]])
|
||||
seen.add(value)
|
||||
}
|
||||
|
||||
if (type.match(/nostr:(note|nevent)/) && !seen.has(value.id)) {
|
||||
tags = tags.concat([["e", value.id, value.relays?.[0] || "", "mention"]])
|
||||
seen.add(value.id)
|
||||
}
|
||||
|
||||
if (type.match(/nostr:(nprofile|npub)/) && !seen.has(value.pubkey)) {
|
||||
tags = tags.concat([mention(value.pubkey)])
|
||||
seen.add(value.pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
const mention = pubkey => {
|
||||
const name = displayPubkey(pubkey)
|
||||
const hint = getRelayForPersonHint(pubkey)
|
||||
|
||||
return ["p", pubkey, hint?.url || "", name]
|
||||
}
|
||||
|
||||
const getReplyTags = (n, inherit = false) => {
|
||||
const extra = inherit
|
||||
? Tags.from(n)
|
||||
.type("e")
|
||||
.reject(t => last(t) === "mention")
|
||||
.all()
|
||||
.map(t => t.slice(0, 3))
|
||||
: []
|
||||
const pHint = getRelayForPersonHint(n.pubkey)
|
||||
const eHint = getRelayForEventHint(n) || pHint
|
||||
const reply = ["e", n.id, eHint?.url || "", "reply"]
|
||||
const root = doPipe(findRoot(n) || findReply(n) || reply, [
|
||||
t => (t.length < 3 ? t.concat(eHint?.url || "") : t),
|
||||
t => t.slice(0, 3).concat("root"),
|
||||
])
|
||||
|
||||
return [mention(n.pubkey), root, ...extra, reply]
|
||||
}
|
||||
|
||||
const uniqTags = uniqBy(t => t.slice(0, 2).join(":"))
|
||||
|
||||
class PublishableEvent {
|
||||
event: Record<string, any>
|
||||
constructor(kind, {content = "", tags = [], tagClient = true}) {
|
||||
const pubkey = get(keys.pubkey)
|
||||
const createdAt = Math.round(new Date().valueOf() / 1000)
|
||||
|
||||
if (tagClient) {
|
||||
tags = tags.filter(t => t[0] !== "client").concat([["client", "coracle"]])
|
||||
}
|
||||
|
||||
this.event = {kind, content, tags, pubkey, created_at: createdAt}
|
||||
}
|
||||
getSignedEvent() {
|
||||
try {
|
||||
return keys.sign(this.event)
|
||||
} catch (e) {
|
||||
console.log(this.event)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
async publish(relays, onProgress = null, verb = "EVENT") {
|
||||
const event = await this.getSignedEvent()
|
||||
// return console.log(event)
|
||||
const promise = pool.publish({relays, event, onProgress, verb})
|
||||
|
||||
// Copy the event since loki mutates it to add metadata
|
||||
sync.processEvents({...event, seen_on: []})
|
||||
|
||||
return [event, promise]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
mention,
|
||||
authenticate,
|
||||
updateUser,
|
||||
setRelays,
|
||||
setAppData,
|
||||
setPetnames,
|
||||
setMutes,
|
||||
createList,
|
||||
createRoom,
|
||||
updateRoom,
|
||||
createChatMessage,
|
||||
createDirectMessage,
|
||||
createNote,
|
||||
createReaction,
|
||||
createReply,
|
||||
requestZap,
|
||||
deleteEvent,
|
||||
createLabel,
|
||||
PublishableEvent,
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import {sortBy, nth, prop, inc} from 'ramda'
|
||||
import {sortBy, nth, inc} from "ramda"
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {Table, watch} from "src/agent/db"
|
||||
|
@ -7,22 +7,29 @@ import initDirectory from "src/system/directory"
|
||||
import initNip05 from "src/system/nip05"
|
||||
import initNip57 from "src/system/nip57"
|
||||
import initContent from "src/system/content"
|
||||
import initRouting from "src/system/routing"
|
||||
import initCmd from "src/system/cmd"
|
||||
import {getUserWriteRelays} from "src/agent/relays"
|
||||
import {default as agentSync} from "src/agent/sync"
|
||||
import pool from "src/agent/pool"
|
||||
import cmd from "src/agent/cmd"
|
||||
import user from "src/agent/user"
|
||||
|
||||
// Hacks for circular deps
|
||||
|
||||
const getCmd = () => cmd
|
||||
|
||||
// ===========================================================
|
||||
// Initialize various components
|
||||
|
||||
const sync = initSync({keys})
|
||||
const social = initSocial({keys, sync, cmd, getUserWriteRelays})
|
||||
const settings = initSettings({keys, sync, cmd, getUserWriteRelays})
|
||||
const social = initSocial({keys, sync, getCmd, getUserWriteRelays})
|
||||
const settings = initSettings({keys, sync, getCmd, getUserWriteRelays})
|
||||
const directory = initDirectory({sync, sortByGraph: social.sortByGraph})
|
||||
const nip05 = initNip05({sync, sortByGraph: social.sortByGraph})
|
||||
const nip57 = initNip57({sync, sortByGraph: social.sortByGraph})
|
||||
const routing = initContent({sync, sortByGraph: social.sortByGraph})
|
||||
const routing = initRouting({sync, sortByGraph: social.sortByGraph})
|
||||
const content = initContent({sync})
|
||||
const cmd = initCmd({keys, sync, pool, displayPubkey: directory.displayPubkey})
|
||||
|
||||
// Glue stuff together
|
||||
|
||||
@ -32,9 +39,17 @@ settings.store.subscribe($settings => {
|
||||
pool.Config.multiplextrUrl = $settings.multiplextrUrl
|
||||
})
|
||||
|
||||
cmd.ext.displayPubkey = directory.displayPubkey
|
||||
user.ext.cmd = cmd
|
||||
pool.ext.routing = routing
|
||||
|
||||
// ===========================================================
|
||||
// Initialization
|
||||
|
||||
const initialize = () => {
|
||||
routing.initialize()
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Exports
|
||||
|
||||
export {keys, sync, social, settings, directory, nip05, nip57, routing, content}
|
||||
export {keys, sync, social, settings, directory, nip05, nip57, routing, content, cmd, initialize}
|
||||
|
@ -1,27 +1,25 @@
|
||||
import {sortBy, nth, prop, inc} from 'ramda'
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {sortBy, last, inc} from "ramda"
|
||||
import {fuzzy, tryJson, now, fetchJson} from "src/util/misc"
|
||||
import {warn} from "src/util/logger"
|
||||
import {normalizeRelayUrl, isShareableRelay} from "src/util/nostr"
|
||||
import {DUFFLEPUD_URL, DEFAULT_RELAYS, FORCE_RELAYS} from "src/system/env"
|
||||
import {Table, watch} from "src/agent/db"
|
||||
|
||||
export default ({sync, sortByGraph) => {
|
||||
export default ({sync, sortByGraph}) => {
|
||||
const relays = new Table("routing/relays", "url", {sort: sortBy(e => -e.count)})
|
||||
const relaySelections = new Table("routing/relaySelections", "pubkey", {sort: sortByGraph})
|
||||
|
||||
const processTopics = e => {
|
||||
const tagTopics = Tags.from(e).topics()
|
||||
const contentTopics = Array.from(e.content.toLowerCase().matchAll(/#(\w{2,100})/g)).map(nth(1))
|
||||
|
||||
for (const name of tagTopics.concat(contentTopics)) {
|
||||
const topic = topics.get(name)
|
||||
|
||||
topics.patch({name, count: inc(topic?.count || 0)})
|
||||
}
|
||||
}
|
||||
|
||||
const addRelay = url => {
|
||||
const relay = relays.get(url)
|
||||
|
||||
relays.patch({url, count: inc(relay?.count || 0)})
|
||||
relays.patch({
|
||||
url,
|
||||
count: inc(relay?.count || 0),
|
||||
first_seen: relay?.first_seen || now(),
|
||||
meta: {
|
||||
last_checked: 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const addPolicies = ({pubkey, created_at}, policies) => {
|
||||
@ -44,40 +42,70 @@ export default ({sync, sortByGraph) => {
|
||||
})
|
||||
|
||||
sync.addHandler(3, e => {
|
||||
addPolicies(e, tryJson(() => {
|
||||
Object.entries(JSON.parse(e.content || ""))
|
||||
.filter(([url]) => isShareableRelay(url))
|
||||
.map(([url, conditions]) => {
|
||||
const write = ![false, '!'].includes(conditions.write)
|
||||
const read = ![false, '!'].includes(conditions.read)
|
||||
addPolicies(
|
||||
e,
|
||||
tryJson(() => {
|
||||
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}
|
||||
})
|
||||
}))
|
||||
return {url: normalizeRelayUrl(url), write, read}
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
sync.addHandler(10002, e => {
|
||||
addPolicies(e, e.tags.map(([_, url, mode]) => {
|
||||
const write = mode === 'write'
|
||||
const read = mode === 'read'
|
||||
addPolicies(
|
||||
e,
|
||||
e.tags.map(([_, url, mode]) => {
|
||||
const write = mode === "write"
|
||||
const read = mode === "read"
|
||||
|
||||
if (!write && !read) {
|
||||
warn(`Encountered unknown relay mode: ${mode}`)
|
||||
}
|
||||
if (!write && !read) {
|
||||
warn(`Encountered unknown relay mode: ${mode}`)
|
||||
}
|
||||
|
||||
return {url: normalizeRelayUrl(url), write, read}
|
||||
}))
|
||||
return {url: normalizeRelayUrl(url), write, read}
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
const getRelay = url => relays.get(url) || {url}
|
||||
|
||||
const getRelayMeta = url => relays.get(url)?.meta || {}
|
||||
|
||||
const searchRelays = watch(relays, () => fuzzy(relays.all(), {keys: ["url"]}))
|
||||
|
||||
const displayRelay = ({url}) => last(url.split("://"))
|
||||
|
||||
const initialize = async () => {
|
||||
// Throw some hardcoded defaults in there
|
||||
DEFAULT_RELAYS.forEach(addRelay)
|
||||
|
||||
// Load relays from nostr.watch via dufflepud
|
||||
if (FORCE_RELAYS.length === 0) {
|
||||
try {
|
||||
const json = await fetchJson(DUFFLEPUD_URL + "/relay")
|
||||
|
||||
json.relays.filter(isShareableRelay).forEach(addRelay)
|
||||
} catch (e) {
|
||||
warn("Failed to fetch relays list", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
relays,
|
||||
relaySelections,
|
||||
getRelay,
|
||||
getRelayMeta,
|
||||
searchRelays,
|
||||
displayRelay,
|
||||
initialize,
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import {synced, getter} from "src/util/misc"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {DUFFLEPUD_URL, MULTIPLEXTR_URL} from "src/system/env"
|
||||
|
||||
export default ({keys, sync, cmd, getUserWriteRelays}) => {
|
||||
export default ({keys, sync, getCmd, getUserWriteRelays}) => {
|
||||
const store = synced("settings/store", {
|
||||
lastUpdated: 0,
|
||||
relayLimit: 20,
|
||||
@ -38,7 +38,7 @@ export default ({keys, sync, cmd, getUserWriteRelays}) => {
|
||||
const d = "coracle/settings/v1"
|
||||
const v = await keys.encryptJson(settings)
|
||||
|
||||
return cmd.setAppData(d, v).publish(getUserWriteRelays())
|
||||
return getCmd().setAppData(d, v).publish(getUserWriteRelays())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import {now} from "src/util/misc"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {Table} from "src/agent/db"
|
||||
|
||||
export default ({keys, sync, cmd, getUserWriteRelays}) => {
|
||||
export default ({keys, sync, getCmd, getUserWriteRelays}) => {
|
||||
// Don't delete the user's own info or those of direct follows
|
||||
const sortByGraph = xs => {
|
||||
const pubkey = keys.getPubkey()
|
||||
@ -78,7 +78,7 @@ export default ({keys, sync, cmd, getUserWriteRelays}) => {
|
||||
|
||||
const updatePetnames = async $petnames => {
|
||||
if (get(keys.canSign)) {
|
||||
await cmd.setPetnames($petnames).publish(getUserWriteRelays())
|
||||
await getCmd().setPetnames($petnames).publish(getUserWriteRelays())
|
||||
} else {
|
||||
graph.patch({
|
||||
pubkey: getUserKey(),
|
||||
|
@ -116,8 +116,6 @@ export const findRoot = e => prop("root", findReplyAndRoot(e))
|
||||
|
||||
export const findRootId = e => findRoot(e)?.[1]
|
||||
|
||||
export const displayRelay = ({url}) => last(url.split("://"))
|
||||
|
||||
export const isLike = content => ["", "+", "🤙", "👍", "❤️", "😎", "🏅"].includes(content)
|
||||
|
||||
export const isNotification = (e, pubkey) => {
|
||||
|
@ -172,9 +172,11 @@ export const truncateContent = (content, {showEntire, maxLength, showMedia = fal
|
||||
const truncateAt = maxLength * 0.6
|
||||
|
||||
content.every((part, i) => {
|
||||
const isText = [TOPIC, TEXT].includes(part.type) || (part.type === LINK && !part.value.isMedia)
|
||||
const isMedia = part.type === INVOICE || part.type.startsWith("nostr:") || part.value.isMedia
|
||||
const textLength = part.value.url?.length || part.value.length
|
||||
const isText =
|
||||
[NOSTR_NPUB, NOSTR_NPROFILE, NOSTR_NADDR, TOPIC, TEXT].includes(part.type) ||
|
||||
(part.type === LINK && !part.value.isMedia)
|
||||
|
||||
if (isText) {
|
||||
length += textLength
|
||||
|
Loading…
Reference in New Issue
Block a user