2025-07-30 16:03:14 +02:00

190 lines
5.5 KiB
TypeScript

import { Pressable, ScrollView, StyleSheet, View } from 'react-native';
import Ionicons from '@expo/vector-icons/Ionicons';
import { useFocusEffect } from 'expo-router';
import { navigate } from 'expo-router/build/global-state/routing';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PermissionsAndroid, Platform } from 'react-native';
import { TaskItem } from '../../components/taskItem';
import { ThemedText } from '../../components/ThemedText';
import { ThemedView } from '../../components/ThemedView';
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';
export const checkApplicationPermission = async () => {
if (Platform.OS === 'android') {
try {
await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
);
} catch (error) {
}
}
}
const categoryRepository = new CategoryRepository(new SQLiteDataService<Category>(CategoryQuery));
const taskRepository = new TaskRepository(new SQLiteDataService<Task>(TaskQuery));
export default function HomeScreen() {
const [tasks, setTasks] = useState<Task[]>([]);
const [categories, setCategories] = useState<Category[]>([]);
const [ready, setReady] = useState(false);
const { t } = useTranslation();
useEffect(() => { // Initial data fetch
(async () => {
console.log(t('fetching_categories'));
try {
const fetchedCategories = await categoryRepository.findAll();
if (fetchedCategories.length === 0) {
// If no categories, create a default one
const defaultCategory = new Category(t('default_category_name'), t('default_category_icon'));
const createdCat = await categoryRepository.create(defaultCategory);
setCategories([createdCat]);
} else {
setCategories(fetchedCategories);
}
} catch (error) {
console.error(t('error_fetching_categories'), error);
}
try {
console.log(t('fetching_tasks'));
RefreshTasks();
} catch (error) {
console.error(t('error_fetching_tasks'), error);
}
setReady(true);
})();
}, []);
useEffect(() => { // NOTIFICATION HANDLER
if (Platform.OS !== 'web') {
if (Platform.OS === 'android') {
checkApplicationPermission();
}
}
}, []);
const RefreshTasks = async () => {
try {
const fetchedTasks = await taskRepository.findAll();
fetchedTasks.sort((a, b) => {
return (a.daysToRedo! * 24 * 3600 * 1000 - (Date.now() - a.lastDone!)) - ((b.daysToRedo! * 24 * 3600 * 1000) - (Date.now() - b.lastDone!));
});
console.log(t('tasks_fetched'), fetchedTasks);
setTasks(fetchedTasks);
} catch (error) {
console.error(t('error_refreshing_tasks'), error);
}
};
const RefreshCategories = async () => {
try {
const fetchedCategories = await categoryRepository.findAll();
setCategories(fetchedCategories);
} catch (error) {
console.error(t('error_refreshing_categories'), error);
}
};
useEffect(() => {
if (ready) {
(async () => {
await RefreshTasks();
await RefreshCategories();
})();
}
}, [ready]);
// On layout focus to refresh tasks
useFocusEffect(React.useCallback(() => {
console.log(t('focus_event'));
(async () => {
await RefreshTasks();
await RefreshCategories();
})();
}, []));
return (
<ThemedView style={styles.mainContainer}>
<ThemedView style={styles.headerContainer}>
<ThemedText type="title" style={{ color: '#fff' }}>
{t('app_name')}
</ThemedText>
<Pressable onPress={() => {
navigate('./taskForm', {});
}}>
<Ionicons name='add-circle-sharp' size={32} color="#fff" />
</Pressable>
</ThemedView>
<ThemedText style={{ padding: 24 }} type="subtitle">
{t('your_tasks')}
</ThemedText>
<ScrollView style={styles.scrollView}>
{tasks.map((task, index) => (
<TaskItem categories={categories} key={Date.now() + "-" + task.id} task={task} onUpdate={() => {
RefreshTasks();
}} />
))}
{tasks.length === 0 && (<>
<ThemedText type="defaultSemiBold" style={{ textAlign: 'center', marginTop: 20 }}>
{t('no_tasks_available')}
</ThemedText>
<ThemedText type="default" style={{ textAlign: 'center', marginTop: 20 }}>
{t('add_task_to_get_started')}
</ThemedText>
</>)}
<View style={{ height: 50 }} />
</ScrollView>
</ThemedView>
);
}
const styles = StyleSheet.create({
mainContainer: {
flex: 1,
},
headerContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: 8,
paddingHorizontal: 24,
paddingTop: 50,
paddingBottom: 18,
backgroundColor: '#6a254fff',
},
scrollView: {
paddingHorizontal: 24,
flex: 1,
},
stepContainer: {
gap: 8,
marginBottom: 8,
},
reactLogo: {
height: 178,
width: 290,
bottom: 0,
left: 0,
position: 'absolute',
},
addButton: {
backgroundColor: '#b91f7eff',
height: 50,
color: '#fff',
borderRadius: 8,
padding: 10,
lineHeight: 30,
alignItems: 'center',
justifyContent: 'center',
}
});