forked from coracle/flotilla
Rework onboarding flow, add recovery
This commit is contained in:
@@ -7,13 +7,13 @@
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import ProfileEject from "@app/components/ProfileEject.svelte"
|
||||
import KeyRecoveryRequest from "@app/components/KeyRecoveryRequest.svelte"
|
||||
import {PLATFORM_NAME} from "@app/core/state"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const startEject = () => pushModal(ProfileEject)
|
||||
const startRecoveryRequest = () => pushModal(KeyRecoveryRequest)
|
||||
</script>
|
||||
|
||||
<div class="column gap-4">
|
||||
@@ -44,7 +44,7 @@
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button class="btn btn-primary" onclick={startEject}>
|
||||
<Button class="btn btn-primary" onclick={startRecoveryRequest}>
|
||||
<Icon icon={CheckCircle} />
|
||||
I want to hold my own keys
|
||||
</Button>
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
const loadSessions = async () => {
|
||||
if (!isPomadeSession($session)) return
|
||||
|
||||
const client = new Client($session.clientOptions)
|
||||
|
||||
try {
|
||||
const client = new Client($session.clientOptions)
|
||||
const result = await client.listSessions()
|
||||
const pubkey = await client.getPubkey()
|
||||
|
||||
@@ -42,20 +43,9 @@
|
||||
}
|
||||
|
||||
sessions = Array.from(sessionMap.values())
|
||||
} else {
|
||||
pushToast({
|
||||
theme: "error",
|
||||
message: "Failed to load sessions",
|
||||
})
|
||||
}
|
||||
|
||||
} finally {
|
||||
client.stop()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
pushToast({
|
||||
theme: "error",
|
||||
message: "Failed to load sessions",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import {clearModals} from "@app/util/modal"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {PROTECTED} from "@app/core/state"
|
||||
import {updateProfile} from "../core/commands"
|
||||
import {updateProfile} from "@app/core/commands"
|
||||
|
||||
const profile = $profilesByPubkey.get($pubkey!) || makeProfile()
|
||||
const shouldBroadcast = !getTag(PROTECTED, profile.event?.tags || [])
|
||||
|
||||
@@ -1,21 +1,65 @@
|
||||
<script lang="ts">
|
||||
import type {ClientOptions} from "@pomade/core"
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {makeProfile, makeSecret, getPubkey} from "@welshman/util"
|
||||
import {loginWithNip01, loginWithPomade} from "@welshman/app"
|
||||
import Key from "@assets/icons/key-minimalistic.svg?dataurl"
|
||||
import Letter from "@assets/icons/letter.svg?dataurl"
|
||||
import {getKey, setKey} from "@lib/implicit"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import LogIn from "@app/components/LogIn.svelte"
|
||||
import InfoNostr from "@app/components/InfoNostr.svelte"
|
||||
import SignUpKey from "@app/components/SignUpKey.svelte"
|
||||
import SignUpEmail from "@app/components/SignUpEmail.svelte"
|
||||
import SignUpProfile from "@app/components/SignUpProfile.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import SignUpComplete from "@app/components/SignUpComplete.svelte"
|
||||
import {setChecked} from "@app/util/notifications"
|
||||
import {pushModal, clearModals} from "@app/util/modal"
|
||||
import {initProfile} from "@app/core/commands"
|
||||
import {POMADE_SIGNERS, PLATFORM_NAME} from "@app/core/state"
|
||||
|
||||
setKey("signup.email", "")
|
||||
setKey("signup.secret", makeSecret())
|
||||
setKey("signup.profile", makeProfile())
|
||||
setKey("signup.clientOptions", undefined)
|
||||
|
||||
const hasPomade = POMADE_SIGNERS.length >= 3
|
||||
|
||||
const login = () => pushModal(LogIn)
|
||||
|
||||
const useEmail = () => pushModal(SignUpProfile, {flow: "email"})
|
||||
const flows = {
|
||||
email: {
|
||||
start: () => pushModal(SignUpEmail, {next: flows.email.profile}),
|
||||
profile: () => pushModal(SignUpProfile, {next: flows.email.complete}),
|
||||
complete: () => pushModal(SignUpComplete, {next: flows.email.finalize}),
|
||||
finalize: () => {
|
||||
const email = getKey<string>("signup.email")!
|
||||
const secret = getKey<string>("signup.secret")!
|
||||
const profile = getKey<Profile>("signup.profile")!
|
||||
const clientOptions = getKey<ClientOptions>("signup.clientOptions")!
|
||||
|
||||
const useNostr = () => pushModal(SignUpProfile, {flow: "nostr"})
|
||||
loginWithPomade(getPubkey(secret), email, clientOptions!)
|
||||
initProfile(profile)
|
||||
setChecked("*")
|
||||
clearModals()
|
||||
},
|
||||
},
|
||||
nostr: {
|
||||
start: () => pushModal(SignUpProfile, {next: flows.nostr.key}),
|
||||
key: () => pushModal(SignUpKey, {next: flows.nostr.complete}),
|
||||
complete: () => pushModal(SignUpComplete, {next: flows.nostr.finalize}),
|
||||
finalize: () => {
|
||||
const secret = getKey<string>("signup.secret")!
|
||||
const profile = getKey<Profile>("signup.profile")!
|
||||
|
||||
loginWithNip01(secret)
|
||||
initProfile(profile)
|
||||
setChecked("*")
|
||||
clearModals()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="column gap-4">
|
||||
@@ -26,12 +70,12 @@
|
||||
users control over their digital identity using <strong>cryptographic key pairs</strong>.
|
||||
</p>
|
||||
{#if hasPomade}
|
||||
<Button onclick={useEmail} class="btn btn-primary">
|
||||
<Button onclick={flows.email.start} class="btn btn-primary">
|
||||
<Icon icon={Letter} />
|
||||
Sign up with email
|
||||
</Button>
|
||||
{/if}
|
||||
<Button onclick={useNostr} class="btn {hasPomade ? 'btn-neutral' : 'btn-primary'}">
|
||||
<Button onclick={flows.nostr.start} class="btn {hasPomade ? 'btn-neutral' : 'btn-primary'}">
|
||||
<Icon icon={Key} />
|
||||
Generate a key
|
||||
</Button>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script lang="ts">
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {loginWithNip01} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import HomeSmile from "@assets/icons/home-smile.svg?dataurl"
|
||||
@@ -8,23 +6,14 @@
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {clearModals} from "@app/util/modal"
|
||||
import {initProfile} from "@app/core/commands"
|
||||
|
||||
type Props = {
|
||||
secret: string
|
||||
profile: Profile
|
||||
next: () => void
|
||||
}
|
||||
|
||||
const {secret, profile}: Props = $props()
|
||||
const {next}: Props = $props()
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const next = () => {
|
||||
loginWithNip01(secret)
|
||||
initProfile(profile)
|
||||
clearModals()
|
||||
}
|
||||
</script>
|
||||
|
||||
<form class="column gap-4" onsubmit={preventDefault(next)}>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<script lang="ts">
|
||||
import {Client} from "@pomade/core"
|
||||
import {choice} from "@welshman/lib"
|
||||
import {makeSecret} from "@welshman/util"
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import Letter from "@assets/icons/letter.svg?dataurl"
|
||||
import Key from "@assets/icons/key.svg?dataurl"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
|
||||
import {getKey, setKey} from "@lib/implicit"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
@@ -19,10 +18,10 @@
|
||||
import {pushModal} from "@app/util/modal"
|
||||
|
||||
type Props = {
|
||||
profile: Profile
|
||||
next: () => void
|
||||
}
|
||||
|
||||
const {profile}: Props = $props()
|
||||
const {next}: Props = $props()
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
@@ -39,7 +38,8 @@
|
||||
let client: Client | undefined = undefined
|
||||
|
||||
try {
|
||||
const {clientOptions, ...registerRes} = await Client.register(2, 3, makeSecret())
|
||||
const secret = getKey<string>("signup.secret")!
|
||||
const {clientOptions, ...registerRes} = await Client.register(2, 3, secret)
|
||||
|
||||
if (!registerRes.ok) {
|
||||
return pushToast({
|
||||
@@ -70,7 +70,10 @@
|
||||
})
|
||||
}
|
||||
|
||||
pushModal(SignUpEmailConfirm, {email, profile, clientOptions})
|
||||
setKey("signup.email", email)
|
||||
setKey("signup.clientOptions", clientOptions)
|
||||
|
||||
pushModal(SignUpEmailConfirm, {next})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {ClientOptions} from "@pomade/core"
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {sleep} from "@welshman/lib"
|
||||
import {loginWithPomade} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import {getKey} from "@lib/implicit"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
@@ -13,18 +11,14 @@
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {initProfile} from "@app/core/commands"
|
||||
import {clearModals} from "@app/util/modal"
|
||||
import {setChecked} from "@app/util/notifications"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
|
||||
type Props = {
|
||||
email: string
|
||||
profile: Profile
|
||||
clientOptions: ClientOptions
|
||||
next: () => void
|
||||
}
|
||||
|
||||
const {email, profile, clientOptions}: Props = $props()
|
||||
const {next}: Props = $props()
|
||||
|
||||
const email = getKey<string>("signup.email")
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
@@ -34,15 +28,7 @@
|
||||
// Just pretend we're validating, they clearly got a code from somewhere
|
||||
await sleep(800)
|
||||
|
||||
try {
|
||||
loginWithPomade(clientOptions.group.group_pk.slice(2), email, clientOptions)
|
||||
pushToast({message: "Successfully logged in!"})
|
||||
initProfile(profile)
|
||||
setChecked("*")
|
||||
clearModals()
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
let challenge = $state("")
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
<script lang="ts">
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {getKey} from "@lib/implicit"
|
||||
import KeyDownload from "@app/components/KeyDownload.svelte"
|
||||
import SignUpComplete from "@app/components/SignUpComplete.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
|
||||
type Props = {
|
||||
secret: string
|
||||
profile: Profile
|
||||
next: () => void
|
||||
}
|
||||
|
||||
const {secret, profile}: Props = $props()
|
||||
const {next}: Props = $props()
|
||||
|
||||
const next = () => pushModal(SignUpComplete, {secret, profile})
|
||||
const secret = getKey<string>("signup.secret")!
|
||||
</script>
|
||||
|
||||
<KeyDownload {secret} {next} />
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
<script lang="ts">
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {makeProfile, makeSecret} from "@welshman/util"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
|
||||
import {getKey, setKey} from "@lib/implicit"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import ProfileEditForm from "@app/components/ProfileEditForm.svelte"
|
||||
import SignUpKey from "@app/components/SignUpKey.svelte"
|
||||
import SignUpEmail from "@app/components/SignUpEmail.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
|
||||
type Props = {
|
||||
flow: "nostr" | "email"
|
||||
next: () => void
|
||||
}
|
||||
|
||||
const {flow}: Props = $props()
|
||||
const {next}: Props = $props()
|
||||
|
||||
const initialValues = {
|
||||
secret: makeSecret(),
|
||||
profile: makeProfile(),
|
||||
shouldBroadcast: false,
|
||||
}
|
||||
const profile = getKey<Profile>("signup.profile")!
|
||||
|
||||
const initialValues = {profile, shouldBroadcast: false}
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const onsubmit = (values: {profile: Profile}) =>
|
||||
pushModal(flow === "nostr" ? SignUpKey : SignUpEmail, values)
|
||||
const onsubmit = ({profile}: {profile: Profile}) => {
|
||||
setKey("signup.profile", profile)
|
||||
next()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import * as net from "@welshman/net"
|
||||
import * as app from "@welshman/app"
|
||||
import {isMobile} from "@lib/html"
|
||||
import * as implicit from "@lib/implicit"
|
||||
import AppContainer from "@app/components/AppContainer.svelte"
|
||||
import ModalContainer from "@app/components/ModalContainer.svelte"
|
||||
import {setupHistory} from "@app/util/history"
|
||||
@@ -50,6 +51,7 @@
|
||||
nip19,
|
||||
theme,
|
||||
...lib,
|
||||
...implicit,
|
||||
...welshmanSigner,
|
||||
...router,
|
||||
...store,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import * as nip19 from "nostr-tools/nip19"
|
||||
import {hexToBytes} from "@welshman/lib"
|
||||
import {displayPubkey, displayProfile} from "@welshman/util"
|
||||
import {pubkey, session, displayNip05, deriveProfile, SessionMethod} from "@welshman/app"
|
||||
import {pubkey, session, displayNip05, deriveProfile} from "@welshman/app"
|
||||
import {slideAndFade} from "@lib/transition"
|
||||
import PenNewSquare from "@assets/icons/pen-new-square.svg?dataurl"
|
||||
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
|
||||
@@ -20,7 +20,6 @@
|
||||
import ContentMinimal from "@app/components/ContentMinimal.svelte"
|
||||
import ProfileEdit from "@app/components/ProfileEdit.svelte"
|
||||
import ProfileDelete from "@app/components/ProfileDelete.svelte"
|
||||
import KeyRecoveryRequest from "@app/components/KeyRecoveryRequest.svelte"
|
||||
import SignerStatus from "@app/components/SignerStatus.svelte"
|
||||
import InfoKeys from "@app/components/InfoKeys.svelte"
|
||||
import {PLATFORM_NAME} from "@app/core/state"
|
||||
@@ -37,11 +36,9 @@
|
||||
|
||||
const startEdit = () => pushModal(ProfileEdit)
|
||||
|
||||
const startEject = () => pushModal(InfoKeys)
|
||||
|
||||
const startDelete = () => pushModal(ProfileDelete)
|
||||
|
||||
const startRecovery = () => pushModal(KeyRecoveryRequest)
|
||||
const startRecovery = () => pushModal(InfoKeys)
|
||||
|
||||
let showAdvanced = false
|
||||
</script>
|
||||
@@ -87,7 +84,7 @@
|
||||
{#snippet info()}
|
||||
<p>
|
||||
Your email and password can only be used to log into {PLATFORM_NAME}.
|
||||
<Button class="link" onclick={startEject}>Start holding your own keys</Button>
|
||||
<Button class="link" onclick={startRecovery}>Start holding your own keys</Button>
|
||||
</p>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
@@ -159,12 +156,6 @@
|
||||
</div>
|
||||
{#if showAdvanced}
|
||||
<div transition:slideAndFade class="flex flex-col gap-2 pt-4">
|
||||
{#if $session?.method === SessionMethod.Pomade}
|
||||
<Button class="btn btn-neutral" onclick={startRecovery}>
|
||||
<Icon icon={Key} />
|
||||
Recover your key
|
||||
</Button>
|
||||
{/if}
|
||||
<Button class="btn btn-error" onclick={startDelete}>
|
||||
<Icon icon={TrashBin2} />
|
||||
Delete your profile
|
||||
|
||||
Reference in New Issue
Block a user