104 lines
3.3 KiB
JavaScript
104 lines
3.3 KiB
JavaScript
(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;
|
|
let confirmedCount = 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 render() {
|
|
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 && available()) {
|
|
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 the confirmed unread Updates count.'}
|
|
: {state:'incomplete', detail:'Show the confirmed unread Updates count without opening Stackchain.'};
|
|
}
|
|
|
|
async function enable() {
|
|
if (!available()) return false;
|
|
if (control) control.checked = true;
|
|
await change();
|
|
return true;
|
|
}
|
|
|
|
async function reconcile(count, {authoritative = false} = {}) {
|
|
if (!authoritative || !Number.isSafeInteger(count) || count < 0) return false;
|
|
confirmedCount = count;
|
|
return render();
|
|
}
|
|
|
|
return {start, change, reconcile, readiness, enable};
|
|
});
|