Prompt user to select a relay on login

This commit is contained in:
Jonathan Staab 2023-02-17 17:57:15 -06:00
parent d4aaf98985
commit 9dc5c01241
8 changed files with 138 additions and 66 deletions

View File

@ -13,7 +13,10 @@
- [x] Make chat header overlap main header to save space
- [x] Strip formatting when pasting into Compose
- [x] Upgraded nostr-tools to 1.4.1
)
- [x] Improve anonymous and new user experience by prompting for relays and follows
- [x] Fixed kind0 merging to avoid dropping properties coracle doesn't support
- [x] Implement NIP-65 properly
## 0.2.11
- [x] Converted threshold to percentage

View File

@ -1,9 +1,7 @@
# Current
- [ ] Fix profile merging, put kind0 on its own property so we're not messing other people's profile data up.
- [ ] Fix anon/new user experience
- [ ] When logging in rather than generating a new keypair, ask for a relay to bootstrap from
- [ ] Preload/wait for our big list of relays so we can offer suggestions. Search in the background and let them know if we found their profile.
- [ ] Clicking stuff that would publish kicks you to the login page, we should open a modal instead.
- [ ] Test publishing events with zero relays
- [ ] Try lumping tables into a single key each to reduce load/save contention and time

View File

@ -18,7 +18,7 @@
import keys from 'src/agent/keys'
import network from 'src/agent/network'
import pool from 'src/agent/pool'
import {getUserRelays} from 'src/agent/relays'
import {getUserRelays, initializeRelayList} from 'src/agent/relays'
import sync from 'src/agent/sync'
import user from 'src/agent/user'
import {loadAppData} from "src/app"
@ -41,6 +41,7 @@
import PersonShare from "src/views/PersonShare.svelte"
import PrivKeyLogin from "src/views/PrivKeyLogin.svelte"
import PubKeyLogin from "src/views/PubKeyLogin.svelte"
import ConnectUser from "src/views/ConnectUser.svelte"
import SignUp from "src/views/SignUp.svelte"
import AddRelay from "src/routes/AddRelay.svelte"
import Alerts from "src/routes/Alerts.svelte"
@ -110,6 +111,8 @@
})
database.onReady(() => {
initializeRelayList()
if (user.getProfile()) {
loadAppData(user.getPubkey())
}
@ -126,7 +129,7 @@
// few so we're not sending too many concurrent http requests
const staleRelays = shuffle(
await database.relays.all({
'refreshed_at:lt': now() - timedelta(7, 'days'),
'refreshed_at:lt': now() - timedelta(7, 'days'),
})
).slice(0, 10)
@ -237,6 +240,8 @@
<PrivKeyLogin />
{:else if $modal.type === 'login/pubkey'}
<PubKeyLogin />
{:else if $modal.type === 'login/connect'}
<ConnectUser />
{:else if $modal.type === 'person/settings'}
<PersonSettings />
{:else if $modal.type === 'person/share'}

View File

@ -233,23 +233,7 @@ const people = new Table('people', 'pubkey')
const rooms = new Table('rooms', 'id')
const messages = new Table('messages', 'id')
const alerts = new Table('alerts', 'id')
const relays = new Table('relays', 'url', {
initialize: async table => {
const data = await table.dump()
const defaults = createMap('url', [
{url: 'wss://brb.io'},
{url: 'wss://nostr.zebedee.cloud'},
{url: 'wss://nostr-pub.wellorder.net'},
{url: 'wss://relay.nostr.band'},
{url: 'wss://nostr.pleb.network'},
{url: 'wss://relay.nostrich.de'},
{url: 'wss://relay.damus.io'},
])
return Object.assign(data, defaults)
},
})
const relays = new Table('relays', 'url')
const routes = new Table('routes', 'id', {
initialize: async table => {

View File

@ -1,7 +1,8 @@
import type {Relay} from 'src/util/types'
import {pick, map, assoc, sortBy, uniqBy, prop} from 'ramda'
import {first} from 'hurdak/lib/hurdak'
import {Tags, findReplyId} from 'src/util/nostr'
import {warn} from 'src/util/logger'
import {pick, objOf, map, assoc, sortBy, uniqBy, prop} from 'ramda'
import {first, createMap} from 'hurdak/lib/hurdak'
import {Tags, isRelay, findReplyId} from 'src/util/nostr'
import database from 'src/agent/database'
import user from 'src/agent/user'
@ -17,6 +18,33 @@ import user from 'src/agent/user'
// doesn't need to see.
// 5) Advertise relays — write and read back your own relay list
// Initialize our database
export const initializeRelayList = async () => {
// Throw some hardcoded defaults in there
await database.relays.bulkPatch(
createMap('url', [
{url: 'wss://brb.io'},
{url: 'wss://nostr.zebedee.cloud'},
{url: 'wss://nostr-pub.wellorder.net'},
{url: 'wss://relay.nostr.band'},
{url: 'wss://nostr.pleb.network'},
{url: 'wss://relay.nostrich.de'},
{url: 'wss://relay.damus.io'},
])
)
// Load relays from nostr.watch via dufflepud
try {
const url = import.meta.env.VITE_DUFFLEPUD_URL + '/relay'
const relays = prop('relays', await fetch(url).then(r => r.json())).filter(isRelay)
await database.relays.bulkPatch(createMap('url', map(objOf('url'), relays)))
} catch (e) {
warn("Failed to fetch relays list", e)
}
}
// Pubkey relays
export const getPubkeyRelays = (pubkey, mode = null) => {

View File

@ -1,19 +1,16 @@
import type {DisplayEvent} from 'src/util/types'
import {objOf, omit, sortBy, identity} from 'ramda'
import {get} from 'svelte/store'
import {navigate} from 'svelte-routing'
import {omit, sortBy, identity} from 'ramda'
import {createMap, ellipsize} from 'hurdak/lib/hurdak'
import {renderContent} from 'src/util/html'
import {shuffle} from 'src/util/misc'
import {Tags, displayPerson, findReplyId} from 'src/util/nostr'
import {getNetwork} from 'src/agent/social'
import {getUserFollows} from 'src/agent/social'
import {getUserReadRelays} from 'src/agent/relays'
import database from 'src/agent/database'
import network from 'src/agent/network'
import keys from 'src/agent/keys'
import alerts from 'src/app/alerts'
import messages from 'src/app/messages'
import {routes, settings, modal} from 'src/app/ui'
import {routes, modal} from 'src/app/ui'
export const loadAppData = async pubkey => {
if (getUserReadRelays().length > 0) {
@ -21,7 +18,7 @@ export const loadAppData = async pubkey => {
alerts.load(pubkey),
alerts.listen(pubkey),
messages.listen(pubkey),
network.loadPeople(getNetwork(pubkey)),
network.loadPeople(getUserFollows()),
])
}
}
@ -33,40 +30,7 @@ export const login = async ({privkey, pubkey}: {privkey?: string, pubkey?: strin
keys.setPublicKey(pubkey)
}
modal.set({
type: 'message',
message: "Loading your profile data...",
spinner: true,
noEscape: true,
})
// Get a reasonably sized sample of relays and ask them all for relay information
// for our user so we can bootstrap. This could be improved.
let relays = []
try {
relays = (
await fetch(get(settings).dufflepudUrl + '/relay').then(r => r.json())
).relays.map(objOf('url'))
} catch (e) {
relays = database.relays.all()
}
// Load our user so we can populate network and show profile info
await network.loadPeople([pubkey], {
relays: shuffle(relays).slice(0, 50),
})
if (getUserReadRelays().length === 0) {
navigate('/relays')
} else {
// Load network and start listening, but don't wait for it
loadAppData(pubkey)
// Not ideal, but the network tab depends on the user's social network being
// loaded, so put them on global when they first log in so we're not slowing
// down users' first run experience too much
navigate('/notes/network')
}
modal.set({type: 'login/connect', noEscape: true})
}
export const renderNote = (note, {showEntire = false}) => {

View File

@ -0,0 +1,89 @@
<script lang="ts">
import {pluck, reject, last} from 'ramda'
import {navigate} from 'svelte-routing'
import {displayList} from 'hurdak/lib/hurdak'
import {sleep} from 'src/util/misc'
import Content from 'src/partials/Content.svelte'
import Spinner from 'src/partials/Spinner.svelte'
import Heading from 'src/partials/Heading.svelte'
import Anchor from 'src/partials/Anchor.svelte'
import Input from 'src/partials/Input.svelte'
import Modal from 'src/partials/Modal.svelte'
import {getUserReadRelays} from 'src/agent/relays'
import database from 'src/agent/database'
import network from 'src/agent/network'
import user from 'src/agent/user'
import {loadAppData} from 'src/app'
let url = ''
let message = null
let currentRelays = []
let attemptedRelays = new Set()
let knownRelays = database.watch('relays', table => table.all())
const searchSample = async () => {
currentRelays = reject(r => attemptedRelays.has(r.url), $knownRelays).slice(0, 10)
currentRelays.forEach(({url}) => attemptedRelays.add(url))
if (currentRelays.length === 0) {
message = `
We weren't able to find your profile data, you'll need to select your
relays manually to continue.`
await sleep(3000)
navigate('/relays')
} else {
await network.loadPeople([user.getPubkey()], {relays: currentRelays})
console.log(user.getProfile(), getUserReadRelays())
if (getUserReadRelays().length > 0) {
message = `Success! Just a moment while we get things set up.`
await Promise.all([
loadAppData(user.getPubkey()),
sleep(3000),
])
navigate('/notes/network')
} else {
await sleep(1000)
searchSample()
}
}
}
const skip = () => {
navigate('/notes/network')
}
searchSample()
</script>
<Content size="lg" class="text-center">
<Heading>Connect to Nostr</Heading>
<p class="text-left">
We're searching for your profile on the network. If you'd like to select your
relays manually instead, click <Anchor on:click={skip}>here</Anchor>.
</p>
{#if currentRelays.length > 0}
<p class="text-left">
Currently searching:
</p>
<ul class="text-left list-disc ml-6">
{#each currentRelays as relay}
<li>{last(relay.url.split('://'))}</li>
{/each}
</ul>
{/if}
</Content>
{#if message}
<Modal nested>
<Content size="lg" class="text-center">
{message}
<Spinner delay={0} />
</Content>
</Modal>
{/if}

View File

@ -24,6 +24,7 @@
})
// Prime our database, in case we don't have any people stored yet
console.log(getUserReadRelays())
network.listenUntilEose(getUserReadRelays(), {kinds: personKinds, limit: 300})
</script>