Email digest: GET-first restore on boot so reloads stop re-sending confirmations
All checks were successful
CI / lint-check-build (pull_request) Successful in 3m25s

Mail.sync's boot reconcile now GETs the existing subscription (NIP-98
auth) before deciding what to do. When a subscription already exists for
the pubkey with the same email and frequency, its key/callback are
restored in memory and the PUT is skipped entirely. Only a matching
subscription missing the requested params triggers a PUT, so a page
refresh of an unconfirmed address no longer makes mailship re-send a
confirmation email (previously each reload sent one).
This commit is contained in:
mplorentz 2026-09-22 12:31:56 -04:00
parent 63cf801ef1
commit 609a2f65ea

View file

@ -150,6 +150,42 @@ export class MailNotifications {
return this._register
}
// Fetch the existing subscription for this pubkey (if any) without creating
// or changing anything. Used at boot to restore state instead of re-PUTting,
// which would otherwise re-send a confirmation email for an unconfirmed
// address on every reload.
async lookup(): Promise<
| {
key: string
callback: string
email: string
frequency: string
confirmed: boolean
}
| undefined
> {
const $signer = app.get().user?.signer
if (!$signer || !EMAIL_NOTIFICATION_SERVER) return undefined
try {
const authEvent = await $signer.sign(
await makeHttpAuth(buildUrl(EMAIL_NOTIFICATION_SERVER, "subscription", "email"), "GET"),
)
const authHeader = makeHttpAuthHeader(authEvent)
const res = await fetch(buildUrl(EMAIL_NOTIFICATION_SERVER, "subscription", "email"), {
method: "GET",
headers: {Authorization: authHeader},
})
if (!res.ok) return undefined
return await res.json()
} catch {
return undefined
}
}
async unregister() {
const {key} = emailNotificationState.get()
if (!key) return
@ -231,8 +267,27 @@ export class MailNotifications {
reconciledPubkey = $pubkey
// GET-first: if a subscription already exists for this pubkey with the
// same email and cadence, restore its state (key/callback) instead of
// PUTting. A PUT here would make mailship re-send a confirmation email
// for an unconfirmed address on every app reload. Only register when
// there's nothing to restore or the settings changed.
const existing = await this.lookup()
try {
await this.register(emailAddress, emailFrequency)
if (!existing) {
await this.register(emailAddress, emailFrequency)
} else if (existing.email !== emailAddress || existing.frequency !== emailFrequency) {
await this.register(emailAddress, emailFrequency)
} else {
emailNotificationState.set({
key: existing.key,
callback: existing.callback,
email: existing.email,
frequency: existing.frequency,
error: undefined,
})
}
} catch {
return
}