今天讓 SyncETA Lite 具備推播通知能力,在資料出現異常延誤時主動提醒使用者。
完整的 Web Push 需要後端伺服器搭配 Push API,設定較複雜;今天我們先實作本機通知(Notification API) ,資料在瀏覽器仍在執行時觸發提醒,適合我們目前的前端專案規模。之後如果要做到「App 沒開也能收到通知」,才需要額外導入 Push 服務。
// src/main.js
async function requestNotificationPermission() {
if (!('Notification' in window)) return false;
const permission = await Notification.requestPermission();
return permission === 'granted';
}
回到 Day 10 的輪詢邏輯,加入延誤判斷:
// src/main.js
const NORMAL_ETA_THRESHOLD = 15; // 假設超過 15 分鐘視為明顯延誤
async function loadArrivals() {
const raw = await fetchBusArrivals('Taipei', '0100000A00');
const arrivals = normalizeArrivals(raw);
renderArrivalList(arrivalContainer, arrivals);
const nextBus = arrivals[0];
if (nextBus && nextBus.etaMinutes > NORMAL_ETA_THRESHOLD) {
notifyDelay(nextBus);
}
}
function notifyDelay(bus) {
if (Notification.permission !== 'granted') return;
new Notification('SyncETA 延誤提醒', {
body: `${bus.stopName} 目前預估需要 ${bus.etaMinutes} 分鐘,建議調整出發時間`,
icon: '/icons/icon-192.png',
});
}
如果每 30 秒輪詢一次都符合延誤條件,使用者會被通知洗版。加入一個簡單的節流機制:
let lastNotifiedAt = 0;
const NOTIFY_COOLDOWN_MS = 10 * 60 * 1000; // 10 分鐘內不重複通知
function notifyDelay(bus) {
const now = Date.now();
if (now - lastNotifiedAt < NOTIFY_COOLDOWN_MS) return;
if (Notification.permission !== 'granted') return;
lastNotifiedAt = now;
new Notification('SyncETA 延誤提醒', {
body: `${bus.stopName} 目前預估需要 ${bus.etaMinutes} 分鐘,建議調整出發時間`,
icon: '/icons/icon-192.png',
});
}
這種「重要但不打擾」的通知設計原則,其實跟我研究中提到的「群組延誤通知」功能是同一個精神:只在真正需要決策調整時提醒使用者,而不是每次資料更新都發送。
明天,我們要來處理不同裝置尺寸下的畫面呈現,讓 SyncETA Lite 在手機上也一樣好用。