import * as Notifications from 'expo-notifications'; import React from 'react'; import type { WidgetTaskHandlerProps } from 'react-native-android-widget'; import { Category, CategoryQuery } from '../../models/category'; import { Task, TaskQuery } from '../../models/task'; import { CategoryRepository } from '../../repositories/CategoryRepository'; import { TaskRepository } from '../../repositories/TaskRepository'; import { SQLiteDataService } from '../../services/data/sqliteDataService'; import { throttle } from '../../utils/throttle'; import { HelloWidget } from './hello'; const taskRepository = new TaskRepository(new SQLiteDataService(TaskQuery)); const categoryRepository = new CategoryRepository(new SQLiteDataService(CategoryQuery)); let tasks: Task[] = []; let categories: Category[] = []; Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: false, shouldShowBanner: false, shouldShowList: true, }), }); let notify_channels: Notifications.NotificationChannel[] = []; let notify_notification: Notifications.Notification | undefined = undefined; let notify_listener: Notifications.EventSubscription | null = null; let notify_responseListener: Notifications.EventSubscription | null = null; Notifications.getNotificationChannelsAsync().then((channels) => { if (channels.length === 0) { Notifications.setNotificationChannelAsync('default', { name: 'Default Channel', importance: Notifications.AndroidImportance.HIGH, sound: 'default', vibrationPattern: [0, 250, 250, 250], }).then((channel) => { if (channel) notify_channels = [channel]; }); } notify_channels = channels; }); notify_listener = Notifications.addNotificationReceivedListener(notification => { notify_notification = notification; }); notify_responseListener = Notifications.addNotificationResponseReceivedListener(response => { }); const CheckTaskAndNotify = async (): Promise<{ newTasks: Task[], newCategories: Category[] }> => { const tasks = await taskRepository.findAll(); const categories = await categoryRepository.findAll(); const expiredTasks = tasks.filter(task => { const expirationDate = task.lastDone! + (task.daysToRedo! * 24 * 60 * 60 * 1000); // Convert days to milliseconds return expirationDate < Date.now(); }); if (expiredTasks.length > 0) { for (const task of expiredTasks) { const category = categories.find(cat => cat.id === task.category); await Notifications.scheduleNotificationAsync({ identifier: `task-due-${task.id}`, content: { title: 'Task Reminder', body: `${category?.icon} "${task.title}" is due! ⌛`, data: { taskId: task.id }, }, trigger: null }); } } return { newTasks: tasks, newCategories: categories }; } const throttledCheckTaskAndNotify = throttle(CheckTaskAndNotify, 1000); // Throttle to prevent too frequent calls setInterval(async () => { const { newTasks, newCategories } = await throttledCheckTaskAndNotify(); console.log("Throttled task check and notify executed OUT"); console.log("New tasks:", newTasks); console.log("New categories:", newCategories); tasks = newTasks; categories = newCategories; }, 1000 * 60 * 30); // Check every 30 minutes export async function widgetTaskHandler(props: WidgetTaskHandlerProps) { async function updateAndRenderWidget() { const { newTasks, newCategories } = await throttledCheckTaskAndNotify(); tasks = newTasks; categories = newCategories; console.log("Throttled task check and notify executed IN"); console.log("New tasks:", newTasks); console.log("New categories:", newCategories); props.renderWidget(); } switch (props.widgetAction) { case 'WIDGET_ADDED': case 'WIDGET_UPDATE': case 'WIDGET_RESIZED': // Not needed for now updateAndRenderWidget(); break; case 'WIDGET_DELETED': // Not needed for now break; case 'WIDGET_CLICK': switch (props.clickAction) { case 'REFRESH': await updateAndRenderWidget(); break; case 'UPDATE_TASK': if (props.clickActionData && props.clickActionData.id) { const taskId = props.clickActionData.id; const task = await taskRepository.findById(parseInt(taskId.toString())); if (task) { task.lastDone = Math.floor(Date.now() / 1000 / 60 / 60 / 24); // Set last done to today await taskRepository.update(parseInt(taskId.toString()), { ...task, lastDone: new Date().getTime() }); await updateAndRenderWidget(); } } break; default: break; } } }