160 lines
6.7 KiB
JavaScript
160 lines
6.7 KiB
JavaScript
(function(root, factory) {
|
|
if (typeof module === 'object' && module.exports) module.exports = factory;
|
|
else root.createPushNotifications = factory;
|
|
})(typeof self !== 'undefined' ? self : this, function createPushNotifications({
|
|
control, status, deadlineControl, deadlineStatus, deadlineHour, deadlineDays, notification, serviceWorker, fetchJson,
|
|
}) {
|
|
let configuration = null;
|
|
|
|
function formattedHour(value) {
|
|
return `${String(Number(value)).padStart(2, '0')}:00`;
|
|
}
|
|
|
|
function enabledDeadlineText(hour, days) {
|
|
const value = Number(days ?? 2);
|
|
const horizon = value === 0 ? 'due today' : `next ${value} days`;
|
|
return `Deadline reminders enabled for ${formattedHour(hour)} local time, ${horizon}.`;
|
|
}
|
|
|
|
function deadlineReadiness() {
|
|
if (!configuration?.available || deadlineControl?.disabled) {
|
|
return {state:'unavailable', detail:deadlineStatus?.textContent || 'Deadline reminders are unavailable.'};
|
|
}
|
|
return configuration.deadline_enabled
|
|
? {state:'complete', detail:enabledDeadlineText(configuration.reminder_hour, configuration.reminder_days)}
|
|
: {state:'incomplete', detail:deadlineStatus?.textContent || 'Choose when to receive deadline reminders.'};
|
|
}
|
|
|
|
function applicationServerKey(value) {
|
|
const padded = value.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - value.length % 4) % 4);
|
|
if (typeof atob === 'function') {
|
|
return Uint8Array.from(atob(padded), character => character.charCodeAt(0));
|
|
}
|
|
return Uint8Array.from(Buffer.from(padded, 'base64'));
|
|
}
|
|
|
|
async function disable() {
|
|
const registration = await serviceWorker.ready;
|
|
const subscription = await registration.pushManager.getSubscription();
|
|
await fetchJson('api/v1/push-subscription', {method:'DELETE'});
|
|
await subscription?.unsubscribe?.();
|
|
control.checked = false;
|
|
if (deadlineControl) deadlineControl.checked = false;
|
|
status.textContent = 'New update notifications are off for this device.';
|
|
if (deadlineStatus) deadlineStatus.textContent = 'Deadline reminders are off for this device.';
|
|
}
|
|
|
|
async function ensureSubscription() {
|
|
const permission = await notification.requestPermission();
|
|
if (permission !== 'granted') {
|
|
control.checked = false;
|
|
status.textContent = 'Notifications are blocked. Allow them in your browser settings to enable updates.';
|
|
return null;
|
|
}
|
|
const registration = await serviceWorker.ready;
|
|
let subscription = await registration.pushManager.getSubscription();
|
|
if (!subscription) {
|
|
subscription = await registration.pushManager.subscribe({
|
|
userVisibleOnly: true,
|
|
applicationServerKey: applicationServerKey(configuration.public_key),
|
|
});
|
|
}
|
|
await fetchJson('api/v1/push-subscription', {
|
|
method:'PUT',
|
|
headers:{'Content-Type':'application/json'},
|
|
body:JSON.stringify(subscription.toJSON()),
|
|
});
|
|
control.checked = true;
|
|
status.textContent = 'New update notifications enabled for this device.';
|
|
configuration.subscribed = true;
|
|
return subscription;
|
|
}
|
|
|
|
async function enable() {
|
|
await ensureSubscription();
|
|
}
|
|
|
|
async function change() {
|
|
control.disabled = true;
|
|
try {
|
|
if (control.checked) await enable();
|
|
else await disable();
|
|
} catch (error) {
|
|
control.checked = !control.checked;
|
|
status.textContent = 'Could not change update notifications. Check your connection and try again.';
|
|
} finally {
|
|
control.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function changeDeadline() {
|
|
deadlineControl.disabled = true;
|
|
if (deadlineHour) deadlineHour.disabled = true;
|
|
if (deadlineDays) deadlineDays.disabled = true;
|
|
try {
|
|
const registration = await serviceWorker.ready;
|
|
let subscription = await registration.pushManager.getSubscription();
|
|
if (deadlineControl.checked && !subscription) subscription = await ensureSubscription();
|
|
if (deadlineControl.checked && !subscription) {
|
|
deadlineControl.checked = false;
|
|
deadlineStatus.textContent = status.textContent;
|
|
return false;
|
|
}
|
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
|
|
const reminderHour = Number(deadlineHour?.value ?? configuration?.reminder_hour ?? 9);
|
|
const reminderDays = Number(deadlineDays?.value ?? configuration?.reminder_days ?? 2);
|
|
await fetchJson('api/v1/push-subscription/deadlines', {
|
|
method:'PUT',
|
|
headers:{'Content-Type':'application/json'},
|
|
body:JSON.stringify({enabled:deadlineControl.checked, timezone, reminder_hour:reminderHour, reminder_days:reminderDays}),
|
|
});
|
|
configuration.deadline_enabled = deadlineControl.checked;
|
|
configuration.reminder_hour = reminderHour;
|
|
configuration.reminder_days = reminderDays;
|
|
configuration.timezone = timezone;
|
|
deadlineStatus.textContent = deadlineControl.checked
|
|
? enabledDeadlineText(reminderHour, reminderDays)
|
|
: 'Deadline reminders are off for this device.';
|
|
return true;
|
|
} catch (error) {
|
|
deadlineControl.checked = !deadlineControl.checked;
|
|
deadlineStatus.textContent = 'Could not change deadline reminders. Check your connection and try again.';
|
|
return false;
|
|
} finally {
|
|
deadlineControl.disabled = false;
|
|
if (deadlineHour) deadlineHour.disabled = false;
|
|
if (deadlineDays) deadlineDays.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function enableDeadline() {
|
|
deadlineControl.checked = true;
|
|
return changeDeadline();
|
|
}
|
|
|
|
async function init() {
|
|
if (!control || !notification || !serviceWorker) return;
|
|
control.addEventListener('change', change);
|
|
deadlineControl?.addEventListener('change', changeDeadline);
|
|
configuration = await fetchJson('api/v1/push-subscription');
|
|
if (!configuration.available) {
|
|
control.disabled = true;
|
|
if (deadlineControl) deadlineControl.disabled = true;
|
|
status.textContent = 'New update notifications are not available on this server.';
|
|
return;
|
|
}
|
|
control.checked = Boolean(configuration.subscribed);
|
|
if (deadlineControl) deadlineControl.checked = Boolean(configuration.deadline_enabled);
|
|
if (deadlineHour) deadlineHour.value = String(configuration.reminder_hour ?? 9);
|
|
if (deadlineDays) deadlineDays.value = String(configuration.reminder_days ?? 2);
|
|
status.textContent = configuration.subscribed
|
|
? 'New update notifications enabled for this device.'
|
|
: 'New update notifications are off for this device.';
|
|
if (deadlineStatus) deadlineStatus.textContent = configuration.deadline_enabled
|
|
? enabledDeadlineText(configuration.reminder_hour, configuration.reminder_days)
|
|
: 'Deadline reminders are off for this device.';
|
|
}
|
|
|
|
return {init, change, changeDeadline, enableDeadline, deadlineReadiness};
|
|
});
|