213 lines
6.1 KiB
Dart
213 lines
6.1 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:kettlebell_tracker/models/training_session.dart';
|
|
import 'package:kettlebell_tracker/services/api_service.dart';
|
|
import 'package:kettlebell_tracker/services/database_helper.dart';
|
|
import 'dart:async';
|
|
|
|
// Training State
|
|
class TrainingState {
|
|
final bool isTrainingRunning;
|
|
final int remainingSeconds;
|
|
final int initialDurationSeconds;
|
|
final int setsDone;
|
|
final int goalSets;
|
|
final int repsPerSet;
|
|
final List<DateTime> setTimes;
|
|
final double progress;
|
|
final int secondsSinceLastSet;
|
|
final DateTime? lastSetTimestamp;
|
|
final String currentProgram;
|
|
final int currentBlockDay;
|
|
final int currentReps;
|
|
final int totalTrainingDays;
|
|
|
|
TrainingState({
|
|
this.isTrainingRunning = false,
|
|
this.remainingSeconds = 0,
|
|
this.initialDurationSeconds = 0,
|
|
this.setsDone = 0,
|
|
this.goalSets = 5,
|
|
this.repsPerSet = 5,
|
|
this.setTimes = const [],
|
|
this.progress = 0.0,
|
|
this.secondsSinceLastSet = 0,
|
|
this.lastSetTimestamp,
|
|
this.currentProgram = 'giant_1.0',
|
|
this.currentBlockDay = 1,
|
|
this.currentReps = 5,
|
|
this.totalTrainingDays = 0,
|
|
});
|
|
|
|
TrainingState copyWith({
|
|
bool? isTrainingRunning,
|
|
int? remainingSeconds,
|
|
int? initialDurationSeconds,
|
|
int? setsDone,
|
|
int? goalSets,
|
|
int? repsPerSet,
|
|
List<DateTime>? setTimes,
|
|
double? progress,
|
|
int? secondsSinceLastSet,
|
|
DateTime? lastSetTimestamp,
|
|
bool clearLastSetTimestamp = false,
|
|
String? currentProgram,
|
|
int? currentBlockDay,
|
|
int? currentReps,
|
|
int? totalTrainingDays,
|
|
}) {
|
|
return TrainingState(
|
|
isTrainingRunning: isTrainingRunning ?? this.isTrainingRunning,
|
|
remainingSeconds: remainingSeconds ?? this.remainingSeconds,
|
|
initialDurationSeconds:
|
|
initialDurationSeconds ?? this.initialDurationSeconds,
|
|
setsDone: setsDone ?? this.setsDone,
|
|
goalSets: goalSets ?? this.goalSets,
|
|
repsPerSet: repsPerSet ?? this.repsPerSet,
|
|
setTimes: setTimes ?? this.setTimes,
|
|
progress: progress ?? this.progress,
|
|
secondsSinceLastSet: secondsSinceLastSet ?? this.secondsSinceLastSet,
|
|
lastSetTimestamp: clearLastSetTimestamp
|
|
? null
|
|
: lastSetTimestamp ?? this.lastSetTimestamp,
|
|
currentProgram: currentProgram ?? this.currentProgram,
|
|
currentBlockDay: currentBlockDay ?? this.currentBlockDay,
|
|
currentReps: currentReps ?? this.currentReps,
|
|
totalTrainingDays: totalTrainingDays ?? this.totalTrainingDays,
|
|
);
|
|
}
|
|
}
|
|
|
|
class TrainingNotifier extends StateNotifier<TrainingState> {
|
|
final Ref ref;
|
|
TrainingNotifier(this.ref) : super(TrainingState());
|
|
|
|
void _updateProgram() {
|
|
int newTotalDays = state.totalTrainingDays + 1;
|
|
String newProgram = state.currentProgram;
|
|
int newDay = (state.currentBlockDay % 3) + 1;
|
|
int newReps = state.currentReps;
|
|
|
|
if (newTotalDays > 0 && newTotalDays % 12 == 0) {
|
|
switch (state.currentProgram) {
|
|
case 'giant_1.0':
|
|
newProgram = 'giant_1.1';
|
|
break;
|
|
case 'giant_1.1':
|
|
newProgram = 'giant_1.2';
|
|
break;
|
|
case 'giant_1.2':
|
|
newProgram = 'ksk_1.0';
|
|
break;
|
|
case 'ksk_1.0':
|
|
newProgram = 'ksk_1.1';
|
|
break;
|
|
case 'ksk_1.1':
|
|
newProgram = 'ksk_1.2';
|
|
break;
|
|
case 'ksk_1.2':
|
|
newProgram = 'giant_1.0';
|
|
break;
|
|
default:
|
|
newProgram = 'giant_1.0';
|
|
}
|
|
newDay = 1;
|
|
}
|
|
|
|
if (newProgram == 'giant_1.0') {
|
|
newReps = [5, 6, 4][newDay - 1];
|
|
} else if (newProgram == 'giant_1.1') {
|
|
newReps = [6, 8, 7][newDay - 1];
|
|
} else if (newProgram == 'giant_1.2') {
|
|
newReps = [7, 9, 8][newDay - 1];
|
|
} else if (newProgram == 'ksk_1.0') {
|
|
newReps = [5, 6, 4][newDay - 1];
|
|
} else if (newProgram == 'ksk_1.1') {
|
|
newReps = [6, 8, 7][newDay - 1];
|
|
} else if (newProgram == 'ksk_1.2') {
|
|
newReps = [7, 9, 8][newDay - 1];
|
|
} else {
|
|
newReps = 5;
|
|
}
|
|
|
|
state = state.copyWith(
|
|
currentProgram: newProgram,
|
|
currentBlockDay: newDay,
|
|
currentReps: newReps,
|
|
totalTrainingDays: newTotalDays,
|
|
);
|
|
}
|
|
|
|
void startTraining(int minutes, int goal) {
|
|
_updateProgram();
|
|
final duration = minutes * 60;
|
|
state = state.copyWith(
|
|
isTrainingRunning: true,
|
|
initialDurationSeconds: duration,
|
|
remainingSeconds: duration,
|
|
goalSets: goal,
|
|
repsPerSet: state.currentReps,
|
|
);
|
|
}
|
|
|
|
void tick() {
|
|
if (state.remainingSeconds > 0) {
|
|
state = state.copyWith(remainingSeconds: state.remainingSeconds - 1);
|
|
}
|
|
}
|
|
|
|
void tickLastSetTimer() {
|
|
if (state.isTrainingRunning && state.lastSetTimestamp != null) {
|
|
state = state.copyWith(
|
|
secondsSinceLastSet:
|
|
DateTime.now().difference(state.lastSetTimestamp!).inSeconds,
|
|
);
|
|
}
|
|
}
|
|
|
|
void completeSet() {
|
|
final newSetsDone = state.setsDone + 1;
|
|
final newSetTimes = List<DateTime>.from(state.setTimes)
|
|
..add(DateTime.now());
|
|
final newProgress = state.goalSets > 0 ? newSetsDone / state.goalSets : 0.0;
|
|
|
|
state = state.copyWith(
|
|
setsDone: newSetsDone,
|
|
setTimes: newSetTimes,
|
|
progress: newProgress > 1.0 ? 1.0 : newProgress,
|
|
lastSetTimestamp: DateTime.now(),
|
|
secondsSinceLastSet: 0,
|
|
);
|
|
}
|
|
|
|
Future<void> finishTraining(TrainingSession session) async {
|
|
final updatedSession = session.copyWith(
|
|
program: state.currentProgram,
|
|
blockDay: state.currentBlockDay,
|
|
);
|
|
await DatabaseHelper().saveTraining(updatedSession);
|
|
try {
|
|
await sendTrainingToBackend(
|
|
reps: session.repsPerSet,
|
|
rest: session.duration / session.sets,
|
|
sets: session.sets);
|
|
} catch (e) {
|
|
print("Error sending information to backend");
|
|
}
|
|
ref.refresh(historyProvider);
|
|
resetTraining();
|
|
}
|
|
|
|
void resetTraining() {
|
|
state = TrainingState();
|
|
}
|
|
}
|
|
|
|
final historyProvider = FutureProvider<List<TrainingSession>>((ref) async {
|
|
return DatabaseHelper().getHistory();
|
|
});
|
|
|
|
final trainingProvider = StateNotifierProvider<TrainingNotifier, TrainingState>(
|
|
(ref) {
|
|
return TrainingNotifier(ref);
|
|
},
|
|
);
|