mirror of
https://github.com/coracle-social/flotilla.git
synced 2025-12-10 10:57:04 +00:00
Add email confirmation and password reset
This commit is contained in:
@@ -4,16 +4,26 @@
|
||||
import Landing from "@app/components/Landing.svelte"
|
||||
import Toast from "@app/components/Toast.svelte"
|
||||
import PrimaryNav from "@app/components/PrimaryNav.svelte"
|
||||
import SignUpConfirm from "@app/components/SignUpConfirm.svelte"
|
||||
import EmailConfirm from "@app/components/EmailConfirm.svelte"
|
||||
import PasswordReset from "@app/components/PasswordReset.svelte"
|
||||
import {BURROW_URL} from "@app/state"
|
||||
import {modals, pushModal} from "@app/modal"
|
||||
|
||||
if (BURROW_URL && $page.route.id === "/confirm-email") {
|
||||
pushModal(SignUpConfirm, {
|
||||
if (BURROW_URL && !$pubkey) {
|
||||
if ($page.url.pathname === "/confirm-email") {
|
||||
pushModal(EmailConfirm, {
|
||||
email: $page.url.searchParams.get("email"),
|
||||
token: $page.url.searchParams.get("token"),
|
||||
confirm_token: $page.url.searchParams.get("confirm_token"),
|
||||
})
|
||||
}
|
||||
|
||||
if ($page.url.pathname === "/reset-password") {
|
||||
pushModal(PasswordReset, {
|
||||
email: $page.url.searchParams.get("email"),
|
||||
reset_token: $page.url.searchParams.get("reset_token"),
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex h-screen overflow-hidden">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import {BURROW_URL} from "@app/state"
|
||||
|
||||
export let email
|
||||
export let token
|
||||
export let confirm_token
|
||||
|
||||
const login = () => {
|
||||
pushModal(LogInPassword, {email}, {path: "/"})
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
onMount(async () => {
|
||||
const [res] = await Promise.all([
|
||||
postJson(BURROW_URL + "/user/confirm", {email, token}),
|
||||
postJson(BURROW_URL + "/user/confirm-email", {email, confirm_token}),
|
||||
sleep(2000),
|
||||
])
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import {onMount, onDestroy} from "svelte"
|
||||
import {postJson, stripProtocol} from "@welshman/lib"
|
||||
import {Nip46Broker, makeSecret} from "@welshman/signer"
|
||||
import {normalizeRelayUrl} from "@welshman/util"
|
||||
import {addSession} from "@welshman/app"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
@@ -9,8 +10,9 @@
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import PasswordResetRequest from "@app/components/PasswordResetRequest.svelte"
|
||||
import {loadUserData} from "@app/commands"
|
||||
import {clearModals} from "@app/modal"
|
||||
import {clearModals, pushModal} 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"
|
||||
@@ -19,9 +21,11 @@
|
||||
|
||||
const clientSecret = makeSecret()
|
||||
|
||||
const startReset = () => pushModal(PasswordResetRequest, {email})
|
||||
|
||||
const abortController = new AbortController()
|
||||
|
||||
const relays = ["ws://" + stripProtocol(BURROW_URL)]
|
||||
const relays = [normalizeRelayUrl("ws://" + stripProtocol(BURROW_URL))]
|
||||
|
||||
const broker = Nip46Broker.get({clientSecret, relays})
|
||||
|
||||
@@ -30,12 +34,17 @@
|
||||
const onSubmit = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
const res = await postJson(BURROW_URL + "/session", {email, password, nostrconnect: url})
|
||||
|
||||
if (res.error) {
|
||||
pushToast({message: res.error, theme: "error"})
|
||||
loading = false
|
||||
}
|
||||
} catch (e) {
|
||||
pushToast({message: "Something went wrong, please try again!", theme: "error"})
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
let url = ""
|
||||
@@ -91,7 +100,7 @@
|
||||
<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>
|
||||
<div slot="info">Log in using your email and password</div>
|
||||
</ModalHeader>
|
||||
<FieldInline>
|
||||
<p slot="label">Email</p>
|
||||
@@ -107,9 +116,10 @@
|
||||
<input bind:value={password} type="password" />
|
||||
</label>
|
||||
</FieldInline>
|
||||
<p class="text-sm opacity-75">
|
||||
<p class="text-sm">
|
||||
Your email and password only work to log in to {PLATFORM_NAME}. To use your key on other nostr
|
||||
applications, visit your settings page.
|
||||
applications, visit your settings page. <Button class="link" on:click={startReset}
|
||||
>Forgot your password?</Button>
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" on:click={back} disabled={loading}>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<div slot="info">Learn about {PLATFORM_NAME} and support the developer</div>
|
||||
</CardButton>
|
||||
</Link>
|
||||
<Button on:click={logout} class="btn btn-error">
|
||||
<Button on:click={logout} class="btn btn-neutral">
|
||||
<Icon icon="exit" /> Log Out
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
61
src/app/components/PasswordReset.svelte
Normal file
61
src/app/components/PasswordReset.svelte
Normal file
@@ -0,0 +1,61 @@
|
||||
<script lang="ts">
|
||||
import {postJson, sleep} from "@welshman/lib"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import LogInPassword from "@app/components/LogInPassword.svelte"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {BURROW_URL} from "@app/state"
|
||||
|
||||
export let email
|
||||
export let reset_token
|
||||
|
||||
const onSubmit = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
const [res] = await Promise.all([
|
||||
postJson(BURROW_URL + "/user/confirm-reset", {email, password, reset_token}),
|
||||
sleep(1000),
|
||||
])
|
||||
|
||||
if (res.error) {
|
||||
pushToast({message: res.error, theme: "error"})
|
||||
} else {
|
||||
pushToast({message: "Password reset successfully!"})
|
||||
pushModal(LogInPassword, {email}, {path: "/"})
|
||||
}
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
let loading = false
|
||||
let password = ""
|
||||
</script>
|
||||
|
||||
<form class="column gap-4" on:submit|preventDefault={onSubmit}>
|
||||
<ModalHeader>
|
||||
<div slot="title">Reset your password</div>
|
||||
</ModalHeader>
|
||||
<FieldInline disabled={loading}>
|
||||
<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={email} class="grow" />
|
||||
</label>
|
||||
</FieldInline>
|
||||
<FieldInline disabled={loading}>
|
||||
<p slot="label">New Password</p>
|
||||
<label class="input input-bordered flex w-full items-center gap-2" slot="input">
|
||||
<Icon icon="key" />
|
||||
<input bind:value={password} class="grow" type="password" />
|
||||
</label>
|
||||
</FieldInline>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
<Spinner {loading}>Reset password</Spinner>
|
||||
</Button>
|
||||
</form>
|
||||
62
src/app/components/PasswordResetRequest.svelte
Normal file
62
src/app/components/PasswordResetRequest.svelte
Normal file
@@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
import {postJson, sleep} from "@welshman/lib"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import LogInPassword from "@app/components/LogInPassword.svelte"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {BURROW_URL} from "@app/state"
|
||||
|
||||
export let email: string
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const onSubmit = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
const [res] = await Promise.all([
|
||||
postJson(BURROW_URL + "/user/request-reset", {email}),
|
||||
sleep(1000),
|
||||
])
|
||||
|
||||
if (res.error) {
|
||||
pushToast({message: res.error, theme: "error"})
|
||||
} else {
|
||||
pushToast({message: `Password reset email has been sent!`})
|
||||
pushModal(LogInPassword, {email}, {path: "/"})
|
||||
}
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
let loading = false
|
||||
</script>
|
||||
|
||||
<form class="column gap-4" on:submit|preventDefault={onSubmit}>
|
||||
<ModalHeader>
|
||||
<div slot="title">Reset your password</div>
|
||||
</ModalHeader>
|
||||
<FieldInline disabled={loading}>
|
||||
<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 bind:value={email} class="grow" />
|
||||
</label>
|
||||
<p slot="info">You'll be sent an email with a password reset link.</p>
|
||||
</FieldInline>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" on:click={back}>
|
||||
<Icon icon="alt-arrow-left" />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
<Spinner {loading}>Request password reset link</Spinner>
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
@@ -1 +0,0 @@
|
||||
hi
|
||||
Reference in New Issue
Block a user