AppServices.register('todos', { _nudgeTimers: [], initialize() { AppServices.registerTask('todos', 'update-badge', { intervalMs: 5 * 60 * 1000, run: async ({ apiJson }) => { const data = await apiJson('/app/todos/api/badge-count'); if (!data || !Number.isFinite(data.count)) { return undefined; } return data.count; }, onSuccess: (count) => { if (count === undefined) { return; } this.updateBadge(count); } }); AppServices.registerTask('todos', 'check-nudges', { run: async ({ apiJson }) => { const data = await apiJson('/app/todos/api/nudges'); if (!data || !Array.isArray(data.nudges)) { return undefined; } this.checkNudges(data.nudges); return data.nudges.length; } }); }, updateBadge(count) { if (count > 0) { AppServices.badges.app.set('todos', count); } else { AppServices.badges.app.clear('todos'); } }, checkNudges(nudges) { // Clear existing timers this._nudgeTimers.forEach(t => clearTimeout(t)); this._nudgeTimers = []; const now = Date.now(); for (const nudge of nudges) { const nudgeTime = this._parseNudge(nudge.nudge); const delay = nudgeTime - now; if (delay > 0 && delay < 24 * 60 * 60 * 1000) { const timer = setTimeout(() => { AppServices.notifications.show({ app: 'todos', icon: '✅', title: 'Todo Reminder', message: nudge.text, action: `/app/todos/${nudge.day}`, facet: nudge.facet, autoDismiss: 30000, }); }, delay); this._nudgeTimers.push(timer); } } }, _parseNudge(nudgeStr) { // YYYYMMDDTHH:MM -> timestamp const year = parseInt(nudgeStr.slice(0, 4)); const month = parseInt(nudgeStr.slice(4, 6)) - 1; const day = parseInt(nudgeStr.slice(6, 8)); const hour = parseInt(nudgeStr.slice(9, 11)); const minute = parseInt(nudgeStr.slice(12, 14)); return new Date(year, month, day, hour, minute).getTime(); } });