Files
trainhub-flutter/lib/presentation/workout_session/workout_session_controller.dart
Kazimierz Ciołek 0c9eb8878d
Some checks failed
Build Linux App / build (push) Failing after 1m33s
Refactoring
2026-02-23 10:02:23 -05:00

202 lines
5.5 KiB
Dart

import 'dart:async';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:trainhub_flutter/domain/entities/training_plan.dart';
import 'package:trainhub_flutter/domain/entities/workout_activity.dart';
import 'package:trainhub_flutter/presentation/workout_session/workout_session_state.dart';
import 'package:trainhub_flutter/injection.dart';
import 'package:trainhub_flutter/domain/repositories/training_plan_repository.dart';
part 'workout_session_controller.g.dart';
@riverpod
class WorkoutSessionController extends _$WorkoutSessionController {
Timer? _timer;
@override
Future<WorkoutSessionState> build(String planId) async {
final planRepo = getIt<TrainingPlanRepository>();
final plan = await planRepo.getById(planId);
final activities = _buildSequence(plan);
ref.onDispose(() => _timer?.cancel());
if (activities.isEmpty) {
return WorkoutSessionState(activities: activities);
}
final first = activities.first;
final initialState = WorkoutSessionState(
activities: activities,
timeRemaining: first.duration,
);
if (first.isTimeBased) {
Future.microtask(startTimer);
}
return initialState;
}
List<WorkoutActivityEntity> _buildSequence(TrainingPlanEntity plan) {
final List<WorkoutActivityEntity> seq = [];
for (final section in plan.sections) {
for (final ex in section.exercises) {
for (int s = 1; s <= ex.sets; s++) {
seq.add(
WorkoutActivityEntity(
id: '${ex.instanceId}-s$s-work',
name: ex.name,
type: 'work',
duration: ex.isTime ? ex.value : 0,
originalExercise: ex,
sectionName: section.name,
setIndex: s,
totalSets: ex.sets,
),
);
final bool isLastOfWorkout =
s == ex.sets &&
section.exercises.last == ex &&
plan.sections.last == section;
if (ex.rest > 0 && !isLastOfWorkout) {
seq.add(
WorkoutActivityEntity(
id: '${ex.instanceId}-s$s-rest',
name: 'Rest',
type: 'rest',
duration: ex.rest,
sectionName: section.name,
),
);
}
}
}
}
return seq;
}
void startTimer() {
if (_timer != null && _timer!.isActive) return;
_timer = Timer.periodic(const Duration(seconds: 1), _tick);
final currentState = state.value;
if (currentState != null) {
state = AsyncValue.data(currentState.copyWith(isRunning: true));
}
}
void pauseTimer() {
_timer?.cancel();
_timer = null;
final currentState = state.value;
if (currentState != null) {
state = AsyncValue.data(currentState.copyWith(isRunning: false));
}
}
void rewindSeconds(int amount) {
final currentState = state.value;
if (currentState == null) return;
final maxDuration = currentState.currentActivity?.duration ?? 0;
final newRemaining = (currentState.timeRemaining + amount).clamp(
0,
maxDuration,
);
state = AsyncValue.data(
currentState.copyWith(timeRemaining: newRemaining),
);
}
void _tick(Timer timer) {
if (state.value?.isFinished ?? true) return;
var currentState = state.value!;
var newState = currentState.copyWith(
totalTimeElapsed: currentState.totalTimeElapsed + 1,
);
final activity = newState.currentActivity;
if (activity != null && activity.duration > 0 && newState.isRunning) {
if (newState.timeRemaining > 0) {
newState = newState.copyWith(timeRemaining: newState.timeRemaining - 1);
} else {
state = AsyncValue.data(newState);
_goNext(newState);
return;
}
}
state = AsyncValue.data(newState);
}
void next() {
final currentState = state.value;
if (currentState != null) _goNext(currentState);
}
void _goNext(WorkoutSessionState currentState) {
if (currentState.currentIndex < currentState.activities.length - 1) {
final nextIndex = currentState.currentIndex + 1;
final nextActivity = currentState.activities[nextIndex];
final newState = currentState.copyWith(
currentIndex: nextIndex,
timeRemaining: nextActivity.duration,
);
state = AsyncValue.data(newState);
if (nextActivity.isTimeBased) {
startTimer();
} else {
pauseTimer();
}
} else {
_finish();
}
}
void previous() {
final currentState = state.value;
if (currentState != null && currentState.currentIndex > 0) {
final prevIndex = currentState.currentIndex - 1;
final prevActivity = currentState.activities[prevIndex];
state = AsyncValue.data(
currentState.copyWith(
currentIndex: prevIndex,
timeRemaining: prevActivity.duration,
),
);
pauseTimer();
}
}
void jumpTo(int index) {
final currentState = state.value;
if (currentState == null) return;
if (index < 0 || index >= currentState.activities.length) return;
final activity = currentState.activities[index];
state = AsyncValue.data(
currentState.copyWith(
currentIndex: index,
timeRemaining: activity.duration,
),
);
if (activity.isTimeBased) {
startTimer();
} else {
pauseTimer();
}
}
void _finish() {
pauseTimer();
final currentState = state.value;
if (currentState != null) {
state = AsyncValue.data(currentState.copyWith(isFinished: true));
}
}
}