From aaaef18cae0fe0a7fe2298741c7c05273ab5593d Mon Sep 17 00:00:00 2001 From: Jonathan Staab Date: Mon, 16 Jan 2023 10:34:18 -0800 Subject: [PATCH] Re-work login page --- README.md | 29 +++-- src/App.svelte | 27 +++-- src/agent/data.js | 4 +- src/agent/defaults.js | 2 - src/agent/index.js | 4 +- src/agent/keys.js | 2 +- src/agent/pool.js | 16 +-- src/app/cmd.js | 2 + src/app/index.js | 12 +- src/app/loaders.js | 18 ++- src/partials/Anchor.svelte | 1 + src/routes/AddRelay.svelte | 16 ++- src/routes/Keys.svelte | 22 ++-- src/routes/Login.svelte | 107 ++++-------------- src/routes/Logout.svelte | 6 +- src/routes/NotFound.svelte | 2 +- src/routes/Notes.svelte | 6 +- src/routes/Person.svelte | 30 +++-- src/routes/RelayList.svelte | 9 +- src/routes/Settings.svelte | 2 - src/util/nostr.js | 12 +- src/views/PrivKeyLogin.svelte | 40 +++++++ src/views/PubKeyLogin.svelte | 36 ++++++ src/views/SignUp.svelte | 42 +++++++ .../notes/{Global.svelte => Latest.svelte} | 4 +- src/views/notes/Network.svelte | 9 +- 26 files changed, 285 insertions(+), 175 deletions(-) create mode 100644 src/views/PrivKeyLogin.svelte create mode 100644 src/views/PubKeyLogin.svelte create mode 100644 src/views/SignUp.svelte rename src/views/notes/{Global.svelte => Latest.svelte} (88%) diff --git a/README.md b/README.md index e0a2c4c1..04bd292f 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ If you like Coracle and want to support its development, you can donate sats via - [ ] Persist and load relay list - [ ] Add followers/follows lists on profile page - [ ] Image uploads + - Use dufflepud. Default will charge via lightning and have a tos, others can self-host and skip that. - [ ] Server discovery and relay publishing https://github.com/nostr-protocol/nips/pull/32/files - [ ] Support invoices https://twitter.com/jb55/status/1604131336247476224 - [ ] NIP 05 @@ -42,6 +43,9 @@ If you like Coracle and want to support its development, you can donate sats via - [ ] Add settings storage on nostr, maybe use kind 0? - [ ] Stack views so scroll position isn't lost on navigation - [ ] Suggest relays based on network +- [ ] Attachments (a tag w/content type and url) +- [ ] Add Labs tab with cards for non-standard features + - Time travel - see events as of a date/time # Changelog @@ -56,26 +60,37 @@ If you like Coracle and want to support its development, you can donate sats via - [x] Support some read/write config - [x] Get real home relays for defaults.petnames - [x] Add notification for slow relays - - [ ] Fix publishing - - [ ] Relay list isn't getting refreshed since we're using getRelay everywhere + - [x] Fix publishing + - [x] Relay list isn't getting refreshed since we're using getRelay everywhere - [x] Warn that everything will be cleared on logout - [x] Connection management - [x] Do I need to implement re-connecting now? - [x] Handle failed connections - [x] Close connections that haven't been used in a while + - [x] Add strategy that callers can opt into to accept first eose from a relay that has any events - [ ] Login - - [ ] Prefer extension, make private key entry "advanced" - - [ ] Improve login UX for bootstrap delay. Nostr facts? + - [x] Prefer extension, make private key entry "advanced" + - [x] Buttons should redirect to login modal if using pubkey login - [ ] We often get the root as the reply, figure out why that is, compared to astral/damus -- [ ] Load feeds from network rather than user relays? - - Still use "my" relays for global, this could make global feed more useful +- [x] Load feeds from network rather than user relays? + - [x] Still use "my" relays for global, this could make global feed more useful + - [x] If we use my relays for global, we don't have to wait for network to load initially + - [ ] Figure out fast vs complete tradeoff. Skipping loadContext speeds things up a ton - [ ] Figure out migrations from previous version - [ ] Add relays/mentions to note and reply composition - [ ] Add layout component with max-w, padding, etc. Test on mobile size +- [ ] Add tips to login spinner +- [ ] Add banner ## 0.2.7 -- [x] +- [x] Add support for profile banner images +- [x] Re-designed relays page + - [x] Support connection status/speed indication + - [x] Add toggle to enable writing to a connected relay +- [x] Re-designed login page + - [x] Use private key login only if extension is not enabled + - [x] Add pubkey login ## 0.2.6 diff --git a/src/App.svelte b/src/App.svelte index a3f6bbf7..b74696ea 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -12,13 +12,16 @@ import {hasParent} from 'src/util/html' import {displayPerson, isLike} from 'src/util/nostr' import {timedelta, now} from 'src/util/misc' - import {user, pool, getRelays} from 'src/agent' + import {keys, user, pool, getRelays} from 'src/agent' import {modal, toast, settings, alerts} from "src/app" import {routes} from "src/app/ui" import Anchor from 'src/partials/Anchor.svelte' import Spinner from 'src/partials/Spinner.svelte' import Modal from 'src/partials/Modal.svelte' - import NoteDetailModal from "src/views/NoteDetail.svelte" + import SignUp from "src/views/SignUp.svelte" + import PrivKeyLogin from "src/views/PrivKeyLogin.svelte" + import PubKeyLogin from "src/views/PubKeyLogin.svelte" + import NoteDetail from "src/views/NoteDetail.svelte" import PersonSettings from "src/views/PersonSettings.svelte" import NotFound from "src/routes/NotFound.svelte" import Search from "src/routes/Search.svelte" @@ -48,11 +51,13 @@ menuIsOpen.set(false) } + const {privkey} = keys + const {lastCheckedAlerts, mostRecentAlert} = alerts + let menuIcon let scrollY let suspendedSubs = [] let slowConnections = [] - let {lastCheckedAlerts, mostRecentAlert} = alerts onMount(() => { if ($user) { @@ -166,10 +171,11 @@ {/if}
  • - + Notes
  • + {#if $user}
  • @@ -179,7 +185,6 @@ {/if}
  • - {#if $user}
  • Keys @@ -220,7 +225,7 @@ {/if} - {#if $user} + {#if $privkey}
    {#if $modal.note} {#key $modal.note.id} - + {/key} {:else if $modal.form === 'relay'} + {:else if $modal.form === 'signUp'} + + {:else if $modal.form === 'privkeyLogin'} + + {:else if $modal.form === 'pubkeyLogin'} + {:else if $modal.form === 'person/settings'} {:else if $modal.message} -

    {$modal.message}

    +

    {$modal.message}

    {/if} diff --git a/src/agent/data.js b/src/agent/data.js index 28e5368f..aac970a9 100644 --- a/src/agent/data.js +++ b/src/agent/data.js @@ -58,7 +58,7 @@ export const processEvents = async events => { ...switcherFn(e.kind, { 0: () => JSON.parse(e.content), 2: () => { - if (e.created_at > person.updated_at) { + if (e.created_at > (person.relays_updated_at || 0)) { return { relays: ($people[e.pubkey]?.relays || []).concat({url: e.content}), relays_updated_at: e.created_at, @@ -68,7 +68,7 @@ export const processEvents = async events => { 3: () => ({petnames: e.tags}), 12165: () => ({muffle: e.tags}), 10001: () => { - if (e.created_at > person.updated_at) { + if (e.created_at > (person.relays_updated_at || 0)) { return { relays: e.tags.map(([url, read, write]) => ({url, read, write})), relays_updated_at: e.created_at, diff --git a/src/agent/defaults.js b/src/agent/defaults.js index 0b5ea3e8..8070c090 100644 --- a/src/agent/defaults.js +++ b/src/agent/defaults.js @@ -9,9 +9,7 @@ export default { ], relays: [ {url: 'wss://brb.io'}, - {url: 'wss://relay.damus.io'}, {url: 'wss://nostr.zebedee.cloud'}, - {url: 'wss://nostr-relay.wlvs.space'}, {url: 'wss://nostr-pub.wellorder.net'}, ], } diff --git a/src/agent/index.js b/src/agent/index.js index bca89cb5..f03e9d55 100644 --- a/src/agent/index.js +++ b/src/agent/index.js @@ -70,8 +70,8 @@ export const publish = async (relays, event) => { return signedEvent } -export const load = async (relays, filter) => { - const events = await pool.request(relays, filter) +export const load = async (relays, filter, opts) => { + const events = await pool.request(relays, filter, opts) await processEvents(events) diff --git a/src/agent/keys.js b/src/agent/keys.js index cf74e16d..da81383b 100644 --- a/src/agent/keys.js +++ b/src/agent/keys.js @@ -41,4 +41,4 @@ const clear = () => { // Init signing function by re-setting pubkey setPublicKey(get(pubkey)) -export default {pubkey, setPrivateKey, setPublicKey, sign, clear} +export default {pubkey, privkey, setPrivateKey, setPublicKey, sign, clear} diff --git a/src/agent/pool.js b/src/agent/pool.js index e4df82e7..2a600f5c 100644 --- a/src/agent/pool.js +++ b/src/agent/pool.js @@ -24,10 +24,6 @@ class Connection { init(url) { const nostr = relayInit(url) - nostr.on('error', () => { - console.log(`failed to connect to ${url}`) - }) - nostr.on('disconnect', () => { connections = reject(whereEq({url}), connections) }) @@ -50,7 +46,6 @@ class Connection { await this.nostr.connect() this.status = 'ready' } catch (e) { - console.error(`Failed to connect to ${this.url}: ${e}`) this.status = 'error' } } @@ -82,7 +77,7 @@ const connect = async url => { const publish = async (relays, event) => { return Promise.all( - relays.filter(r => r.write !== '!' & isRelay(r.url)).map(async relay => { + relays.filter(r => r.write !== '!' && isRelay(r.url)).map(async relay => { const conn = await connect(relay.url) if (conn) { @@ -172,7 +167,7 @@ const subscribe = async (relays, filters) => { } } -const request = (relays, filters) => { +const request = (relays, filters, {mode = "most"} = {}) => { relays = uniqBy(prop('url'), relays.filter(r => isRelay(r.url))) return new Promise(async resolve => { @@ -188,7 +183,12 @@ const request = (relays, filters) => { eose.length === agg.subs.length || Date.now() - now >= 5000 || ( - Date.now() - now >= 1000 + mode === "fast" + && events.length + ) + || ( + mode === "most" + && Date.now() - now >= 1000 && eose.length > agg.subs.length - Math.round(agg.subs.length / 10) ) ) diff --git a/src/app/cmd.js b/src/app/cmd.js index 39d5a718..d63554fc 100644 --- a/src/app/cmd.js +++ b/src/app/cmd.js @@ -86,6 +86,8 @@ const publishEvent = (relays, kind, {content = '', tags = []} = {}) => { const createdAt = Math.round(new Date().valueOf() / 1000) const event = {kind, content, tags, pubkey, created_at: createdAt} + console.log(`publishing ${JSON.stringify(event)} to ${JSON.stringify(relays)}`) + return publish(relays, event) } diff --git a/src/app/index.js b/src/app/index.js index ae5323c5..a71d7c0d 100644 --- a/src/app/index.js +++ b/src/app/index.js @@ -1,4 +1,5 @@ import {whereEq, sortBy, identity, when, assoc, reject} from 'ramda' +import {navigate} from 'svelte-routing' import {createMap, ellipsize} from 'hurdak/lib/hurdak' import {get} from 'svelte/store' import {renderContent} from 'src/util/html' @@ -19,11 +20,12 @@ export const login = async ({privkey, pubkey}) => { keys.setPublicKey(pubkey) } - await Promise.all([ - loaders.loadNetwork(getRelays(), pubkey), - alerts.load(getRelays(), pubkey), - alerts.listen(getRelays(), pubkey), - ]) + // Load network and start listening, but don't wait for it + loaders.loadNetwork(getRelays(), pubkey), + alerts.load(getRelays(), pubkey), + alerts.listen(getRelays(), pubkey), + + navigate('/notes/latest') } export const addRelay = async relay => { diff --git a/src/app/loaders.js b/src/app/loaders.js index e0d34c4d..12d23099 100644 --- a/src/app/loaders.js +++ b/src/app/loaders.js @@ -1,4 +1,4 @@ -import {uniqBy, prop, uniq, flatten, pluck, groupBy, identity} from 'ramda' +import {uniqBy, prop, uniq, flatten, pluck, identity} from 'ramda' import {ensurePlural, createMap, chunk} from 'hurdak/lib/hurdak' import {findReply, personKinds, Tags, getTagValues} from 'src/util/nostr' import {now, timedelta} from 'src/util/misc' @@ -38,15 +38,13 @@ const loadNetwork = async (relays, pubkey) => { petnames = defaults.petnames } - // Get the user's follows, with a fallback if we have no pubkey, then use nip-2 recommended - // relays to load our user's second-order follows in order to bootstrap our social graph - await Promise.all( - Object.entries(groupBy(t => t[2], petnames)) - .map(([relay, petnames]) => loadPeople([relay], getTagValues(petnames))) - ) + const tags = Tags.wrap(petnames) + + // Use nip-2 recommended relays to load our user's second-order follows + await loadPeople(tags.relays(), tags.values().all(), {mode: 'fast'}) } -const loadContext = async (relays, notes, {loadParents = true} = {}) => { +const loadContext = async (relays, notes, {loadParents = true, ...opts} = {}) => { notes = ensurePlural(notes) if (notes.length === 0) { @@ -68,7 +66,7 @@ const loadContext = async (relays, notes, {loadParents = true} = {}) => { filter.push({kinds: [1], ids: getTagValues(parentTags)}) } - const events = await load(combinedRelays, filter) + const events = await load(combinedRelays, filter, opts) if (parentTags.length === 0) { return events @@ -82,7 +80,7 @@ const loadContext = async (relays, notes, {loadParents = true} = {}) => { return uniqBy( prop('id'), events.concat( - await loadContext(parentRelays, parents, {loadParents: false}) + await loadContext(parentRelays, parents, {loadParents: false, ...opts}) ) ) }) diff --git a/src/partials/Anchor.svelte b/src/partials/Anchor.svelte index 5630bc14..d8eb630c 100644 --- a/src/partials/Anchor.svelte +++ b/src/partials/Anchor.svelte @@ -16,6 +16,7 @@ switcher(type, { anchor: "underline", button: "py-2 px-4 rounded bg-white text-accent", + 'button-accent': "py-2 px-4 rounded bg-accent text-white", }), ) diff --git a/src/routes/AddRelay.svelte b/src/routes/AddRelay.svelte index 0f4223a5..157b710f 100644 --- a/src/routes/AddRelay.svelte +++ b/src/routes/AddRelay.svelte @@ -10,11 +10,21 @@ e.preventDefault() url = url.trim() - if (!url.match(/^wss?:\/\/[\w.:-]+$/)) { - return toast.show("error", 'That isn\'t a valid websocket url - relay urls should start with "wss://"') + if (!url.includes('://')) { + url = 'wss://' + url } - addRelay(url) + try { + new URL(url) + } catch (e) { + return toast.show("error", "That isn't a valid url") + } + + if (!url.match('^wss?://')) { + return toast.show("error", "That isn't a valid websocket url") + } + + addRelay({url, write: '!'}) modal.set(null) } diff --git a/src/routes/Keys.svelte b/src/routes/Keys.svelte index 737067bd..8da5cf88 100644 --- a/src/routes/Keys.svelte +++ b/src/routes/Keys.svelte @@ -6,24 +6,25 @@ import {copyToClipboard} from "src/util/html" import Input from "src/partials/Input.svelte" import Anchor from "src/partials/Anchor.svelte" - import {user} from "src/agent" + import {keys} from "src/agent" import {toast} from "src/app" + const {pubkey, privkey} = keys + const nip07 = "https://github.com/nostr-protocol/nips/blob/master/07.md" 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' const copyKey = type => { copyToClipboard( type === 'private' - ? nip19.nsecEncode($user.privkey) - : nip19.npubEncode($user.pubkey) + ? nip19.nsecEncode($privkey) + : nip19.npubEncode($pubkey) ) toast.show("info", `Your ${type} key has been copied to the clipboard.`) } onMount(async () => { - if (!$user) { + if (!$pubkey) { return navigate("/login") } }) @@ -42,7 +43,7 @@
    Public Key - + copyKey('public')} />

    @@ -50,17 +51,18 @@ trying to find you on nostr.

    - {#if $user?.privkey} + {#if $privkey}
    Private Key - + copyKey('private')} />

    Your private key is used to prove your identity by cryptographically signing messages. Do not share this with anyone. Be careful about - copying this into other apps - instead, consider - using delegation keys instead. + copying this into other apps - instead, consider using + a compatible browser extension to securely + store your key.

    {/if} diff --git a/src/routes/Login.svelte b/src/routes/Login.svelte index ffae5190..9b0ce935 100644 --- a/src/routes/Login.svelte +++ b/src/routes/Login.svelte @@ -1,103 +1,46 @@ -
    -
    +
    +

    Welcome!

    To the Nostr Protocol
    -
    -

    - To log in to existing account, simply enter your private key below. To create a new account, - just let us generate one for you. -

    -

    - You can also use a compatible browser extension to - sign events without having to paste your private key here (recommended). -

    -
    - Private Key - - - -
    - {#if hasExtension} -
    - Get from extension -
    - {/if} -
    - Generate new key -
    -
    - - Your private key is a string of random letters and numbers that allow you to prove - you own your account. Write it down and keep it secret! - +

    + To log in to existing account, simply click below. If you have + a compatible browser extension installed, + we will use that. +

    +
    +
    + Log In + Sign Up
    + + Advanced Login +
    - - Log In -
    diff --git a/src/routes/Logout.svelte b/src/routes/Logout.svelte index ec48a80b..dba73f68 100644 --- a/src/routes/Logout.svelte +++ b/src/routes/Logout.svelte @@ -19,11 +19,11 @@ } -
    +
    {#if confirmed} -
    Clearing your local database...
    +
    Clearing your local database...
    {:else} -
    +
    Are you sure you want to log out? All data will be cleared.
    Log out
    diff --git a/src/routes/NotFound.svelte b/src/routes/NotFound.svelte index 4b91ef42..6ed0f2d9 100644 --- a/src/routes/NotFound.svelte +++ b/src/routes/NotFound.svelte @@ -2,5 +2,5 @@ import {onMount} from 'svelte' import {navigate} from 'svelte-routing' - onMount(() => navigate('/notes/network')) + onMount(() => navigate('/notes/latest')) diff --git a/src/routes/Notes.svelte b/src/routes/Notes.svelte index 4f6e30dc..49276a06 100644 --- a/src/routes/Notes.svelte +++ b/src/routes/Notes.svelte @@ -3,7 +3,7 @@ import Anchor from "src/partials/Anchor.svelte" import Tabs from "src/partials/Tabs.svelte" import Network from "src/views/notes/Network.svelte" - import Global from "src/views/notes/Global.svelte" + import Latest from "src/views/notes/Latest.svelte" import {user} from 'src/agent' export let activeTab @@ -19,10 +19,10 @@
    {/if} - + {#if activeTab === 'network'} {:else} - + {/if} diff --git a/src/routes/Person.svelte b/src/routes/Person.svelte index d22d1ba9..f3343a15 100644 --- a/src/routes/Person.svelte +++ b/src/routes/Person.svelte @@ -9,10 +9,11 @@ import {displayPerson} from 'src/util/nostr' import Tabs from "src/partials/Tabs.svelte" import Button from "src/partials/Button.svelte" + import Spinner from "src/partials/Spinner.svelte" import Notes from "src/views/person/Notes.svelte" import Likes from "src/views/person/Likes.svelte" import Network from "src/views/person/Network.svelte" - import {getPerson, getRelays, listen, user} from "src/agent" + import {getPerson, getRelays, listen, user, keys} from "src/agent" import {modal} from "src/app" import loaders from "src/app/loaders" import {routes} from "src/app/ui" @@ -22,17 +23,23 @@ export let activeTab export let relays = null + const {privkey} = keys + let subs = [] let pubkey = nip19.decode(npub).data - let following = find(t => t[1] === pubkey, $user?.petnames || []) + let following = false let followers = new Set() let followersCount = 0 let person = getPerson(pubkey, true) + let loading = true + + $: following = find(t => t[1] === pubkey, $user?.petnames || []) onMount(async () => { // Refresh our person if needed loaders.loadPeople(relays || getRelays(pubkey), [pubkey]).then(() => { person = getPerson(pubkey, true) + loading = false }) // Get our followers count @@ -56,18 +63,14 @@ const setActiveTab = tab => navigate(routes.person(pubkey, tab)) const follow = async () => { - following = true - const relay = first(relays || getRelays(pubkey)) const tag = ["p", pubkey, relay.url, person.name || ""] - const petnames = reject(t => t[1] === pubkey, $user.petnames).concat(tag) + const petnames = reject(t => t[1] === pubkey, $user.petnames).concat([tag]) cmd.setPetnames(getRelays(), petnames) } const unfollow = async () => { - following = false - const petnames = reject(t => t[1] === pubkey, $user.petnames) cmd.setPetnames(getRelays(), petnames) @@ -78,6 +81,13 @@ } +
    @@ -94,11 +104,11 @@

    {@html renderContent(person.about || '')}

    - {#if $user?.pubkey === pubkey} + {#if $user?.pubkey === pubkey && $privkey} Edit - {:else if $user?.petnames} + {:else if $user?.petnames && $privkey}
    {#if following} @@ -126,6 +136,8 @@ {:else if activeTab === 'network'} {#if person?.petnames} +{:else if loading} + {:else}
    Unable to show network for this person. diff --git a/src/routes/RelayList.svelte b/src/routes/RelayList.svelte index dbf6cdbb..3258667f 100644 --- a/src/routes/RelayList.svelte +++ b/src/routes/RelayList.svelte @@ -1,6 +1,6 @@ diff --git a/src/util/nostr.js b/src/util/nostr.js index 7368bb94..020cecb2 100644 --- a/src/util/nostr.js +++ b/src/util/nostr.js @@ -27,19 +27,13 @@ export class Tags { return uniq(flatten(this.tags).filter(isRelay)).map(objOf('url')) } values() { - this.tags = this.tags.map(t => t[1]) - - return this + return new Tags(this.tags.map(t => t[1])) } type(type) { - this.tags = this.tags.filter(t => t[0] === type) - - return this + return new Tags(this.tags.filter(t => t[0] === type)) } mark(mark) { - this.tags = this.tags.filter(t => last(t) === mark) - - return this + return new Tags(this.tags.filter(t => last(t) === mark)) } } diff --git a/src/views/PrivKeyLogin.svelte b/src/views/PrivKeyLogin.svelte new file mode 100644 index 00000000..a003cabd --- /dev/null +++ b/src/views/PrivKeyLogin.svelte @@ -0,0 +1,40 @@ + + + +
    +

    Login with your Private Key

    +

    + To give Coracle full access to your nostr identity, enter your private key below. +

    +
    +
    + + + +
    + Log In +
    +

    + Note that sharing your private key directly is not recommended, instead you should use + a compatible browser extension to securely store your key. +

    +
    + diff --git a/src/views/PubKeyLogin.svelte b/src/views/PubKeyLogin.svelte new file mode 100644 index 00000000..71e57b9c --- /dev/null +++ b/src/views/PubKeyLogin.svelte @@ -0,0 +1,36 @@ + + + +
    +

    Login with your Public Key

    +

    + For read-only access, enter your public key (or someone else's) below. Your + key should start with "npub". +

    +
    +
    + + + +
    + Log In +
    +
    + diff --git a/src/views/SignUp.svelte b/src/views/SignUp.svelte new file mode 100644 index 00000000..7d4181b9 --- /dev/null +++ b/src/views/SignUp.svelte @@ -0,0 +1,42 @@ + + + +
    +

    Create an Account

    +

    + Don't have a nostr account? We've created a brand new private key for you below. + Make sure to click to copy and store it somewhere safe - this is your account's password! +

    +
    +
    + + + + +
    + Log In +
    +

    + Note that sharing your private key directly is not recommended, instead you should use + a compatible browser extension to securely store your key. +

    +
    + diff --git a/src/views/notes/Global.svelte b/src/views/notes/Latest.svelte similarity index 88% rename from src/views/notes/Global.svelte rename to src/views/notes/Latest.svelte index d9ba6fa4..1cc5476f 100644 --- a/src/views/notes/Global.svelte +++ b/src/views/notes/Latest.svelte @@ -18,9 +18,11 @@ const loadNotes = async () => { const {limit, until} = cursor - const notes = await load(relays, {...filter, limit, until}) + const notes = await load(relays, {...filter, limit, until}, {mode: 'fast'}) const context = await loaders.loadContext(relays, notes) + cursor.onChunk(notes) + return threadify(notes, context, {muffle: getMuffle()}) } diff --git a/src/views/notes/Network.svelte b/src/views/notes/Network.svelte index fab3f9cd..30ceb9d4 100644 --- a/src/views/notes/Network.svelte +++ b/src/views/notes/Network.svelte @@ -1,4 +1,5 @@