209 lines
7.1 KiB
TypeScript
209 lines
7.1 KiB
TypeScript
import React, { createContext, useContext, useState, useEffect } from 'react';
|
|
import { WorkoutSession, WorkoutPlan, WorkoutSet } from '../types';
|
|
import { useAuth } from './AuthContext';
|
|
import {
|
|
getSessions,
|
|
getPlans,
|
|
getActiveSession,
|
|
saveSession,
|
|
deleteSession,
|
|
updateActiveSession,
|
|
deleteActiveSession,
|
|
deleteSetFromActiveSession,
|
|
updateSetInActiveSession
|
|
} from '../services/storage';
|
|
import { getCurrentUserProfile, getMe } from '../services/auth';
|
|
import { generateId } from '../utils/uuid';
|
|
import { logWeight } from '../services/weight';
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
|
interface DataContextType {
|
|
sessions: WorkoutSession[];
|
|
plans: WorkoutPlan[];
|
|
activeSession: WorkoutSession | null;
|
|
activePlan: WorkoutPlan | null;
|
|
startSession: (plan?: WorkoutPlan, startWeight?: number) => Promise<void>;
|
|
endSession: () => Promise<void>;
|
|
quitSession: () => Promise<void>;
|
|
addSet: (set: WorkoutSet) => void;
|
|
removeSet: (setId: string) => Promise<void>;
|
|
updateSet: (updatedSet: WorkoutSet) => Promise<void>;
|
|
updateSession: (updatedSession: WorkoutSession) => void;
|
|
deleteSessionById: (sessionId: string) => void;
|
|
refreshData: () => Promise<void>;
|
|
}
|
|
|
|
const DataContext = createContext<DataContextType | undefined>(undefined);
|
|
|
|
export const DataProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const { currentUser, updateUser } = useAuth();
|
|
const navigate = useNavigate();
|
|
|
|
const [sessions, setSessions] = useState<WorkoutSession[]>([]);
|
|
const [plans, setPlans] = useState<WorkoutPlan[]>([]);
|
|
const [activeSession, setActiveSession] = useState<WorkoutSession | null>(null);
|
|
const [activePlan, setActivePlan] = useState<WorkoutPlan | null>(null);
|
|
|
|
const refreshData = async () => {
|
|
if (currentUser) {
|
|
const s = await getSessions(currentUser.id);
|
|
setSessions(s);
|
|
const p = await getPlans(currentUser.id);
|
|
setPlans(p);
|
|
} else {
|
|
setSessions([]);
|
|
setPlans([]);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
refreshData();
|
|
}, [currentUser]);
|
|
|
|
// Restore active session
|
|
useEffect(() => {
|
|
const restoreActive = async () => {
|
|
if (currentUser) {
|
|
const session = await getActiveSession(currentUser.id);
|
|
if (session) {
|
|
setActiveSession(session);
|
|
if (session.planId) {
|
|
// Ensure plans are loaded or fetch specifically
|
|
const currentPlans = plans.length > 0 ? plans : await getPlans(currentUser.id);
|
|
const plan = currentPlans.find(p => p.id === session.planId);
|
|
if (plan) setActivePlan(plan);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
restoreActive();
|
|
}, [currentUser]); // Dependency logic might need tuning, but this matches App.tsx roughly
|
|
|
|
const startSession = async (plan?: WorkoutPlan, startWeight?: number) => {
|
|
if (!currentUser || activeSession) return;
|
|
|
|
const profile = getCurrentUserProfile(currentUser.id);
|
|
const currentWeight = startWeight || profile?.weight || 70;
|
|
|
|
const newSession: WorkoutSession = {
|
|
id: generateId(),
|
|
startTime: Date.now(),
|
|
type: 'STANDARD',
|
|
userBodyWeight: currentWeight,
|
|
sets: [],
|
|
planId: plan?.id,
|
|
planName: plan?.name
|
|
};
|
|
|
|
setActivePlan(plan || null);
|
|
setActiveSession(newSession);
|
|
navigate('/');
|
|
|
|
await saveSession(currentUser.id, newSession);
|
|
|
|
if (startWeight) {
|
|
await logWeight(startWeight);
|
|
}
|
|
};
|
|
|
|
const endSession = async () => {
|
|
if (activeSession && currentUser) {
|
|
const finishedSession = { ...activeSession, endTime: Date.now() };
|
|
await updateActiveSession(currentUser.id, finishedSession);
|
|
setSessions(prev => [finishedSession, ...prev]);
|
|
setActiveSession(null);
|
|
setActivePlan(null);
|
|
|
|
const res = await getMe();
|
|
if (res.success && res.user) {
|
|
updateUser(res.user);
|
|
}
|
|
}
|
|
};
|
|
|
|
const quitSession = async () => {
|
|
if (currentUser) {
|
|
await deleteActiveSession(currentUser.id);
|
|
setActiveSession(null);
|
|
setActivePlan(null);
|
|
}
|
|
};
|
|
|
|
const addSet = (set: WorkoutSet) => {
|
|
if (activeSession) {
|
|
const updatedSession = { ...activeSession, sets: [...activeSession.sets, set] };
|
|
setActiveSession(updatedSession);
|
|
// Context update is optimistic, actual save usually happens in hooks or components?
|
|
// In App.tsx handleAddSet only updated local state.
|
|
// Wait, useTracker usually handles saving sets via API?
|
|
// In App.tsx: handleAddSet just set state.
|
|
// useTracker.ts calls onSetAdded, but ALSO calls api to save it?
|
|
// Let's look at useTracker.ts.
|
|
// handleLogSet in useTracker calls API then onSetAdded.
|
|
// So this state update is mainly for UI sync in App.
|
|
}
|
|
};
|
|
|
|
const removeSet = async (setId: string) => {
|
|
if (activeSession && currentUser) {
|
|
await deleteSetFromActiveSession(currentUser.id, setId);
|
|
const updatedSession = {
|
|
...activeSession,
|
|
sets: activeSession.sets.filter(s => s.id !== setId)
|
|
};
|
|
setActiveSession(updatedSession);
|
|
}
|
|
};
|
|
|
|
const updateSet = async (updatedSet: WorkoutSet) => {
|
|
if (activeSession && currentUser) {
|
|
const response = await updateSetInActiveSession(currentUser.id, updatedSet.id, updatedSet);
|
|
const updatedSession = {
|
|
...activeSession,
|
|
sets: activeSession.sets.map(s => s.id === updatedSet.id ? response : s)
|
|
};
|
|
setActiveSession(updatedSession);
|
|
}
|
|
};
|
|
|
|
const updateSession = (updatedSession: WorkoutSession) => {
|
|
if (!currentUser) return;
|
|
saveSession(currentUser.id, updatedSession);
|
|
setSessions(prev => prev.map(s => s.id === updatedSession.id ? updatedSession : s));
|
|
};
|
|
|
|
const deleteSessionById = (sessionId: string) => {
|
|
if (!currentUser) return;
|
|
deleteSession(currentUser.id, sessionId);
|
|
setSessions(prev => prev.filter(s => s.id !== sessionId));
|
|
};
|
|
|
|
return (
|
|
<DataContext.Provider value={{
|
|
sessions,
|
|
plans,
|
|
activeSession,
|
|
activePlan,
|
|
startSession,
|
|
endSession,
|
|
quitSession,
|
|
addSet,
|
|
removeSet,
|
|
updateSet,
|
|
updateSession,
|
|
deleteSessionById,
|
|
refreshData
|
|
}}>
|
|
{children}
|
|
</DataContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useData = () => {
|
|
const context = useContext(DataContext);
|
|
if (context === undefined) {
|
|
throw new Error('useData must be used within a DataProvider');
|
|
}
|
|
return context;
|
|
};
|