import { defaultStyles } from "@/constants/defaultStyles"; import { GetActiveSprint, RemoveActiveSprint, type ActiveSprint, } from '@/lib/asyncStorage'; import { formatDate } from '@/lib/date'; import { RegisterForLocalNotificationsAsync } from '@/lib/notifications'; import { supabase } from "@/lib/supabase"; import { Session } from '@supabase/supabase-js'; import { router, Stack, useFocusEffect } from "expo-router"; import { useCallback, useEffect, useState } from 'react'; import { Button, Pressable, StyleSheet, Text, View } from "react-native"; type UpcomingDeadlineTask = { tId: string; title: string; description: string; aId: string; subjectTitle: string; assignmentTitle: string; deadline: string; }; function formatTime(totalSeconds: number) { const minutes = Math.floor(totalSeconds / 60); const seconds = totalSeconds % 60; return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; } export default function HomeScreen() { const [session, SetSession] = useState(null); const [activeSprint, setActiveSprint] = useState(null); const [activeSprintTaskTitle, setActiveSprintTaskTitle] = useState(null); const [activeSprintTaskDesc, setActiveSprintTaskDesc] = useState(null); const [remainingSeconds, setRemainingSeconds] = useState(0); const [upcomingDeadlineTasks, setUpcomingDeadlineTasks] = useState([]); const loadActiveSprint = useCallback(async () => { const storedSprint = await GetActiveSprint(); if (!storedSprint) { setActiveSprint(null); setActiveSprintTaskTitle(null); setActiveSprintTaskDesc(null); setRemainingSeconds(0); return; } const secondsLeft = Math.max( 0, Math.ceil((storedSprint.endTime - Date.now()) / 1000) ); if (secondsLeft <= 0) { await RemoveActiveSprint(); setActiveSprint(null); setActiveSprintTaskTitle(null); setActiveSprintTaskDesc(null); setRemainingSeconds(0); return; } setActiveSprint(storedSprint); setRemainingSeconds(secondsLeft); const { data: dbTitle } = await supabase .from('tasks') .select('title') .eq('tId', storedSprint.taskId) .single(); const { data: dbDesc } = await supabase .from('tasks') .select('description') .eq('tId', storedSprint.taskId) .single(); setActiveSprintTaskTitle(dbTitle?.title ?? null); setActiveSprintTaskDesc(dbDesc?.description); }, []); const loadUpcomingDeadlineTasks = useCallback(async () => { if (!session?.user.id) { setUpcomingDeadlineTasks([]); return; } const { data: tasksData, error: tasksError } = await supabase .from('tasks') .select('tId, title, description, aId') .eq('uId', session.user.id) .eq('isCompleted', false); if (tasksError || !tasksData || tasksData.length === 0) { setUpcomingDeadlineTasks([]); return; } const assignmentIds = [...new Set(tasksData.map((task) => task.aId).filter(Boolean))]; if (assignmentIds.length === 0) { setUpcomingDeadlineTasks([]); return; } const { data: assignmentsData, error: assignmentsError } = await supabase .from('assignments') .select('aId, sId, title, deadline') .in('aId', assignmentIds) .eq('isCompleted', false); if (assignmentsError || !assignmentsData) { setUpcomingDeadlineTasks([]); return; } const subjectIds = [...new Set(assignmentsData.map((assignment) => assignment.sId).filter(Boolean))]; const { data: subjectsData, error: subjectsError } = await supabase .from('subjects') .select('sId, title') .in('sId', subjectIds); if (subjectsError || !subjectsData) { setUpcomingDeadlineTasks([]); return; } const now = Date.now(); const assignmentsById = new Map( assignmentsData.map((assignment) => [assignment.aId, assignment]) ); const subjectsById = new Map( subjectsData.map((subject) => [subject.sId, subject]) ); const enrichedTasks = tasksData .map((task) => { const assignment = assignmentsById.get(task.aId); if (!assignment?.deadline) { return null; } const deadlineTime = new Date(assignment.deadline).getTime(); if (Number.isNaN(deadlineTime) || deadlineTime < now) { return null; } const subject = subjectsById.get(assignment.sId); return { tId: task.tId, title: task.title, description: task.description, aId: task.aId, subjectTitle: subject?.title ?? 'Unknown Subject', assignmentTitle: assignment.title, deadline: assignment.deadline, } satisfies UpcomingDeadlineTask; }) .filter((task): task is UpcomingDeadlineTask => task !== null) .sort( (left, right) => new Date(left.deadline).getTime() - new Date(right.deadline).getTime() ); setUpcomingDeadlineTasks(enrichedTasks); }, [session?.user.id]); useEffect(() => { supabase.auth .getSession() .then(({ data }) => SetSession(data.session ?? null)); const { data: sub } = supabase.auth.onAuthStateChange( (_event, newSession) => { SetSession(newSession); } ); return () => sub.subscription.unsubscribe(); }, []); useEffect(() => { if (session) { RegisterForLocalNotificationsAsync(); } }, [session]); useFocusEffect( useCallback(() => { void loadActiveSprint(); void loadUpcomingDeadlineTasks(); }, [loadActiveSprint, loadUpcomingDeadlineTasks]) ); useEffect(() => { if (!activeSprint) { return; } const intervalId = setInterval(() => { const secondsLeft = Math.max( 0, Math.ceil((activeSprint.endTime - Date.now()) / 1000) ); setRemainingSeconds(secondsLeft); if (secondsLeft <= 0) { void RemoveActiveSprint(); setActiveSprint(null); setActiveSprintTaskTitle(null); } }, 1000); return () => clearInterval(intervalId); }, [activeSprint]); return ( { return (