146 lines
6.0 KiB
TypeScript
146 lines
6.0 KiB
TypeScript
import { Alert, ColorSchemeName, Pressable, StyleSheet, useColorScheme, type ViewProps } from 'react-native';
|
|
|
|
import { toast } from '@backpackapp-io/react-native-toast';
|
|
import { Text } from '@react-navigation/elements';
|
|
import { useRouter } from 'expo-router';
|
|
import { useRef } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useThemeColor } from '../hooks/useThemeColor';
|
|
import { Category } from '../models/category';
|
|
import { Task, TaskQuery } from '../models/task';
|
|
import { TaskRepository } from '../repositories/TaskRepository';
|
|
import { SQLiteDataService } from '../services/data/sqliteDataService';
|
|
import { GetTaskColor } from '../utils/colors';
|
|
import { ThemedView } from './ThemedView';
|
|
import ClockProgress from './clockProgress';
|
|
|
|
export type TaskItemProps = ViewProps & {
|
|
lightColor?: string;
|
|
darkColor?: string;
|
|
task: Task;
|
|
onUpdate: () => void;
|
|
categories: Category[];
|
|
};
|
|
|
|
const taskRepository = new TaskRepository(new SQLiteDataService<Task>(TaskQuery));
|
|
|
|
const adaptDaysToGoToUnit = (t: Task) => {
|
|
const timeLeft = t.lastDone! + (t.daysToRedo! * 24 * 60 * 60 * 1000) - Date.now();
|
|
if (timeLeft < 0) return 0; // Task is overdue
|
|
else if (timeLeft < 3 * 60 * 60 * 1000) return Math.ceil(timeLeft / (60 * 1000)) + " minutes";
|
|
else if (timeLeft < 24 * 60 * 60 * 1000) return Math.ceil(timeLeft / (3600 * 1000)) + " hours";
|
|
else if (timeLeft < 7 * 24 * 60 * 60 * 1000) return Math.ceil(timeLeft / (24 * 3600 * 1000)) + " days";
|
|
else if (timeLeft < 30 * 24 * 60 * 60 * 1000) return Math.ceil(timeLeft / (7 * 24 * 3600 * 1000)) + " weeks";
|
|
else return Math.ceil(timeLeft / (30 * 24 * 3600 * 1000)) + " months";
|
|
};
|
|
|
|
export function TaskItem({ task, categories, onUpdate, lightColor, darkColor, ...otherProps }: TaskItemProps) {
|
|
const { t } = useTranslation();
|
|
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
|
const router = useRouter();
|
|
const daysLeft = useRef(task.daysToRedo! * 24 * 3600 * 1000 - (Date.now() - task.lastDone!));
|
|
const colorScheme = useColorScheme();
|
|
const category = categories?.find((c) => {
|
|
return c.id === task?.category;
|
|
});
|
|
|
|
const LongPressHandler = () => {
|
|
//select edit or remove
|
|
console.log("Long pressed task:", task);
|
|
Alert.alert(
|
|
t('select_action'),
|
|
t('choose_action_for_task'),
|
|
[
|
|
{
|
|
text: t('edit'),
|
|
onPress: () => {
|
|
// Navigate to edit task form
|
|
console.log("Editing task:", task);
|
|
if (!task?.id) return;
|
|
router.push({ pathname: "./taskForm", params: { task: JSON.stringify(task) } });
|
|
}
|
|
},
|
|
{
|
|
text: t('remove'),
|
|
onPress: () => {
|
|
// Remove the task
|
|
console.log("Removing task:", task);
|
|
if (!task?.id) return;
|
|
taskRepository.delete(task.id).then(() => {
|
|
toast.success(t('task_removed_successfully'));
|
|
onUpdate?.();
|
|
}).catch((error) => {
|
|
toast.error(t('error_removing_task'));
|
|
console.error("Error removing task:", error);
|
|
});
|
|
},
|
|
style: "destructive"
|
|
},
|
|
{ text: t('cancel'), style: "cancel" }
|
|
]);
|
|
}
|
|
|
|
return <Pressable onLongPress={() => {
|
|
LongPressHandler();
|
|
}}>
|
|
<ThemedView style={[{ backgroundColor }, styles(colorScheme).taskItemMain, {
|
|
borderLeftColor: GetTaskColor(task!)
|
|
}]} {...otherProps}>
|
|
<ThemedView style={[{ backgroundColor }, styles(colorScheme).taskItemSubcontainer]}>
|
|
<Text style={styles(colorScheme).taskItemTitle}>
|
|
{task?.title}
|
|
</Text>
|
|
<Text style={styles(colorScheme).taskItemText}>
|
|
{category?.icon + " " + category?.title + " - " + (adaptDaysToGoToUnit(task) == 0 ? "Overdue" : adaptDaysToGoToUnit(task))}
|
|
</Text>
|
|
</ThemedView>
|
|
<Pressable style={styles(colorScheme).checkButton} onPress={() => {
|
|
|
|
taskRepository.update(task!.id!, { ...task, lastDone: Date.now() }).then(() => {
|
|
toast.success(t('task_updated_successfully'));
|
|
onUpdate?.();
|
|
}).catch((error) => {
|
|
toast.error(t('error_updating_task'));
|
|
console.error("Error updating task:", error);
|
|
});
|
|
}}>
|
|
<ClockProgress degree={parseInt(adaptDaysToGoToUnit(task).toString()) > 0 ? ((daysLeft.current / (24 * 3600 * 1000)) / task.daysToRedo!) * 360 : 0} width={40} height={40} color={GetTaskColor(task!)} />
|
|
</Pressable>
|
|
</ThemedView></Pressable>;
|
|
}
|
|
|
|
|
|
const styles = (theme: ColorSchemeName) => {
|
|
return StyleSheet.create({
|
|
taskItemMain: {
|
|
borderLeftWidth: 20,
|
|
borderColor: 'color-mix(in oklab, #a71e14 100%, white)',
|
|
flexDirection: 'row',
|
|
padding: 16,
|
|
borderRadius: 8,
|
|
marginBottom: 8,
|
|
backgroundColor: theme === 'dark' ? '#555' : '#f0f0f0',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
taskItemSubcontainer: {
|
|
flexDirection: 'column',
|
|
backgroundColor: 'transparent'
|
|
},
|
|
taskItemText: {
|
|
color: theme === 'dark' ? '#fff' : '#000',
|
|
fontSize: 15,
|
|
marginBottom: 4,
|
|
},
|
|
taskItemTitle: {
|
|
fontWeight: 'bold',
|
|
fontSize: 20,
|
|
marginBottom: 4,
|
|
color: theme === 'dark' ? '#fff' : '#000',
|
|
},
|
|
checkButton: {
|
|
width: 40,
|
|
height: 40,
|
|
}
|
|
})
|
|
} |