Put profile edit in a modal

This commit is contained in:
Jon Staab 2024-11-15 14:25:18 -08:00
parent 0deddc8a4c
commit 763d4a01b1
3 changed files with 101 additions and 108 deletions

View file

@ -0,0 +1,69 @@
<script lang="ts">
import {ctx} from "@welshman/lib"
import {
createEvent,
makeProfile,
editProfile,
createProfile,
isPublishedProfile,
} from "@welshman/util"
import {pubkey, profilesByPubkey, publishThunk} from "@welshman/app"
import Icon from "@lib/components/Icon.svelte"
import Field from "@lib/components/Field.svelte"
import Button from "@lib/components/Button.svelte"
import InputProfilePicture from "@lib/components/InputProfilePicture.svelte"
import InfoHandle from "@app/components/InfoHandle.svelte"
import {pushModal, clearModals} from "@app/modal"
import {pushToast} from "@app/toast"
const values = {...($profilesByPubkey.get($pubkey!) || makeProfile())}
const back = () => history.back()
const saveEdit = () => {
const relays = ctx.app.router.FromUser().getUrls()
const template = isPublishedProfile(values) ? editProfile(values) : createProfile(values)
const event = createEvent(template.kind, template)
publishThunk({event, relays})
pushToast({message: "Your profile has been updated!"})
clearModals()
}
let file: File
</script>
<form class="col-4" on:submit|preventDefault={saveEdit}>
<div class="flex justify-center py-2">
<InputProfilePicture bind:file bind:url={values.picture} />
</div>
<Field>
<p slot="label">Username</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="user-circle" />
<input bind:value={values.name} class="grow" type="text" />
</label>
</Field>
<Field>
<p slot="label">About You</p>
<textarea
class="textarea textarea-bordered leading-4"
rows="3"
bind:value={values.about}
slot="input" />
</Field>
<Field>
<p slot="label">Nostr Address</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="map-point" />
<input bind:value={values.nip05} class="grow" type="text" />
</label>
<p slot="info">
<Button class="link" on:click={() => pushModal(InfoHandle)}>What is a nostr address?</Button>
</p>
</Field>
<div class="mt-4 flex flex-row items-center justify-between gap-4">
<Button class="btn btn-neutral" on:click={back}>Discard Changes</Button>
<Button type="submit" class="btn btn-primary">Save Changes</Button>
</div>
</form>

View file

@ -6,7 +6,8 @@
export let fullscreen = false export let fullscreen = false
$: extraClass = $: extraClass =
!fullscreen && "card2 bg-alt max-h-[90vh] w-[90vw] overflow-auto text-base-content sm:w-[520px]" !fullscreen &&
"card2 bg-alt max-h-[90vh] w-[90vw] overflow-auto text-base-content sm:w-[520px] shadow-xl"
</script> </script>
<div class="center fixed inset-0 z-modal"> <div class="center fixed inset-0 z-modal">

View file

@ -1,28 +1,17 @@
<script lang="ts"> <script lang="ts">
import {nip19} from "nostr-tools" import {nip19} from "nostr-tools"
import {hexToBytes} from "@noble/hashes/utils" import {hexToBytes} from "@noble/hashes/utils"
import {ctx} from "@welshman/lib" import {displayPubkey, displayProfile} from "@welshman/util"
import { import {pubkey, session, displayNip05, deriveProfile} from "@welshman/app"
createEvent,
displayPubkey,
displayProfile,
makeProfile,
editProfile,
createProfile,
isPublishedProfile,
} from "@welshman/util"
import {pubkey, session, publishThunk, displayNip05, deriveProfile} from "@welshman/app"
import {slide} from "@lib/transition" import {slide} from "@lib/transition"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Field from "@lib/components/Field.svelte"
import FieldInline from "@lib/components/FieldInline.svelte" import FieldInline from "@lib/components/FieldInline.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import Avatar from "@lib/components/Avatar.svelte" import Avatar from "@lib/components/Avatar.svelte"
import InputProfilePicture from "@lib/components/InputProfilePicture.svelte"
import Content from "@app/components/Content.svelte" import Content from "@app/components/Content.svelte"
import InfoHandle from "@app/components/InfoHandle.svelte" import ProfileEdit from "@app/components/ProfileEdit.svelte"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {pushToast, clip} from "@app/toast" import {clip} from "@app/toast"
const profile = deriveProfile($pubkey!) const profile = deriveProfile($pubkey!)
@ -32,36 +21,7 @@
const copyNsec = () => clip(nip19.nsecEncode(hexToBytes($session!.secret!))) const copyNsec = () => clip(nip19.nsecEncode(hexToBytes($session!.secret!)))
const cloneProfile = () => ({...($profile || makeProfile())}) const startEdit = () => pushModal(ProfileEdit)
const toggleEdit = () => {
editing = !editing
if (!editing) {
values = cloneProfile()
}
}
const stopEdit = () => {
editing = false
values = cloneProfile()
}
const saveEdit = () => {
const relays = ctx.app.router.FromUser().getUrls()
const template = isPublishedProfile(values) ? editProfile(values) : createProfile(values)
const event = createEvent(template.kind, template)
publishThunk({event, relays})
pushToast({message: "Your profile has been updated!"})
editing = false
}
let file: File
let editing = false
let values = cloneProfile()
$: display = editing ? values : $profile
</script> </script>
<div class="content column gap-4"> <div class="content column gap-4">
@ -69,71 +29,33 @@
<div class="flex justify-between gap-2"> <div class="flex justify-between gap-2">
<div class="flex max-w-full gap-3"> <div class="flex max-w-full gap-3">
<div class="py-1"> <div class="py-1">
<Avatar src={display?.picture} size={10} /> <Avatar src={$profile?.picture} size={10} />
</div> </div>
<div class="flex min-w-0 flex-col"> <div class="flex min-w-0 flex-col">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="text-bold overflow-hidden text-ellipsis"> <div class="text-bold overflow-hidden text-ellipsis">
{displayProfile(display, pubkeyDisplay)} {displayProfile($profile, pubkeyDisplay)}
</div> </div>
</div> </div>
<div class="overflow-hidden text-ellipsis text-sm opacity-75"> <div class="overflow-hidden text-ellipsis text-sm opacity-75">
{display?.nip05 ? displayNip05(display.nip05) : pubkeyDisplay} {$profile?.nip05 ? displayNip05($profile.nip05) : pubkeyDisplay}
</div> </div>
</div> </div>
</div> </div>
<Button class="center btn btn-circle btn-neutral -mr-4 -mt-4 h-12 w-12" on:click={toggleEdit}> <Button class="center btn btn-circle btn-neutral -mr-4 -mt-4 h-12 w-12" on:click={startEdit}>
<Icon icon="pen-new-square" /> <Icon icon="pen-new-square" />
</Button> </Button>
</div> </div>
{#key display?.about} {#key $profile?.about}
<Content event={{content: display?.about || "", tags: []}} hideMedia /> <Content event={{content: $profile?.about || "", tags: []}} hideMedia />
{/key} {/key}
</div> </div>
{#if editing}
<form class="card2 bg-alt col-4 shadow-xl" transition:slide on:submit|preventDefault={saveEdit}>
<div class="flex justify-center py-2">
<InputProfilePicture bind:file bind:url={values.picture} />
</div>
<Field>
<p slot="label">Username</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="user-circle" />
<input bind:value={values.name} class="grow" type="text" />
</label>
</Field>
<Field>
<p slot="label">About You</p>
<textarea
class="textarea textarea-bordered leading-4"
rows="3"
bind:value={values.about}
slot="input" />
</Field>
<Field>
<p slot="label">Nostr Address</p>
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
<Icon icon="map-point" />
<input bind:value={values.nip05} class="grow" type="text" />
</label>
<p slot="info">
<Button class="link" on:click={() => pushModal(InfoHandle)}
>What is a nostr address?</Button>
</p>
</Field>
<div class="mt-4 flex flex-row items-center justify-between gap-4">
<Button class="btn btn-neutral" on:click={stopEdit}>Discard Changes</Button>
<Button type="submit" class="btn btn-primary">Save Changes</Button>
</div>
</form>
{/if}
{#if $session?.method === "nip01"}
<div class="card2 bg-alt col-4 shadow-xl" transition:slide> <div class="card2 bg-alt col-4 shadow-xl" transition:slide>
<FieldInline> <FieldInline>
<p slot="label">Public Key</p> <p slot="label">Public 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.pubkey} class="grow" type="text" /> <input value={$session?.pubkey} class="grow" type="text" />
<Button class="flex items-center" on:click={copyNpub}> <Button class="flex items-center" on:click={copyNpub}>
<Icon icon="copy" /> <Icon icon="copy" />
</Button> </Button>
@ -142,7 +64,8 @@
Your public key is your nostr user identifier. It also allows other people to authenticate Your public key is your nostr user identifier. It also allows other people to authenticate
your messages. your messages.
</p> </p>
<p></p></FieldInline> </FieldInline>
{#if $session?.method === "nip01"}
<FieldInline> <FieldInline>
<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">
@ -153,7 +76,7 @@
</Button> </Button>
</label> </label>
<p slot="info">Your private key is your nostr password. Keep this somewhere safe!</p> <p slot="info">Your private key is your nostr password. Keep this somewhere safe!</p>
<p></p></FieldInline> </FieldInline>
</div>
{/if} {/if}
</div> </div>
</div>