feat: widget dark mode, update checker, contact button (v1.0.1)
- Widget adapts to app theme (light/dark/system) via AsyncStorage - Add "Check for updates" button querying Forgejo releases API - Add "Contact us or report a bug" mailto link in settings - Bump version to 1.0.1 Closes #1, closes #2, closes #3 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c58a4dce2d
commit
e6ac92e745
8 changed files with 183 additions and 45 deletions
2
app.json
2
app.json
|
|
@ -2,7 +2,7 @@
|
|||
"expo": {
|
||||
"name": "Simpl-Liste",
|
||||
"slug": "simpl-liste",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/images/icon.png",
|
||||
"scheme": "simplliste",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { View, Text, Pressable, useColorScheme, TextInput, ScrollView, Alert, Modal, KeyboardAvoidingView, Platform, Switch } from 'react-native';
|
||||
import { View, Text, Pressable, useColorScheme, TextInput, ScrollView, Alert, Modal, KeyboardAvoidingView, Platform, Switch, Linking, ActivityIndicator } from 'react-native';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Sun, Moon, Smartphone, Plus, Trash2, Pencil, Bell, CalendarDays } from 'lucide-react-native';
|
||||
import { Sun, Moon, Smartphone, Plus, Trash2, Pencil, Bell, CalendarDays, Mail, RefreshCw } from 'lucide-react-native';
|
||||
import Constants from 'expo-constants';
|
||||
|
||||
import { colors } from '@/src/theme/colors';
|
||||
|
|
@ -30,6 +30,7 @@ export default function SettingsScreen() {
|
|||
const [editingTagId, setEditingTagId] = useState<string | null>(null);
|
||||
const [tagName, setTagName] = useState('');
|
||||
const [tagColor, setTagColor] = useState(TAG_COLORS[0]);
|
||||
const [checkingUpdate, setCheckingUpdate] = useState(false);
|
||||
|
||||
const loadTags = useCallback(async () => {
|
||||
const result = await getAllTags();
|
||||
|
|
@ -90,6 +91,45 @@ export default function SettingsScreen() {
|
|||
]);
|
||||
};
|
||||
|
||||
const handleCheckUpdate = async () => {
|
||||
setCheckingUpdate(true);
|
||||
try {
|
||||
const res = await fetch(
|
||||
'https://git.lacompagniemaximus.com/api/v1/repos/maximus/simpl-liste/releases/latest'
|
||||
);
|
||||
if (!res.ok) throw new Error('API error');
|
||||
const release = await res.json();
|
||||
const latestTag: string = release.tag_name ?? '';
|
||||
const latestVersion = latestTag.replace(/^v/, '');
|
||||
const currentVersion = Constants.expoConfig?.version ?? '0.0.0';
|
||||
|
||||
if (latestVersion && latestVersion !== currentVersion) {
|
||||
const apkAsset = release.assets?.find(
|
||||
(a: { name: string }) => a.name?.endsWith('.apk')
|
||||
);
|
||||
const downloadUrl = apkAsset?.browser_download_url;
|
||||
const body = release.body ? `\n\n${release.body}` : '';
|
||||
|
||||
Alert.alert(
|
||||
t('settings.checkUpdate'),
|
||||
t('settings.newVersion', { version: latestVersion }) + body,
|
||||
[
|
||||
{ text: t('common.cancel'), style: 'cancel' },
|
||||
...(downloadUrl
|
||||
? [{ text: t('settings.download'), onPress: () => Linking.openURL(downloadUrl) }]
|
||||
: []),
|
||||
]
|
||||
);
|
||||
} else {
|
||||
Alert.alert(t('settings.checkUpdate'), t('settings.upToDate'));
|
||||
}
|
||||
} catch {
|
||||
Alert.alert(t('settings.checkUpdate'), t('settings.updateError'));
|
||||
} finally {
|
||||
setCheckingUpdate(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView className={`flex-1 ${isDark ? 'bg-[#1A1A1A]' : 'bg-creme'}`}>
|
||||
{/* Theme Section */}
|
||||
|
|
@ -400,13 +440,44 @@ export default function SettingsScreen() {
|
|||
>
|
||||
{t('settings.about')}
|
||||
</Text>
|
||||
<View className={`overflow-hidden rounded-xl px-4 py-3.5 ${isDark ? 'bg-[#2A2A2A]' : 'bg-white'}`}>
|
||||
<Text className={`text-sm ${isDark ? 'text-[#A0A0A0]' : 'text-[#6B6B6B]'}`}>
|
||||
Simpl-Liste {t('settings.version')} {Constants.expoConfig?.version ?? '1.0.0'}
|
||||
</Text>
|
||||
<Text className={`mt-1 text-xs ${isDark ? 'text-[#A0A0A0]' : 'text-[#6B6B6B]'}`}>
|
||||
La Compagnie Maximus
|
||||
</Text>
|
||||
<View className={`overflow-hidden rounded-xl ${isDark ? 'bg-[#2A2A2A]' : 'bg-white'}`}>
|
||||
<View className={`px-4 py-3.5 border-b ${isDark ? 'border-[#3A3A3A]' : 'border-[#E5E7EB]'}`}>
|
||||
<Text className={`text-sm ${isDark ? 'text-[#A0A0A0]' : 'text-[#6B6B6B]'}`}>
|
||||
Simpl-Liste {t('settings.version')} {Constants.expoConfig?.version ?? '1.0.0'}
|
||||
</Text>
|
||||
<Text className={`mt-1 text-xs ${isDark ? 'text-[#A0A0A0]' : 'text-[#6B6B6B]'}`}>
|
||||
La Compagnie Maximus
|
||||
</Text>
|
||||
</View>
|
||||
<Pressable
|
||||
onPress={handleCheckUpdate}
|
||||
disabled={checkingUpdate}
|
||||
className={`flex-row items-center border-b px-4 py-3.5 ${isDark ? 'border-[#3A3A3A]' : 'border-[#E5E7EB]'}`}
|
||||
>
|
||||
{checkingUpdate ? (
|
||||
<ActivityIndicator size={20} color={isDark ? '#A0A0A0' : '#6B6B6B'} />
|
||||
) : (
|
||||
<RefreshCw size={20} color={isDark ? '#A0A0A0' : '#6B6B6B'} />
|
||||
)}
|
||||
<Text
|
||||
className={`ml-3 text-base ${isDark ? 'text-[#F5F5F5]' : 'text-[#1A1A1A]'}`}
|
||||
style={{ fontFamily: 'Inter_400Regular' }}
|
||||
>
|
||||
{t('settings.checkUpdate')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
onPress={() => Linking.openURL('mailto:lacompagniemaximus@protonmail.com')}
|
||||
className="flex-row items-center px-4 py-3.5"
|
||||
>
|
||||
<Mail size={20} color={isDark ? '#A0A0A0' : '#6B6B6B'} />
|
||||
<Text
|
||||
className={`ml-3 text-base ${isDark ? 'text-[#F5F5F5]' : 'text-[#1A1A1A]'}`}
|
||||
style={{ fontFamily: 'Inter_400Regular' }}
|
||||
>
|
||||
{t('settings.contactOrReport')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "simpl-liste",
|
||||
"main": "index.js",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"android": "expo start --android",
|
||||
|
|
|
|||
|
|
@ -96,7 +96,13 @@
|
|||
"light": "Light",
|
||||
"system": "System",
|
||||
"about": "About",
|
||||
"version": "Version"
|
||||
"version": "Version",
|
||||
"contactOrReport": "Contact us or report a bug",
|
||||
"checkUpdate": "Check for updates",
|
||||
"upToDate": "You're up to date!",
|
||||
"newVersion": "New version available: {{version}}",
|
||||
"download": "Download",
|
||||
"updateError": "Unable to check for updates"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifications",
|
||||
|
|
|
|||
|
|
@ -96,7 +96,13 @@
|
|||
"light": "Clair",
|
||||
"system": "Système",
|
||||
"about": "À propos",
|
||||
"version": "Version"
|
||||
"version": "Version",
|
||||
"contactOrReport": "Nous joindre ou signaler un bogue",
|
||||
"checkUpdate": "Vérifier les mises à jour",
|
||||
"upToDate": "Vous êtes à jour !",
|
||||
"newVersion": "Nouvelle version disponible : {{version}}",
|
||||
"download": "Télécharger",
|
||||
"updateError": "Impossible de vérifier les mises à jour"
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifications",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Platform } from 'react-native';
|
||||
import { Platform, Appearance } from 'react-native';
|
||||
import { requestWidgetUpdate } from 'react-native-android-widget';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { db } from '../db/client';
|
||||
|
|
@ -8,6 +8,7 @@ import { startOfDay, endOfDay, addWeeks } from 'date-fns';
|
|||
import { TaskListWidget } from '../widgets/TaskListWidget';
|
||||
|
||||
export const WIDGET_DATA_KEY = 'widget:tasks';
|
||||
export const WIDGET_DARK_KEY = 'widget:isDark';
|
||||
|
||||
export interface WidgetTask {
|
||||
id: string;
|
||||
|
|
@ -95,7 +96,25 @@ export async function syncWidgetData(): Promise<void> {
|
|||
...noDateTasks.map(toWidgetTask),
|
||||
];
|
||||
|
||||
// Determine dark mode from settings
|
||||
let isDark = false;
|
||||
try {
|
||||
const settingsRaw = await AsyncStorage.getItem('simpl-liste-settings');
|
||||
if (settingsRaw) {
|
||||
const settings = JSON.parse(settingsRaw);
|
||||
const theme: string = settings?.state?.theme ?? 'system';
|
||||
if (theme === 'dark') {
|
||||
isDark = true;
|
||||
} else if (theme === 'system') {
|
||||
isDark = Appearance.getColorScheme() === 'dark';
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Default to light
|
||||
}
|
||||
|
||||
await AsyncStorage.setItem(WIDGET_DATA_KEY, JSON.stringify(allTasks));
|
||||
await AsyncStorage.setItem(WIDGET_DARK_KEY, JSON.stringify(isDark));
|
||||
|
||||
// Request widget update for all 3 sizes
|
||||
const widgetNames = ['SimplListeSmall', 'SimplListeMedium', 'SimplListeLarge'];
|
||||
|
|
@ -104,7 +123,7 @@ export async function syncWidgetData(): Promise<void> {
|
|||
await requestWidgetUpdate({
|
||||
widgetName,
|
||||
renderWidget: (props) =>
|
||||
TaskListWidget({ ...props, widgetName, tasks: allTasks }),
|
||||
TaskListWidget({ ...props, widgetName, tasks: allTasks, isDark }),
|
||||
widgetNotFound: () => {},
|
||||
});
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -17,13 +17,26 @@ import { fr } from 'date-fns/locale';
|
|||
const FONT_REGULAR = 'Inter_400Regular';
|
||||
const FONT_SEMIBOLD = 'Inter_600SemiBold';
|
||||
|
||||
const BG_COLOR = '#FFF8F0' as const;
|
||||
const TEXT_COLOR = '#1A1A1A' as const;
|
||||
const TEXT_SECONDARY = '#6B6B6B' as const;
|
||||
const BORDER_COLOR = '#E5E7EB' as const;
|
||||
const LIGHT_COLORS = {
|
||||
bg: '#FFF8F0' as const,
|
||||
text: '#1A1A1A' as const,
|
||||
textSecondary: '#6B6B6B' as const,
|
||||
border: '#E5E7EB' as const,
|
||||
surface: '#FFFFFF' as const,
|
||||
checkboxUnchecked: '#D1D5DB' as const,
|
||||
};
|
||||
|
||||
const DARK_COLORS = {
|
||||
bg: '#1A1A1A' as const,
|
||||
text: '#F5F5F5' as const,
|
||||
textSecondary: '#A0A0A0' as const,
|
||||
border: '#3A3A3A' as const,
|
||||
surface: '#2A2A2A' as const,
|
||||
checkboxUnchecked: '#555555' 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 = {
|
||||
|
|
@ -32,13 +45,17 @@ const PRIORITY_COLORS = {
|
|||
1: '#8BA889' as const,
|
||||
} as const;
|
||||
|
||||
function getColors(isDark: boolean) {
|
||||
return isDark ? DARK_COLORS : LIGHT_COLORS;
|
||||
}
|
||||
|
||||
function getPriorityDotColor(priority: number): ColorProp | null {
|
||||
return PRIORITY_COLORS[priority as keyof typeof PRIORITY_COLORS] ?? null;
|
||||
}
|
||||
|
||||
function getDateLabel(dueDate: string | null): { text: string; color: ColorProp } {
|
||||
function getDateLabel(dueDate: string | null, c: ReturnType<typeof getColors>): { text: string; color: ColorProp } {
|
||||
if (!dueDate) {
|
||||
return { text: 'Sans date', color: TEXT_SECONDARY };
|
||||
return { text: 'Sans date', color: c.textSecondary };
|
||||
}
|
||||
|
||||
const date = new Date(dueDate);
|
||||
|
|
@ -51,22 +68,24 @@ function getDateLabel(dueDate: string | null): { text: string; color: ColorProp
|
|||
return { text: "Aujourd'hui", color: TODAY_COLOR };
|
||||
}
|
||||
if (isTomorrow(date)) {
|
||||
return { text: 'Demain', color: TEXT_COLOR };
|
||||
return { text: 'Demain', color: c.text };
|
||||
}
|
||||
|
||||
return {
|
||||
text: format(date, 'EEE d MMM', { locale: fr }),
|
||||
color: TEXT_SECONDARY,
|
||||
color: c.textSecondary,
|
||||
};
|
||||
}
|
||||
|
||||
interface TaskListWidgetProps extends WidgetInfo {
|
||||
widgetName: string;
|
||||
tasks?: WidgetTask[];
|
||||
isDark?: boolean;
|
||||
}
|
||||
|
||||
function TaskItemRow({ task }: { task: WidgetTask }) {
|
||||
const dateInfo = getDateLabel(task.dueDate);
|
||||
function TaskItemRow({ task, isDark }: { task: WidgetTask; isDark: boolean }) {
|
||||
const c = getColors(isDark);
|
||||
const dateInfo = getDateLabel(task.dueDate, c);
|
||||
const priorityColor = getPriorityDotColor(task.priority);
|
||||
|
||||
return (
|
||||
|
|
@ -78,7 +97,7 @@ function TaskItemRow({ task }: { task: WidgetTask }) {
|
|||
paddingVertical: 8,
|
||||
width: 'match_parent',
|
||||
borderBottomWidth: 1,
|
||||
borderColor: BORDER_COLOR,
|
||||
borderColor: c.border,
|
||||
}}
|
||||
clickAction="OPEN_URI"
|
||||
clickActionData={{ uri: `simplliste:///task/${task.id}` }}
|
||||
|
|
@ -101,7 +120,7 @@ function TaskItemRow({ task }: { task: WidgetTask }) {
|
|||
height: 22,
|
||||
borderRadius: 11,
|
||||
borderWidth: 2,
|
||||
borderColor: CHECKBOX_UNCHECKED,
|
||||
borderColor: c.checkboxUnchecked,
|
||||
marginRight: 10,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
|
@ -137,7 +156,7 @@ function TaskItemRow({ task }: { task: WidgetTask }) {
|
|||
style={{
|
||||
fontSize: 14,
|
||||
fontFamily: FONT_REGULAR,
|
||||
color: TEXT_COLOR,
|
||||
color: c.text,
|
||||
}}
|
||||
/>
|
||||
</FlexWidget>
|
||||
|
|
@ -157,14 +176,15 @@ function TaskItemRow({ task }: { task: WidgetTask }) {
|
|||
);
|
||||
}
|
||||
|
||||
function SmallWidget({ tasks }: { tasks: WidgetTask[] }) {
|
||||
function SmallWidget({ tasks, isDark }: { tasks: WidgetTask[]; isDark: boolean }) {
|
||||
const c = getColors(isDark);
|
||||
return (
|
||||
<FlexWidget
|
||||
style={{
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: BG_COLOR,
|
||||
backgroundColor: c.bg,
|
||||
borderRadius: 16,
|
||||
width: 'match_parent',
|
||||
height: 'match_parent',
|
||||
|
|
@ -177,7 +197,7 @@ function SmallWidget({ tasks }: { tasks: WidgetTask[] }) {
|
|||
style={{
|
||||
fontSize: 14,
|
||||
fontFamily: FONT_SEMIBOLD,
|
||||
color: TEXT_COLOR,
|
||||
color: c.text,
|
||||
marginBottom: 4,
|
||||
}}
|
||||
/>
|
||||
|
|
@ -195,7 +215,7 @@ function SmallWidget({ tasks }: { tasks: WidgetTask[] }) {
|
|||
style={{
|
||||
fontSize: 12,
|
||||
fontFamily: FONT_REGULAR,
|
||||
color: TEXT_SECONDARY,
|
||||
color: c.textSecondary,
|
||||
marginBottom: 8,
|
||||
}}
|
||||
/>
|
||||
|
|
@ -228,17 +248,20 @@ function SmallWidget({ tasks }: { tasks: WidgetTask[] }) {
|
|||
function ListWidgetContent({
|
||||
tasks,
|
||||
maxItems,
|
||||
isDark,
|
||||
}: {
|
||||
tasks: WidgetTask[];
|
||||
maxItems: number;
|
||||
isDark: boolean;
|
||||
}) {
|
||||
const c = getColors(isDark);
|
||||
const displayTasks = tasks.slice(0, maxItems);
|
||||
|
||||
return (
|
||||
<FlexWidget
|
||||
style={{
|
||||
flexDirection: 'column',
|
||||
backgroundColor: BG_COLOR,
|
||||
backgroundColor: c.bg,
|
||||
borderRadius: 16,
|
||||
width: 'match_parent',
|
||||
height: 'match_parent',
|
||||
|
|
@ -255,7 +278,7 @@ function ListWidgetContent({
|
|||
paddingVertical: 10,
|
||||
width: 'match_parent',
|
||||
borderBottomWidth: 1,
|
||||
borderColor: BORDER_COLOR,
|
||||
borderColor: c.border,
|
||||
}}
|
||||
clickAction="OPEN_APP"
|
||||
>
|
||||
|
|
@ -264,7 +287,7 @@ function ListWidgetContent({
|
|||
style={{
|
||||
fontSize: 16,
|
||||
fontFamily: FONT_SEMIBOLD,
|
||||
color: TEXT_COLOR,
|
||||
color: c.text,
|
||||
}}
|
||||
/>
|
||||
<FlexWidget
|
||||
|
|
@ -287,7 +310,7 @@ function ListWidgetContent({
|
|||
style={{
|
||||
fontSize: 13,
|
||||
fontFamily: FONT_REGULAR,
|
||||
color: TEXT_SECONDARY,
|
||||
color: c.textSecondary,
|
||||
}}
|
||||
/>
|
||||
</FlexWidget>
|
||||
|
|
@ -303,7 +326,7 @@ function ListWidgetContent({
|
|||
}}
|
||||
>
|
||||
{displayTasks.map((task) => (
|
||||
<TaskItemRow key={task.id} task={task} />
|
||||
<TaskItemRow key={task.id} task={task} isDark={isDark} />
|
||||
))}
|
||||
</FlexWidget>
|
||||
) : (
|
||||
|
|
@ -320,7 +343,7 @@ function ListWidgetContent({
|
|||
style={{
|
||||
fontSize: 14,
|
||||
fontFamily: FONT_REGULAR,
|
||||
color: TEXT_SECONDARY,
|
||||
color: c.textSecondary,
|
||||
}}
|
||||
/>
|
||||
</FlexWidget>
|
||||
|
|
@ -334,7 +357,7 @@ function ListWidgetContent({
|
|||
paddingVertical: 8,
|
||||
width: 'match_parent',
|
||||
borderTopWidth: 1,
|
||||
borderColor: BORDER_COLOR,
|
||||
borderColor: c.border,
|
||||
}}
|
||||
clickAction="OPEN_URI"
|
||||
clickActionData={{ uri: 'simplliste:///task/new' }}
|
||||
|
|
@ -366,11 +389,12 @@ function ListWidgetContent({
|
|||
export function TaskListWidget(props: TaskListWidgetProps) {
|
||||
const widgetTasks = props.tasks ?? [];
|
||||
const widgetName = props.widgetName;
|
||||
const isDark = props.isDark ?? false;
|
||||
|
||||
if (widgetName === 'SimplListeSmall') {
|
||||
return <SmallWidget tasks={widgetTasks} />;
|
||||
return <SmallWidget tasks={widgetTasks} isDark={isDark} />;
|
||||
}
|
||||
|
||||
const maxItems = widgetName === 'SimplListeLarge' ? 8 : 4;
|
||||
return <ListWidgetContent tasks={widgetTasks} maxItems={maxItems} />;
|
||||
return <ListWidgetContent tasks={widgetTasks} maxItems={maxItems} isDark={isDark} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { WidgetTaskHandlerProps } from 'react-native-android-widget';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { TaskListWidget } from './TaskListWidget';
|
||||
import { WIDGET_DATA_KEY, type WidgetTask } from '../services/widgetSync';
|
||||
import { WIDGET_DATA_KEY, WIDGET_DARK_KEY, type WidgetTask } from '../services/widgetSync';
|
||||
import { isValidUUID } from '../lib/validation';
|
||||
|
||||
function isWidgetTask(item: unknown): item is WidgetTask {
|
||||
|
|
@ -29,6 +29,16 @@ async function getWidgetTasks(): Promise<WidgetTask[]> {
|
|||
}
|
||||
}
|
||||
|
||||
async function getWidgetIsDark(): Promise<boolean> {
|
||||
try {
|
||||
const data = await AsyncStorage.getItem(WIDGET_DARK_KEY);
|
||||
if (!data) return false;
|
||||
return JSON.parse(data) === true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function widgetTaskHandler(
|
||||
props: WidgetTaskHandlerProps
|
||||
): Promise<void> {
|
||||
|
|
@ -38,12 +48,13 @@ export async function widgetTaskHandler(
|
|||
case 'WIDGET_ADDED':
|
||||
case 'WIDGET_UPDATE':
|
||||
case 'WIDGET_RESIZED': {
|
||||
const tasks = await getWidgetTasks();
|
||||
const [tasks, isDark] = await Promise.all([getWidgetTasks(), getWidgetIsDark()]);
|
||||
renderWidget(
|
||||
TaskListWidget({
|
||||
...widgetInfo,
|
||||
widgetName: widgetInfo.widgetName,
|
||||
tasks,
|
||||
isDark,
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
|
@ -58,7 +69,7 @@ export async function widgetTaskHandler(
|
|||
if (!isValidUUID(taskId)) break;
|
||||
|
||||
// Update the cached data to remove the completed task immediately
|
||||
const tasks = await getWidgetTasks();
|
||||
const [tasks, isDark] = await Promise.all([getWidgetTasks(), getWidgetIsDark()]);
|
||||
const updatedTasks = tasks.filter((t) => t.id !== taskId);
|
||||
await AsyncStorage.setItem(
|
||||
WIDGET_DATA_KEY,
|
||||
|
|
@ -71,6 +82,7 @@ export async function widgetTaskHandler(
|
|||
...widgetInfo,
|
||||
widgetName: widgetInfo.widgetName,
|
||||
tasks: updatedTasks,
|
||||
isDark,
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue