import '../../about/external/data/Program.dart'; import '../../about/external/data/SessionLog.dart'; import '../../about/external/data/SessionTemplate.dart'; import '../../about/external/data/pages/request/HistoryRequest.dart'; import '../../about/external/data/pages/request/PageAndSort.dart'; import '../../about/external/data/pages/request/Pageable.dart'; import '../../about/external/data/pages/request/Sort.dart'; import '../../about/external/data/pages/response/SessionLogPage.dart'; import '../../about/external/initial/IdRequest.dart'; import '../../utils/IntegrityEngine.dart'; import '../../utils/ObjectConvertors.dart'; import '../parent/ParentViewModel.dart'; import 'ConnectTraining.dart'; class ViewTraining extends ParentViewModel { ConnectTraining connection; ViewTraining(super.context, this.connection); void loadProgram() async { if (!await hasNetwork(() => loadProgram())) return; showLoading("Loading your program"); try { final response = await getDataManager().getMyPrograms(HistoryRequest( query: PageAndSort( sort: Sort('desc', 'active'), page: Pageable(0, 0, 20, 0), ), )); final List programs = getProgramList(response.data); final Program active = programs.firstWhere( (item) => item.active, orElse: () => programs.isEmpty ? Program() : programs.first, ); await getDataManager().setActiveProgram(active); final List sessions = await _loadSessions(active); closeLoading(); connection.onProgramLoaded(active, sessions); loadHistory(active); } catch (e) { handleError(e, () => loadProgram(), () => dismissError(), "Retry"); } } Future> _loadSessions(Program program) async { if (program.id == null) { return []; } final response = await getDataManager() .getProgramSessions(IdRequest(id: program.id ?? "")); return getSessionTemplateList(response.data); } void loadHistory(Program program) async { try { final response = await getDataManager().getSessionHistory(HistoryRequest( query: PageAndSort( sort: Sort('desc', 'startedAt'), page: Pageable(0, 0, 20, 0), ), )); final SessionLogPage page = SessionLogPage.fromJson(response.data); connection.onHistoryLoaded(page.content); _checkLoadCeilings(program, page.content); } catch (e) { handleError(e, () => loadHistory(program), () => dismissError(), "Retry"); } } /// Plyometrics is the one modality where the app should stop you rather than /// push you — CNS and connective tissue do not recover on a motivation /// schedule. void _checkLoadCeilings(Program program, List history) { final DateTime weekStart = DateTime.now().subtract(Duration(days: DateTime.now().weekday - 1)); final List thisWeek = history .where((session) => session.startedAt != null && session.startedAt!.isAfter(weekStart)) .toList(); final int contacts = IntegrityEngine.weeklyContacts(thisWeek); if (IntegrityEngine.contactCeilingBreached( thisWeek, program.weeklyContactCeiling)) { connection.onContactCeilingReached( contacts, program.weeklyContactCeiling); return; } _checkRecovery(program, history); } void _checkRecovery(Program program, List history) { // The most recent hard lower-body session gates the next one. final List hard = history .where((session) => session.sessionRpe >= 8 && session.startedAt != null) .toList(); if (hard.isEmpty) { return; } final DateTime last = hard.first.startedAt!; if (IntegrityEngine.recoveredEnough(last, program.lowerBodyRecoveryHours)) { return; } final int elapsed = DateTime.now().difference(last).inHours; connection.onRecoveryBlocked(program.lowerBodyRecoveryHours - elapsed); } }