mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Fix all major bugs
This commit is contained in:
parent
d23742d33e
commit
78a85be7e5
@ -14,6 +14,7 @@ Coracle is currently in _alpha_ - expect bugs, slow loading times, and rough edg
|
||||
- [x] Notifications
|
||||
- [x] Link previews
|
||||
- [x] Add notes, follows, likes tab to profile
|
||||
- [ ] Add a coracle relay
|
||||
- [ ] Mentions - render done, now reference in compose
|
||||
- [ ] Image uploads
|
||||
- [ ] An actual readme
|
||||
@ -40,11 +41,9 @@ Coracle is currently in _alpha_ - expect bugs, slow loading times, and rough edg
|
||||
|
||||
- [ ] Check firefox - in dev it won't work, but it should in production
|
||||
- [ ] Re-implement muffle
|
||||
- [ ] Move relays to db
|
||||
- [ ] Make user a livequery instead of a store
|
||||
- [ ] Figure out if multiple relays congest response times because we wait for all eose
|
||||
- [ ] Set default relay when storage is empty
|
||||
- [ ] Are connections closed when a relay is removed?
|
||||
- [ ] Delete old events
|
||||
- [ ] Sync accounts to store to avoid loading jank
|
||||
- [ ] Sync account updates to user for e.g. muffle settings
|
||||
- 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/
|
||||
|
@ -6,7 +6,8 @@ import {filterTags, findReply, findRoot} from 'src/util/nostr'
|
||||
|
||||
export const db = new Dexie('coracle/relay')
|
||||
|
||||
db.version(3).stores({
|
||||
db.version(4).stores({
|
||||
relays: '++url, name',
|
||||
events: '++id, pubkey, created_at, kind, content, reply, root',
|
||||
people: '++pubkey, name, about',
|
||||
tags: '++key, event, value',
|
||||
|
@ -38,7 +38,7 @@ const ensureContext = async events => {
|
||||
|
||||
if (ids.length > 0) {
|
||||
promises.push(
|
||||
pool.fetchEvents([
|
||||
pool.loadEvents([
|
||||
{kinds: [1, 5, 7], '#e': ids},
|
||||
{kinds: [1, 5], ids},
|
||||
])
|
||||
@ -107,6 +107,11 @@ const scroller = (filter, delta, onChunk) => {
|
||||
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
|
||||
@ -142,15 +147,13 @@ const findNote = async (id, giveUp = false) => {
|
||||
|
||||
// If we don't have it, try to retrieve it
|
||||
if (!note) {
|
||||
console.warning(`Failed to find context for note ${id}`)
|
||||
console.warn(`Failed to find context for note ${id}`)
|
||||
|
||||
if (giveUp) {
|
||||
return null
|
||||
}
|
||||
|
||||
await ensureContext([
|
||||
await pool.fetchEvents({ids: [id]}),
|
||||
])
|
||||
await ensureContext(await pool.loadEvents({ids: [id]}))
|
||||
|
||||
return findNote(id, true)
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {uniqBy, prop} from 'ramda'
|
||||
import {uniqBy, without, prop} from 'ramda'
|
||||
import {writable} 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"
|
||||
@ -10,6 +11,16 @@ 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
|
||||
@ -77,10 +88,12 @@ const getPubkey = () => {
|
||||
|
||||
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 => {
|
||||
@ -127,10 +140,6 @@ const syncPersonInfo = async person => {
|
||||
return await db.people.where('pubkey').equals(person.pubkey).first()
|
||||
}
|
||||
|
||||
const fetchEvents = async filter => {
|
||||
db.events.process(await req(filter))
|
||||
}
|
||||
|
||||
let syncSub = null
|
||||
let syncChan = new Channel('sync')
|
||||
|
||||
@ -163,7 +172,9 @@ const sync = async person => {
|
||||
)
|
||||
}
|
||||
|
||||
setup()
|
||||
|
||||
export default {
|
||||
getPubkey, addRelay, removeRelay, setPrivateKey, setPublicKey,
|
||||
publishEvent, loadEvents, syncPersonInfo, fetchEvents, sync,
|
||||
publishEvent, loadEvents, syncPersonInfo, sync, relays,
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import toast from "src/state/toast"
|
||||
import {dispatch} from "src/state/dispatch"
|
||||
import {relays, user} from "src/state/app"
|
||||
import {user} from "src/state/app"
|
||||
|
||||
let privkey = ''
|
||||
let hasExtension = false
|
||||
@ -32,19 +32,11 @@
|
||||
}
|
||||
|
||||
const logIn = async ({privkey, pubkey}) => {
|
||||
console.log(1)
|
||||
const person = await dispatch("user/init", pubkey)
|
||||
console.log(person)
|
||||
|
||||
user.set({...person, pubkey, privkey})
|
||||
|
||||
if ($relays.length === 0) {
|
||||
navigate('/relays')
|
||||
} else if (user.name) {
|
||||
navigate('/notes/global')
|
||||
} else {
|
||||
navigate('/profile')
|
||||
}
|
||||
}
|
||||
|
||||
const logInWithExtension = async () => {
|
||||
|
@ -4,11 +4,13 @@
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Tabs from "src/partials/Tabs.svelte"
|
||||
import Notes from "src/views/Notes.svelte"
|
||||
import {user, relays} from "src/state/app"
|
||||
import {user} from "src/state/app"
|
||||
import {timedelta} from 'src/util/misc'
|
||||
import relay 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>
|
||||
|
@ -1,20 +1,20 @@
|
||||
<script>
|
||||
import {fly} from 'svelte/transition'
|
||||
import {find, identity, whereEq, reject} from 'ramda'
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import {dispatch} from "src/state/dispatch"
|
||||
import {modal, relays, knownRelays} from "src/state/app"
|
||||
import {modal} from "src/state/app"
|
||||
import relay from 'src/relay'
|
||||
|
||||
let q = ""
|
||||
let search
|
||||
let data
|
||||
|
||||
$: data = reject(r => $relays.includes(r.url), $knownRelays || [])
|
||||
$: search = fuzzy(data, {keys: ["name", "description", "url"]})
|
||||
const relays = relay.pool.relays
|
||||
const knownRelays = relay.lq(() => relay.db.relays.toArray())
|
||||
|
||||
const join = url => dispatch("relay/join", url)
|
||||
const leave = url => dispatch("relay/leave", url)
|
||||
$: search = fuzzy($knownRelays, {keys: ["name", "description", "url"]})
|
||||
|
||||
const join = url => relay.pool.addRelay(url)
|
||||
const leave = url => relay.pool.removeRelay(url)
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center py-8 px-4" in:fly={{y: 20}}>
|
||||
@ -30,27 +30,29 @@
|
||||
<i slot="before" class="fa-solid fa-search" />
|
||||
</Input>
|
||||
<div class="flex flex-col gap-6 overflow-auto flex-grow -mx-6 px-6">
|
||||
{#each $relays.map(url => find(whereEq({url}), $knownRelays)).filter(identity) as relay}
|
||||
{#each ($knownRelays || []) as r}
|
||||
{#if $relays.includes(r.url)}
|
||||
<div class="flex gap-2 justify-between">
|
||||
<div>
|
||||
<strong>{relay.name || relay.url}</strong>
|
||||
<p class="text-light">{relay.description || ''}</p>
|
||||
<strong>{r.name || r.url}</strong>
|
||||
<p class="text-light">{r.description || ''}</p>
|
||||
</div>
|
||||
<a class="underline cursor-pointer" on:click={() => leave(relay.url)}>
|
||||
<a class="underline cursor-pointer" on:click={() => leave(r.url)}>
|
||||
Leave
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if $relays.length > 0}
|
||||
{#if ($knownRelays || []).length > 0}
|
||||
<div class="pt-2 mb-2 border-b border-solid border-medium" />
|
||||
{/if}
|
||||
{#each search(q).slice(0, 10) as relay}
|
||||
{#each (search(q) || []).slice(0, 10) as r}
|
||||
<div class="flex gap-2 justify-between">
|
||||
<div>
|
||||
<strong>{relay.name || relay.url}</strong>
|
||||
<p class="text-light">{relay.description || ''}</p>
|
||||
<strong>{r.name || r.url}</strong>
|
||||
<p class="text-light">{r.description || ''}</p>
|
||||
</div>
|
||||
<a class="underline cursor-pointer" on:click={() => join(relay.url)}>
|
||||
<a class="underline cursor-pointer" on:click={() => join(r.url)}>
|
||||
Join
|
||||
</a>
|
||||
</div>
|
||||
|
@ -1,4 +1,3 @@
|
||||
import {uniqBy, prop} from 'ramda'
|
||||
import {writable, get} from 'svelte/store'
|
||||
import {navigate} from "svelte-routing"
|
||||
import {globalHistory} from "svelte-routing/src/history"
|
||||
@ -32,46 +31,6 @@ userLq.subscribe(person => {
|
||||
user.update($user => $user ? ({...$user, ...person}) : null)
|
||||
})
|
||||
|
||||
// Keep track of known relays
|
||||
|
||||
export const knownRelays = writable((getLocalJson("coracle/knownRelays") || [
|
||||
{url: "wss://nostr.zebedee.cloud"},
|
||||
{url: "wss://nostr-pub.wellorder.net"},
|
||||
{url: "wss://nostr.rocks"},
|
||||
{url: "wss://nostr-pub.semisol.dev"},
|
||||
{url: "wss://nostr.drss.io"},
|
||||
{url: "wss://relay.damus.io"},
|
||||
{url: "wss://nostr.openchain.fr"},
|
||||
{url: "wss://nostr.delo.software"},
|
||||
{url: "wss://relay.nostr.info"},
|
||||
{url: "wss://nostr.ono.re"},
|
||||
{url: "wss://relay.grunch.dev"},
|
||||
{url: "wss://nostr.sandwich.farm"},
|
||||
{url: "wss://relay.nostr.ch"},
|
||||
{url: "wss://nostr-relay.wlvs.space"},
|
||||
]).filter(x => x.url))
|
||||
|
||||
knownRelays.subscribe($knownRelays => {
|
||||
setLocalJson("coracle/knownRelays", $knownRelays)
|
||||
})
|
||||
|
||||
export const registerRelay = async url => {
|
||||
let json
|
||||
try {
|
||||
const res = await fetch(url.replace(/^ws/, 'http'), {
|
||||
headers: {
|
||||
Accept: 'application/nostr_json',
|
||||
},
|
||||
})
|
||||
|
||||
json = await res.json()
|
||||
} catch (e) {
|
||||
json = {}
|
||||
}
|
||||
|
||||
knownRelays.update($xs => uniqBy(prop('url'), $xs.concat({...json, url})))
|
||||
}
|
||||
|
||||
// Keep track of which relays we're subscribed to
|
||||
|
||||
export const relays = writable(getLocalJson("coracle/relays") || [])
|
||||
@ -139,3 +98,40 @@ 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)
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
import {identity, isNil, uniqBy, last, without} from 'ramda'
|
||||
import {get} from 'svelte/store'
|
||||
import {identity, isNil, uniqBy, last} from 'ramda'
|
||||
import {first, defmulti} from "hurdak/lib/hurdak"
|
||||
import relay from 'src/relay'
|
||||
import {relays} from 'src/state/app'
|
||||
|
||||
// Commands are processed in two layers:
|
||||
// - App-oriented commands are created via dispatch
|
||||
@ -12,7 +10,10 @@ import {relays} from 'src/state/app'
|
||||
export const dispatch = defmulti("dispatch", identity)
|
||||
|
||||
dispatch.addMethod("user/init", (topic, pubkey) => {
|
||||
return relay.pool.syncUserInfo({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) => {
|
||||
@ -27,14 +28,6 @@ dispatch.addMethod("user/muffle", async (topic, muffle) => {
|
||||
await relay.pool.publishEvent(makeEvent(12165, '', muffle))
|
||||
})
|
||||
|
||||
dispatch.addMethod("relay/join", async (topic, url) => {
|
||||
relays.update(r => r.concat(url))
|
||||
})
|
||||
|
||||
dispatch.addMethod("relay/leave", (topic, url) => {
|
||||
relays.update(r => without([url], r))
|
||||
})
|
||||
|
||||
dispatch.addMethod("room/create", async (topic, room) => {
|
||||
const event = makeEvent(40, JSON.stringify(room))
|
||||
|
||||
@ -104,7 +97,7 @@ export const copyTags = (e, newTags = []) => {
|
||||
}
|
||||
|
||||
export const t = (type, content, marker) => {
|
||||
const tag = [type, content, first(get(relays))]
|
||||
const tag = [type, content, first(Object.keys(relay.pool.relays))]
|
||||
|
||||
if (!isNil(marker)) {
|
||||
tag.push(marker)
|
||||
|
@ -64,7 +64,7 @@ export const createScroller = loadMore => {
|
||||
await loadMore()
|
||||
}
|
||||
|
||||
await sleep(100)
|
||||
await sleep(1000)
|
||||
|
||||
if (!done) {
|
||||
requestAnimationFrame(check)
|
||||
|
@ -1,26 +1,35 @@
|
||||
<script>
|
||||
import {onDestroy} from 'svelte'
|
||||
import {fly} from 'svelte/transition'
|
||||
import {propEq} from 'ramda'
|
||||
import {uniqBy, identity, prop} from 'ramda'
|
||||
import {timedelta} from 'src/util/misc'
|
||||
import Note from "src/views/Note.svelte"
|
||||
import {findReply} from 'src/util/nostr'
|
||||
import relay from 'src/relay'
|
||||
|
||||
export let author
|
||||
|
||||
const notes = relay.lq(async () => {
|
||||
const reactions = await relay.db.events
|
||||
.where('pubkey').equals(author).filter(propEq('kind', 7)).toArray()
|
||||
const filter = {kinds: [7], authors: [author]}
|
||||
const delta = timedelta(1, 'days')
|
||||
|
||||
return Promise.all(reactions.map(r => relay.findNote(findReply(r))))
|
||||
let notes
|
||||
|
||||
onDestroy(relay.scroller(filter, delta, async chunk => {
|
||||
notes = relay.lq(async () => {
|
||||
const notes = await Promise.all(chunk.map(r => relay.findNote(findReply(r))))
|
||||
|
||||
return uniqBy(prop('id'), notes.filter(identity))
|
||||
})
|
||||
}))
|
||||
</script>
|
||||
|
||||
{#if $notes}
|
||||
<ul class="py-4 flex flex-col gap-2 max-w-xl m-auto">
|
||||
{#each $notes as n (n.id)}
|
||||
<li><Note interactive noReply note={n} depth={1} /></li>
|
||||
<li><Note note={n} depth={1} /></li>
|
||||
{:else}
|
||||
<li class="p-20 text-center" in:fly={{y: 20}}>No notes found.</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
const values = {
|
||||
// Scale up to integers for each choice we have
|
||||
muffle: switcher(Math.round(getMuffleValue($modal.user) * 3), muffleOptions),
|
||||
muffle: switcher(Math.round(getMuffleValue($modal.person) * 3), muffleOptions),
|
||||
}
|
||||
|
||||
const save = async e => {
|
||||
@ -21,8 +21,8 @@
|
||||
// Scale back down to a decimal based on string value
|
||||
const muffleValue = muffleOptions.indexOf(values.muffle) / 3
|
||||
const muffle = $user.muffle
|
||||
.filter(x => x[1] !== $modal.user.pubkey)
|
||||
.concat([t("p", $modal.user.pubkey, muffleValue.toString())])
|
||||
.filter(x => x[1] !== $modal.person.pubkey)
|
||||
.concat([t("p", $modal.person.pubkey, muffleValue.toString())])
|
||||
.filter(x => last(x) !== "1")
|
||||
|
||||
dispatch('user/muffle', muffle)
|
||||
|
@ -27,7 +27,7 @@
|
||||
</script>
|
||||
|
||||
<ul class="py-8 flex flex-col gap-2 max-w-xl m-auto">
|
||||
{#each results as e (e.id)}
|
||||
{#each results.slice(0, 50) as e (e.id)}
|
||||
<li in:fly={{y: 20}}>
|
||||
<Note interactive note={e} />
|
||||
</li>
|
||||
|
@ -6,37 +6,29 @@
|
||||
|
||||
export let q
|
||||
|
||||
let results = []
|
||||
let search
|
||||
|
||||
const search = relay.lq(async () => {
|
||||
return fuzzy(await relay.db.people.toArray(), {keys: ["name", "about", "pubkey"]})
|
||||
})
|
||||
const people = relay.lq(() => relay.db.people.toArray())
|
||||
|
||||
$: {
|
||||
if ($search) {
|
||||
Promise.all(
|
||||
$search(q).map(n => relay.findNote(n.id))
|
||||
).then(notes => {
|
||||
results = notes
|
||||
})
|
||||
}
|
||||
}
|
||||
$: search = fuzzy($people || [], {keys: ["name", "about", "pubkey"]})
|
||||
</script>
|
||||
|
||||
{#if search}
|
||||
<ul class="py-8 flex flex-col gap-2 max-w-xl m-auto">
|
||||
{#each results as e (e.pubkey)}
|
||||
{#if e.pubkey !== $user.pubkey}
|
||||
{#each search(q) as p (p.pubkey)}
|
||||
{#if p.pubkey !== $user.pubkey}
|
||||
<li in:fly={{y: 20}}>
|
||||
<a href="/people/{e.pubkey}/notes" class="flex gap-4 my-4">
|
||||
<a href="/people/{p.pubkey}/notes" class="flex gap-4 my-4">
|
||||
<div
|
||||
class="overflow-hidden w-12 h-12 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
||||
style="background-image: url({e.picture})" />
|
||||
style="background-image: url({p.picture})" />
|
||||
<div class="flex-grow">
|
||||
<h1 class="text-2xl">{e.name || e.pubkey.slice(0, 8)}</h1>
|
||||
<p>{e.about || ''}</p>
|
||||
<h1 class="text-2xl">{p.name || p.pubkey.slice(0, 8)}</h1>
|
||||
<p>{p.about || ''}</p>
|
||||
</div>
|
||||
</a>
|
||||
<li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
|
Loading…
Reference in New Issue
Block a user