From 8a329eb962eb98e89b41708d92b3f9298e4c21e1 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Fri, 2 May 2025 02:13:19 -0400 Subject: Warn the user before navigating away, when the outbox has messages in it. This is in lieu of saving the outbox. I tried that, and: * If messages are dropped from the saved outbox before calling `api.postToChannel`, then messages "in flight" are lost when the page is reloaded unless the send succeeds after the client vanishes, as they are not re-sent when the page loads. * If messages are dropped from the saved outbox after calling `api.postToChannel`, then messages "in flight" are duplicated when the page is reloaded and they get re-sent. The CAP theorem is real and can hurt you. The appropriate compensating mechanism would be a client-generated per-operation nonce, with server-side support for replaying responses by nonce if an operation already completed. That's a pretty big undertaking, and it's one we should probably do, but it's larger than I want to take on right now. Instead, we warn the user, and they can make their own decision. Except we don't, sometimes. When the client runs in a browser, this event handler prompts the user for confirmation before reloading, navigating away, closing the tab, or quitting. When run in a Safari app container, though, it only warns before reloading. Closing the window or quitting the app do not provoke a prompt. The warning is "best" effort. The failure mode is lost messages, which isn't particularly best. --- ui/routes/(app)/+layout.svelte | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'ui/routes/(app)') diff --git a/ui/routes/(app)/+layout.svelte b/ui/routes/(app)/+layout.svelte index 1ba3fa9..116767d 100644 --- a/ui/routes/(app)/+layout.svelte +++ b/ui/routes/(app)/+layout.svelte @@ -14,7 +14,7 @@ let gesture = null; const { data, children } = $props(); - const { session } = data; + const { session, outbox } = data; onMount(session.begin.bind(session)); onDestroy(session.end.bind(session)); @@ -89,8 +89,44 @@ async function createChannel(name) { await api.createChannel(name); } + + function onbeforeunload(event) { + if (outbox.pending.length > 0) { + // Prompt the user that they have unsaved (unsent) messages that may be lost. + event.preventDefault(); + } + } + + + pilcrow -- cgit v1.2.3