(function(root, factory) { if (typeof module === 'object' && module.exports) module.exports = factory; else root.createMobileAppBadge = factory; })(typeof self !== 'undefined' ? self : this, function createMobileAppBadge({ control, status, container, navigator, storage, serviceWorker, }) { const ENABLED_KEY = 'stackchain.app-badge.enabled.v1'; let enabled = false; const confirmedCounts = {updates:0, following:0, 'human-gates':0}; let renderedCount = null; function available() { return typeof navigator?.setAppBadge === 'function' && typeof navigator?.clearAppBadge === 'function'; } async function syncPreference() { try { const registration = await serviceWorker?.ready; registration?.active?.postMessage({type:'stackchain-app-badge-preference', enabled}); return Boolean(registration?.active); } catch (_error) { return false; } } async function syncCount(channel, count) { if (!enabled) return; try { const registration = await serviceWorker?.ready; registration?.active?.postMessage({type:'stackchain-app-badge-count', channel, count}); } catch (_error) { /* A later refresh can retry. */ } } async function render() { const confirmedCount = Math.min( 9999, confirmedCounts.updates + confirmedCounts.following + confirmedCounts['human-gates'] ); if (!enabled || !available() || renderedCount === confirmedCount) return true; try { if (confirmedCount > 0) await navigator.setAppBadge(confirmedCount); else await navigator.clearAppBadge(); renderedCount = confirmedCount; if (status) status.textContent = 'App icon badge is on and up to date.'; return true; } catch (_error) { if (status) status.textContent = 'Could not update the app icon badge. Stackchain still has the confirmed count.'; return false; } } async function change() { enabled = Boolean(control?.checked); if (enabled) storage?.setItem(ENABLED_KEY, 'true'); else storage?.removeItem(ENABLED_KEY); await syncPreference(); if (enabled) { if (confirmedCounts.updates > 0) await syncCount('updates', confirmedCounts.updates); if (confirmedCounts.following > 0) await syncCount('following', confirmedCounts.following); if (confirmedCounts['human-gates'] > 0) await syncCount('human-gates', confirmedCounts['human-gates']); } if (!enabled && available()) { confirmedCounts.updates = 0; confirmedCounts.following = 0; confirmedCounts['human-gates'] = 0; try { await navigator.clearAppBadge(); renderedCount = null; if (status) status.textContent = 'App icon badge is off.'; return true; } catch (_error) { if (status) status.textContent = 'App icon badge is off, but the browser could not clear the old count.'; return false; } } return render(); } function start() { if (!available()) { if (container) container.hidden = true; if (control) control.disabled = true; if (status) status.textContent = 'App icon badges are unavailable in this browser.'; return false; } enabled = storage?.getItem(ENABLED_KEY) === 'true'; if (enabled) void syncPreference(); if (control) { control.checked = enabled; control.addEventListener('change', change); } if (status) status.textContent = enabled ? 'App icon badge is on.' : 'App icon badge is off.'; return true; } function readiness() { if (!available()) return {state:'unavailable', detail:'App icon badges are unavailable in this browser.'}; return enabled ? {state:'complete', detail:'The app icon shows review attention.'} : {state:'incomplete', detail:'Show review attention without opening Stackchain.'}; } async function enable() { if (!available()) return false; if (control) control.checked = true; await change(); return true; } async function reconcile(channel, count, authoritative = false) { if (!Object.hasOwn(confirmedCounts, channel) || authoritative !== true || !Number.isSafeInteger(count) || count < 0 || count > 9999) return false; confirmedCounts[channel] = count; await syncCount(channel, count); return render(); } return {start, change, reconcile, readiness, enable}; });