No recent activity found.
diff --git a/src/state/app.js b/src/state/app.js
index 802995ea..249649e5 100644
--- a/src/state/app.js
+++ b/src/state/app.js
@@ -1,11 +1,9 @@
-import {uniq} from 'ramda'
-import {writable, get} from 'svelte/store'
+import {writable} from 'svelte/store'
import {navigate} from "svelte-routing"
import {globalHistory} from "svelte-routing/src/history"
-import {switcherFn} from 'hurdak/lib/hurdak'
import {getLocalJson, setLocalJson, now, timedelta} from "src/util/misc"
import {user} from 'src/state/user'
-import {channels, relays} from 'src/state/nostr'
+import {relays} from 'src/state/nostr'
export const modal = {
subscribe: cb => {
@@ -55,60 +53,3 @@ export const logout = () => {
navigate("/login")
}, 200)
}
-
-// Accounts
-
-export const accounts = writable(getLocalJson("coracle/accounts") || {})
-
-accounts.subscribe($accounts => {
- setLocalJson("coracle/accounts", $accounts)
-})
-
-user.subscribe($user => {
- if ($user) {
- accounts.update($accounts => ({...$accounts, [$user.pubkey]: $user}))
- }
-})
-
-export const ensureAccounts = async (pubkeys, {force = false} = {}) => {
- const $accounts = get(accounts)
-
- // Don't request accounts we recently updated
- pubkeys = pubkeys.filter(
- k => force || !$accounts[k] || $accounts[k].refreshed < now() - timedelta(10, 'minutes')
- )
-
- if (pubkeys.length) {
- const events = await channels.getter.all({kinds: [0, 3, 12165], authors: uniq(pubkeys)})
-
- await accounts.update($accounts => {
- events.forEach(e => {
- const values = {
- muffle: [],
- petnames: [],
- ...$accounts[e.pubkey],
- pubkey: e.pubkey,
- refreshed: now(),
- isUser: true,
- }
-
- switcherFn(e.kind, {
- 0: () => {
- $accounts[e.pubkey] = {...values, ...JSON.parse(e.content)}
- },
- 3: () => {
- $accounts[e.pubkey] = {...values, petnames: e.tags}
- },
- 12165: () => {
- $accounts[e.pubkey] = {...values, muffle: e.tags}
- },
- })
- })
-
- return $accounts
- })
- }
-
- // Keep our user in sync
- user.update($user => $user ? {...$user, ...get(accounts)[$user.pubkey]} : null)
-}
diff --git a/src/state/dispatch.js b/src/state/dispatch.js
index 6a7a2050..95f16233 100644
--- a/src/state/dispatch.js
+++ b/src/state/dispatch.js
@@ -3,8 +3,7 @@ import {get} from 'svelte/store'
import {first, defmulti} from "hurdak/lib/hurdak"
import {user} from "src/state/user"
import relay from 'src/relay'
-import {nostr, relays} from 'src/state/nostr'
-import {ensureAccounts} from 'src/state/app'
+import {relays} from 'src/state/nostr'
// Commands are processed in two layers:
// - App-oriented commands are created via dispatch
@@ -35,7 +34,7 @@ dispatch.addMethod("account/update", async (topic, updates) => {
user.set({...get(user), ...updates})
// Tell the network
- await relay.worker.post('event/publish', nostr.event(0, JSON.stringify(updates)))
+ await relay.pool.publishEvent(makeEvent(0, JSON.stringify(updates)))
})
dispatch.addMethod("account/petnames", async (topic, petnames) => {
@@ -45,7 +44,7 @@ dispatch.addMethod("account/petnames", async (topic, petnames) => {
user.set({...$user, petnames})
// Tell the network
- await relay.worker.post('event/publish', nostr.event(3, '', petnames))
+ await relay.pool.publishEvent(makeEvent(3, '', petnames))
})
dispatch.addMethod("account/muffle", async (topic, muffle) => {
@@ -55,17 +54,13 @@ dispatch.addMethod("account/muffle", async (topic, muffle) => {
user.set({...$user, muffle})
// Tell the network
- await relay.worker.post('event/publish', nostr.event(12165, '', muffle))
+ await relay.pool.publishEvent(makeEvent(12165, '', muffle))
})
dispatch.addMethod("relay/join", async (topic, url) => {
const $user = get(user)
relays.update(r => r.concat(url))
-
- if ($user) {
- await ensureAccounts([$user.pubkey], {force: true})
- }
})
dispatch.addMethod("relay/leave", (topic, url) => {
@@ -73,59 +68,59 @@ dispatch.addMethod("relay/leave", (topic, url) => {
})
dispatch.addMethod("room/create", async (topic, room) => {
- const event = nostr.event(40, JSON.stringify(room))
+ const event = makeEvent(40, JSON.stringify(room))
- await relay.worker.post('event/publish', event)
+ await relay.pool.publishEvent(event)
return event
})
dispatch.addMethod("room/update", async (topic, {id, ...room}) => {
- const event = nostr.event(41, JSON.stringify(room), [t("e", id)])
+ const event = makeEvent(41, JSON.stringify(room), [t("e", id)])
- await relay.worker.post('event/publish', event)
+ await relay.pool.publishEvent(event)
return event
})
dispatch.addMethod("message/create", async (topic, roomId, content) => {
- const event = nostr.event(42, content, [t("e", roomId, "root")])
+ const event = makeEvent(42, content, [t("e", roomId, "root")])
- await relay.worker.post('event/publish', event)
+ await relay.pool.publishEvent(event)
return event
})
dispatch.addMethod("note/create", async (topic, content, tags=[]) => {
- const event = nostr.event(1, content, tags)
+ const event = makeEvent(1, content, tags)
- await relay.worker.post('event/publish', event)
+ await relay.pool.publishEvent(event)
return event
})
dispatch.addMethod("reaction/create", async (topic, content, e) => {
const tags = copyTags(e, [t("p", e.pubkey), t("e", e.id, 'reply')])
- const event = nostr.event(7, content, tags)
+ const event = makeEvent(7, content, tags)
- await relay.worker.post('event/publish', event)
+ await relay.pool.publishEvent(event)
return event
})
dispatch.addMethod("reply/create", async (topic, content, e) => {
const tags = copyTags(e, [t("p", e.pubkey), t("e", e.id, 'reply')])
- const event = nostr.event(1, content, tags)
+ const event = makeEvent(1, content, tags)
- await relay.worker.post('event/publish', event)
+ await relay.pool.publishEvent(event)
return event
})
dispatch.addMethod("event/delete", async (topic, ids) => {
- const event = nostr.event(5, '', ids.map(id => t("e", id)))
+ const event = makeEvent(5, '', ids.map(id => t("e", id)))
- await relay.worker.post('event/publish', event)
+ await relay.pool.publishEvent(event)
return event
})
@@ -149,3 +144,10 @@ export const t = (type, content, marker) => {
return tag
}
+
+export const makeEvent = (kind, content = '', tags = []) => {
+ const pubkey = relay.pool.getPubkey()
+ const createdAt = Math.round(new Date().valueOf() / 1000)
+
+ return {kind, content, tags, pubkey, created_at: createdAt}
+}
diff --git a/src/state/nostr.js b/src/state/nostr.js
index 41af4915..a97f0da7 100644
--- a/src/state/nostr.js
+++ b/src/state/nostr.js
@@ -1,56 +1,9 @@
import {writable, get} from 'svelte/store'
-import {relayPool, getPublicKey} from 'nostr-tools'
-import {assoc, last, find, intersection, uniqBy, prop} from 'ramda'
-import {first, noop, ensurePlural} from 'hurdak/lib/hurdak'
+import {assoc, uniqBy, prop} from 'ramda'
+import {noop, ensurePlural} from 'hurdak/lib/hurdak'
import relay from 'src/relay'
import {getLocalJson, setLocalJson, now, timedelta} from "src/util/misc"
-export const nostr = relayPool()
-
-export const epoch = 1633046400
-
-export const filterTags = (where, events) =>
- ensurePlural(events)
- .flatMap(
- e => e.tags.filter(t => {
- if (where.tag && where.tag !== t[0]) {
- return false
- }
-
- if (where.type && where.type !== last(t)) {
- return false
- }
-
- return true
- }).map(t => t[1])
- )
-
-export const findTag = (where, events) => first(filterTags(where, events))
-
-// Support the deprecated version where tags are not marked as replies
-export const findReply = e =>
- findTag({tag: "e", type: "reply"}, e) || findTag({tag: "e"}, e)
-
-export const findRoot = e =>
- findTag({tag: "e", type: "root"}, e)
-
-export const filterMatches = (filter, e) => {
- return Boolean(find(
- f => {
- return (
- (!f.ids || f.ids.includes(e.id))
- && (!f.authors || f.authors.includes(e.pubkey))
- && (!f.kinds || f.kinds.includes(e.kind))
- && (!f['#e'] || intersection(f['#e'], e.tags.filter(t => t[0] === 'e').map(t => t[1])))
- && (!f['#p'] || intersection(f['#p'], e.tags.filter(t => t[0] === 'p').map(t => t[1])))
- && (!f.since || f.since >= e.created_at)
- && (!f.until || f.until <= e.created_at)
- )
- },
- ensurePlural(filter)
- ))
-}
-
export class Channel {
constructor(name) {
this.name = name
@@ -72,7 +25,7 @@ export class Channel {
let resolve
const eoseRelays = []
- const sub = nostr.sub({filter, cb}, this.name, r => {
+ const sub = relay.pool.sub({filter, cb}, this.name, r => {
eoseRelays.push(r)
if (eoseRelays.length === get(relays).length) {
@@ -223,28 +176,6 @@ export class Listener {
}
}
-// Augment nostr with some extra methods
-
-nostr.login = privkey => {
- nostr.setPrivateKey(privkey)
- nostr._privkey = privkey
-}
-
-nostr.pubkeyLogin = pubkey => {
- nostr.registerSigningFunction( async (event) => {
- const {sig} = await window.nostr.signEvent(event)
- return sig
- })
- nostr._pubkey = pubkey
-}
-
-nostr.event = (kind, content = '', tags = []) => {
- const pubkey = nostr._pubkey || getPublicKey(nostr._privkey)
- const createdAt = Math.round(new Date().valueOf() / 1000)
-
- return {kind, content, tags, pubkey, created_at: createdAt}
-}
-
// Keep track of known relays
export const knownRelays = writable((getLocalJson("coracle/knownRelays") || [
@@ -293,15 +224,13 @@ let prevRelays = []
relays.subscribe($relays => {
prevRelays.forEach(url => {
if (!$relays.includes(url)) {
- nostr.removeRelay(url)
- relay.worker.post('pool/removeRelay', url)
+ relay.pool.removeRelay(url)
}
})
$relays.forEach(url => {
if (!prevRelays.includes(url)) {
- nostr.addRelay(url)
- relay.worker.post('pool/addRelay', url)
+ relay.pool.addRelay(url)
}
})
diff --git a/src/state/user.js b/src/state/user.js
index 423f58a3..fd3a2535 100644
--- a/src/state/user.js
+++ b/src/state/user.js
@@ -1,6 +1,5 @@
import {writable} from "svelte/store"
import {getLocalJson, setLocalJson} from "src/util/misc"
-import {nostr} from 'src/state/nostr'
import relay from 'src/relay'
export const user = writable(getLocalJson("coracle/user"))
@@ -10,11 +9,9 @@ user.subscribe($user => {
// Keep nostr in sync
if ($user?.privkey) {
- nostr.login($user.privkey)
- relay.worker.post('pool/setPrivateKey', $user.privkey)
+ relay.pool.setPrivateKey($user.privkey)
} else if ($user?.pubkey) {
- nostr.pubkeyLogin($user.pubkey)
- relay.worker.post('pool/setPublicKey', $user.pubkey)
+ relay.pool.setPublicKey($user.pubkey)
}
// Migrate data from old formats
diff --git a/src/util/nostr.js b/src/util/nostr.js
new file mode 100644
index 00000000..885e5b48
--- /dev/null
+++ b/src/util/nostr.js
@@ -0,0 +1,62 @@
+import {last, intersection} from 'ramda'
+import {ensurePlural, first} from 'hurdak/lib/hurdak'
+
+export const epoch = 1633046400
+
+export const filterTags = (where, events) =>
+ ensurePlural(events)
+ .flatMap(
+ e => e.tags.filter(t => {
+ if (where.tag && where.tag !== t[0]) {
+ return false
+ }
+
+ if (where.type && where.type !== last(t)) {
+ return false
+ }
+
+ return true
+ }).map(t => t[1])
+ )
+
+export const findTag = (where, events) => first(filterTags(where, events))
+
+// Support the deprecated version where tags are not marked as replies
+export const findReply = e =>
+ findTag({tag: "e", type: "reply"}, e) || findTag({tag: "e"}, e)
+
+export const findRoot = e =>
+ findTag({tag: "e", type: "root"}, e)
+
+export const filterMatches = (filter, e) => {
+ return Boolean(find(
+ f => {
+ return (
+ (!f.ids || f.ids.includes(e.id))
+ && (!f.authors || f.authors.includes(e.pubkey))
+ && (!f.kinds || f.kinds.includes(e.kind))
+ && (!f['#e'] || intersection(f['#e'], e.tags.filter(t => t[0] === 'e').map(t => t[1])))
+ && (!f['#p'] || intersection(f['#p'], e.tags.filter(t => t[0] === 'p').map(t => t[1])))
+ && (!f.since || f.since >= e.created_at)
+ && (!f.until || f.until <= e.created_at)
+ )
+ },
+ ensurePlural(filter)
+ ))
+}
+
+export const getMuffleValue = pubkey => {
+ const $user = get(user)
+
+ if (!$user) {
+ return 1
+ }
+
+ const tag = find(t => t[1] === pubkey, $user.muffle)
+
+ if (!tag) {
+ return 1
+ }
+
+ return parseFloat(last(tag))
+}
diff --git a/src/util/notes.js b/src/util/notes.js
index 60a22ce2..0328043e 100644
--- a/src/util/notes.js
+++ b/src/util/notes.js
@@ -1,111 +1,10 @@
-import {identity, uniq, concat, propEq, uniqBy, prop, groupBy, find, last, pluck} from 'ramda'
+import {identity, uniq, propEq, uniqBy, prop, groupBy, pluck} from 'ramda'
import {debounce} from 'throttle-debounce'
import {get} from 'svelte/store'
-import {switcherFn, ellipsize, createMap} from 'hurdak/lib/hurdak'
+import {getMuffleValue, epoch, filterMatches, findReply} from 'src/util/nostr'
+import {switcherFn, createMap} from 'hurdak/lib/hurdak'
import {timedelta, sleep} from "src/util/misc"
-import {escapeHtml} from 'src/util/html'
-import {user} from 'src/state/user'
-import {epoch, filterMatches, Listener, channels, findReply, findRoot} from 'src/state/nostr'
-import {accounts, ensureAccounts} from 'src/state/app'
-
-export const renderNote = (note, {showEntire = false}) => {
- const shouldEllipsize = note.content.length > 500 && !showEntire
- const content = shouldEllipsize ? ellipsize(note.content, 500) : note.content
- const $accounts = get(accounts)
-
- return escapeHtml(content)
- .replace(/\n/g, '
')
- .replace(/https?:\/\/([\w.-]+)[^ ]*/g, (url, domain) => {
- return `
${domain}`
- })
- .replace(/#\[(\d+)\]/g, (tag, i) => {
- if (!note.tags[parseInt(i)]) {
- return tag
- }
-
- const pubkey = note.tags[parseInt(i)][1]
- const user = $accounts[pubkey]
- const name = user?.name || pubkey.slice(0, 8)
-
- return `@
${name}`
- })
-}
-
-export const getMuffleValue = pubkey => {
- const $user = get(user)
-
- if (!$user) {
- return 1
- }
-
- const tag = find(t => t[1] === pubkey, $user.muffle)
-
- if (!tag) {
- return 1
- }
-
- return parseFloat(last(tag))
-}
-
-export const threadify = async notes => {
- if (notes.length === 0) {
- return []
- }
-
- const noteIds = pluck('id', notes)
- const rootIds = notes.map(findReply)
- const parentIds = notes.map(findRoot)
- const ancestorIds = concat(rootIds, parentIds).filter(identity)
-
- // Find all direct parents and thread roots
- const filters = ancestorIds.length === 0
- ? [{kinds: [1, 7], '#e': noteIds}]
- : [{kinds: [1], ids: ancestorIds},
- {kinds: [1, 7], '#e': noteIds.concat(ancestorIds)}]
-
- const events = await channels.getter.all(filters)
-
- await ensureAccounts(uniq(pluck('pubkey', notes.concat(events))))
-
- const $accounts = get(accounts)
- const reactionsByParent = groupBy(findReply, events.filter(propEq('kind', 7)))
- const allNotes = uniqBy(prop('id'), notes.concat(events.filter(propEq('kind', 1))))
- const notesById = createMap('id', allNotes)
- const notesByRoot = groupBy(
- n => {
- const rootId = findRoot(n)
- const parentId = findReply(n)
-
- // Actually dereference the notes in case we weren't able to retrieve them
- if (notesById[rootId]) {
- return rootId
- }
-
- if (notesById[parentId]) {
- return parentId
- }
-
- return n.id
- },
- allNotes
- )
-
- const threads = []
- for (const [rootId, _notes] of Object.entries(notesByRoot)) {
- const annotate = note => {
- return {
- ...note,
- user: $accounts[note.pubkey],
- reactions: reactionsByParent[note.id] || [],
- children: uniqBy(prop('id'), _notes.filter(n => findReply(n) === note.id)).map(annotate),
- }
- }
-
- threads.push(annotate(notesById[rootId]))
- }
-
- return threads
-}
+import {Listener, channels} from 'src/state/nostr'
export const annotateNotes = async (notes, {showParent = false} = {}) => {
if (notes.length === 0) {
@@ -149,41 +48,6 @@ export const annotateNotes = async (notes, {showParent = false} = {}) => {
})
}
-export const annotateAlerts = async events => {
- if (events.length === 0) {
- return []
- }
-
- const eventIds = pluck('id', events)
- const parentIds = events.map(findReply).filter(identity)
- const filters = [
- {kinds: [1], ids: parentIds},
- {kinds: [7], '#e': parentIds},
- {kinds: [1, 7], '#e': eventIds},
- ]
-
- const relatedEvents = await channels.getter.all(filters)
-
- await ensureAccounts(uniq(pluck('pubkey', events.concat(relatedEvents))))
-
- const $accounts = get(accounts)
- const reactionsByParent = groupBy(findReply, relatedEvents.filter(e => e.kind === 7 && e.content === '+'))
- const allNotes = uniqBy(prop('id'), events.concat(relatedEvents).filter(propEq('kind', 1)))
- const notesById = createMap('id', allNotes)
-
- const annotate = note => ({
- ...note,
- user: $accounts[note.pubkey],
- reactions: reactionsByParent[note.id] || [],
- children: uniqBy(prop('id'), allNotes.filter(n => findReply(n) === note.id)).map(annotate),
- })
-
- return uniqBy(e => e.parent?.id || e.id, events.map(event => {
- const parentId = findReply(event)
-
- return {...annotate(event), parent: annotate(notesById[parentId])}
- }))
-}
export const annotateNewNote = async (note) => {
await ensureAccounts([note.pubkey])