diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx index beca90c..d9f63af 100644 --- a/app/(tabs)/settings.tsx +++ b/app/(tabs)/settings.tsx @@ -2,13 +2,14 @@ import { useState, useEffect, useCallback } from 'react'; import { View, Text, Pressable, useColorScheme, TextInput, ScrollView, Alert, Modal, Platform, Switch, Linking, ActivityIndicator } from 'react-native'; import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; import { useTranslation } from 'react-i18next'; -import { Sun, Moon, Smartphone, Plus, Trash2, Pencil, Bell, CalendarDays, Mail, RefreshCw } from 'lucide-react-native'; +import { Sun, Moon, Smartphone, Plus, Trash2, Pencil, Bell, CalendarDays, LayoutGrid, Mail, RefreshCw } from 'lucide-react-native'; import Constants from 'expo-constants'; import { colors } from '@/src/theme/colors'; import { useSettingsStore } from '@/src/stores/useSettingsStore'; import { getAllTags, createTag, updateTag, deleteTag } from '@/src/db/repository/tags'; import { initCalendar } from '@/src/services/calendar'; +import { syncWidgetData } from '@/src/services/widgetSync'; import i18n from '@/src/i18n'; type ThemeMode = 'light' | 'dark' | 'system'; @@ -23,6 +24,7 @@ export default function SettingsScreen() { notificationsEnabled, setNotificationsEnabled, reminderOffset, setReminderOffset, calendarSyncEnabled, setCalendarSyncEnabled, + widgetPeriodWeeks, setWidgetPeriodWeeks, } = useSettingsStore(); const isDark = (theme === 'system' ? systemScheme : theme) === 'dark'; @@ -299,6 +301,56 @@ export default function SettingsScreen() { + {/* Widget Section */} + + + {t('widget.title')} + + + + + + + {t('widget.period')} + + + + {[ + { value: 1, label: t('widget.periodWeek', { count: 1 }) }, + { value: 2, label: t('widget.periodWeek', { count: 2 }) }, + { value: 4, label: t('widget.periodWeek', { count: 4 }) }, + { value: 0, label: t('widget.periodAll') }, + ].map((opt) => { + const isActive = widgetPeriodWeeks === opt.value; + return ( + { + setWidgetPeriodWeeks(opt.value); + syncWidgetData(); + }} + className={`rounded-full px-3 py-1.5 ${isActive ? 'bg-bleu' : isDark ? 'bg-[#3A3A3A]' : 'bg-[#E5E7EB]'}`} + > + + {opt.label} + + + ); + })} + + + + + {/* Tags Section */} diff --git a/src/i18n/en.json b/src/i18n/en.json index 5ecf688..b8626be 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -138,6 +138,10 @@ "overdue": "Overdue", "today": "Today", "tomorrow": "Tomorrow", - "noDate": "No date" + "noDate": "No date", + "period": "Display period", + "periodWeek_one": "{{count}} week", + "periodWeek_other": "{{count}} weeks", + "periodAll": "All" } } diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 738d9e3..f029c31 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -138,6 +138,10 @@ "overdue": "En retard", "today": "Aujourd'hui", "tomorrow": "Demain", - "noDate": "Sans date" + "noDate": "Sans date", + "period": "Période affichée", + "periodWeek_one": "{{count}} semaine", + "periodWeek_other": "{{count}} semaines", + "periodAll": "Toutes" } } diff --git a/src/services/widgetSync.ts b/src/services/widgetSync.ts index e60c623..38755dd 100644 --- a/src/services/widgetSync.ts +++ b/src/services/widgetSync.ts @@ -34,7 +34,20 @@ export async function syncWidgetData(): Promise { try { const now = new Date(); const todayStart = startOfDay(now); - const twoWeeksEnd = endOfDay(addWeeks(now, 2)); + + // Read widget period setting from AsyncStorage (0 = all, N = N weeks ahead) + // Coupled with useSettingsStore.ts — key 'simpl-liste-settings', path state.widgetPeriodWeeks + let widgetPeriodWeeks = 0; + try { + const settingsRaw = await AsyncStorage.getItem('simpl-liste-settings'); + if (settingsRaw) { + const settings = JSON.parse(settingsRaw); + const stored = settings?.state?.widgetPeriodWeeks; + if (typeof stored === 'number') widgetPeriodWeeks = stored; + } + } catch { + // Default to all tasks + } const selectFields = { id: tasks.id, @@ -48,19 +61,20 @@ export async function syncWidgetData(): Promise { subtaskDoneCount: sql`(SELECT COUNT(*) FROM tasks AS sub WHERE sub.parent_id = ${tasks.id} AND sub.completed = 1)`.as('subtask_done_count'), }; - // Fetch tasks with due date in the next 2 weeks + // Fetch upcoming tasks (filtered by period setting, 0 = all future tasks) + const upcomingConditions = [ + eq(tasks.completed, false), + isNull(tasks.parentId), + gte(tasks.dueDate, todayStart), + ]; + if (widgetPeriodWeeks > 0) { + upcomingConditions.push(lte(tasks.dueDate, endOfDay(addWeeks(now, widgetPeriodWeeks)))); + } const upcomingTasks = await db .select(selectFields) .from(tasks) .leftJoin(lists, eq(tasks.listId, lists.id)) - .where( - and( - eq(tasks.completed, false), - isNull(tasks.parentId), - gte(tasks.dueDate, todayStart), - lte(tasks.dueDate, twoWeeksEnd) - ) - ) + .where(and(...upcomingConditions)) .orderBy(asc(tasks.dueDate)); // Fetch overdue tasks diff --git a/src/stores/useSettingsStore.ts b/src/stores/useSettingsStore.ts index ee5963d..c9998df 100644 --- a/src/stores/useSettingsStore.ts +++ b/src/stores/useSettingsStore.ts @@ -10,11 +10,13 @@ interface SettingsState { notificationsEnabled: boolean; reminderOffset: number; // hours before due date (0 = at time) calendarSyncEnabled: boolean; + widgetPeriodWeeks: number; // 0 = all tasks, otherwise number of weeks ahead setTheme: (theme: ThemeMode) => void; setLocale: (locale: 'fr' | 'en') => void; setNotificationsEnabled: (enabled: boolean) => void; setReminderOffset: (offset: number) => void; setCalendarSyncEnabled: (enabled: boolean) => void; + setWidgetPeriodWeeks: (weeks: number) => void; } export const useSettingsStore = create()( @@ -25,11 +27,13 @@ export const useSettingsStore = create()( notificationsEnabled: true, reminderOffset: 0, calendarSyncEnabled: false, + widgetPeriodWeeks: 0, setTheme: (theme) => set({ theme }), setLocale: (locale) => set({ locale }), setNotificationsEnabled: (notificationsEnabled) => set({ notificationsEnabled }), setReminderOffset: (reminderOffset) => set({ reminderOffset }), setCalendarSyncEnabled: (calendarSyncEnabled) => set({ calendarSyncEnabled }), + setWidgetPeriodWeeks: (widgetPeriodWeeks) => set({ widgetPeriodWeeks }), }), { name: 'simpl-liste-settings', diff --git a/src/widgets/TaskListWidget.tsx b/src/widgets/TaskListWidget.tsx index ccfe348..c084192 100644 --- a/src/widgets/TaskListWidget.tsx +++ b/src/widgets/TaskListWidget.tsx @@ -380,17 +380,14 @@ function SmallWidget({ tasks, isDark }: { tasks: WidgetTask[]; isDark: boolean } function ListWidgetContent({ tasks, - maxItems, isDark, expandedTaskIds, }: { tasks: WidgetTask[]; - maxItems: number; isDark: boolean; expandedTaskIds: Set; }) { const c = getColors(isDark); - const displayTasks = tasks.slice(0, maxItems); return ( - {/* Task list */} - {displayTasks.length > 0 ? ( + {/* Task list — cap at 30 items to avoid Android widget memory limits */} + {tasks.length > 0 ? ( - {displayTasks.map((task) => ( + {tasks.slice(0, 30).map((task) => ( ; } - const maxItems = widgetName === 'SimplListeLarge' ? 8 : 4; return (