kettlebell-tracker/lib/providers/training_provider.dart
2025-06-16 18:56:34 +02:00

132 lines
3.8 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;
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,
});
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,
}) {
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,
);
}
}
class TrainingNotifier extends StateNotifier<TrainingState> {
final Ref ref;
TrainingNotifier(this.ref) : super(TrainingState());
void startTraining(int minutes, int reps, int goal) {
final duration = minutes * 60;
state = TrainingState(
isTrainingRunning: true,
initialDurationSeconds: duration,
remainingSeconds: duration,
repsPerSet: reps,
goalSets: goal,
);
}
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 {
await DatabaseHelper().saveTraining(session);
await sendTrainingToBackend(
reps: session.repsPerSet,
rest: session.duration / session.sets,
sets: session.sets);
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);
},
);