debounce subscription syncing
All checks were successful
CI / lint-check-build (pull_request) Successful in 4m4s

This commit is contained in:
mplorentz 2026-09-03 13:43:10 -04:00
parent cbba1035e7
commit d6f0e47edf

View file

@ -1,4 +1,4 @@
import {hash, maybe, spec, throttle} from "@welshman/lib" import {call, hash, maybe, spec, throttle} from "@welshman/lib"
import type {Filter} from "@welshman/util" import type {Filter} from "@welshman/util"
import {Address, DELETE, MESSAGE, makeEvent, makeHttpAuth, makeHttpAuthHeader} from "@welshman/util" import {Address, DELETE, MESSAGE, makeEvent, makeHttpAuth, makeHttpAuthHeader} from "@welshman/util"
import {merged} from "@welshman/store" import {merged} from "@welshman/store"
@ -75,12 +75,22 @@ const syncRelaySubscriptionsForEmail = (
export class MailNotifications { export class MailNotifications {
_controller = maybe<AbortController>() _controller = maybe<AbortController>()
// In-flight register promise, so overlapping calls (settings page submit plus
// Mail.sync) coalesce into a single PUT instead of racing two subscriptions.
_register = maybe<Promise<void>>()
async register(email: string, frequency: string) { async register(email: string, frequency: string) {
if (this._register) return this._register
this._register = call(async () => {
try {
const $user = User.require(app.get()) const $user = User.require(app.get())
const $signer = $user.signer const $signer = $user.signer
if (!$signer) { if (!$signer) {
emailNotificationState.set({error: "No signer available. Check your browser extension."}) emailNotificationState.set({
error: "No signer available. Check your browser extension.",
})
throw new Error("No signer available") throw new Error("No signer available")
} }
@ -93,7 +103,9 @@ export class MailNotifications {
try { try {
authEvent = await $signer.sign(await makeHttpAuth(EMAIL_NOTIFICATION_SERVER, "PUT")) authEvent = await $signer.sign(await makeHttpAuth(EMAIL_NOTIFICATION_SERVER, "PUT"))
} catch (e) { } catch (e) {
emailNotificationState.set({error: "Failed to create authentication. Check your signer."}) emailNotificationState.set({
error: "Failed to create authentication. Check your signer.",
})
throw new Error("Failed to sign NIP-98 auth", {cause: e}) throw new Error("Failed to sign NIP-98 auth", {cause: e})
} }
@ -107,7 +119,9 @@ export class MailNotifications {
headers: {Authorization: authHeader, "Content-Type": "application/json"}, headers: {Authorization: authHeader, "Content-Type": "application/json"},
}) })
} catch (e) { } catch (e) {
emailNotificationState.set({error: `Could not reach email server. Check your connection.`}) emailNotificationState.set({
error: `Could not reach email server. Check your connection.`,
})
throw new Error("Network error", {cause: e}) throw new Error("Network error", {cause: e})
} }
@ -119,6 +133,12 @@ export class MailNotifications {
const {key, callback} = await res.json() const {key, callback} = await res.json()
emailNotificationState.set({key, callback, email, frequency, error: undefined}) emailNotificationState.set({key, callback, email, frequency, error: undefined})
} finally {
this._register = undefined
}
})
return this._register
} }
async unregister() { async unregister() {