Code maintainability fixes
This commit is contained in:
208
src/context/DataContext.tsx
Normal file
208
src/context/DataContext.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
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;
|
||||
};
|
||||
Reference in New Issue
Block a user