mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Allow adding additional exercises to the workout
While these are not real ad-hoc workouts, at least it's a first step
This commit is contained in:
@@ -105,16 +105,25 @@ class Log {
|
||||
Log.fromSetConfigData(SetConfigData data) {
|
||||
date = DateTime.now();
|
||||
sessionId = null;
|
||||
|
||||
slotEntryId = data.slotEntryId;
|
||||
exerciseBase = data.exercise;
|
||||
|
||||
weight = data.weight;
|
||||
weightTarget = data.weight;
|
||||
weightUnit = data.weightUnit;
|
||||
if (data.weight != null) {
|
||||
weight = data.weight;
|
||||
weightTarget = data.weight;
|
||||
}
|
||||
if (data.weightUnit != null) {
|
||||
weightUnit = data.weightUnit;
|
||||
}
|
||||
|
||||
repetitions = data.repetitions;
|
||||
repetitionsTarget = data.repetitions;
|
||||
repetitionUnit = data.repetitionsUnit;
|
||||
if (data.repetitions != null) {
|
||||
repetitions = data.repetitions;
|
||||
repetitionsTarget = data.repetitions;
|
||||
}
|
||||
if (data.repetitionsUnit != null) {
|
||||
repetitionUnit = data.repetitionsUnit;
|
||||
}
|
||||
|
||||
rir = data.rir;
|
||||
rirTarget = data.rir;
|
||||
|
||||
@@ -46,55 +46,55 @@ class SetConfigData {
|
||||
String get textReprWithType => '$textRepr${type.typeLabel}';
|
||||
|
||||
@JsonKey(required: true, name: 'sets')
|
||||
late num? nrOfSets;
|
||||
num? nrOfSets;
|
||||
|
||||
@JsonKey(required: true, name: 'max_sets')
|
||||
late num? maxNrOfSets;
|
||||
num? maxNrOfSets;
|
||||
|
||||
@JsonKey(required: true, fromJson: stringToNumNull)
|
||||
late num? weight;
|
||||
num? weight;
|
||||
|
||||
@JsonKey(required: true, name: 'max_weight', fromJson: stringToNumNull)
|
||||
late num? maxWeight;
|
||||
num? maxWeight;
|
||||
|
||||
@JsonKey(required: true, name: 'weight_unit')
|
||||
late int? weightUnitId;
|
||||
int? weightUnitId;
|
||||
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
late WeightUnit? weightUnit;
|
||||
WeightUnit? weightUnit;
|
||||
|
||||
@JsonKey(required: true, name: 'weight_rounding', fromJson: stringToNumNull)
|
||||
late num? weightRounding;
|
||||
num? weightRounding;
|
||||
|
||||
@JsonKey(required: true, name: 'repetitions', fromJson: stringToNumNull)
|
||||
late num? repetitions;
|
||||
num? repetitions;
|
||||
|
||||
@JsonKey(required: true, name: 'max_repetitions', fromJson: stringToNumNull)
|
||||
late num? maxRepetitions;
|
||||
num? maxRepetitions;
|
||||
|
||||
@JsonKey(required: true, name: 'repetitions_unit')
|
||||
late int? repetitionsUnitId;
|
||||
int? repetitionsUnitId;
|
||||
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
late RepetitionUnit? repetitionsUnit;
|
||||
RepetitionUnit? repetitionsUnit;
|
||||
|
||||
@JsonKey(required: true, name: 'repetitions_rounding', fromJson: stringToNumNull)
|
||||
late num? repetitionsRounding;
|
||||
num? repetitionsRounding;
|
||||
|
||||
@JsonKey(required: true, fromJson: stringToNumNull)
|
||||
late num? rir;
|
||||
num? rir;
|
||||
|
||||
@JsonKey(required: true, name: 'max_rir', fromJson: stringToNumNull)
|
||||
late num? maxRir;
|
||||
num? maxRir;
|
||||
|
||||
@JsonKey(required: true, fromJson: stringToNumNull)
|
||||
late num? rpe;
|
||||
num? rpe;
|
||||
|
||||
@JsonKey(required: true, name: 'rest', fromJson: stringToNumNull)
|
||||
late num? restTime;
|
||||
num? restTime;
|
||||
|
||||
@JsonKey(required: true, name: 'max_rest', fromJson: stringToNumNull)
|
||||
late num? maxRestTime;
|
||||
num? maxRestTime;
|
||||
|
||||
@JsonKey(required: true)
|
||||
late String comment;
|
||||
@@ -103,20 +103,20 @@ class SetConfigData {
|
||||
required this.exerciseId,
|
||||
required this.slotEntryId,
|
||||
this.type = SlotEntryType.normal,
|
||||
required this.nrOfSets,
|
||||
this.nrOfSets,
|
||||
this.maxNrOfSets,
|
||||
required this.weight,
|
||||
this.weight,
|
||||
this.maxWeight,
|
||||
this.weightUnitId = WEIGHT_UNIT_KG,
|
||||
this.weightRounding,
|
||||
required this.repetitions,
|
||||
this.repetitions,
|
||||
this.maxRepetitions,
|
||||
this.repetitionsUnitId = REP_UNIT_REPETITIONS_ID,
|
||||
this.repetitionsRounding,
|
||||
required this.rir,
|
||||
this.rir,
|
||||
this.maxRir,
|
||||
required this.rpe,
|
||||
required this.restTime,
|
||||
this.rpe,
|
||||
this.restTime,
|
||||
this.maxRestTime,
|
||||
this.comment = '',
|
||||
this.textRepr = '',
|
||||
|
||||
@@ -298,52 +298,57 @@ class GymStateNotifier extends _$GymStateNotifier {
|
||||
|
||||
/// Calculates the page entries
|
||||
void calculatePages() {
|
||||
var totalPages = 1;
|
||||
final List<PageEntry> pages = [PageEntry(type: PageType.start, pageIndex: totalPages - 1)];
|
||||
var pageIndex = 0;
|
||||
|
||||
final List<PageEntry> pages = [
|
||||
// Start page
|
||||
PageEntry(type: PageType.start, pageIndex: pageIndex),
|
||||
];
|
||||
|
||||
pageIndex++;
|
||||
for (final slotData in state.dayDataGym.slots) {
|
||||
final slotPageIndex = totalPages;
|
||||
final slotPageIndex = pageIndex;
|
||||
final slotEntries = <SlotPageEntry>[];
|
||||
int setIndex = 0;
|
||||
|
||||
// exercise overview page
|
||||
if (state.showExercisePages) {
|
||||
totalPages++;
|
||||
slotEntries.add(
|
||||
SlotPageEntry(
|
||||
type: SlotPageType.exerciseOverview,
|
||||
setIndex: setIndex,
|
||||
pageIndex: totalPages - 1,
|
||||
pageIndex: pageIndex,
|
||||
setConfigData: slotData.setConfigs.first,
|
||||
),
|
||||
);
|
||||
pageIndex++;
|
||||
}
|
||||
|
||||
for (final config in slotData.setConfigs) {
|
||||
// Timer page
|
||||
if (state.showTimerPages) {
|
||||
totalPages++;
|
||||
slotEntries.add(
|
||||
SlotPageEntry(
|
||||
type: SlotPageType.timer,
|
||||
setIndex: setIndex,
|
||||
pageIndex: totalPages - 1,
|
||||
pageIndex: pageIndex,
|
||||
setConfigData: config,
|
||||
),
|
||||
);
|
||||
pageIndex++;
|
||||
}
|
||||
|
||||
// Log page
|
||||
totalPages++;
|
||||
slotEntries.add(
|
||||
SlotPageEntry(
|
||||
type: SlotPageType.log,
|
||||
setIndex: setIndex,
|
||||
pageIndex: totalPages - 1,
|
||||
pageIndex: pageIndex,
|
||||
setConfigData: config,
|
||||
),
|
||||
);
|
||||
|
||||
pageIndex++;
|
||||
setIndex++;
|
||||
}
|
||||
|
||||
@@ -357,13 +362,65 @@ class GymStateNotifier extends _$GymStateNotifier {
|
||||
}
|
||||
|
||||
pages.add(
|
||||
PageEntry(type: PageType.session, pageIndex: totalPages),
|
||||
// Session page
|
||||
PageEntry(type: PageType.session, pageIndex: pageIndex),
|
||||
);
|
||||
|
||||
state = state.copyWith(pages: pages);
|
||||
debugStructure();
|
||||
_logger.finer('Initialized ${state.pages.length} pages');
|
||||
}
|
||||
|
||||
// Recalculates the indices of all pages
|
||||
void recalculateIndices() {
|
||||
var pageIndex = 0;
|
||||
final updatedPages = <PageEntry>[];
|
||||
|
||||
for (final page in state.pages) {
|
||||
final slotPageIndex = pageIndex;
|
||||
var setIndex = 0;
|
||||
final updatedSlotPages = <SlotPageEntry>[];
|
||||
|
||||
for (final slotPage in page.slotPages) {
|
||||
updatedSlotPages.add(
|
||||
slotPage.copyWith(
|
||||
pageIndex: pageIndex,
|
||||
setIndex: setIndex,
|
||||
),
|
||||
);
|
||||
setIndex++;
|
||||
pageIndex++;
|
||||
}
|
||||
|
||||
if (page.type != PageType.set) {
|
||||
pageIndex++;
|
||||
}
|
||||
|
||||
updatedPages.add(
|
||||
page.copyWith(
|
||||
pageIndex: slotPageIndex,
|
||||
slotPages: updatedSlotPages,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
state = state.copyWith(pages: updatedPages);
|
||||
debugStructure();
|
||||
_logger.fine('Recalculated page indices');
|
||||
}
|
||||
|
||||
void debugStructure() {
|
||||
_logger.fine('GymModeState structure:');
|
||||
for (final page in state.pages) {
|
||||
_logger.fine('Page ${page.pageIndex}: ${page.type}');
|
||||
for (final slotPage in page.slotPages) {
|
||||
_logger.fine(
|
||||
' SlotPage ${slotPage.pageIndex.toString().padLeft(2, ' ')} (set index ${slotPage.setIndex}): ${slotPage.type}',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int initData(Routine routine, int dayId, int iteration) {
|
||||
final validUntil = state.validUntil;
|
||||
final currentPage = state.currentPage;
|
||||
@@ -470,6 +527,46 @@ class GymStateNotifier extends _$GymStateNotifier {
|
||||
_logger.fine('Replaced exercise $originalExerciseId with ${newExercise.id}');
|
||||
}
|
||||
|
||||
void addExerciseAfterPage(
|
||||
String pageEntryUUID, {
|
||||
required Exercise newExercise,
|
||||
}) {
|
||||
final List<PageEntry> pages = [];
|
||||
for (final page in state.pages) {
|
||||
pages.add(page);
|
||||
|
||||
if (page.uuid == pageEntryUUID) {
|
||||
final setConfigData = page.slotPages.first.setConfigData!;
|
||||
|
||||
final List<SlotPageEntry> newSlotPages = [];
|
||||
for (var i = 1; i <= 4; i++) {
|
||||
newSlotPages.add(
|
||||
SlotPageEntry(
|
||||
type: SlotPageType.log,
|
||||
pageIndex: 1,
|
||||
setIndex: 0,
|
||||
setConfigData: SetConfigData(
|
||||
exerciseId: newExercise.id!,
|
||||
exercise: newExercise,
|
||||
slotEntryId: setConfigData.slotEntryId,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final newPage = PageEntry(type: PageType.set, pageIndex: 1, slotPages: newSlotPages);
|
||||
|
||||
pages.add(newPage);
|
||||
}
|
||||
}
|
||||
|
||||
state = state.copyWith(
|
||||
pages: pages,
|
||||
);
|
||||
|
||||
recalculateIndices();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_logger.fine('Clearing state');
|
||||
state = state.copyWith(
|
||||
|
||||
@@ -113,8 +113,8 @@ class ProgressionTab extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _ProgressionTabState extends ConsumerState<ProgressionTab> {
|
||||
String? showDetailsForPageId;
|
||||
String? showAddExerciseAfterPageId;
|
||||
String? showSwapWidgetToPage;
|
||||
String? showAddExerciseWidgetToPage;
|
||||
_ProgressionTabState();
|
||||
|
||||
@override
|
||||
@@ -177,7 +177,6 @@ class _ProgressionTabState extends ConsumerState<ProgressionTab> {
|
||||
},
|
||||
),
|
||||
|
||||
if (showDetailsForPageId == page.uuid) ExerciseSwapWidget(page.uuid),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -186,29 +185,46 @@ class _ProgressionTabState extends ConsumerState<ProgressionTab> {
|
||||
onPressed: page.allLogsDone
|
||||
? null
|
||||
: () {
|
||||
if (showDetailsForPageId == page.uuid) {
|
||||
if (showSwapWidgetToPage == page.uuid) {
|
||||
setState(() {
|
||||
widget._logger.fine('Hiding details');
|
||||
showDetailsForPageId = null;
|
||||
showSwapWidgetToPage = null;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
widget._logger.fine('Showing details for page ${page.uuid}');
|
||||
showDetailsForPageId = page.uuid;
|
||||
showSwapWidgetToPage = page.uuid;
|
||||
showAddExerciseWidgetToPage = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
icon: Icon(
|
||||
key: ValueKey('swap-icon-${page.uuid}'),
|
||||
showDetailsForPageId == page.uuid
|
||||
showSwapWidgetToPage == page.uuid
|
||||
? Icons.change_circle
|
||||
: Icons.change_circle_outlined,
|
||||
),
|
||||
),
|
||||
// IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: const Icon(Icons.add),
|
||||
// ),
|
||||
IconButton(
|
||||
onPressed: page.allLogsDone
|
||||
? null
|
||||
: () {
|
||||
if (showAddExerciseWidgetToPage == page.uuid) {
|
||||
setState(() {
|
||||
showAddExerciseWidgetToPage = null;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
showAddExerciseWidgetToPage = page.uuid;
|
||||
showSwapWidgetToPage = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
icon: Icon(
|
||||
key: ValueKey('add-icon-${page.uuid}'),
|
||||
showAddExerciseWidgetToPage == page.uuid ? Icons.add_circle : Icons.add,
|
||||
),
|
||||
),
|
||||
Expanded(child: Container()),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
@@ -223,6 +239,24 @@ class _ProgressionTabState extends ConsumerState<ProgressionTab> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (showSwapWidgetToPage == page.uuid)
|
||||
ExerciseSwapWidget(
|
||||
page.uuid,
|
||||
onDone: () {
|
||||
setState(() {
|
||||
showSwapWidgetToPage = null;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (showAddExerciseWidgetToPage == page.uuid)
|
||||
ExerciseAddWidget(
|
||||
page.uuid,
|
||||
onDone: () {
|
||||
setState(() {
|
||||
showAddExerciseWidgetToPage = null;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
);
|
||||
@@ -246,8 +280,9 @@ class ExerciseSwapWidget extends ConsumerWidget {
|
||||
final _logger = Logger('ExerciseSwapWidget');
|
||||
|
||||
final String pageUUID;
|
||||
final VoidCallback? onDone;
|
||||
|
||||
ExerciseSwapWidget(this.pageUUID, {super.key});
|
||||
ExerciseSwapWidget(this.pageUUID, {this.onDone, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -278,6 +313,7 @@ class ExerciseSwapWidget extends ConsumerWidget {
|
||||
originalExerciseId: e.id!,
|
||||
newExercise: exercise,
|
||||
);
|
||||
onDone?.call();
|
||||
_logger.fine('Replaced exercise ${e.id} with ${exercise.id}');
|
||||
},
|
||||
),
|
||||
@@ -293,6 +329,54 @@ class ExerciseSwapWidget extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class ExerciseAddWidget extends ConsumerWidget {
|
||||
final _logger = Logger('ExerciseAddWidget');
|
||||
|
||||
final String pageUUID;
|
||||
final VoidCallback? onDone;
|
||||
|
||||
ExerciseAddWidget(this.pageUUID, {this.onDone, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(gymStateProvider);
|
||||
final gymProvider = ref.read(gymStateProvider.notifier);
|
||||
final page = state.pages.firstWhere((p) => p.uuid == pageUUID);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
child: Column(
|
||||
children: [
|
||||
...page.exercises.map((e) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
ExerciseAutocompleter(
|
||||
onExerciseSelected: (exercise) {
|
||||
gymProvider.addExerciseAfterPage(
|
||||
page.uuid,
|
||||
newExercise: exercise,
|
||||
);
|
||||
onDone?.call();
|
||||
_logger.fine('Added exercise ${exercise.id} after page $pageUUID');
|
||||
},
|
||||
),
|
||||
const Icon(Icons.arrow_downward),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class WorkoutMenuDialog extends ConsumerWidget {
|
||||
final PageController controller;
|
||||
final bool showEndWorkoutButton;
|
||||
|
||||
Reference in New Issue
Block a user