diff --git a/lib/daos/session_activities_dao.dart b/lib/daos/session_activities_dao.dart index 8751cc3..5a37906 100644 --- a/lib/daos/session_activities_dao.dart +++ b/lib/daos/session_activities_dao.dart @@ -4,17 +4,21 @@ import 'package:sendtrain/database/database.dart'; part 'session_activities_dao.g.dart'; @DriftAccessor(tables: [SessionActivities]) -class SessionActivitiesDao extends DatabaseAccessor with _$SessionActivitiesDaoMixin { +class SessionActivitiesDao extends DatabaseAccessor + with _$SessionActivitiesDaoMixin { SessionActivitiesDao(super.db); - Future createOrUpdate(SessionActivitiesCompanion sessionActivity) => into(sessionActivities).insertOnConflictUpdate(sessionActivity); + Future createOrUpdate(SessionActivitiesCompanion sessionActivity) => + into(sessionActivities).insertOnConflictUpdate(sessionActivity); Future> all() async { return await select(sessionActivities).get(); } Future find(int id) async { - return await (select(sessionActivities)..where((sessionActivity) => sessionActivity.id.equals(id) )).getSingle(); + return await (select(sessionActivities) + ..where((sessionActivity) => sessionActivity.id.equals(id))) + .getSingle(); } Future> fromSessionId(int id) async { @@ -23,4 +27,16 @@ class SessionActivitiesDao extends DatabaseAccessor with _$SessionA return result.get(); } -} \ No newline at end of file + + Future remove(SessionActivity sessionActivity) => + delete(sessionActivities).delete(sessionActivity); + + Future removeAssociation(int activityId, int sessionId) { + return (delete(sessionActivities) + ..where((t) => + t.sessionId.equals(sessionId) & t.activityId.equals(activityId))) + .go(); + } +} + +// return (delete(todos)..where((t) => t.id.isSmallerThanValue(10))).go(); \ No newline at end of file diff --git a/lib/models/activity_timer_model.dart b/lib/models/activity_timer_model.dart index e7d2723..054bd6e 100644 --- a/lib/models/activity_timer_model.dart +++ b/lib/models/activity_timer_model.dart @@ -19,9 +19,9 @@ class ActivityTimerModel with ChangeNotifier { int get actionCount => _actionCounter; int get currentActionNum => _currentActionNum; - dynamic get currentAction => currentSet[_currentActionNum]; + dynamic get currentAction => currentSet.isNotEmpty ? currentSet[_currentActionNum] : {}; int get currentSetNum => _currentSetNum; - dynamic get currentSet => _sets[_currentSetNum]; + dynamic get currentSet => _sets.isNotEmpty ? _sets[_currentSetNum] : {}; Activity? get activity => _activity; List get sets => _sets; Timer? get periodicTimer => _periodicTimer; @@ -36,7 +36,7 @@ class ActivityTimerModel with ChangeNotifier { _isc = null; _activity = activity; // only one action for now - _sets = json.decode(actions[0].set); + _sets = actions.isNotEmpty ? json.decode(actions[0].set) : []; // _actions = actions; _currentActionNum = 0; _currentSetNum = 0; @@ -92,7 +92,7 @@ class ActivityTimerModel with ChangeNotifier { } void setActionCount() { - _actionCounter = currentAction['amount']; + _actionCounter = currentAction.isNotEmpty ? currentAction['amount'] : 0; } void pause() { diff --git a/lib/widgets/activities/activity_card.dart b/lib/widgets/activities/activity_card.dart index df30da2..95de21a 100644 --- a/lib/widgets/activities/activity_card.dart +++ b/lib/widgets/activities/activity_card.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:sendtrain/daos/activities_dao.dart'; import 'package:sendtrain/daos/media_items_dao.dart'; +import 'package:sendtrain/daos/session_activities_dao.dart'; import 'package:sendtrain/database/database.dart'; import 'package:sendtrain/extensions/string_extensions.dart'; import 'package:sendtrain/helpers/date_time_helpers.dart'; @@ -14,9 +14,14 @@ import 'package:sendtrain/widgets/generic/elements/generic_progress_indicator.da class ActivityCard extends StatefulWidget { final Activity activity; + final Session session; final Function? callback; - const ActivityCard({super.key, required this.activity, this.callback}); + const ActivityCard( + {super.key, + required this.activity, + required this.session, + this.callback}); @override State createState() => ActivityCardState(); @@ -62,8 +67,8 @@ class ActivityCardState extends State { } }, ), - subtitle: - Text(maxLines: 2, widget.activity.description ?? ""), + subtitle: Text( + maxLines: 2, widget.activity.description ?? ""), contentPadding: EdgeInsets.only(left: 13), trailing: Flex( direction: Axis.vertical, @@ -79,10 +84,11 @@ class ActivityCardState extends State { 'Activity Removal', 'Would you like to permanently remove this activity from the current session?', context, () { - ActivitiesDao(Provider.of( - context, - listen: false)) - .remove(widget.activity); + SessionActivitiesDao( + Provider.of(context, + listen: false)) + .removeAssociation(widget.activity.id, + widget.session.id); }).then((result) { setState(() {}); }); diff --git a/lib/widgets/activities/activity_view.dart b/lib/widgets/activities/activity_view.dart index 573c2d8..4285a0d 100644 --- a/lib/widgets/activities/activity_view.dart +++ b/lib/widgets/activities/activity_view.dart @@ -11,8 +11,7 @@ import 'package:sendtrain/widgets/activities/activity_view_media.dart'; import 'package:sendtrain/widgets/activities/activity_view_types.dart'; class ActivityView extends StatefulWidget { - const ActivityView( - {super.key, required this.activity}); + const ActivityView({super.key, required this.activity}); final Activity activity; @override @@ -20,6 +19,76 @@ class ActivityView extends StatefulWidget { } class _ActivityViewState extends State { + List action(actions) { + if (actions.isNotEmpty) { + return [ + Padding( + padding: const EdgeInsets.only(left: 10, right: 10), + child: Card( + clipBehavior: Clip.antiAlias, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10)), + ), + color: Theme.of(context).colorScheme.onPrimary, + child: Row(children: [ + Ink( + width: 70, + color: Theme.of(context).colorScheme.primaryContainer, + child: Consumer( + builder: (context, atm, child) { + return IconButton( + alignment: AlignmentDirectional.center, + icon: atm.isActive + ? const Icon(Icons.pause_rounded) + : const Icon(Icons.play_arrow_rounded), + onPressed: () => + {atm.isActive ? atm.pause() : atm.start()}); + }, + )), + Expanded( + flex: 1, + child: Stack(alignment: Alignment.center, children: [ + Container( + alignment: Alignment.center, + child: Consumer( + builder: (context, atm, child) { + return Text( + style: const TextStyle(fontSize: 20), + textAlign: TextAlign.center, + '${atm.actionCount} ${atm.currentAction['type']}' + .toTitleCase()); + }, + ), + ), + Container( + alignment: Alignment.centerRight, + padding: EdgeInsets.only(right: 15), + child: Consumer( + builder: (context, atm, child) { + return Text( + style: const TextStyle(fontSize: 12), + textAlign: TextAlign.right, + '${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}'); + })), + ])), + ]))), + Padding( + padding: EdgeInsets.only(left: 14, right: 14), + child: Consumer(builder: (context, atm, child) { + return LinearProgressIndicator( + value: atm.progress, + semanticsLabel: 'Activity Progress', + ); + })), + ActivityActionView(actions: actions) + ]; + } else { + return [Text('add an action')]; + } + } + @override Widget build(BuildContext context) { final Activity activity = widget.activity; @@ -44,11 +113,11 @@ class _ActivityViewState extends State { blur: 10, ), children: [ - FloatingActionButton.extended( - icon: const Icon(Icons.upload_outlined), - label: Text('Upload Media'), - onPressed: () {}, - ), + // FloatingActionButton.extended( + // icon: const Icon(Icons.upload_outlined), + // label: Text('Upload Media'), + // onPressed: () {}, + // ), FloatingActionButton.extended( icon: const Icon(Icons.note_add_outlined), label: Text('Add Note'), @@ -68,125 +137,159 @@ class _ActivityViewState extends State { body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AppBar( - titleSpacing: 0, - centerTitle: true, - title: const Text('Activity', - style: TextStyle(fontSize: 15)), - ), - Padding( - padding: const EdgeInsets.only( - left: 15, right: 20, top: 15, bottom: 10), - child: Text( - maxLines: 1, - style: const TextStyle( - fontSize: 25, fontWeight: FontWeight.bold), - activity.title.toTitleCase())), - Padding( - padding: const EdgeInsets.fromLTRB(10, 0, 0, 10), - child: Flex(direction: Axis.horizontal, children: [ - ActivityViewCategories( - categories: activity.category != null ? [activity.category!] : []), - ActivityViewTypes(types: [activity.type!]) - ])), - Padding( - padding: const EdgeInsets.only( - top: 0, bottom: 10, left: 15, right: 15), - child: Text( - textAlign: TextAlign.left, - style: const TextStyle(fontSize: 15), - activity.description ?? "")), - const Padding( - padding: EdgeInsets.fromLTRB(15, 20, 0, 10), - child: Text( - style: TextStyle( - fontSize: 20, fontWeight: FontWeight.bold), - 'Media:')), - ActivityViewMedia(activity: activity), - const Padding( - padding: EdgeInsets.fromLTRB(15, 30, 0, 10), - child: Text( - textAlign: TextAlign.left, - style: TextStyle( - fontSize: 20, fontWeight: FontWeight.bold), - 'Actions')), - Padding( - padding: const EdgeInsets.only(left: 10, right: 10), - child: Card( - clipBehavior: Clip.antiAlias, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(10)), - ), - color: Theme.of(context).colorScheme.onPrimary, - child: Row(children: [ - Ink( - width: 70, - color: Theme.of(context) - .colorScheme - .primaryContainer, - child: Consumer( - builder: (context, atm, child) { - return IconButton( - alignment: - AlignmentDirectional.center, - icon: atm.isActive - ? const Icon( - Icons.pause_rounded) - : const Icon( - Icons.play_arrow_rounded), - onPressed: () => { - atm.isActive - ? atm.pause() - : atm.start() - }); - }, - )), - Expanded( - flex: 1, - child: Stack( - alignment: Alignment.center, - children: [ - Container( - alignment: Alignment.center, - child: Consumer( - builder: (context, atm, child) { - return Text( - style: const TextStyle( - fontSize: 20), - textAlign: TextAlign.center, - '${atm.actionCount} ${atm.currentAction['type']}'.toTitleCase()); - }, - ), - ), - Container( - alignment: Alignment.centerRight, - padding: - EdgeInsets.only(right: 15), - child: - Consumer( - builder: (context, atm, - child) { - return Text( - style: const TextStyle( - fontSize: 12), - textAlign: TextAlign.right, - '${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}'); - })), - ])), - ]))), - Padding( - padding: EdgeInsets.only(left: 14, right: 14), - child: Consumer( - builder: (context, atm, child) { - return LinearProgressIndicator( - value: atm.progress, - semanticsLabel: 'Activity Progress', - ); - })), - ActivityActionView(actions: actions), - ])); + AppBar( + titleSpacing: 0, + centerTitle: true, + title: const Text('Activity', + style: TextStyle(fontSize: 15)), + ), + Padding( + padding: const EdgeInsets.only( + left: 15, right: 20, top: 15, bottom: 10), + child: Text( + maxLines: 1, + style: const TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold), + activity.title.toTitleCase())), + Padding( + padding: const EdgeInsets.fromLTRB(10, 0, 0, 10), + child: + Flex(direction: Axis.horizontal, children: [ + ActivityViewCategories>( + icon: Icon(Icons.stairs_rounded), + text: "Activity Level", + object: activity.level != null + ? [activity.level!] + : []), + // ActivityViewCategories>( + // icon: Icon(Icons.), + // text: 'Activity Mechanic', + // object: activity.mechanic != null + // ? [activity.mechanic!] + // : []), + ActivityViewCategories>( + icon: Icon(Icons.fitness_center_rounded), + text: 'Activity Equipments', + object: activity.equipment != null + ? [activity.equipment!] + : []), + ActivityViewCategories>( + icon: Icon(Icons.type_specimen_rounded), + text: 'Activity Equipments', + object: activity.equipment != null + ? [activity.type!] + : []), + ])), + Padding( + padding: const EdgeInsets.only( + top: 0, bottom: 10, left: 15, right: 15), + child: Text( + maxLines: 5, + textAlign: TextAlign.left, + style: const TextStyle(fontSize: 15), + activity.description ?? "")), + Padding( + padding: EdgeInsets.only(left: 15), + child: Text("read more...", + style: TextStyle( + color: Colors.deepPurpleAccent))), + const Padding( + padding: EdgeInsets.fromLTRB(15, 20, 0, 10), + child: Text( + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold), + 'Media:')), + ActivityViewMedia(activity: activity), + const Padding( + padding: EdgeInsets.fromLTRB(15, 30, 0, 10), + child: Text( + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold), + 'Actions')), + + // Padding( + // padding: const EdgeInsets.only(left: 10, right: 10), + // child: Card( + // clipBehavior: Clip.antiAlias, + // shape: const RoundedRectangleBorder( + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(10), + // topRight: Radius.circular(10)), + // ), + // color: Theme.of(context).colorScheme.onPrimary, + // child: Row(children: [ + // Ink( + // width: 70, + // color: Theme.of(context) + // .colorScheme + // .primaryContainer, + // child: Consumer( + // builder: (context, atm, child) { + // return IconButton( + // alignment: + // AlignmentDirectional.center, + // icon: atm.isActive + // ? const Icon( + // Icons.pause_rounded) + // : const Icon( + // Icons.play_arrow_rounded), + // onPressed: () => { + // atm.isActive + // ? atm.pause() + // : atm.start() + // }); + // }, + // )), + // Expanded( + // flex: 1, + // child: Stack( + // alignment: Alignment.center, + // children: [ + // Container( + // alignment: Alignment.center, + // child: Consumer( + // builder: (context, atm, child) { + // return Text( + // style: const TextStyle( + // fontSize: 20), + // textAlign: TextAlign.center, + // '${atm.actionCount} ${atm.currentAction['type']}' + // .toTitleCase()); + // }, + // ), + // ), + // Container( + // alignment: Alignment.centerRight, + // padding: + // EdgeInsets.only(right: 15), + // child: + // Consumer( + // builder: (context, atm, + // child) { + // return Text( + // style: const TextStyle( + // fontSize: 12), + // textAlign: TextAlign.right, + // '${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}'); + // })), + // ])), + // ]))), + // Padding( + // padding: EdgeInsets.only(left: 14, right: 14), + // child: Consumer( + // builder: (context, atm, child) { + // return LinearProgressIndicator( + // value: atm.progress, + // semanticsLabel: 'Activity Progress', + // ); + // })), + // ActivityActionView(actions: actions), + ] + + action(actions))); } else { return Container( alignment: Alignment.center, diff --git a/lib/widgets/activities/activity_view_categories.dart b/lib/widgets/activities/activity_view_categories.dart index 60ec09b..57f51e6 100644 --- a/lib/widgets/activities/activity_view_categories.dart +++ b/lib/widgets/activities/activity_view_categories.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; -import 'package:sendtrain/database/database.dart'; import 'package:sendtrain/extensions/string_extensions.dart'; -class ActivityViewCategories extends StatelessWidget { - const ActivityViewCategories({super.key, required this.categories}); +class ActivityViewCategories> extends StatelessWidget { + const ActivityViewCategories({super.key, required this.object, required this.icon, required this.text}); - final List categories; + final T object; + final Icon icon; + final String text; @override Widget build(BuildContext context) { @@ -15,13 +16,13 @@ class ActivityViewCategories extends StatelessWidget { shrinkWrap: true, scrollDirection: Axis.horizontal, padding: const EdgeInsets.only(right: 10), - itemCount: categories.length, + itemCount: object.length, itemBuilder: (BuildContext context, int index) { return ActionChip( visualDensity: VisualDensity.compact, - avatar: const Icon(Icons.category_rounded), - label: Text(maxLines: 1, categories[index].name.toTitleCase()), - tooltip: "Activity Category", + avatar: icon, + label: Text(maxLines: 1, object[index].name.toTitleCase()), + tooltip: text, onPressed: () {}, ); }, diff --git a/lib/widgets/generic/elements/form_search_input.dart b/lib/widgets/generic/elements/form_search_input.dart index ed78401..1836f25 100644 --- a/lib/widgets/generic/elements/form_search_input.dart +++ b/lib/widgets/generic/elements/form_search_input.dart @@ -57,8 +57,7 @@ class _FormSearchInputState extends State { _currentQuery = query; // In a real application, there should be some error handling here. - // final Iterable options = await _FakeAPI.search(_currentQuery!); - if (query.isNotEmpty) { + if (query.isNotEmpty && query.length > 3) { final List? suggestions = await service.fetchSuggestions(_currentQuery!); diff --git a/lib/widgets/sessions/session_view_activities.dart b/lib/widgets/sessions/session_view_activities.dart index 2f8683c..3048712 100644 --- a/lib/widgets/sessions/session_view_activities.dart +++ b/lib/widgets/sessions/session_view_activities.dart @@ -32,7 +32,7 @@ class _SessionViewActivitiesState extends State { padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), itemCount: activities.length, itemBuilder: (BuildContext context, int index) { - return ActivityCard(activity: activities[index]); + return ActivityCard(activity: activities[index], session: widget.session); }, )); } else {