Merge branch 'dev' into email-notifications
Some checks failed
CI / lint-check-build (pull_request) Has been cancelled

This commit is contained in:
mplorentz 2026-08-24 14:40:44 -04:00
commit c0f545350c
3 changed files with 64 additions and 12 deletions

View file

@ -15,21 +15,46 @@ export class MailNotifications {
const $user = User.require(app.get())
const $signer = $user.signer
if (!$signer) throw new Error("No signer available")
if (!$signer) {
emailNotificationState.set({error: "No signer available. Check your browser extension."})
throw new Error("No signer available")
}
if (!EMAIL_NOTIFICATION_SERVER) {
emailNotificationState.set({error: "Email notification server is not configured."})
throw new Error("Email notification server not configured")
}
let authEvent
try {
authEvent = await $signer.sign(await makeHttpAuth(EMAIL_NOTIFICATION_SERVER, "POST"))
} catch (e) {
emailNotificationState.set({error: "Failed to create authentication. Check your signer."})
throw new Error("Failed to sign NIP-98 auth", {cause: e})
}
const authEvent = await $signer.sign(await makeHttpAuth(EMAIL_NOTIFICATION_SERVER, "POST"))
const authHeader = makeHttpAuthHeader(authEvent)
const res = await fetch(buildUrl(EMAIL_NOTIFICATION_SERVER, "subscription", "email"), {
let res
try {
res = await fetch(buildUrl(EMAIL_NOTIFICATION_SERVER, "subscription", "email"), {
method: "POST",
body: JSON.stringify({email, frequency, pubkey: $user.pubkey}),
headers: {Authorization: authHeader, "Content-Type": "application/json"},
})
} catch (e) {
emailNotificationState.set({error: `Could not reach email server. Check your connection.`})
throw new Error("Network error", {cause: e})
}
if (!res.ok) throw new Error(`Registration failed: ${await res.text()}`)
if (!res.ok) {
const text = await res.text().catch(() => "unknown error")
emailNotificationState.set({error: `Registration failed: ${text}`})
throw new Error(`Registration failed: ${text}`)
}
const {key, callback} = await res.json()
emailNotificationState.set({key, callback, email, frequency})
emailNotificationState.set({key, callback, email, frequency, error: undefined})
}
async unregister() {

View file

@ -45,6 +45,7 @@ export type EmailNotificationState = {
callback?: string
email?: string
frequency?: string
error?: string
}
export const emailNotificationState = withGetter(writable<EmailNotificationState>({}))

View file

@ -16,9 +16,17 @@
import {Push} from "@app/push"
import {Mail} from "@app/mail"
import {notificationSettings} from "@app/settings"
import {emailNotificationState} from "@app/push/adapters/common"
const clearError = () => {
if ($emailNotificationState.error) {
emailNotificationState.set({...emailNotificationState.get(), error: undefined})
}
}
const reset = () => {
settings = {...notificationSettings.get()}
clearError()
}
const onsubmit = preventDefault(async () => {
@ -52,8 +60,17 @@
await Mail.register(settings.emailAddress, settings.emailFrequency)
await Mail.enable()
} catch (e) {
console.warn("Failed to enable email notifications:", e)
pushToast({theme: "error", message: "Failed to enable email notifications."})
settings.email = false
await sleep(300)
return pushToast({
theme: "error",
message: `Failed to enable email notifications.`,
action: {
message: "Details",
onclick: () =>
emailNotificationState.set({...emailNotificationState.get(), error: undefined}),
},
})
}
} else if (!settings.email) {
await Mail.disable()
@ -161,6 +178,7 @@
type="email"
class="input input-bordered w-full"
bind:value={settings.emailAddress}
oninput={clearError}
placeholder="you@example.com" />
{/snippet}
</FieldInline>
@ -169,13 +187,21 @@
<p>Frequency</p>
{/snippet}
{#snippet input()}
<select class="select select-bordered" bind:value={settings.emailFrequency}>
<select
class="select select-bordered"
bind:value={settings.emailFrequency}
onchange={clearError}>
<option value="daily">Daily</option>
<option value="weekly">Weekly</option>
</select>
{/snippet}
</FieldInline>
{/if}
{#if $emailNotificationState.error}
<div class="text-sm text-red-500 bg-red-50 rounded-lg p-3">
{$emailNotificationState.error}
</div>
{/if}
</div>
<div class="card sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4">
<Button class="button button-neutral" onclick={reset} disabled={loading}