feat: change medium widget to 2x4, show list color indicator
Change medium widget from 4x2 (wide) to 2x4 (tall) with horizontal resize support. Add a colored vertical bar before each task showing the list color for quick visual identification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0ebc340f37
commit
e6867d84b3
4 changed files with 95 additions and 67 deletions
92
app.json
92
app.json
|
|
@ -31,49 +31,59 @@
|
|||
"expo-font",
|
||||
"expo-localization",
|
||||
"@react-native-community/datetimepicker",
|
||||
["react-native-android-widget", {
|
||||
"fonts": [
|
||||
"./assets/fonts/Inter_400Regular.ttf",
|
||||
"./assets/fonts/Inter_600SemiBold.ttf"
|
||||
],
|
||||
"widgets": [
|
||||
{
|
||||
"name": "SimplListeSmall",
|
||||
"label": "Simpl-Liste",
|
||||
"description": "Aperçu rapide de vos tâches",
|
||||
"minWidth": "110dp",
|
||||
"minHeight": "110dp",
|
||||
"targetCellWidth": 2,
|
||||
"targetCellHeight": 2,
|
||||
"updatePeriodMillis": 1800000
|
||||
},
|
||||
{
|
||||
"name": "SimplListeMedium",
|
||||
"label": "Simpl-Liste",
|
||||
"description": "Vos prochaines tâches",
|
||||
"minWidth": "250dp",
|
||||
"minHeight": "110dp",
|
||||
"targetCellWidth": 4,
|
||||
"targetCellHeight": 2,
|
||||
"resizeMode": "vertical",
|
||||
"updatePeriodMillis": 1800000
|
||||
},
|
||||
{
|
||||
"name": "SimplListeLarge",
|
||||
"label": "Simpl-Liste (grand)",
|
||||
"description": "Vue détaillée de vos tâches",
|
||||
"minWidth": "250dp",
|
||||
"minHeight": "250dp",
|
||||
"targetCellWidth": 4,
|
||||
"targetCellHeight": 4,
|
||||
"resizeMode": "horizontal|vertical",
|
||||
"updatePeriodMillis": 1800000
|
||||
}
|
||||
]
|
||||
}]
|
||||
[
|
||||
"react-native-android-widget",
|
||||
{
|
||||
"fonts": [
|
||||
"./assets/fonts/Inter_400Regular.ttf",
|
||||
"./assets/fonts/Inter_600SemiBold.ttf"
|
||||
],
|
||||
"widgets": [
|
||||
{
|
||||
"name": "SimplListeSmall",
|
||||
"label": "Simpl-Liste",
|
||||
"description": "Aperçu rapide de vos tâches",
|
||||
"minWidth": "110dp",
|
||||
"minHeight": "110dp",
|
||||
"targetCellWidth": 2,
|
||||
"targetCellHeight": 2,
|
||||
"updatePeriodMillis": 1800000
|
||||
},
|
||||
{
|
||||
"name": "SimplListeMedium",
|
||||
"label": "Simpl-Liste",
|
||||
"description": "Vos prochaines tâches",
|
||||
"minWidth": "110dp",
|
||||
"minHeight": "250dp",
|
||||
"targetCellWidth": 2,
|
||||
"targetCellHeight": 4,
|
||||
"resizeMode": "horizontal",
|
||||
"updatePeriodMillis": 1800000
|
||||
},
|
||||
{
|
||||
"name": "SimplListeLarge",
|
||||
"label": "Simpl-Liste (grand)",
|
||||
"description": "Vue détaillée de vos tâches",
|
||||
"minWidth": "250dp",
|
||||
"minHeight": "250dp",
|
||||
"targetCellWidth": 4,
|
||||
"targetCellHeight": 4,
|
||||
"resizeMode": "horizontal|vertical",
|
||||
"updatePeriodMillis": 1800000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"experiments": {
|
||||
"typedRoutes": true
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"router": {},
|
||||
"eas": {
|
||||
"projectId": "eea9da71-e467-49ba-9b59-2febc86a4d1b"
|
||||
}
|
||||
},
|
||||
"owner": "lacompagniemaximus"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Platform } from 'react-native';
|
|||
import { requestWidgetUpdate } from 'react-native-android-widget';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { db } from '../db/client';
|
||||
import { tasks } from '../db/schema';
|
||||
import { tasks, lists } from '../db/schema';
|
||||
import { eq, and, isNull, gte, lte, lt, asc } from 'drizzle-orm';
|
||||
import { startOfDay, endOfDay, addWeeks } from 'date-fns';
|
||||
import { TaskListWidget } from '../widgets/TaskListWidget';
|
||||
|
|
@ -15,6 +15,7 @@ export interface WidgetTask {
|
|||
priority: number;
|
||||
dueDate: string | null;
|
||||
completed: boolean;
|
||||
listColor: string | null;
|
||||
}
|
||||
|
||||
export async function syncWidgetData(): Promise<void> {
|
||||
|
|
@ -25,10 +26,21 @@ export async function syncWidgetData(): Promise<void> {
|
|||
const todayStart = startOfDay(now);
|
||||
const twoWeeksEnd = endOfDay(addWeeks(now, 2));
|
||||
|
||||
const selectFields = {
|
||||
id: tasks.id,
|
||||
title: tasks.title,
|
||||
priority: tasks.priority,
|
||||
dueDate: tasks.dueDate,
|
||||
completed: tasks.completed,
|
||||
position: tasks.position,
|
||||
listColor: lists.color,
|
||||
};
|
||||
|
||||
// Fetch tasks with due date in the next 2 weeks
|
||||
const upcomingTasks = await db
|
||||
.select()
|
||||
.select(selectFields)
|
||||
.from(tasks)
|
||||
.leftJoin(lists, eq(tasks.listId, lists.id))
|
||||
.where(
|
||||
and(
|
||||
eq(tasks.completed, false),
|
||||
|
|
@ -41,8 +53,9 @@ export async function syncWidgetData(): Promise<void> {
|
|||
|
||||
// Fetch overdue tasks
|
||||
const overdueTasks = await db
|
||||
.select()
|
||||
.select(selectFields)
|
||||
.from(tasks)
|
||||
.leftJoin(lists, eq(tasks.listId, lists.id))
|
||||
.where(
|
||||
and(
|
||||
eq(tasks.completed, false),
|
||||
|
|
@ -54,8 +67,9 @@ export async function syncWidgetData(): Promise<void> {
|
|||
|
||||
// Fetch tasks without a due date
|
||||
const noDateTasks = await db
|
||||
.select()
|
||||
.select(selectFields)
|
||||
.from(tasks)
|
||||
.leftJoin(lists, eq(tasks.listId, lists.id))
|
||||
.where(
|
||||
and(
|
||||
eq(tasks.completed, false),
|
||||
|
|
@ -65,29 +79,20 @@ export async function syncWidgetData(): Promise<void> {
|
|||
)
|
||||
.orderBy(asc(tasks.position));
|
||||
|
||||
const toWidgetTask = (t: typeof upcomingTasks[number]): WidgetTask => ({
|
||||
id: t.id,
|
||||
title: t.title,
|
||||
priority: t.priority,
|
||||
dueDate: t.dueDate ? new Date(t.dueDate).toISOString() : null,
|
||||
completed: t.completed,
|
||||
listColor: t.listColor,
|
||||
});
|
||||
|
||||
// Combine: overdue first, then upcoming, then no date
|
||||
const allTasks: WidgetTask[] = [
|
||||
...overdueTasks.map((t) => ({
|
||||
id: t.id,
|
||||
title: t.title,
|
||||
priority: t.priority,
|
||||
dueDate: t.dueDate ? new Date(t.dueDate).toISOString() : null,
|
||||
completed: t.completed,
|
||||
})),
|
||||
...upcomingTasks.map((t) => ({
|
||||
id: t.id,
|
||||
title: t.title,
|
||||
priority: t.priority,
|
||||
dueDate: t.dueDate ? new Date(t.dueDate).toISOString() : null,
|
||||
completed: t.completed,
|
||||
})),
|
||||
...noDateTasks.map((t) => ({
|
||||
id: t.id,
|
||||
title: t.title,
|
||||
priority: t.priority,
|
||||
dueDate: null,
|
||||
completed: t.completed,
|
||||
})),
|
||||
...overdueTasks.map(toWidgetTask),
|
||||
...upcomingTasks.map(toWidgetTask),
|
||||
...noDateTasks.map(toWidgetTask),
|
||||
];
|
||||
|
||||
await AsyncStorage.setItem(WIDGET_DATA_KEY, JSON.stringify(allTasks));
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const BORDER_COLOR = '#E5E7EB' as const;
|
|||
const OVERDUE_COLOR = '#C17767' as const;
|
||||
const TODAY_COLOR = '#4A90A4' as const;
|
||||
const CHECKBOX_UNCHECKED = '#D1D5DB' as const;
|
||||
const DEFAULT_LIST_COLOR = '#4A90A4' as const;
|
||||
|
||||
const PRIORITY_COLORS = {
|
||||
3: '#C17767' as const,
|
||||
|
|
@ -82,6 +83,17 @@ function TaskItemRow({ task }: { task: WidgetTask }) {
|
|||
clickAction="OPEN_URI"
|
||||
clickActionData={{ uri: `simplliste:///task/${task.id}` }}
|
||||
>
|
||||
{/* List color indicator */}
|
||||
<FlexWidget
|
||||
style={{
|
||||
width: 4,
|
||||
height: 28,
|
||||
borderRadius: 2,
|
||||
backgroundColor: (task.listColor ?? DEFAULT_LIST_COLOR) as ColorProp,
|
||||
marginRight: 8,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Checkbox */}
|
||||
<FlexWidget
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ function isWidgetTask(item: unknown): item is WidgetTask {
|
|||
typeof obj.title === 'string' &&
|
||||
typeof obj.priority === 'number' &&
|
||||
typeof obj.completed === 'boolean' &&
|
||||
(obj.dueDate === null || typeof obj.dueDate === 'string')
|
||||
(obj.dueDate === null || typeof obj.dueDate === 'string') &&
|
||||
(obj.listColor === null || obj.listColor === undefined || typeof obj.listColor === 'string')
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue