diff --git a/lib/plan_list.dart b/lib/plan_list.dart index 3fe346a..370f432 100644 --- a/lib/plan_list.dart +++ b/lib/plan_list.dart @@ -3,6 +3,7 @@ import 'package:fmassive/constants.dart'; import 'package:fmassive/database.dart'; import 'package:fmassive/edit_plan.dart'; import 'package:fmassive/main.dart'; +import 'package:fmassive/start_plan.dart'; import 'package:moor_flutter/moor_flutter.dart'; class PlanList extends StatelessWidget { @@ -61,7 +62,7 @@ class PlanList extends StatelessWidget { context, MaterialPageRoute( builder: (context) => - EditPlanPage(plan: plans[index].toCompanion(false)), + StartPlan(plan: plans[index].toCompanion(false)), ), ); }); diff --git a/lib/start_plan.dart b/lib/start_plan.dart new file mode 100644 index 0000000..fa490ad --- /dev/null +++ b/lib/start_plan.dart @@ -0,0 +1,184 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' as material; +import 'package:flutter/services.dart'; +import 'package:fmassive/database.dart'; +import 'package:fmassive/edit_plan.dart'; +import 'package:fmassive/main.dart'; +import 'package:moor_flutter/moor_flutter.dart'; + +class StartPlan extends StatefulWidget { + final PlansCompanion plan; + + const StartPlan({required this.plan, super.key}); + + @override + createState() => _StartPlanState(); +} + +class _StartPlanState extends State { + late List exercises; + List counts = []; + List totals = []; + int selectedExercise = 0; + final repsController = TextEditingController(); + final repsNode = FocusNode(); + final weightController = TextEditingController(); + final weightNode = FocusNode(); + + Future getTotals() async { + final query = await (db.selectOnly(db.gymSets) + ..addColumns([db.gymSets.name, db.gymSets.sets]) + ..where(db.gymSets.name.isIn(exercises)) + ..groupBy([db.gymSets.name, db.gymSets.sets])) + .map((row) => + MapEntry(row.read(db.gymSets.name), row.read(db.gymSets.sets))) + .get(); + final map = Map.fromIterables( + query.map((entry) => entry.key), query.map((entry) => entry.value)); + setState(() { + totals = []; + for (var exercise in exercises) { + totals.add(map[exercise] ?? 0); + } + }); + print("totals=$totals"); + } + + Future getCounts() async { + var countExp = db.gymSets.name.count(); + final today = DateTime.now().toIso8601String().split('T')[0]; + final query = await (db.selectOnly(db.gymSets) + ..addColumns([countExp, db.gymSets.name]) + ..where(db.gymSets.created.contains(today)) + ..groupBy([db.gymSets.name])) + .map((row) => MapEntry(row.read(db.gymSets.name), row.read(countExp))) + .get(); + final map = Map.fromIterables( + query.map((entry) => entry.key), query.map((entry) => entry.value)); + setState(() { + counts = []; + for (var exercise in exercises) { + counts.add(map[exercise] ?? 0); + } + }); + print("counts=$counts"); + } + + Future focus(int index) async { + final name = exercises[index]; + final sets = await (db.gymSets.select() + ..where((gymSet) => gymSet.name.contains(name)) + ..orderBy([ + (u) => OrderingTerm(expression: u.created, mode: OrderingMode.desc), + ]) + ..limit(1)) + .get(); + final firstSet = sets.first; + repsController.text = firstSet.reps.toString(); + repsController.selection = TextSelection( + baseOffset: 0, extentOffset: firstSet.reps.toString().length); + weightController.text = firstSet.weight.toString(); + } + + @override + void initState() { + super.initState(); + exercises = widget.plan.exercises.value.split(','); + repsNode.requestFocus(); + + getCounts(); + getTotals(); + + focus(selectedExercise); + } + + @override + dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + List actions = []; + if (widget.plan.id.present) + actions.add(IconButton( + onPressed: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => EditPlanPage(plan: widget.plan), + ), + ); + }, + icon: const Icon(Icons.edit))); + + if (totals.isEmpty || counts.isEmpty) + return const CircularProgressIndicator(); + + return SafeArea( + child: Scaffold( + appBar: AppBar(title: const Text('Start plan'), actions: actions), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: material.Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: ListView.builder( + itemBuilder: ((context, index) { + return ListTile( + title: Text(exercises[index]), + subtitle: Text("${counts[index]}/${totals[index]}"), + onTap: () { + setState(() { + selectedExercise = index; + focus(index); + }); + }, + leading: Radio( + value: index, + groupValue: selectedExercise, + onChanged: (value) { + print("onChanged $value"); + if (value == null) return; + setState(() { + selectedExercise = value; + focus(index); + }); + }, + ), + ); + }), + itemCount: exercises.length, + ), + ), + TextFormField( + decoration: const InputDecoration(labelText: 'Reps'), + controller: repsController, + focusNode: repsNode, + ), + TextFormField( + decoration: const InputDecoration(labelText: 'Weight'), + controller: weightController, + focusNode: weightNode, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () async { + final gymSet = GymSetsCompanion( + created: Value(DateTime.now().toIso8601String()), + name: Value(exercises[selectedExercise]), + reps: Value(int.tryParse(repsController.text) ?? 0), + weight: Value(double.tryParse(weightController.text) ?? 0)); + await db.into(db.gymSets).insert(gymSet); + const platform = MethodChannel('com.massive/android'); + platform.invokeMethod('timer', [180000]); + await getCounts(); + }, + child: const Icon(Icons.check), + ), + )); + } +}