mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Reworking network stuff again
This commit is contained in:
parent
4ec8178660
commit
f2d1b0c951
@ -33,21 +33,21 @@ Coracle is currently in _alpha_ - expect bugs, slow loading times, and rough edg
|
||||
- [ ] Stack views so scroll position isn't lost on navigation
|
||||
- [ ] We're sending client=astral tags, event id 125ff9dc495f65d302e8d95ea6f9385106cc31b81c80e8c582b44be92fa50c44
|
||||
|
||||
# Workers
|
||||
# Curreent update
|
||||
|
||||
- [ ] Check firefox - in dev it won't work, but it should in production
|
||||
- [ ] Re-implement muffle
|
||||
- [ ] Delete old events
|
||||
- [ ] Sync accounts to store to avoid loading jank
|
||||
- [ ] Sync account updates to user for e.g. muffle settings
|
||||
- [ ] Test nos2x
|
||||
- https://vitejs.dev/guide/features.html#web-workers
|
||||
- https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
|
||||
- https://web.dev/module-workers/
|
||||
|
||||
- [ ] Sync user
|
||||
- [ ] Based on petnames, sync network to 2 or 3 degrees of separation
|
||||
- When a user is added/removed, sync them and add to or remove from network
|
||||
- [ ] Main fetch requests:
|
||||
- Fetch network, including feed
|
||||
- Fetch feed by name, since last sync
|
||||
- Fetch person, including feed
|
||||
- Fetch note, including context
|
||||
- This is based on detail pages. Each request should check local db and fall back to network, all within an await.
|
||||
|
@ -12,8 +12,8 @@
|
||||
import {hasParent} from 'src/util/html'
|
||||
import {timedelta} from 'src/util/misc'
|
||||
import {store as toast} from "src/state/toast"
|
||||
import {modal, alerts, user} from "src/state/app"
|
||||
import relay from 'src/relay'
|
||||
import {modal, alerts} from "src/state/app"
|
||||
import relay, {user} from 'src/relay'
|
||||
import Anchor from 'src/partials/Anchor.svelte'
|
||||
import NoteDetail from "src/views/NoteDetail.svelte"
|
||||
import PersonSettings from "src/views/PersonSettings.svelte"
|
||||
@ -68,8 +68,6 @@
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
relay.pool.sync($user)
|
||||
|
||||
return modal.subscribe($modal => {
|
||||
// Keep scroll position on body, but don't allow scrolling
|
||||
if ($modal) {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import Dexie from 'dexie'
|
||||
import {writable} from 'svelte/store'
|
||||
import {groupBy, prop, flatten, pick} from 'ramda'
|
||||
import {ensurePlural, switcherFn} from 'hurdak/lib/hurdak'
|
||||
import {now} from 'src/util/misc'
|
||||
import {now, getLocalJson, setLocalJson} from 'src/util/misc'
|
||||
import {filterTags, findReply, findRoot} from 'src/util/nostr'
|
||||
|
||||
export const db = new Dexie('coracle/relay')
|
||||
@ -9,12 +10,23 @@ export const db = new Dexie('coracle/relay')
|
||||
db.version(4).stores({
|
||||
relays: '++url, name',
|
||||
events: '++id, pubkey, created_at, kind, content, reply, root',
|
||||
people: '++pubkey, name, about',
|
||||
tags: '++key, event, value',
|
||||
})
|
||||
|
||||
window.db = db
|
||||
|
||||
// Some things work better as observables than database tables
|
||||
|
||||
db.user = writable(getLocalJson("db/user"))
|
||||
db.people = writable(getLocalJson('db.people') || {})
|
||||
db.network = writable(getLocalJson('db/network') || [])
|
||||
db.connections = writable(getLocalJson("db/connections") || [])
|
||||
|
||||
db.user.subscribe($user => setLocalJson("coracle/user", $user))
|
||||
db.people.subscribe($people => setLocalJson("coracle/people", $people))
|
||||
db.network.subscribe($network => setLocalJson("coracle/network", $network))
|
||||
db.connections.subscribe($connections => setLocalJson("coracle/connections", $connections))
|
||||
|
||||
// Hooks
|
||||
|
||||
db.events.process = async events => {
|
||||
@ -58,12 +70,19 @@ db.events.process = async events => {
|
||||
}
|
||||
|
||||
// Update our people
|
||||
db.people.update($people => {
|
||||
for (const event of profileUpdates) {
|
||||
const {pubkey, kind, content, tags} = event
|
||||
const person = await db.people.where('pubkey').equals(pubkey).first()
|
||||
const putPerson = data => db.people.put({...person, ...data, pubkey, updated_at: now()})
|
||||
const putPerson = data => {
|
||||
$people[pubkey] = {
|
||||
...$people[pubkey],
|
||||
...data,
|
||||
pubkey,
|
||||
updated_at: now(),
|
||||
}
|
||||
}
|
||||
|
||||
await switcherFn(kind, {
|
||||
switcherFn(kind, {
|
||||
0: () => putPerson(JSON.parse(content)),
|
||||
3: () => putPerson({petnames: tags}),
|
||||
12165: () => putPerson({muffle: tags}),
|
||||
@ -72,4 +91,7 @@ db.events.process = async events => {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return $people
|
||||
})
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
import {liveQuery} from 'dexie'
|
||||
import {pluck, uniq, objOf, isNil} from 'ramda'
|
||||
import {pluck, without, uniqBy, prop, groupBy, concat, uniq, objOf, isNil, identity} from 'ramda'
|
||||
import {ensurePlural, createMap, ellipsize, first} from 'hurdak/lib/hurdak'
|
||||
import {now, timedelta, createScroller} from 'src/util/misc'
|
||||
import {escapeHtml} from 'src/util/html'
|
||||
import {filterTags} from 'src/util/nostr'
|
||||
import {filterTags, findRoot, findReply} from 'src/util/nostr'
|
||||
import {db} from 'src/relay/db'
|
||||
import pool from 'src/relay/pool'
|
||||
|
||||
@ -16,14 +15,13 @@ const lq = f => liveQuery(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
// Context getters attempt to retrieve from the db and fall back to the network
|
||||
|
||||
const ensurePerson = async ({pubkey}) => {
|
||||
const person = await db.people.where('pubkey').equals(pubkey).first()
|
||||
|
||||
// Throttle updates for people
|
||||
if (!person || person.updated_at < now() - timedelta(1, 'hours')) {
|
||||
await pool.syncPersonInfo({pubkey, ...person})
|
||||
}
|
||||
}
|
||||
|
||||
const ensureContext = async events => {
|
||||
const promises = []
|
||||
@ -48,6 +46,8 @@ const ensureContext = async events => {
|
||||
await Promise.all(promises)
|
||||
}
|
||||
|
||||
// Utils for qurying dexie
|
||||
|
||||
const prefilterEvents = filter => {
|
||||
if (filter.ids) {
|
||||
return db.events.where('id').anyOf(ensurePlural(filter.ids))
|
||||
@ -64,6 +64,8 @@ const prefilterEvents = filter => {
|
||||
return db.events
|
||||
}
|
||||
|
||||
// Utils for filtering db
|
||||
|
||||
const filterEvents = filter => {
|
||||
return prefilterEvents(filter)
|
||||
.filter(e => {
|
||||
@ -78,44 +80,34 @@ const filterEvents = filter => {
|
||||
})
|
||||
}
|
||||
|
||||
const getOrLoadChunk = async (filter, since, until) => {
|
||||
const getChunk = () => {
|
||||
return filterEvents({...filter, since}).reverse().sortBy('created_at')
|
||||
const annotateChunk = async chunk => {
|
||||
const ancestorIds = concat(chunk.map(findRoot), chunk.map(findReply)).filter(identity)
|
||||
const ancestors = await filterEvents({kinds: [1], ids: ancestorIds}).toArray()
|
||||
|
||||
const allNotes = uniqBy(prop('id'), chunk.concat(ancestors))
|
||||
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
|
||||
}
|
||||
|
||||
const chunk = getChunk()
|
||||
|
||||
// If we have a chunk, go ahead and use it. This will result in not showing all
|
||||
// data, but it's the best UX I could come up with
|
||||
if (chunk.length > 0) {
|
||||
return chunk
|
||||
if (notesById[parentId]) {
|
||||
return parentId
|
||||
}
|
||||
|
||||
// If we didn't have anything, try loading it
|
||||
await ensureContext(await pool.loadEvents({...filter, since, until}))
|
||||
return n.id
|
||||
},
|
||||
allNotes
|
||||
)
|
||||
|
||||
// Now return what's in our database
|
||||
return getChunk()
|
||||
return await Promise.all(Object.keys(notesByRoot).map(findNote))
|
||||
}
|
||||
|
||||
const scroller = (filter, delta, onChunk) => {
|
||||
let since = now() - delta
|
||||
let until = now()
|
||||
|
||||
const unsub = createScroller(async () => {
|
||||
since -= delta
|
||||
until -= delta
|
||||
|
||||
await onChunk(await getOrLoadChunk(filter, since, until))
|
||||
|
||||
// Set a hard cutoff at 3 weeks back
|
||||
if (since < now() - timedelta(21, 'days')) {
|
||||
unsub()
|
||||
}
|
||||
})
|
||||
|
||||
return unsub
|
||||
}
|
||||
|
||||
const filterReplies = async (id, filter) => {
|
||||
const tags = db.tags.where('value').equals(id).filter(t => t.mark === 'reply')
|
||||
@ -203,8 +195,67 @@ const filterAlerts = async (person, filter) => {
|
||||
return events
|
||||
}
|
||||
|
||||
// Synchronization
|
||||
|
||||
const login = ({privkey, pubkey}) => {
|
||||
db.user.set({relays: [], muffle: [], petnames: [], updated_at: 0, pubkey, privkey})
|
||||
}
|
||||
|
||||
const addRelay = url => {
|
||||
db.connections.update($connections => $connections.concat(url))
|
||||
|
||||
pool.syncNetwork()
|
||||
pool.syncNetworkNotes()
|
||||
}
|
||||
|
||||
const removeRelay = url => {
|
||||
db.connections.update($connections => without([url], $connections))
|
||||
}
|
||||
|
||||
const follow = async pubkey => {
|
||||
db.network.update($network => $network.concat(pubkey))
|
||||
|
||||
pool.syncNetwork()
|
||||
pool.syncNetworkNotes()
|
||||
}
|
||||
|
||||
const unfollow = async pubkey => {
|
||||
db.network.update($network => $network.concat(pubkey))
|
||||
}
|
||||
|
||||
// Initialization
|
||||
|
||||
db.user.subscribe($user => {
|
||||
if ($user?.privkey) {
|
||||
pool.setPrivateKey($user.privkey)
|
||||
} else if ($user?.pubkey) {
|
||||
pool.setPublicKey($user.pubkey)
|
||||
}
|
||||
})
|
||||
|
||||
db.connections.subscribe($connections => {
|
||||
const poolRelays = pool.getRelays()
|
||||
|
||||
for (const url of $connections) {
|
||||
if (!poolRelays.includes(url)) {
|
||||
pool.addRelay(url)
|
||||
}
|
||||
}
|
||||
|
||||
for (const url of poolRelays) {
|
||||
if (!$connections.includes(url)) {
|
||||
pool.removeRelay(url)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const user = db.user
|
||||
export const people = db.people
|
||||
export const network = db.network
|
||||
export const connections = db.connections
|
||||
|
||||
export default {
|
||||
db, pool, lq, ensurePerson, ensureContext, filterEvents, filterReactions,
|
||||
countReactions, findReaction, filterReplies, findNote, renderNote, filterAlerts,
|
||||
scroller,
|
||||
annotateChunk, login, addRelay, removeRelay, follow, unfollow,
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {uniqBy, without, prop} from 'ramda'
|
||||
import {writable} from 'svelte/store'
|
||||
import {uniqBy, prop, uniq} from 'ramda'
|
||||
import {get} from 'svelte/store'
|
||||
import {relayPool, getPublicKey} from 'nostr-tools'
|
||||
import {noop, range} from 'hurdak/lib/hurdak'
|
||||
import {now, randomChoice, timedelta, getLocalJson, setLocalJson} from "src/util/misc"
|
||||
import {getTagValues} from "src/util/nostr"
|
||||
import {now, timedelta, randomChoice} from "src/util/misc"
|
||||
import {getTagValues, filterTags} from "src/util/nostr"
|
||||
import {db} from 'src/relay/db'
|
||||
|
||||
// ============================================================================
|
||||
@ -11,16 +11,6 @@ import {db} from 'src/relay/db'
|
||||
|
||||
const pool = relayPool()
|
||||
|
||||
const relays = writable([])
|
||||
|
||||
const setup = () => {
|
||||
for (const url of getLocalJson('pool/relays') || []) {
|
||||
addRelay(url)
|
||||
}
|
||||
|
||||
relays.subscribe($relays => setLocalJson('pool/relays', $relays))
|
||||
}
|
||||
|
||||
class Channel {
|
||||
constructor(name) {
|
||||
this.name = name
|
||||
@ -80,20 +70,23 @@ class Channel {
|
||||
|
||||
export const channels = range(0, 10).map(i => new Channel(i.toString()))
|
||||
|
||||
const req = filter => randomChoice(channels).all(filter)
|
||||
const req = (...args) => randomChoice(channels).all(...args)
|
||||
const sub = (...args) => randomChoice(channels).sub(...args)
|
||||
|
||||
const getPubkey = () => {
|
||||
return pool._pubkey || getPublicKey(pool._privkey)
|
||||
}
|
||||
|
||||
const getRelays = () => {
|
||||
return Object.keys(pool.relays)
|
||||
}
|
||||
|
||||
const addRelay = url => {
|
||||
pool.addRelay(url)
|
||||
relays.update($r => $r.concat(url))
|
||||
}
|
||||
|
||||
const removeRelay = url => {
|
||||
pool.removeRelay(url)
|
||||
relays.update($r => without([url], $r))
|
||||
}
|
||||
|
||||
const setPrivateKey = privkey => {
|
||||
@ -102,7 +95,6 @@ const setPrivateKey = privkey => {
|
||||
}
|
||||
|
||||
const setPublicKey = pubkey => {
|
||||
// TODO fix this, it ain't gonna work
|
||||
pool.registerSigningFunction(async event => {
|
||||
const {sig} = await window.nostr.signEvent(event)
|
||||
|
||||
@ -125,56 +117,71 @@ const loadEvents = async filter => {
|
||||
return events
|
||||
}
|
||||
|
||||
const syncPersonInfo = async person => {
|
||||
const [events] = await Promise.all([
|
||||
// Get profile info events
|
||||
req({kinds: [0, 3, 12165], authors: [person.pubkey]}),
|
||||
// Make sure we have something in the database
|
||||
db.people.put({muffle: [], petnames: [], updated_at: 0, ...person}),
|
||||
])
|
||||
const subs = {}
|
||||
|
||||
// Process the events to flesh out the person
|
||||
await db.events.process(events)
|
||||
|
||||
// Return our person for convenience
|
||||
return await db.people.where('pubkey').equals(person.pubkey).first()
|
||||
const listenForEvents = async (key, filter) => {
|
||||
if (subs[key]) {
|
||||
subs[key].unsub()
|
||||
}
|
||||
|
||||
let syncSub = null
|
||||
let syncChan = new Channel('sync')
|
||||
|
||||
const sync = async person => {
|
||||
if (syncSub) {
|
||||
(await syncSub).unsub()
|
||||
subs[key] = await sub(filter, db.events.process)
|
||||
}
|
||||
|
||||
if (!person) return
|
||||
|
||||
// Get person info right away
|
||||
const {petnames, pubkey} = await syncPersonInfo(person)
|
||||
|
||||
// Don't grab nothing, but don't grab everything either
|
||||
const since = Math.max(
|
||||
now() - timedelta(3, 'days'),
|
||||
Math.min(
|
||||
now() - timedelta(3, 'hours'),
|
||||
getLocalJson('pool/lastSync') || 0
|
||||
)
|
||||
)
|
||||
|
||||
setLocalJson('pool/lastSync', now())
|
||||
|
||||
// Populate recent activity in network so the person has something to look at right away
|
||||
syncSub = syncChan.sub(
|
||||
[{since, authors: getTagValues(petnames).concat(pubkey)},
|
||||
{since, '#p': [pubkey]}],
|
||||
db.events.process
|
||||
)
|
||||
const loadPeople = pubkeys => {
|
||||
return pubkeys.length ? loadEvents({kinds: [0, 3, 12165], authors: pubkeys}) : []
|
||||
}
|
||||
|
||||
setup()
|
||||
const syncNetwork = async () => {
|
||||
const $user = get(db.user)
|
||||
|
||||
let pubkeys = []
|
||||
if ($user) {
|
||||
// Get this user's profile to start with
|
||||
await loadPeople([$user.pubkey])
|
||||
|
||||
// Get our refreshed person
|
||||
const people = get(db.people)
|
||||
|
||||
// Merge the new info into our user
|
||||
Object.assign($user, people[$user.pubkey])
|
||||
|
||||
console.log($user)
|
||||
|
||||
// Update our user store
|
||||
db.user.update(() => $user)
|
||||
|
||||
// Get n degreees of separation using petnames
|
||||
pubkeys = uniq(getTagValues($user.petnames))
|
||||
}
|
||||
|
||||
// Fall back to some pubkeys we like so we can support new users
|
||||
if (pubkeys.length === 0) {
|
||||
pubkeys = [
|
||||
"97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322", // hodlbod
|
||||
]
|
||||
}
|
||||
|
||||
let networkPubkeys = pubkeys
|
||||
for (let depth = 0; depth < 1; depth++) {
|
||||
const events = await loadPeople(pubkeys)
|
||||
|
||||
pubkeys = uniq(filterTags({type: "p"}, events.filter(e => e.kind === 3)))
|
||||
|
||||
networkPubkeys = networkPubkeys.concat(pubkeys)
|
||||
}
|
||||
|
||||
db.network.set(networkPubkeys)
|
||||
}
|
||||
|
||||
const syncNetworkNotes = () => {
|
||||
const authors = get(db.network)
|
||||
const since = now() - timedelta(30, 'days')
|
||||
|
||||
loadEvents({kinds: [1, 5, 7], authors, since, until: now()})
|
||||
listenForEvents('networkNotes', {kinds: [1, 5, 7], authors, since: now()})
|
||||
}
|
||||
|
||||
export default {
|
||||
getPubkey, addRelay, removeRelay, setPrivateKey, setPublicKey,
|
||||
publishEvent, loadEvents, syncPersonInfo, sync, relays,
|
||||
getPubkey, getRelays, addRelay, removeRelay, setPrivateKey, setPublicKey,
|
||||
publishEvent, loadEvents, syncNetwork, syncNetworkNotes,
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
<script>
|
||||
import {fly} from 'svelte/transition'
|
||||
import toast from 'src/state/toast'
|
||||
import {modal, registerRelay} from 'src/state/app'
|
||||
import {modal} from 'src/state/app'
|
||||
import {dispatch} from 'src/state/dispatch'
|
||||
import Input from 'src/partials/Input.svelte'
|
||||
import Button from 'src/partials/Button.svelte'
|
||||
import relay from 'src/relay'
|
||||
|
||||
let url = ''
|
||||
|
||||
@ -16,8 +17,8 @@
|
||||
return toast.show("error", 'That isn\'t a valid websocket url - relay urls should start with "wss://"')
|
||||
}
|
||||
|
||||
registerRelay(url)
|
||||
dispatch("relay/join", url)
|
||||
relay.db.relays.put(url)
|
||||
relay.addRelay(url)
|
||||
modal.set(null)
|
||||
}
|
||||
</script>
|
||||
|
@ -3,8 +3,8 @@
|
||||
import {now} from 'src/util/misc'
|
||||
import {findReply} from 'src/util/nostr'
|
||||
import {ellipsize} from 'hurdak/src/core'
|
||||
import relay from 'src/relay'
|
||||
import {alerts, modal, user} from 'src/state/app'
|
||||
import relay, {user} from 'src/relay'
|
||||
import {alerts, modal} from 'src/state/app'
|
||||
import Badge from "src/partials/Badge.svelte"
|
||||
import Note from 'src/views/Note.svelte'
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
import {copyToClipboard} from "src/util/html"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import {user} from "src/state/app"
|
||||
import toast from "src/state/toast"
|
||||
import {user} from "src/relay"
|
||||
|
||||
const keypairUrl = 'https://www.cloudflare.com/learning/ssl/how-does-public-key-encryption-work/'
|
||||
const delegationUrl = 'https://github.com/nostr-protocol/nips/blob/b62aa418dee13aac1899ea7c6946a0f55dd7ee84/26.md'
|
||||
|
@ -7,8 +7,7 @@
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import toast from "src/state/toast"
|
||||
import {dispatch} from "src/state/dispatch"
|
||||
import {user} from "src/state/app"
|
||||
import relay from 'src/relay'
|
||||
|
||||
let privkey = ''
|
||||
let hasExtension = false
|
||||
@ -31,12 +30,10 @@
|
||||
toast.show("info", "Your private key has been re-generated.")
|
||||
}
|
||||
|
||||
const logIn = async ({privkey, pubkey}) => {
|
||||
const person = await dispatch("user/init", pubkey)
|
||||
const logIn = ({privkey, pubkey}) => {
|
||||
relay.login({privkey, pubkey})
|
||||
|
||||
user.set({...person, pubkey, privkey})
|
||||
|
||||
navigate('/notes/global')
|
||||
navigate('/relays')
|
||||
}
|
||||
|
||||
const logInWithExtension = async () => {
|
||||
|
@ -5,8 +5,8 @@
|
||||
import Textarea from "src/partials/Textarea.svelte"
|
||||
import Button from "src/partials/Button.svelte"
|
||||
import {dispatch} from "src/state/dispatch"
|
||||
import {user} from "src/state/app"
|
||||
import toast from "src/state/toast"
|
||||
import {user} from "src/relay"
|
||||
|
||||
let values = {}
|
||||
|
||||
|
@ -1,21 +1,18 @@
|
||||
<script>
|
||||
import {navigate} from 'svelte-routing'
|
||||
import {timedelta} from 'src/util/misc'
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Tabs from "src/partials/Tabs.svelte"
|
||||
import Notes from "src/views/Notes.svelte"
|
||||
import {user} from "src/state/app"
|
||||
import {timedelta} from 'src/util/misc'
|
||||
import relay from 'src/relay'
|
||||
import relay, {user, connections} from 'src/relay'
|
||||
|
||||
export let activeTab
|
||||
|
||||
const relays = relay.pool.relays
|
||||
const authors = $user ? $user.petnames.map(t => t[1]) : []
|
||||
const setActiveTab = tab => navigate(`/notes/${tab}`)
|
||||
</script>
|
||||
|
||||
{#if $relays.length === 0}
|
||||
{#if $connections.length === 0}
|
||||
<div class="flex w-full justify-center items-center py-16">
|
||||
<div class="text-center max-w-md">
|
||||
You aren't yet connected to any relays. Please click <Anchor href="/relays"
|
||||
|
@ -8,8 +8,9 @@
|
||||
import Notes from "src/views/Notes.svelte"
|
||||
import Likes from "src/views/Likes.svelte"
|
||||
import {t, dispatch} from 'src/state/dispatch'
|
||||
import {modal, user} from "src/state/app"
|
||||
import {modal} from "src/state/app"
|
||||
import relay from 'src/relay'
|
||||
import {user} from "src/relay"
|
||||
|
||||
export let pubkey
|
||||
export let activeTab
|
||||
|
@ -8,9 +8,9 @@
|
||||
import Textarea from "src/partials/Textarea.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Button from "src/partials/Button.svelte"
|
||||
import {user} from "src/state/app"
|
||||
import {dispatch} from "src/state/dispatch"
|
||||
import toast from "src/state/toast"
|
||||
import {user} from "src/relay"
|
||||
|
||||
let values = {picture: null, about: null, name: null}
|
||||
|
||||
|
@ -3,18 +3,31 @@
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import {modal} from "src/state/app"
|
||||
import relay from 'src/relay'
|
||||
import relay, {connections} from 'src/relay'
|
||||
|
||||
let q = ""
|
||||
let search
|
||||
|
||||
const relays = relay.pool.relays
|
||||
const defaultRelays = [
|
||||
"wss://nostr.zebedee.cloud",
|
||||
"wss://nostr-pub.wellorder.net",
|
||||
"wss://relay.damus.io",
|
||||
"wss://relay.grunch.dev",
|
||||
"wss://nostr.sandwich.farm",
|
||||
"wss://relay.nostr.ch",
|
||||
"wss://nostr-relay.wlvs.space",
|
||||
]
|
||||
|
||||
for (const url of defaultRelays) {
|
||||
relay.db.relays.put({url})
|
||||
}
|
||||
|
||||
const knownRelays = relay.lq(() => relay.db.relays.toArray())
|
||||
|
||||
$: search = fuzzy($knownRelays, {keys: ["name", "description", "url"]})
|
||||
|
||||
const join = url => relay.pool.addRelay(url)
|
||||
const leave = url => relay.pool.removeRelay(url)
|
||||
const join = url => relay.addRelay(url)
|
||||
const leave = url => relay.removeRelay(url)
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center py-8 px-4" in:fly={{y: 20}}>
|
||||
@ -31,7 +44,7 @@
|
||||
</Input>
|
||||
<div class="flex flex-col gap-6 overflow-auto flex-grow -mx-6 px-6">
|
||||
{#each ($knownRelays || []) as r}
|
||||
{#if $relays.includes(r.url)}
|
||||
{#if $connections.includes(r.url)}
|
||||
<div class="flex gap-2 justify-between">
|
||||
<div>
|
||||
<strong>{r.name || r.url}</strong>
|
||||
|
@ -5,8 +5,9 @@
|
||||
import Toggle from "src/partials/Toggle.svelte"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Button from "src/partials/Button.svelte"
|
||||
import {settings, user} from "src/state/app"
|
||||
import {settings} from "src/state/app"
|
||||
import toast from "src/state/toast"
|
||||
import {user} from "src/relay"
|
||||
|
||||
let values = {...$settings}
|
||||
|
||||
|
@ -1,59 +1,7 @@
|
||||
import {writable, get} from 'svelte/store'
|
||||
import {writable} from 'svelte/store'
|
||||
import {navigate} from "svelte-routing"
|
||||
import {globalHistory} from "svelte-routing/src/history"
|
||||
import {getLocalJson, setLocalJson, now, timedelta} from "src/util/misc"
|
||||
import relay from 'src/relay'
|
||||
|
||||
// Keep track of our user
|
||||
|
||||
export const user = writable(getLocalJson("coracle/user"))
|
||||
|
||||
user.subscribe($user => {
|
||||
setLocalJson("coracle/user", $user)
|
||||
|
||||
// Keep nostr in sync
|
||||
if ($user?.privkey) {
|
||||
relay.pool.setPrivateKey($user.privkey)
|
||||
} else if ($user?.pubkey) {
|
||||
relay.pool.setPublicKey($user.pubkey)
|
||||
}
|
||||
})
|
||||
|
||||
const userLq = relay.lq(() => {
|
||||
const $user = get(user)
|
||||
|
||||
if ($user) {
|
||||
return relay.db.people.where('pubkey').equals($user?.pubkey).first()
|
||||
}
|
||||
})
|
||||
|
||||
userLq.subscribe(person => {
|
||||
user.update($user => $user ? ({...$user, ...person}) : null)
|
||||
})
|
||||
|
||||
// Keep track of which relays we're subscribed to
|
||||
|
||||
export const relays = writable(getLocalJson("coracle/relays") || [])
|
||||
|
||||
let prevRelays = []
|
||||
|
||||
relays.subscribe($relays => {
|
||||
prevRelays.forEach(url => {
|
||||
if (!$relays.includes(url)) {
|
||||
relay.pool.removeRelay(url)
|
||||
}
|
||||
})
|
||||
|
||||
$relays.forEach(url => {
|
||||
if (!prevRelays.includes(url)) {
|
||||
relay.pool.addRelay(url)
|
||||
}
|
||||
})
|
||||
|
||||
setLocalJson("coracle/relays", $relays)
|
||||
|
||||
relay.pool.sync(get(user))
|
||||
})
|
||||
|
||||
// Modals
|
||||
|
||||
@ -98,40 +46,3 @@ export const alerts = writable({
|
||||
alerts.subscribe($alerts => {
|
||||
setLocalJson("coracle/alerts", $alerts)
|
||||
})
|
||||
|
||||
// Relays
|
||||
|
||||
const defaultRelays = [
|
||||
"wss://nostr.zebedee.cloud",
|
||||
"wss://nostr-pub.wellorder.net",
|
||||
"wss://relay.damus.io",
|
||||
"wss://relay.grunch.dev",
|
||||
"wss://nostr.sandwich.farm",
|
||||
"wss://relay.nostr.ch",
|
||||
"wss://nostr-relay.wlvs.space",
|
||||
]
|
||||
|
||||
export const registerRelay = async url => {
|
||||
const {dufflepudUrl} = get(settings)
|
||||
|
||||
let json
|
||||
try {
|
||||
const res = await fetch(dufflepudUrl + '/relay/info', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({url}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
json = await res.json()
|
||||
} catch (e) {
|
||||
json = {}
|
||||
}
|
||||
|
||||
relay.db.relays.put({...json, url})
|
||||
}
|
||||
|
||||
for (const url of defaultRelays) {
|
||||
registerRelay(url)
|
||||
}
|
||||
|
@ -9,13 +9,6 @@ import relay from 'src/relay'
|
||||
|
||||
export const dispatch = defmulti("dispatch", identity)
|
||||
|
||||
dispatch.addMethod("user/init", (topic, pubkey) => {
|
||||
// Hardcode one to get them started
|
||||
relay.pool.addRelay("wss://nostr.zebedee.cloud")
|
||||
|
||||
return relay.pool.syncPersonInfo({pubkey})
|
||||
})
|
||||
|
||||
dispatch.addMethod("user/update", async (topic, updates) => {
|
||||
await relay.pool.publishEvent(makeEvent(0, JSON.stringify(updates)))
|
||||
})
|
||||
|
@ -8,10 +8,11 @@
|
||||
import Preview from 'src/partials/Preview.svelte'
|
||||
import Anchor from 'src/partials/Anchor.svelte'
|
||||
import {dispatch} from "src/state/dispatch"
|
||||
import {settings, user, modal} from "src/state/app"
|
||||
import {settings, modal} from "src/state/app"
|
||||
import {formatTimestamp} from 'src/util/misc'
|
||||
import Badge from "src/partials/Badge.svelte"
|
||||
import Card from "src/partials/Card.svelte"
|
||||
import {user} from 'src/relay'
|
||||
|
||||
export let note
|
||||
export let depth = 0
|
||||
|
@ -2,7 +2,7 @@
|
||||
import {onDestroy} from 'svelte'
|
||||
import {prop, identity, concat, uniqBy, groupBy} from 'ramda'
|
||||
import {createMap} from 'hurdak/lib/hurdak'
|
||||
import {timedelta} from 'src/util/misc'
|
||||
import {createScroller} from 'src/util/misc'
|
||||
import {findReply, findRoot} from 'src/util/nostr'
|
||||
import Spinner from 'src/partials/Spinner.svelte'
|
||||
import Note from "src/views/Note.svelte"
|
||||
@ -10,13 +10,16 @@
|
||||
|
||||
export let filter
|
||||
export let showParent = false
|
||||
export let shouldMuffle = false
|
||||
export let delta = timedelta(10, 'minutes')
|
||||
|
||||
let notes
|
||||
let limit = 0
|
||||
|
||||
onDestroy(createScroller(async () => {
|
||||
limit += 20
|
||||
|
||||
onDestroy(relay.scroller(filter, delta, async chunk => {
|
||||
notes = relay.lq(async () => {
|
||||
const notes = await relay.filterEvents(filter).reverse().sortBy('created_at')
|
||||
const chunk = notes.slice(0, limit)
|
||||
const ancestorIds = concat(chunk.map(findRoot), chunk.map(findReply)).filter(identity)
|
||||
const ancestors = await relay.filterEvents({kinds: [1], ids: ancestorIds}).toArray()
|
||||
|
||||
|
@ -6,7 +6,8 @@
|
||||
import SelectButton from "src/partials/SelectButton.svelte"
|
||||
import {getMuffleValue} from "src/util/nostr"
|
||||
import {dispatch, t} from 'src/state/dispatch'
|
||||
import {modal, user} from "src/state/app"
|
||||
import {modal} from "src/state/app"
|
||||
import {user} from 'src/relay'
|
||||
|
||||
const muffleOptions = ['Never', 'Sometimes', 'Often', 'Always']
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
<script>
|
||||
import {fly} from 'svelte/transition'
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import {user} from "src/state/app"
|
||||
import relay from 'src/relay'
|
||||
import relay, {user} from 'src/relay'
|
||||
|
||||
export let q
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user