Add profile and note to onboarding

This commit is contained in:
Jonathan Staab 2023-04-27 10:31:03 -05:00
parent dcdac88b75
commit 97d37eaca0
10 changed files with 130 additions and 43 deletions

View File

@ -1,5 +1,7 @@
# Current # Current
- [ ] Enable global view via env var
- [ ] Ask for name/image, first post on signup
- [ ] Image classification - [ ] Image classification
- https://github.com/bhky/opennsfw2 - https://github.com/bhky/opennsfw2

View File

@ -7,6 +7,8 @@ export const defaultFollows = (import.meta.env.VITE_DEFAULT_FOLLOWS || "")
.split(",") .split(",")
.filter(identity) .filter(identity)
console.log(defaultFollows)
export const getFollows = pubkey => export const getFollows = pubkey =>
Tags.wrap(getPersonWithFallback(pubkey).petnames).type("p").values().all() Tags.wrap(getPersonWithFallback(pubkey).petnames).type("p").values().all()

View File

@ -7,10 +7,11 @@
import {shuffle} from "src/util/misc" import {shuffle} from "src/util/misc"
import {displayPerson} from "src/util/nostr" import {displayPerson} from "src/util/nostr"
import OnboardingIntro from "src/app/views/OnboardingIntro.svelte" import OnboardingIntro from "src/app/views/OnboardingIntro.svelte"
import OnboardingProfile from "src/app/views/OnboardingProfile.svelte"
import OnboardingKey from "src/app/views/OnboardingKey.svelte" import OnboardingKey from "src/app/views/OnboardingKey.svelte"
import OnboardingRelays from "src/app/views/OnboardingRelays.svelte" import OnboardingRelays from "src/app/views/OnboardingRelays.svelte"
import OnboardingFollows from "src/app/views/OnboardingFollows.svelte" import OnboardingFollows from "src/app/views/OnboardingFollows.svelte"
import OnboardingComplete from "src/app/views/OnboardingComplete.svelte" import OnboardingNote from "src/app/views/OnboardingNote.svelte"
import {getFollows, defaultFollows} from "src/agent/social" import {getFollows, defaultFollows} from "src/agent/social"
import {getPubkeyWriteRelays, sampleRelays} from "src/agent/relays" import {getPubkeyWriteRelays, sampleRelays} from "src/agent/relays"
import {getPersonWithFallback} from "src/agent/db" import {getPersonWithFallback} from "src/agent/db"
@ -18,13 +19,14 @@
import user from "src/agent/user" import user from "src/agent/user"
import pool from "src/agent/pool" import pool from "src/agent/pool"
import keys from "src/agent/keys" import keys from "src/agent/keys"
import cmd from "src/agent/cmd"
import {loadAppData} from "src/app/state" import {loadAppData} from "src/app/state"
import {modal} from "src/partials/state" import {modal} from "src/partials/state"
export let stage export let stage
const privkey = generatePrivateKey() const privkey = generatePrivateKey()
const profile = {}
const {relays} = user const {relays} = user
if ($relays.length === 0) { if ($relays.length === 0) {
@ -36,19 +38,23 @@
) )
} }
const signup = async () => { const signup = async note => {
await keys.login("privkey", privkey) await keys.login("privkey", privkey)
// Re-save preferences now that we have a key // Re-save preferences now that we have a key
await user.updateRelays(() => user.getRelays()) await Promise.all([
await user.updatePetnames(() => user.updateRelays(() => user.getRelays()),
user.getPetnamePubkeys().map(pubkey => { cmd.updateUser(profile).publish(user.getRelays()),
const [{url}] = sampleRelays(getPubkeyWriteRelays(pubkey)) note && cmd.createNote(note.content, note.mentions, note.topics).publish(user.getRelays()),
const name = displayPerson(getPersonWithFallback(pubkey)) user.updatePetnames(() =>
user.getPetnamePubkeys().map(pubkey => {
const [{url}] = sampleRelays(getPubkeyWriteRelays(pubkey))
const name = displayPerson(getPersonWithFallback(pubkey))
return ["p", pubkey, url, name] return ["p", pubkey, url, name]
}) })
) ),
])
loadAppData(user.getPubkey()) loadAppData(user.getPubkey())
@ -73,14 +79,16 @@
<div in:fly={{y: 20}}> <div in:fly={{y: 20}}>
{#if stage === "intro"} {#if stage === "intro"}
<OnboardingIntro /> <OnboardingIntro />
{:else if stage === "profile"}
<OnboardingProfile {profile} />
{:else if stage === "key"} {:else if stage === "key"}
<OnboardingKey {privkey} /> <OnboardingKey {privkey} />
{:else if stage === "relays"} {:else if stage === "relays"}
<OnboardingRelays /> <OnboardingRelays />
{:else if stage === "follows"} {:else if stage === "follows"}
<OnboardingFollows /> <OnboardingFollows />
{:else} {:else if stage === "note"}
<OnboardingComplete {signup} /> <OnboardingNote {signup} />
{/if} {/if}
</div> </div>
{/key} {/key}

View File

@ -1,27 +0,0 @@
<script lang="ts">
import Anchor from "src/partials/Anchor.svelte"
import Heading from "src/partials/Heading.svelte"
import Content from "src/partials/Content.svelte"
import Spinner from "src/partials/Spinner.svelte"
export let signup
let loading = false
const startSignup = () => {
loading = true
signup()
}
</script>
<Content size="lg" class="text-center">
<Heading>Welcome to Nostr</Heading>
<p>
Youre all set! If have any questions, or need any help, just ask. Your fellow nostriches are
always happy to lend a hand.
</p>
<Anchor {loading} type="button-accent" on:click={startSignup}>Get on Nostr</Anchor>
{#if loading}
<Spinner />
{/if}
</Content>

View File

@ -39,7 +39,7 @@
</p> </p>
<Anchor <Anchor
type="button-accent" type="button-accent"
on:click={() => modal.replace({type: "onboarding", stage: "complete"})}> on:click={() => modal.replace({type: "onboarding", stage: "note"})}>
Continue Continue
</Anchor> </Anchor>
</Content> </Content>

View File

@ -19,7 +19,9 @@
When youre ready to dive in, click below and well guide you through the process of creating an When youre ready to dive in, click below and well guide you through the process of creating an
account. account.
</p> </p>
<Anchor type="button-accent" on:click={() => modal.replace({type: "onboarding", stage: "key"})}> <Anchor
type="button-accent"
on:click={() => modal.replace({type: "onboarding", stage: "profile"})}>
Let's go! Let's go!
</Anchor> </Anchor>
</Content> </Content>

View File

@ -33,7 +33,7 @@
<Anchor <Anchor
type="button-accent" type="button-accent"
on:click={() => modal.replace({type: "onboarding", stage: nextStage})}> on:click={() => modal.replace({type: "onboarding", stage: nextStage})}>
Log in Got it
</Anchor> </Anchor>
</div> </div>
<p>If you don't want to save your keys now, you can find them later in {appName}'s settings.</p> <p>If you don't want to save your keys now, you can find them later in {appName}'s settings.</p>

View File

@ -0,0 +1,36 @@
<script lang="ts">
import {onMount} from "svelte"
import Compose from "src/partials/Compose.svelte"
import Anchor from "src/partials/Anchor.svelte"
import Heading from "src/partials/Heading.svelte"
import Content from "src/partials/Content.svelte"
export let signup
let compose = null
const onSubmit = () => {
signup(compose.parse())
}
const skip = () => signup()
onMount(() => {
compose.write("Hello world! #introductions")
})
</script>
<Content size="lg">
<Heading class="text-center">Welcome to Nostr</Heading>
<p class="text-center">
Your're all set! If have any questions, just ask! People around these parts are always ready to
lend a hand.
</p>
<div class="border-l-2 border-solid border-gray-6 pl-4">
<Compose bind:this={compose} {onSubmit} />
</div>
<Anchor type="button-accent" class="flex-grow text-center" on:click={onSubmit}>Say Hello!</Anchor>
<Anchor type="unstyled" class="text-center" on:click={skip}>
Skip and see your feed <i class="fa fa-arrow-right" />
</Anchor>
</Content>

View File

@ -0,0 +1,46 @@
<script lang="ts">
import {modal} from "src/partials/state"
import Input from "src/partials/Input.svelte"
import Textarea from "src/partials/Textarea.svelte"
import ImageInput from "src/partials/ImageInput.svelte"
import Anchor from "src/partials/Anchor.svelte"
import Heading from "src/partials/Heading.svelte"
import Content from "src/partials/Content.svelte"
export let profile
</script>
<Content size="lg">
<Heading class="text-center">Introduce Yourself</Heading>
<p class="text-center">
Give other people something to go on. Remember that "privacy is the power to selectively reveal
oneself to the world".
</p>
<div class="flex flex-col gap-2">
<div class="flex flex-col gap-1">
<strong>Your Name</strong>
<Input type="text" name="name" wrapperClass="flex-grow" bind:value={profile.name}>
<i slot="before" class="fa-solid fa-user-astronaut" />
</Input>
</div>
<div class="flex flex-col gap-1">
<strong>About You</strong>
<Textarea name="about" bind:value={profile.about} />
</div>
<div class="flex flex-col gap-1">
<strong>Profile Picture</strong>
<ImageInput
bind:value={profile.picture}
icon="image-portrait"
maxWidth={480}
maxHeight={480} />
<p class="text-sm text-gray-1">Please be mindful of others and only use small images.</p>
</div>
</div>
<Anchor
type="button-accent"
class="text-center"
on:click={() => modal.replace({type: "onboarding", stage: "key"})}>
Continue
</Anchor>
</Content>

View File

@ -170,6 +170,24 @@
selection.collapse(input, 0) selection.collapse(input, 0)
} }
export const write = text => {
const selection = window.getSelection()
const textNode = document.createTextNode(text)
selection.getRangeAt(0).insertNode(textNode)
selection.collapse(textNode, text.length)
autocomplete()
}
export const newlines = n => {
const selection = window.getSelection()
const newLines = createNewLines(2)
selection.getRangeAt(0).insertNode(newLines)
selection.collapse(newLines, 2)
}
export const parse = () => { export const parse = () => {
let {content, annotations} = contenteditable.parse() let {content, annotations} = contenteditable.parse()
const topics = pluck("value", annotations.filter(propEq("prefix", "#"))) const topics = pluck("value", annotations.filter(propEq("prefix", "#")))