Add burrow support

This commit is contained in:
Jon Staab
2024-11-26 16:39:49 -08:00
parent 220f26253d
commit a58fc68235
16 changed files with 375 additions and 55 deletions
+1
View File
@@ -1,4 +1,5 @@
VITE_DEFAULT_PUBKEYS=fe7f6bc6f7338b76bbf80db402ade65953e20b2f23e66e898204b63cc42539a3,391819e2f2f13b90cac7209419eb574ef7c0d1f4e81867fc24c47a3ce5e8a248,84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240,dace63b00c42e6e017d00dd190a9328386002ff597b841eb5ef91de4f1ce8491,82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2,58c741aa630c2da35a56a77c1d05381908bd10504fdd2d8b43f725efa6d23196,eeb11961b25442b16389fe6c7ebea9adf0ac36dd596816ea7119e521b8821b9e,b676ded7c768d66a757aa3967b1243d90bf57afb09d1044d3219d8d424e4aea0,61066504617ee79387021e18c89fb79d1ddbc3e7bff19cf2298f40466f8715e9,3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24,6389be6491e7b693e9f368ece88fcd145f07c068d2c1bbae4247b9b5ef439d32,97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322 VITE_DEFAULT_PUBKEYS=fe7f6bc6f7338b76bbf80db402ade65953e20b2f23e66e898204b63cc42539a3,391819e2f2f13b90cac7209419eb574ef7c0d1f4e81867fc24c47a3ce5e8a248,84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240,dace63b00c42e6e017d00dd190a9328386002ff597b841eb5ef91de4f1ce8491,82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2,58c741aa630c2da35a56a77c1d05381908bd10504fdd2d8b43f725efa6d23196,eeb11961b25442b16389fe6c7ebea9adf0ac36dd596816ea7119e521b8821b9e,b676ded7c768d66a757aa3967b1243d90bf57afb09d1044d3219d8d424e4aea0,61066504617ee79387021e18c89fb79d1ddbc3e7bff19cf2298f40466f8715e9,3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24,6389be6491e7b693e9f368ece88fcd145f07c068d2c1bbae4247b9b5ef439d32,97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322
VITE_BURROW_URL=
VITE_PLATFORM_URL=https://flotilla.social VITE_PLATFORM_URL=https://flotilla.social
VITE_PLATFORM_NAME=Flotilla VITE_PLATFORM_NAME=Flotilla
VITE_PLATFORM_LOGO=static/flotilla.png VITE_PLATFORM_LOGO=static/flotilla.png
+10 -1
View File
@@ -4,7 +4,16 @@
import Landing from "@app/components/Landing.svelte" import Landing from "@app/components/Landing.svelte"
import Toast from "@app/components/Toast.svelte" import Toast from "@app/components/Toast.svelte"
import PrimaryNav from "@app/components/PrimaryNav.svelte" import PrimaryNav from "@app/components/PrimaryNav.svelte"
import {modals} from "@app/modal" import SignUpConfirm from "@app/components/SignUpConfirm.svelte"
import {BURROW_URL} from "@app/state"
import {modals, pushModal} from "@app/modal"
if (BURROW_URL && $page.route.id === "/confirm-email") {
pushModal(SignUpConfirm, {
email: $page.url.searchParams.get("email"),
token: $page.url.searchParams.get("token"),
})
}
</script> </script>
<div class="flex h-screen overflow-hidden"> <div class="flex h-screen overflow-hidden">
+31 -10
View File
@@ -1,8 +1,17 @@
<script lang="ts"> <script lang="ts">
import {session} from "@welshman/app"
import Link from "@lib/components/Link.svelte" import Link from "@lib/components/Link.svelte"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import ModalHeader from "@lib/components/ModalHeader.svelte" import ModalHeader from "@lib/components/ModalHeader.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import ProfileEject from "@app/components/ProfileEject.svelte"
import {PLATFORM_NAME} from "@app/state" import {PLATFORM_NAME} from "@app/state"
import {pushModal} from "@app/modal"
const back = () => history.back()
const startEject = () => pushModal(ProfileEject)
</script> </script>
<div class="column gap-4"> <div class="column gap-4">
@@ -10,21 +19,33 @@
<div slot="title">What is a private key?</div> <div slot="title">What is a private key?</div>
</ModalHeader> </ModalHeader>
<p> <p>
Most software keeps track of users by giving them a username and password. This gives the Most online services keep track of users by giving them a username and password. This gives the
service service <strong>total control</strong> over their users, allowing them to ban them at any time, or
<strong>total control</strong> over their users, allowing them to ban them at any time, or sell their sell their activity.
activity.
</p> </p>
<p> <p>
On <Link external href="https://nostr.com/">Nostr</Link>, <strong>you</strong> control your own On <Link external href="https://nostr.com/">Nostr</Link>, <strong>you</strong> control your own
identity and social data, through the magic of crytography. The basic idea is that you have a identity and social data, through the magic of crytography. The basic idea is that you have a
<strong>public key</strong>, which acts as your user id, and a <strong>private key</strong> which <strong>public key</strong>, which acts as your user id, and a
allows you to authenticate any message you send. <strong>private key</strong> which allows you to prove your identity.
</p> </p>
{#if $session?.email}
<p> <p>
It's very important to keep private keys safe, but this can sometimes be confusing for It's very important to keep private keys safe, but this can sometimes be tricky, which is why {PLATFORM_NAME}
newcomers. This is why {PLATFORM_NAME} supports <strong>remote signer</strong> login. These services supports a traditional account-based login for new users.
can store your keys securely for you, giving you access using a username and password.
</p> </p>
<Button class="btn btn-primary" on:click={() => history.back()}>Got it</Button> <p>If you'd like to switch to self-custody, please click below to get started.</p>
<ModalFooter>
<Button class="btn btn-link" on:click={back}>
<Icon icon="alt-arrow-left" />
Go back
</Button>
<Button class="btn btn-primary" on:click={startEject}>
<Icon icon="check-circle" />
I want to hold my own keys
</Button>
</ModalFooter>
{:else}
<Button class="btn btn-primary" on:click={back}>Got it</Button>
{/if}
</div> </div>
+23 -3
View File
@@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import cx from "classnames"
import {onMount} from "svelte" import {onMount} from "svelte"
import {Capacitor} from "@capacitor/core" import {Capacitor} from "@capacitor/core"
import {getNip07, getNip55, Nip55Signer} from "@welshman/signer" import {getNip07, getNip55, Nip55Signer} from "@welshman/signer"
@@ -9,8 +10,9 @@
import SignUp from "@app/components/SignUp.svelte" import SignUp from "@app/components/SignUp.svelte"
import InfoNostr from "@app/components/InfoNostr.svelte" import InfoNostr from "@app/components/InfoNostr.svelte"
import LogInBunker from "@app/components/LogInBunker.svelte" import LogInBunker from "@app/components/LogInBunker.svelte"
import LogInBurrow from "@app/components/LogInBurrow.svelte"
import {pushModal, clearModals} from "@app/modal" import {pushModal, clearModals} from "@app/modal"
import {PLATFORM_NAME} from "@app/state" import {PLATFORM_NAME, BURROW_URL} from "@app/state"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
import {loadUserData} from "@app/commands" import {loadUserData} from "@app/commands"
import {setChecked} from "@app/notifications" import {setChecked} from "@app/notifications"
@@ -66,6 +68,8 @@
} }
}) })
const loginWithBurrow = () => pushModal(LogInBurrow)
const loginWithBunker = () => pushModal(LogInBunker) const loginWithBunker = () => pushModal(LogInBunker)
let loading = false let loading = false
@@ -90,8 +94,21 @@
<Button class="link" on:click={() => pushModal(InfoNostr)}>nostr protocol</Button>, which allows <Button class="link" on:click={() => pushModal(InfoNostr)}>nostr protocol</Button>, which allows
you to own your social identity. you to own your social identity.
</p> </p>
{#if BURROW_URL}
<Button disabled={loading} on:click={loginWithBurrow} class="btn btn-primary">
{#if loading}
<span class="loading loading-spinner mr-3" />
{:else}
<Icon icon="key" />
{/if}
Log in with Password
</Button>
{/if}
{#if getNip07()} {#if getNip07()}
<Button disabled={loading} on:click={loginWithNip07} class="btn btn-primary"> <Button
disabled={loading}
on:click={loginWithNip07}
class={cx("btn", {"btn-primary": !BURROW_URL, "btn-neutral": BURROW_URL})}>
{#if loading} {#if loading}
<span class="loading loading-spinner mr-3" /> <span class="loading loading-spinner mr-3" />
{:else} {:else}
@@ -101,7 +118,10 @@
</Button> </Button>
{/if} {/if}
{#each signers as app} {#each signers as app}
<Button disabled={loading} class="btn btn-primary" on:click={() => loginWithSigner(app)}> <Button
disabled={loading}
class={cx("btn", {"btn-primary": !BURROW_URL, "btn-neutral": BURROW_URL})}
on:click={() => loginWithSigner(app)}>
{#if loading} {#if loading}
<span class="loading loading-spinner mr-3" /> <span class="loading loading-spinner mr-3" />
{:else} {:else}
+124
View File
@@ -0,0 +1,124 @@
<script lang="ts">
import {onMount, onDestroy} from "svelte"
import {postJson, stripProtocol} from "@welshman/lib"
import {Nip46Broker, makeSecret} from "@welshman/signer"
import {addSession} from "@welshman/app"
import Spinner from "@lib/components/Spinner.svelte"
import Button from "@lib/components/Button.svelte"
import FieldInline from "@lib/components/FieldInline.svelte"
import Icon from "@lib/components/Icon.svelte"
import ModalHeader from "@lib/components/ModalHeader.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import {loadUserData} from "@app/commands"
import {clearModals} from "@app/modal"
import {setChecked} from "@app/notifications"
import {pushToast} from "@app/toast"
import {NIP46_PERMS, BURROW_URL, PLATFORM_URL, PLATFORM_NAME, PLATFORM_LOGO} from "@app/state"
export let email = ""
const clientSecret = makeSecret()
const abortController = new AbortController()
const relays = ["ws://" + stripProtocol(BURROW_URL)]
const broker = Nip46Broker.get({clientSecret, relays})
const back = () => history.back()
const onSubmit = async () => {
loading = true
const res = await postJson(BURROW_URL + "/session", {email, password, nostrconnect: url})
if (res.error) {
pushToast({message: res.error, theme: "error"})
loading = false
}
}
let url = ""
let password = ""
let loading = false
onMount(async () => {
url = await broker.makeNostrconnectUrl({
perms: NIP46_PERMS,
url: PLATFORM_URL,
name: PLATFORM_NAME,
image: PLATFORM_LOGO,
})
let response
try {
response = await broker.waitForNostrconnect(url, abortController)
} catch (errorResponse: any) {
if (errorResponse?.error) {
pushToast({
theme: "error",
message: `Received error from signer: ${errorResponse.error}`,
})
} else if (errorResponse) {
console.error(errorResponse)
}
}
if (response) {
loading = true
const userPubkey = await broker.getPublicKey()
addSession({
method: "nip46",
pubkey: userPubkey,
secret: clientSecret,
handler: {pubkey: response.event.pubkey, relays},
})
await loadUserData(userPubkey)
setChecked("*")
clearModals()
}
})
onDestroy(() => {
abortController.abort()
})
</script>
<form class="column gap-4" on:submit|preventDefault={onSubmit}>
<ModalHeader>
<div slot="title">Log In</div>
<div slot="info">Log in using your email and password.</div>
</ModalHeader>
<FieldInline>
<p slot="label">Email</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="user-rounded" />
<input bind:value={email} />
</label>
</FieldInline>
<FieldInline>
<p slot="label">Password</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="key" />
<input bind:value={password} type="password" />
</label>
</FieldInline>
<p class="text-sm opacity-75">
Your email and password only work to log in to {PLATFORM_NAME}. To use your key on other nostr
applications, visit your settings page.
</p>
<ModalFooter>
<Button class="btn btn-link" on:click={back} disabled={loading}>
<Icon icon="alt-arrow-left" />
Go back
</Button>
<Button type="submit" class="btn btn-primary" disabled={loading || !email || !password}>
<Spinner {loading}>Next</Spinner>
<Icon icon="alt-arrow-right" />
</Button>
</ModalFooter>
</form>
+1
View File
@@ -0,0 +1 @@
hi
+68 -19
View File
@@ -1,17 +1,20 @@
<script lang="ts"> <script lang="ts">
import {postJson} from "@welshman/lib"
import {makeSecret, Nip46Broker} from "@welshman/signer" import {makeSecret, Nip46Broker} from "@welshman/signer"
import {loadHandle} from "@welshman/app" import {loadHandle} from "@welshman/app"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Field from "@lib/components/Field.svelte" import Field from "@lib/components/Field.svelte"
import FieldInline from "@lib/components/FieldInline.svelte"
import Link from "@lib/components/Link.svelte" import Link from "@lib/components/Link.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import Divider from "@lib/components/Divider.svelte" import Divider from "@lib/components/Divider.svelte"
import Spinner from "@lib/components/Spinner.svelte" import Spinner from "@lib/components/Spinner.svelte"
import LogIn from "@app/components/LogIn.svelte" import LogIn from "@app/components/LogIn.svelte"
import InfoNostr from "@app/components/InfoNostr.svelte" import InfoNostr from "@app/components/InfoNostr.svelte"
import SignUpSuccess from "@app/components/SignUpSuccess.svelte"
import {pushModal, clearModals} from "@app/modal" import {pushModal, clearModals} from "@app/modal"
import {setChecked} from "@app/notifications" import {setChecked} from "@app/notifications"
import {PLATFORM_NAME, NIP46_PERMS} from "@app/state" import {BURROW_URL, PLATFORM_NAME, NIP46_PERMS} from "@app/state"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
import {loginWithNip46} from "@app/commands" import {loginWithNip46} from "@app/commands"
@@ -23,7 +26,30 @@
const login = () => pushModal(LogIn) const login = () => pushModal(LogIn)
const trySignup = async () => { const withLoading =
(cb: (...args: any[]) => any) =>
async (...args: any[]) => {
loading = true
try {
await cb(...args)
} finally {
loading = false
}
}
const signupBurrow = withLoading(async () => {
const res = await postJson(BURROW_URL + "/user", {email, password})
if (res.error) {
return pushToast({message: res.error, theme: "error"})
}
pushModal(SignUpSuccess, {email}, {replaceState: true})
})
const signupNsecApp = withLoading(async () => {
const secret = makeSecret()
const handle = await loadHandle(`${username}@${signerDomain}`) const handle = await loadHandle(`${username}@${signerDomain}`)
if (handle?.pubkey) { if (handle?.pubkey) {
@@ -63,24 +89,18 @@
pushToast({message: "Successfully logged in!"}) pushToast({message: "Successfully logged in!"})
setChecked("*") setChecked("*")
clearModals() clearModals()
} })
const signup = async () => { const signup = () => {
loading = true if (BURROW_URL) {
signupBurrow()
try { } else {
await trySignup() signupNsecApp()
} finally {
loading = false
} }
} }
const handler = { let email = ""
domain: "nsec.app", let password = ""
relays: ["wss://relay.nsec.app"],
pubkey: "e24a86943d37a91ab485d6f9a7c66097c25ddd67e8bd1b75ed252a3c266cf9bb",
}
let username = "" let username = ""
let loading = false let loading = false
</script> </script>
@@ -92,21 +112,50 @@
<Button class="link" on:click={() => pushModal(InfoNostr)}>nostr protocol</Button>, which allows <Button class="link" on:click={() => pushModal(InfoNostr)}>nostr protocol</Button>, which allows
you to own your social identity. you to own your social identity.
</p> </p>
{#if BURROW_URL}
<FieldInline>
<p slot="label">Email</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="user-rounded" />
<input bind:value={email} />
</label>
</FieldInline>
<FieldInline>
<p slot="label">Password</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="key" />
<input bind:value={password} type="password" />
</label>
</FieldInline>
<Button type="submit" class="btn btn-primary" disabled={loading || !email || !password}>
<Spinner {loading}>Sign Up</Spinner>
<Icon icon="alt-arrow-right" />
</Button>
<p class="text-sm opacity-75">
Note that your email and password will only work to log in to {PLATFORM_NAME}. To use your key
on other nostr applications, you can create a nostr key yourself, or export your key from {PLATFORM_NAME}
later.
</p>
{:else}
<Field> <Field>
<div class="flex items-center gap-2" slot="input"> <div class="flex items-center gap-2" slot="input">
<label class="input input-bordered flex w-full items-center gap-2"> <label class="input input-bordered flex w-full items-center gap-2">
<Icon icon="user-rounded" /> <Icon icon="user-rounded" />
<input bind:value={username} class="grow" type="text" placeholder="username" /> <input bind:value={username} class="grow" type="text" placeholder="username" />
</label> </label>
@{handler.domain} @{signerDomain}
</div> </div>
</Field> </Field>
<Button type="submit" class="btn btn-primary" disabled={!username || loading}> <Button type="submit" class="btn btn-primary" disabled={loading || !username}>
<Spinner {loading}>Sign Up</Spinner> <Spinner {loading}>Sign Up</Spinner>
<Icon icon="alt-arrow-right" /> <Icon icon="alt-arrow-right" />
</Button> </Button>
{/if}
<Divider>Or</Divider> <Divider>Or</Divider>
<Link external href="https://nosta.me" class="btn {username ? 'btn-neutral' : 'btn-primary'}"> <Link
external
href="https://nosta.me"
class="btn {username || email || password ? 'btn-neutral' : 'btn-primary'}">
<Icon icon="square-share-line" /> <Icon icon="square-share-line" />
Get started on Nosta.me Get started on Nosta.me
</Link> </Link>
+53
View File
@@ -0,0 +1,53 @@
<script lang="ts">
import {onMount} from "svelte"
import {postJson, sleep} from "@welshman/lib"
import Button from "@lib/components/Button.svelte"
import Spinner from "@lib/components/Spinner.svelte"
import LogInBurrow from "@app/components/LogInBurrow.svelte"
import {pushModal} from "@app/modal"
import {BURROW_URL} from "@app/state"
export let email
export let token
const login = () => {
pushModal(LogInBurrow, {email}, {path: "/"})
}
let error: string
let loading = true
onMount(async () => {
const [res] = await Promise.all([
postJson(BURROW_URL + "/user/confirm", {email, token}),
sleep(2000),
])
error = res.error
loading = false
})
</script>
<div class="column gap-4">
<h1 class="heading">
{#if loading}
Just a second...
{:else if error}
Oops!
{:else}
Success!
{/if}
</h1>
<p class="m-auto max-w-sm text-center">
<Spinner {loading}>
{#if loading}
Hang tight, we're checking your confirmation link.
{:else if error}
Looks like something went wrong. {error}
{:else}
You're all set - click below to log in.
{/if}
</Spinner>
</p>
<Button class="btn btn-primary" on:click={login} disabled={loading}>Continue to Login</Button>
</div>
+18
View File
@@ -0,0 +1,18 @@
<script lang="ts">
import Button from "@lib/components/Button.svelte"
import LogInBurrow from "@app/components/LogInBurrow.svelte"
import {pushModal} from "@app/modal"
export let email
const login = () => pushModal(LogInBurrow)
</script>
<div class="column gap-4">
<h1 class="heading">Success!</h1>
<p class="m-auto max-w-sm text-center">
A confirmation email has been sent to {email}.
</p>
<p>Once you've confirmed your account you'll be redirected to the login page.</p>
<Button class="btn btn-primary" on:click={login}>Back to Login</Button>
</div>
+1 -1
View File
@@ -11,7 +11,7 @@
{#key $toast.id} {#key $toast.id}
<div <div
role="alert" role="alert"
class="alert flex justify-center" class="alert flex justify-center whitespace-normal text-left"
class:bg-base-100={theme === "info"} class:bg-base-100={theme === "info"}
class:text-base-content={theme === "info"} class:text-base-content={theme === "info"}
class:alert-error={theme === "error"}> class:alert-error={theme === "error"}>
+3 -1
View File
@@ -7,6 +7,7 @@ export type ModalOptions = {
drawer?: boolean drawer?: boolean
fullscreen?: boolean fullscreen?: boolean
replaceState?: boolean replaceState?: boolean
path?: string
} }
export type Modal = { export type Modal = {
@@ -26,10 +27,11 @@ export const pushModal = (
options: ModalOptions = {}, options: ModalOptions = {},
) => { ) => {
const id = randomId() const id = randomId()
const path = options.path || ""
modals.update(assoc(id, {id, component, props, options})) modals.update(assoc(id, {id, component, props, options}))
goto("#" + id, {replaceState: options.replaceState}) goto(path + "#" + id, {replaceState: options.replaceState})
return id return id
} }
+2
View File
@@ -93,6 +93,8 @@ export const PLATFORM_ACCENT = import.meta.env.VITE_PLATFORM_ACCENT
export const PLATFORM_DESCRIPTION = import.meta.env.VITE_PLATFORM_DESCRIPTION export const PLATFORM_DESCRIPTION = import.meta.env.VITE_PLATFORM_DESCRIPTION
export const BURROW_URL = import.meta.env.VITE_BURROW_URL
export const DEFAULT_PUBKEYS = import.meta.env.VITE_DEFAULT_PUBKEYS export const DEFAULT_PUBKEYS = import.meta.env.VITE_DEFAULT_PUBKEYS
export const DUFFLEPUD_URL = "https://dufflepud.onrender.com" export const DUFFLEPUD_URL = "https://dufflepud.onrender.com"
+4 -4
View File
@@ -1,13 +1,13 @@
<div class="grid grid-cols-1 gap-2 lg:grid-cols-2 {$$props.class}"> <div class="grid grid-cols-1 gap-2 md:grid-cols-3 {$$props.class}">
<label class="flex items-center gap-2 font-bold"> <label class="flex items-center gap-2 font-bold">
<slot name="label" /> <slot name="label" />
</label> </label>
<div class="flex items-center gap-2"> <div class="col-span-2 flex items-center gap-2">
<slot name="input" /> <slot name="input" />
</div> </div>
<p class="flex-end text-sm md:col-span-3">
{#if $$slots.info} {#if $$slots.info}
<p class="flex-end text-sm sm:col-span-2">
<slot name="info" /> <slot name="info" />
</p>
{/if} {/if}
</p>
</div> </div>
+1 -1
View File
@@ -4,7 +4,7 @@
export let loading export let loading
</script> </script>
<span class="flex items-center"> <span class="flex min-h-10 items-center">
{#if loading} {#if loading}
<span class="pr-3" transition:slide|local={{axis: "x"}}> <span class="pr-3" transition:slide|local={{axis: "x"}}>
<span class="loading loading-spinner" transition:fade|local={{duration: 100}} /> <span class="loading loading-spinner" transition:fade|local={{duration: 100}} />
+1
View File
@@ -0,0 +1 @@
hi
+21 -2
View File
@@ -9,6 +9,8 @@
import Avatar from "@lib/components/Avatar.svelte" import Avatar from "@lib/components/Avatar.svelte"
import Content from "@app/components/Content.svelte" import Content from "@app/components/Content.svelte"
import ProfileEdit from "@app/components/ProfileEdit.svelte" import ProfileEdit from "@app/components/ProfileEdit.svelte"
import InfoKeys from "@app/components/InfoKeys.svelte"
import {PLATFORM_NAME} from "@app/state"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {clip} from "@app/toast" import {clip} from "@app/toast"
@@ -21,6 +23,8 @@
const copyNsec = () => clip(nip19.nsecEncode(hexToBytes($session!.secret!))) const copyNsec = () => clip(nip19.nsecEncode(hexToBytes($session!.secret!)))
const startEdit = () => pushModal(ProfileEdit) const startEdit = () => pushModal(ProfileEdit)
const startEject = () => pushModal(InfoKeys)
</script> </script>
<div class="content column gap-4"> <div class="content column gap-4">
@@ -49,6 +53,21 @@
<Content event={{content: $profile?.about || "", tags: []}} hideMedia /> <Content event={{content: $profile?.about || "", tags: []}} hideMedia />
{/key} {/key}
</div> </div>
{#if $session?.email}
<div class="card2 bg-alt col-4 shadow-xl">
<FieldInline>
<p slot="label">Email Address</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="user-rounded" />
<input readonly value={$session.email} class="grow" />
</label>
<p slot="info">
Your email and password can only be used to log into {PLATFORM_NAME}.
<Button class="link" on:click={startEject}>Start holding your own keys</Button>
</p>
</FieldInline>
</div>
{/if}
<div class="card2 bg-alt col-4 shadow-xl"> <div class="card2 bg-alt col-4 shadow-xl">
<FieldInline> <FieldInline>
<p slot="label">Public Key</p> <p slot="label">Public Key</p>
@@ -57,7 +76,7 @@
slot="input"> slot="input">
<div class="row-2 flex-grow items-center"> <div class="row-2 flex-grow items-center">
<Icon icon="link-round" /> <Icon icon="link-round" />
<input class="ellipsize flex-grow" value={$session?.pubkey} /> <input readonly class="ellipsize flex-grow" value={$session?.pubkey} />
</div> </div>
<Button class="flex items-center" on:click={copyNpub}> <Button class="flex items-center" on:click={copyNpub}>
<Icon icon="copy" /> <Icon icon="copy" />
@@ -73,7 +92,7 @@
<p slot="label">Private Key</p> <p slot="label">Private Key</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input"> <label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="link-round" /> <Icon icon="link-round" />
<input value={$session.secret} class="grow" type="password" /> <input readonly value={$session.secret} class="grow" type="password" />
<Button class="flex items-center" on:click={copyNsec}> <Button class="flex items-center" on:click={copyNsec}>
<Icon icon="copy" /> <Icon icon="copy" />
</Button> </Button>