RyanHub – file viewer
filename: functions/assembleMetricsHistory.js
branch: master
back to repo
import { collection, getDocs, query } from 'firebase/firestore';
import { FIREBASE_DB } from '../FirebaseConfig';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { DeviceEventEmitter } from 'react-native';

// Set up listener for food history changes
DeviceEventEmitter.addListener('foodHistoryChanged', async () => {
    try {
        await AsyncStorage.removeItem('cachedMetrics');
        console.log('Metrics cache cleared due to food history change');
    } catch (error) {
        console.warn('Failed to clear metrics cache:', error);
    }
});

const createEmptyMetrics = async (date) => {
    const savedGoals = await AsyncStorage.getItem('userGoals');
    const goals = savedGoals ? JSON.parse(savedGoals) : {};
    return {
        date,
        actual: { calories: 0, protein: 0, carbs: 0, fat: 0 },
        goals: {
            calories: parseInt(goals.dailyCalories) || 0,
            protein: parseInt(goals.protein) || 0,
            carbs: parseInt(goals.carbs) || 0,
            fat: parseInt(goals.fats) || 0
        }
    };
};

export const assembleMetricsHistory = async (userId) => {
    try {
        try {
            const cachedData = await AsyncStorage.getItem('cachedMetrics');
            if (cachedData) {
                const parsedCache = JSON.parse(cachedData);
                if (Array.isArray(parsedCache) && parsedCache.length > 0) {
                    const hydratedCache = parsedCache.map(metric => ({
                        ...metric,
                        date: new Date(metric.date)
                    }));
                    console.log("Retrieved cached metrics");
                    return hydratedCache;
                }
            }
        } catch (cacheError) {
            console.warn("Cache retrieval failed:", cacheError);
            // Clear corrupted cache
            await AsyncStorage.removeItem('cachedMetrics');
        }

        if (!userId) {
            console.error('Missing userId parameter');
            return [];
        }

        console.log("Fetching metrics from firebase");

        const today = new Date();
        today.setHours(0, 0, 0, 0);
        
        const sevenDaysAgo = new Date(today);
        sevenDaysAgo.setDate(today.getDate() - 6);

        let allMetrics = [];
        try {
            const metricsRef = collection(FIREBASE_DB, "users", userId, "metrics");
            const q = query(metricsRef);
            const querySnapshot = await getDocs(q);

            allMetrics = querySnapshot.docs.map(doc => {
                const data = doc.data();
                return {
                    date: new Date(doc.id),
                    actual: {
                        calories: Number(data.actual?.calories) || 0,
                        protein: Number(data.actual?.protein) || 0,
                        carbs: Number(data.actual?.carbs) || 0,
                        fat: Number(data.actual?.fat) || 0
                    },
                    goals: {
                        calories: Number(data.goals?.calories) || 0,
                        protein: Number(data.goals?.protein) || 0,
                        carbs: Number(data.goals?.carbs) || 0,
                        fat: Number(data.goals?.fat) || 0
                    }
                };
            });

        } catch (e) {
            console.warn('Error fetching metrics:', e);
        }

        // Fill gaps and ensure minimum 7 days
        if (allMetrics.length > 0) {
            const filledMetrics = [];
            const startDate = new Date(Math.min(
                sevenDaysAgo.getTime(),
                allMetrics[0].date.getTime()
            ));
            const endDate = today;

            for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
                const currentDate = new Date(d);
                const existingMetric = allMetrics.find(m => {
                    if (!m || !m.date) return false;
                    return m.date.toISOString().split('T')[0] === currentDate.toISOString().split('T')[0];
                });
                
                filledMetrics.push(
                    existingMetric || 
                    await createEmptyMetrics(new Date(currentDate))
                );
            }
            console.log("Retrieved metrics from firebase");
            //console.log(filledMetrics);

            try {
                // Ensure the data is serializable before caching
                const metricsToCache = filledMetrics.map(metric => ({
                    ...metric,
                    date: metric.date.toISOString() // Convert Date to ISO string for reliable serialization
                }));
                await AsyncStorage.setItem('cachedMetrics', JSON.stringify(metricsToCache));
            } catch (cacheError) {
                console.warn("Failed to cache metrics:", cacheError);
            }
            return filledMetrics;
        }

        // If no data exists, return 7 days of empty metrics
        const emptyWeek = [];
        for (let i = 0; i < 7; i++) {
            const date = new Date(today);
            date.setDate(today.getDate() - i);
            emptyWeek.unshift(await createEmptyMetrics(date));
        }
        return emptyWeek;
    } catch (error) {
        console.error("Error assembling metrics history:", error);
        return [];
    }
};

export const clearMetricsCache = async () => {
    try {
        await AsyncStorage.removeItem('cachedMetrics');
        console.log('Metrics cache cleared');
    } catch (error) {
        console.warn('Failed to clear metrics cache:', error);
    }
}