-
+
- {#key params.pubkey + params.activeTab}
+ {#key params.pubkey}
{/key}
- -->
diff --git a/src/partials/Likes.svelte b/src/partials/Likes.svelte
deleted file mode 100644
index fb8ad55d..00000000
--- a/src/partials/Likes.svelte
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
-
- {#each $notes as n (n.id)}
-
- {:else}
- {#if loading}
-
- {:else}
- - No notes found.
- {/if}
- {/each}
-
diff --git a/src/partials/NoteDetail.svelte b/src/partials/NoteDetail.svelte
deleted file mode 100644
index eef2b530..00000000
--- a/src/partials/NoteDetail.svelte
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-{#each $notes as n (n.id)}
-
-
-
-{:else}
-
-{/each}
diff --git a/src/partials/Notes.svelte b/src/partials/Notes.svelte
deleted file mode 100644
index 8718f253..00000000
--- a/src/partials/Notes.svelte
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
- {#each ($notes || []) as n (n.id)}
-
- {/each}
-
-
-{#if $notes?.length === 0}
-
-{/if}
diff --git a/src/relay/db.js b/src/relay/db.js
index 7f25c5a0..e377cf78 100644
--- a/src/relay/db.js
+++ b/src/relay/db.js
@@ -3,8 +3,8 @@ import {filterTags} from 'src/util/nostr'
export const db = new Dexie('coracle/relay')
-db.version(2).stores({
- events: '++id, pubkey, created_at, kind, content',
+db.version(3).stores({
+ events: '++id, pubkey, created_at, kind, content, reply, root',
users: '++pubkey, name, about',
tags: '++key, event, value',
})
diff --git a/src/relay/index.js b/src/relay/index.js
index 6b0b3858..8e709f05 100644
--- a/src/relay/index.js
+++ b/src/relay/index.js
@@ -18,13 +18,21 @@ const lq = f => liveQuery(async () => {
const ensureContext = async e => {
// We can't return a promise, so use setTimeout instead
- const user = await db.users.where('pubkey').equals(e.pubkey).first()
+ const user = await db.users.where('pubkey').equals(e.pubkey).first() || {
+ muffle: [],
+ petnames: [],
+ updated_at: 0,
+ pubkey: e.pubkey,
+ }
// Throttle updates for users
- if (!user || user.updated_at < now() - timedelta(1, 'hours')) {
- await pool.updateUser(user || {pubkey: e.pubkey, updated_at: 0})
+ if (user.updated_at < now() - timedelta(1, 'hours')) {
+ Object.assign(user, await pool.getUserInfo({pubkey: e.pubkey, ...user}))
}
+ // Even if we didn't find a match, save it so we don't keep trying to refresh
+ db.users.put({...user, updated_at: now()})
+
// TODO optimize this like user above so we're not double-fetching
await pool.fetchContext(e)
}
@@ -79,6 +87,25 @@ const findReaction = async (id, filter) =>
const countReactions = async (id, filter) =>
(await filterReactions(id, filter)).length
+const findNote = async id => {
+ const [note, children] = await Promise.all([
+ db.events.get(id),
+ db.events.where('reply').equals(id),
+ ])
+
+ const [replies, reactions, user, html] = await Promise.all([
+ children.clone().filter(e => e.kind === 1).toArray(),
+ children.clone().filter(e => e.kind === 7).toArray(),
+ db.users.get(note.pubkey),
+ renderNote(note, {showEntire: false}),
+ ])
+
+ return {
+ ...note, reactions, user, html,
+ replies: await Promise.all(replies.map(r => findNote(r.id))),
+ }
+}
+
const renderNote = async (note, {showEntire = false}) => {
const shouldEllipsize = note.content.length > 500 && !showEntire
const content = shouldEllipsize ? ellipsize(note.content, 500) : note.content
@@ -105,5 +132,5 @@ const renderNote = async (note, {showEntire = false}) => {
export default {
db, pool, lq, ensureContext, filterEvents, filterReactions, countReactions,
- findReaction, filterReplies, renderNote,
+ findReaction, filterReplies, findNote, renderNote,
}
diff --git a/src/relay/pool.js b/src/relay/pool.js
index 1ba8fe56..4aa18a77 100644
--- a/src/relay/pool.js
+++ b/src/relay/pool.js
@@ -1,7 +1,8 @@
+import {uniqBy, prop} from 'ramda'
import {relayPool, getPublicKey} from 'nostr-tools'
import {noop, switcherFn, uuid} from 'hurdak/lib/hurdak'
-import {now, timedelta} from "src/util/misc"
-import {filterTags} from "src/util/nostr"
+import {now, randomChoice, timedelta} from "src/util/misc"
+import {filterTags, findReply, findRoot} from "src/util/nostr"
import {db} from 'src/relay/db'
// ============================================================================
@@ -9,43 +10,78 @@ import {db} from 'src/relay/db'
const pool = relayPool()
-const post = (topic, payload) => postMessage({topic, payload})
-
-const req = ({filter, onEvent, onEose = noop}) => {
- // If we don't have any relays, we'll wait forever for an eose, but
- // we already know we're done. Use a timeout since callers are
- // expecting this to be async and we run into errors otherwise.
- if (pool.relays.length === 0) {
- onEose()
-
- return {unsub: noop}
+class Channel {
+ constructor(name) {
+ this.name = name
+ this.p = Promise.resolve()
}
+ async sub(filter, onEvent, onEose = noop) {
+ // If we don't have any relays, we'll wait forever for an eose, but
+ // we already know we're done. Use a timeout since callers are
+ // expecting this to be async and we run into errors otherwise.
+ if (Object.keys(pool.relays).length === 0) {
+ setTimeout(onEose)
- const eoseRelays = []
- return pool.sub({filter, cb: onEvent}, uuid(), r => {
- eoseRelays.push(r)
-
- if (eoseRelays.length === pool.relays.length) {
- onEose()
+ return {unsub: noop}
}
- })
+
+ // Grab our spot in the queue, save resolve for later
+ let resolve
+ let p = this.p
+ this.p = new Promise(r => {
+ resolve = r
+ })
+
+ // Make sure callers have to wait for the previous sub to be done
+ // before they can get a new one.
+ await p
+
+ // Start our subscription, wait for all relays to eose before
+ // calling it done
+ const eoseRelays = []
+ const sub = pool.sub({filter, cb: onEvent}, this.name, r => {
+ eoseRelays.push(r)
+
+ if (eoseRelays.length === Object.keys(pool.relays).length) {
+ onEose()
+ }
+ })
+
+ return {
+ unsub: () => {
+ sub.unsub()
+
+ resolve()
+ }
+ }
+ }
+ all(filter) {
+ /* eslint no-async-promise-executor: 0 */
+ return new Promise(async resolve => {
+ const result = []
+
+ const sub = await this.sub(
+ filter,
+ e => result.push(e),
+ r => {
+ sub.unsub()
+
+ resolve(uniqBy(prop('id'), result))
+ },
+ )
+ })
+ }
}
-// ============================================================================
-// Start up a subscription to get recent data and listen for new stuff
+export const channels = [
+ new Channel('a'),
+ new Channel('b'),
+ new Channel('c'),
+]
-const lastSync = now() - timedelta(1, 'days')
+const req = filter => randomChoice(channels).all(filter)
-req({
- filter: {
- kinds: [1],
- since: lastSync,
- limit: 10,
- },
- onEvent: e => {
- post('events/put', e)
- },
-})
+const prepEvent = e => ({...e, root: findRoot(e), reply: findReply(e)})
// ============================================================================
// Listen to messages posted from the main application
@@ -81,47 +117,37 @@ const setPublicKey = pubkey => {
const publishEvent = event => {
pool.publish(event)
- db.events.put(event)
+ db.events.put(prepEvent(event))
}
-const updateUser = async user => {
- if (!user.pubkey) throw new Error("Invalid user")
+const loadEvents = async filter => {
+ const events = await req(filter)
- user = {muffle: [], petnames: [], ...user}
+ db.events.bulkPut(events.map(prepEvent))
+}
- const sub = req({
- filter: {
- kinds: [0, 3, 12165],
- authors: [user.pubkey],
- since: user.updated_at,
- },
- onEvent: e => {
- switcherFn(e.kind, {
- 0: () => Object.assign(user, JSON.parse(e.content)),
- 3: () => Object.assign(user, {petnames: e.tags}),
- 12165: () => Object.assign(user, {muffle: e.tags}),
- })
- },
- onEose: () => {
- sub.unsub()
+const getUserInfo = async user => {
+ for (const e of await req({kinds: [0, 3, 12165], authors: [user.pubkey]})) {
+ switcherFn(e.kind, {
+ 0: () => Object.assign(user, JSON.parse(e.content)),
+ 3: () => Object.assign(user, {petnames: e.tags}),
+ 12165: () => Object.assign(user, {muffle: e.tags}),
+ })
+ }
- db.users.put({...user, updated_at: now()})
- },
- })
+ return user
}
const fetchContext = async event => {
- const sub = req({
- filter: [
- {kinds: [5, 7], '#e': [event.id]},
- {kinds: [5], 'ids': filterTags({tag: "e"}, event)},
- ],
- onEvent: e => post('events/put', e),
- onEose: () => sub.unsub(),
- })
+ const events = await req([
+ {kinds: [5, 7], '#e': [event.id]},
+ {kinds: [5], 'ids': filterTags({tag: "e"}, event)},
+ ])
+
+ db.events.bulkPut(events.map(prepEvent))
}
export default {
getPubkey, addRelay, removeRelay, setPrivateKey, setPublicKey,
- publishEvent, updateUser, fetchContext,
+ publishEvent, loadEvents, getUserInfo, fetchContext,
}
diff --git a/src/routes/Alerts.svelte b/src/routes/Alerts.svelte
index 3b66cf47..5d89b647 100644
--- a/src/routes/Alerts.svelte
+++ b/src/routes/Alerts.svelte
@@ -7,7 +7,7 @@
import {user} from 'src/state/user'
import {alerts, modal} from 'src/state/app'
import UserBadge from "src/partials/UserBadge.svelte"
- import Note from 'src/partials/Note.svelte'
+ import Note from 'src/views/Note.svelte'
const events = relay.lq(async () => {
const events = await relay
diff --git a/src/routes/Notes.svelte b/src/routes/Notes.svelte
index 0441acce..7e67552c 100644
--- a/src/routes/Notes.svelte
+++ b/src/routes/Notes.svelte
@@ -3,7 +3,7 @@
import {timedelta} from 'src/util/misc'
import Anchor from "src/partials/Anchor.svelte"
import Tabs from "src/partials/Tabs.svelte"
- import Notes from "src/partials/Notes.svelte"
+ import Notes from "src/views/Notes.svelte"
import {relays} from "src/state/nostr"
import {user} from "src/state/user"
diff --git a/src/routes/Search.svelte b/src/routes/Search.svelte
index 18a41b54..3f32ae2c 100644
--- a/src/routes/Search.svelte
+++ b/src/routes/Search.svelte
@@ -1,70 +1,14 @@
-
-
-
-
+
{#if type === 'people'}
-
+
+{:else if type === 'notes'}
+
{/if}
-
-{#if type === 'notes'}
-
- {#each (results || []) as e (e.id)}
- -
-
-
- {/each}
-
-{/if}
-
-
-
-
-{#if $relays.length === 0}
-
-
- You aren't yet connected to any relays. Please click
here to get started.
-
-
-{/if}
-
diff --git a/src/routes/UserDetail.svelte b/src/routes/UserDetail.svelte
index 0bbd9a30..d906b171 100644
--- a/src/routes/UserDetail.svelte
+++ b/src/routes/UserDetail.svelte
@@ -1,24 +1,20 @@
@@ -52,15 +48,15 @@
+ style="background-image: url({$user?.picture})" />
-
{user?.name || pubkey.slice(0, 8)}
+ {$user?.name || pubkey.slice(0, 8)}
{#if $currentUser && $currentUser.pubkey !== pubkey}
{/if}
-
{user?.about || ''}
+
{$user?.about || ''}
{#if $currentUser?.pubkey === pubkey}
@@ -80,11 +76,18 @@