243 lines
7.7 KiB
Dart
243 lines
7.7 KiB
Dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
import 'package:trainhub_flutter/core/utils/id_generator.dart';
|
|
import 'package:trainhub_flutter/injection.dart';
|
|
import 'package:trainhub_flutter/domain/entities/exercise.dart';
|
|
import 'package:trainhub_flutter/domain/entities/training_exercise.dart';
|
|
import 'package:trainhub_flutter/domain/entities/training_section.dart';
|
|
import 'package:trainhub_flutter/domain/repositories/exercise_repository.dart';
|
|
import 'package:trainhub_flutter/domain/repositories/training_plan_repository.dart';
|
|
import 'package:trainhub_flutter/presentation/plan_editor/plan_editor_state.dart';
|
|
|
|
part 'plan_editor_controller.g.dart';
|
|
|
|
@riverpod
|
|
class PlanEditorController extends _$PlanEditorController {
|
|
late TrainingPlanRepository _planRepo;
|
|
late ExerciseRepository _exerciseRepo;
|
|
|
|
@override
|
|
Future<PlanEditorState> build(String planId) async {
|
|
_planRepo = getIt<TrainingPlanRepository>();
|
|
_exerciseRepo = getIt<ExerciseRepository>();
|
|
|
|
final plan = await _planRepo.getById(planId);
|
|
final exercises = await _exerciseRepo.getAll();
|
|
|
|
return PlanEditorState(plan: plan, availableExercises: exercises);
|
|
}
|
|
|
|
void updatePlanName(String name) {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
state = AsyncValue.data(
|
|
current.copyWith(plan: current.plan.copyWith(name: name), isDirty: true),
|
|
);
|
|
}
|
|
|
|
void addSection() {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
final newSection = TrainingSectionEntity(
|
|
id: IdGenerator.generate(),
|
|
name: 'New Section',
|
|
);
|
|
state = AsyncValue.data(
|
|
current.copyWith(
|
|
plan: current.plan.copyWith(
|
|
sections: [...current.plan.sections, newSection],
|
|
),
|
|
isDirty: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
void deleteSection(int index) {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
final sections = List<TrainingSectionEntity>.from(current.plan.sections);
|
|
sections.removeAt(index);
|
|
state = AsyncValue.data(
|
|
current.copyWith(
|
|
plan: current.plan.copyWith(sections: sections),
|
|
isDirty: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
void reorderSection(int oldIndex, int newIndex) {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
final sections = List<TrainingSectionEntity>.from(current.plan.sections);
|
|
if (oldIndex < newIndex) newIndex -= 1;
|
|
final item = sections.removeAt(oldIndex);
|
|
sections.insert(newIndex, item);
|
|
state = AsyncValue.data(
|
|
current.copyWith(
|
|
plan: current.plan.copyWith(sections: sections),
|
|
isDirty: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
void updateSectionName(int sectionIndex, String name) {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
final sections = List<TrainingSectionEntity>.from(current.plan.sections);
|
|
sections[sectionIndex] = sections[sectionIndex].copyWith(name: name);
|
|
state = AsyncValue.data(
|
|
current.copyWith(
|
|
plan: current.plan.copyWith(sections: sections),
|
|
isDirty: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
void addExerciseToSection(int sectionIndex, ExerciseEntity exercise) {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
final sections = List<TrainingSectionEntity>.from(current.plan.sections);
|
|
final newExercise = TrainingExerciseEntity(
|
|
instanceId: IdGenerator.generate(),
|
|
exerciseId: exercise.id,
|
|
name: exercise.name,
|
|
);
|
|
sections[sectionIndex] = sections[sectionIndex].copyWith(
|
|
exercises: [...sections[sectionIndex].exercises, newExercise],
|
|
);
|
|
state = AsyncValue.data(
|
|
current.copyWith(
|
|
plan: current.plan.copyWith(sections: sections),
|
|
isDirty: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
void removeExerciseFromSection(int sectionIndex, int exerciseIndex) {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
final sections = List<TrainingSectionEntity>.from(current.plan.sections);
|
|
final exercises = List<TrainingExerciseEntity>.from(
|
|
sections[sectionIndex].exercises,
|
|
);
|
|
exercises.removeAt(exerciseIndex);
|
|
sections[sectionIndex] = sections[sectionIndex].copyWith(
|
|
exercises: exercises,
|
|
);
|
|
state = AsyncValue.data(
|
|
current.copyWith(
|
|
plan: current.plan.copyWith(sections: sections),
|
|
isDirty: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
void moveExerciseBetweenSections({
|
|
required int fromSectionIndex,
|
|
required int exerciseIndex,
|
|
required int toSectionIndex,
|
|
}) {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
if (fromSectionIndex == toSectionIndex) return;
|
|
final sections = List<TrainingSectionEntity>.from(current.plan.sections);
|
|
final fromExercises = List<TrainingExerciseEntity>.from(
|
|
sections[fromSectionIndex].exercises,
|
|
);
|
|
final toExercises = List<TrainingExerciseEntity>.from(
|
|
sections[toSectionIndex].exercises,
|
|
);
|
|
final exercise = fromExercises.removeAt(exerciseIndex);
|
|
toExercises.add(exercise);
|
|
sections[fromSectionIndex] = sections[fromSectionIndex].copyWith(
|
|
exercises: fromExercises,
|
|
);
|
|
sections[toSectionIndex] = sections[toSectionIndex].copyWith(
|
|
exercises: toExercises,
|
|
);
|
|
state = AsyncValue.data(
|
|
current.copyWith(
|
|
plan: current.plan.copyWith(sections: sections),
|
|
isDirty: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
void updateExerciseParams(
|
|
int sectionIndex,
|
|
int exerciseIndex, {
|
|
int? sets,
|
|
int? value,
|
|
bool? isTime,
|
|
int? rest,
|
|
}) {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
final sections = List<TrainingSectionEntity>.from(current.plan.sections);
|
|
final exercises = List<TrainingExerciseEntity>.from(
|
|
sections[sectionIndex].exercises,
|
|
);
|
|
exercises[exerciseIndex] = exercises[exerciseIndex].copyWith(
|
|
sets: sets ?? exercises[exerciseIndex].sets,
|
|
value: value ?? exercises[exerciseIndex].value,
|
|
isTime: isTime ?? exercises[exerciseIndex].isTime,
|
|
rest: rest ?? exercises[exerciseIndex].rest,
|
|
);
|
|
sections[sectionIndex] = sections[sectionIndex].copyWith(
|
|
exercises: exercises,
|
|
);
|
|
state = AsyncValue.data(
|
|
current.copyWith(
|
|
plan: current.plan.copyWith(sections: sections),
|
|
isDirty: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
void reorderExercise(int sectionIndex, int oldIndex, int newIndex) {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
final sections = List<TrainingSectionEntity>.from(current.plan.sections);
|
|
final exercises = List<TrainingExerciseEntity>.from(
|
|
sections[sectionIndex].exercises,
|
|
);
|
|
if (oldIndex < newIndex) newIndex -= 1;
|
|
final item = exercises.removeAt(oldIndex);
|
|
exercises.insert(newIndex, item);
|
|
sections[sectionIndex] = sections[sectionIndex].copyWith(
|
|
exercises: exercises,
|
|
);
|
|
state = AsyncValue.data(
|
|
current.copyWith(
|
|
plan: current.plan.copyWith(sections: sections),
|
|
isDirty: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<ExerciseEntity> createExercise({
|
|
required String name,
|
|
String? instructions,
|
|
String? tags,
|
|
String? videoUrl,
|
|
}) async {
|
|
final current = state.valueOrNull;
|
|
if (current == null) throw StateError('Controller state not loaded');
|
|
final exercise = await _exerciseRepo.create(
|
|
name: name,
|
|
instructions: instructions,
|
|
tags: tags,
|
|
videoUrl: videoUrl,
|
|
);
|
|
final exercises = await _exerciseRepo.getAll();
|
|
state = AsyncValue.data(current.copyWith(availableExercises: exercises));
|
|
return exercise;
|
|
}
|
|
|
|
Future<void> save() async {
|
|
final current = state.valueOrNull;
|
|
if (current == null) return;
|
|
await _planRepo.update(current.plan);
|
|
state = AsyncValue.data(current.copyWith(isDirty: false));
|
|
}
|
|
}
|