Initial commit
This commit is contained in:
182
lib/presentation/workout_session/workout_session_controller.dart
Normal file
182
lib/presentation/workout_session/workout_session_controller.dart
Normal file
@@ -0,0 +1,182 @@
|
||||
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());
|
||||
|
||||
final initialState = WorkoutSessionState(activities: activities);
|
||||
|
||||
if (activities.isNotEmpty) {
|
||||
final first = activities.first;
|
||||
return initialState.copyWith(timeRemaining: first.duration);
|
||||
}
|
||||
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 _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); // update interim state before next
|
||||
_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.isRest) {
|
||||
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 &&
|
||||
index >= 0 &&
|
||||
index < currentState.activities.length) {
|
||||
final activity = currentState.activities[index];
|
||||
|
||||
state = AsyncValue.data(
|
||||
currentState.copyWith(
|
||||
currentIndex: index,
|
||||
timeRemaining: activity.duration,
|
||||
),
|
||||
);
|
||||
|
||||
if (activity.isRest) {
|
||||
startTimer();
|
||||
} else {
|
||||
pauseTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _finish() {
|
||||
pauseTimer();
|
||||
final currentState = state.value;
|
||||
if (currentState != null) {
|
||||
state = AsyncValue.data(currentState.copyWith(isFinished: true));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user