refactor(web): runMutation helper for generic-toast admin handlers (#375)

Wraps the dominant pattern (try { await fn } catch { pushToast(errMessage,
'error') }) for the 5 admin/+page.svelte handlers that surface errMessage
directly: onApprove, onReject, onResolve, onDeleteFile, onDeleteLidarr.

Tighter scope than originally planned — the 3 handlers in
admin/users/+page.svelte (onToggleAutoApprove, onGenerateInvite,
onRevokeInvite) use errCode + verb prefix ("Generate failed: ${code}")
which is structurally different and would change UX wire output if
forced through the helper. Skipping them keeps behavior identical.

The helper SWALLOWS errors so callers no longer need try/finally for
busy-state — `saving = true; await runMutation(...); saving = false;`
is correct because runMutation can't throw.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 15:39:00 -04:00
parent 0eaaf66748
commit 6e0223a9b1
2 changed files with 40 additions and 21 deletions
+28
View File
@@ -0,0 +1,28 @@
// Wraps the dominant admin-handler mutation pattern:
// try { await fn(); pushToast(success?); }
// catch (e) { pushToast(errMessage(e), 'error'); }
//
// Errors are SWALLOWED — caller does not need a try/catch. Any
// `saving = true / saving = false` state flag can sit around the
// `await runMutation(...)` call without try/finally because the
// helper never rethrows.
//
// Use ONLY for handlers that surface errMessage(e) directly.
// Handlers that branch on specific error codes (last_admin,
// password_too_short) or build custom copy with errCode keep
// their inline catch — wrapping those would obscure intent.
import { errMessage } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
export async function runMutation(
fn: () => Promise<unknown>,
opts: { successMessage?: string } = {}
): Promise<void> {
try {
await fn();
if (opts.successMessage) pushToast(opts.successMessage);
} catch (e) {
pushToast(errMessage(e), 'error');
}
}