diff --git a/lib/daos/sessions_dao.dart b/lib/daos/sessions_dao.dart index 3f1f1a3..d82f420 100644 --- a/lib/daos/sessions_dao.dart +++ b/lib/daos/sessions_dao.dart @@ -8,6 +8,7 @@ class SessionsDao extends DatabaseAccessor with _$SessionsDaoMixin SessionsDao(super.db); Future find(int id) => (select(sessions)..where((session) => session.id.equals(id) )).getSingle(); + Stream watchSession(int id) => (select(sessions)..where((session) => session.id.equals(id) )).watchSingle(); Future> all() => select(sessions).get(); Stream> watch() => select(sessions).watch(); Future createOrUpdate(SessionsCompanion session) => into(sessions).insertOnConflictUpdate(session); diff --git a/lib/helpers/widget_helpers.dart b/lib/helpers/widget_helpers.dart index ec434b5..3b98f20 100644 --- a/lib/helpers/widget_helpers.dart +++ b/lib/helpers/widget_helpers.dart @@ -9,7 +9,7 @@ showMediaDetailWidget(BuildContext context, MediaItem media) { showEditorSheet(BuildContext context, Widget widget) { showModalBottomSheet( shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10.0)), + borderRadius: BorderRadius.only(topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0)), ), context: context, showDragHandle: true, diff --git a/lib/widgets/achievements/achievement_editor.dart b/lib/widgets/achievements/achievement_editor.dart index 8691768..6179476 100644 --- a/lib/widgets/achievements/achievement_editor.dart +++ b/lib/widgets/achievements/achievement_editor.dart @@ -1,18 +1,74 @@ +import 'dart:convert'; + +import 'package:drift/drift.dart' hide Column; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:sendtrain/daos/sessions_dao.dart'; +import 'package:sendtrain/database/database.dart'; +import 'package:sendtrain/widgets/generic/elements/form_text_input.dart'; -class AchievementEditor extends StatefulWidget { - const AchievementEditor({super.key, this.achievements, this.callback}); +// class AchievementEditor extends StatefulWidget { +// const AchievementEditor({super.key, required this.session, this.callback}); - final List? achievements; +// final Session session; +// final Function? callback; + +// @override +// State createState() => _AchievementEditorState(); +// } + +// class _AchievementEditorState extends State { +class AchievementEditor extends StatelessWidget { + AchievementEditor({super.key, required this.session, this.callback}); + + final GlobalKey _formKey = GlobalKey(); + final TextEditingController tec = TextEditingController(); + final Session session; final Function? callback; @override - State createState() => _AchievementEditorState(); -} + Widget build( + BuildContext context, + ) { + return Padding( + padding: EdgeInsets.fromLTRB(15, 0, 15, 15), + child: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(top: 10, bottom: 10), + child: Text('Create Achievement', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleLarge)), + FormTextInput(controller: tec, title: 'Achievement'), + Row(mainAxisAlignment: MainAxisAlignment.end, children: [ + Padding( + padding: EdgeInsets.only(top: 10), + child: FilledButton( + child: Text('Submit'), + onPressed: () async { + session.achievements; + List achievements = + json.decode(session.achievements!); + achievements.add(tec.text); + Session updatedSession = session.copyWith( + achievements: + Value(json.encode(achievements))); -class _AchievementEditorState extends State { - @override - Widget build(BuildContext context, ) { - return Text('this is a test'); + SessionsDao(Provider.of(context, + listen: false)) + .replace(updatedSession); + + Navigator.pop(_formKey.currentContext!, 'Submit'); + + if (callback != null) { + await callback!(); + } + })) + ]) + ]))); } -} \ No newline at end of file +} diff --git a/lib/widgets/activities/activity_card.dart b/lib/widgets/activities/activity_card.dart index e09908b..28482f7 100644 --- a/lib/widgets/activities/activity_card.dart +++ b/lib/widgets/activities/activity_card.dart @@ -24,8 +24,7 @@ class ActivityCard extends StatefulWidget { class ActivityCardState extends State { @override Widget build(BuildContext context) { - final ActivityTimerModel atm = - Provider.of(context); + final ActivityTimerModel atm = Provider.of(context); return FutureBuilder>( future: MediaItemsDao(Provider.of(context)) @@ -48,7 +47,8 @@ class ActivityCardState extends State { ListTile( // visualDensity: VisualDensity(horizontal: VisualDensity.maximumDensity), leading: CardImage( - image: findMediaByType(mediaItems, MediaType.image)), + image: + findMediaByType(mediaItems, MediaType.image)), title: Consumer( builder: (context, atm, child) { if (atm.activity?.id == widget.activity.id) { @@ -76,15 +76,14 @@ class ActivityCardState extends State { icon: Icon(Icons.close_rounded), onPressed: () { showRemovalDialog( - 'Activity Removal', - 'Would you like to permanently remove this activity from the current session?', - context, - ActivitiesDao( - Provider.of( - context, - listen: false)), - widget.activity) - .then((result) { + 'Activity Removal', + 'Would you like to permanently remove this activity from the current session?', + context, () { + ActivitiesDao(Provider.of( + context, + listen: false)) + .remove(widget.activity); + }).then((result) { setState(() {}); }); }, diff --git a/lib/widgets/builders/dialogs.dart b/lib/widgets/builders/dialogs.dart index ac84799..72cb411 100644 --- a/lib/widgets/builders/dialogs.dart +++ b/lib/widgets/builders/dialogs.dart @@ -21,7 +21,7 @@ Future showGenericDialog(dynamic object, BuildContext parentContext) { } Future showCrudDialog(String title, String content, BuildContext context, - dynamic daoCall, dynamic object) { + [Function? callback]) { return showAdaptiveDialog( context: context, builder: (BuildContext context) => AlertDialog( @@ -35,7 +35,10 @@ Future showCrudDialog(String title, String content, BuildContext context, child: const Text('Cancel'), ), TextButton( - onPressed: () => {daoCall(object), Navigator.pop(context, 'OK')}, + onPressed: () => { + if (callback != null) {callback()}, + Navigator.pop(context, 'OK') + }, child: const Text('OK'), ), ], @@ -44,11 +47,11 @@ Future showCrudDialog(String title, String content, BuildContext context, } Future showRemovalDialog(String title, String content, BuildContext context, - dynamic dao, dynamic object) { - return showCrudDialog(title, content, context, dao.remove, object); + [Function? callback]) { + return showCrudDialog(title, content, context, callback); } Future showUpdateDialog(String title, String content, BuildContext context, - dynamic dao, dynamic object) { - return showCrudDialog(title, content, context, dao.replace, object); + [Function? callback]) { + return showCrudDialog(title, content, context, callback); } diff --git a/lib/widgets/media/media_card.dart b/lib/widgets/media/media_card.dart index 58e7511..27c102f 100644 --- a/lib/widgets/media/media_card.dart +++ b/lib/widgets/media/media_card.dart @@ -16,41 +16,6 @@ class MediaCard extends StatelessWidget { final MediaItem media; final Function? callback; -// @override -// State createState() => _MediaCardState(); -// } - -// class _MediaCardState extends State { -// // late VideoPlayerController _controller; -// late MediaItem media; -// late Function? callback; - -// @override -// void initState() { -// super.initState(); -// media = widget.media; -// callback = widget.callback; - // _controller = VideoPlayerController.asset(dataSource) - // ..initialize().then((_) { - // setState(() {}); //when your thumbnail will show. - // }); -// } - - // @override - // void dispose() { - // super.dispose(); - // _controller.dispose(); - // } - - // Future createVideoPlayer() async { - // final File file = - // await ImgB64Decoder.fileFromB64String(widget.encodedBytes); - // final VideoPlayerController controller = VideoPlayerController.file(file); - // await controller.initialize(); - // await controller.setLooping(true); - // return controller; - // } - @override Widget build(BuildContext context) { DecorationImage mediaImage(MediaItem media) { @@ -63,9 +28,7 @@ class MediaCard extends StatelessWidget { } else if (media.type == MediaType.youtube) { image = NetworkImage('https://img.youtube.com/vi/${media.reference}/0.jpg'); - } else if (media.type == MediaType.localVideo) { - - } + } else if (media.type == MediaType.localVideo) {} return DecorationImage(image: image, fit: BoxFit.cover); } @@ -82,16 +45,16 @@ class MediaCard extends StatelessWidget { shadowColor: const Color.fromARGB(0, 255, 255, 255), child: TextButton( onLongPress: () => showRemovalDialog( - 'Media Removal', - 'Would you like to permanently remove this media from the current session?', - context, - MediaItemsDao(Provider.of(context, - listen: false)), - media) - .then((result) { - if (callback != null) { - callback!(); - } + 'Media Removal', + 'Would you like to permanently remove this media from the current session?', + context, () { + MediaItemsDao( + Provider.of(context, listen: false)) + .remove(media); + }).then((result) { + if (callback != null) { + callback!(); + } }), onPressed: () => showMediaDetailWidget(context, media), child: const ListTile( diff --git a/lib/widgets/sessions/session_card_full.dart b/lib/widgets/sessions/session_card_full.dart index 0c1e9b8..46aabea 100644 --- a/lib/widgets/sessions/session_card_full.dart +++ b/lib/widgets/sessions/session_card_full.dart @@ -80,13 +80,13 @@ class _SessionCardFullState extends State { icon: Icon(Icons.close_rounded), onPressed: () { showRemovalDialog( - 'Session Removal', - 'Would you like to permanently remove this session?', - context, - SessionsDao(Provider.of(context, - listen: false)), - session) - .then((result) { + 'Session Removal', + 'Would you like to permanently remove this session?', + context, () { + SessionsDao( + Provider.of(context, listen: false)) + .remove(session); + }).then((result) { setState(() {}); }); }, diff --git a/lib/widgets/sessions/session_editor.dart b/lib/widgets/sessions/session_editor.dart index c79c70c..2c13610 100644 --- a/lib/widgets/sessions/session_editor.dart +++ b/lib/widgets/sessions/session_editor.dart @@ -281,7 +281,7 @@ class _SessionEditorState extends State { if (widget.callback != null) { await widget - .callback!(widget.session!.id); + .callback!(); } }) } diff --git a/lib/widgets/sessions/session_view.dart b/lib/widgets/sessions/session_view.dart index 8af325e..71e6e57 100644 --- a/lib/widgets/sessions/session_view.dart +++ b/lib/widgets/sessions/session_view.dart @@ -9,6 +9,7 @@ import 'package:sendtrain/daos/sessions_dao.dart'; import 'package:sendtrain/database/database.dart'; import 'package:sendtrain/extensions/string_extensions.dart'; import 'package:sendtrain/helpers/widget_helpers.dart'; +import 'package:sendtrain/widgets/achievements/achievement_editor.dart'; import 'package:sendtrain/widgets/generic/elements/generic_progress_indicator.dart'; import 'package:sendtrain/widgets/sessions/session_editor.dart'; import 'package:sendtrain/widgets/sessions/session_view_achievements.dart'; @@ -39,13 +40,13 @@ class _SessionViewState extends State { return title; } - void resetState(int sessionId) async { + void resetState() async { Session updatedSession = await SessionsDao(Provider.of(context, listen: false)) - .find(sessionId); + .find(session.id); final state = _fabKey.currentState; - if (state != null) { + if (state != null && state.isOpen) { state.toggle(); } @@ -64,13 +65,16 @@ class _SessionViewState extends State { @override Widget build(BuildContext context) { - return StreamBuilder>( - stream: ActivitiesDao(Provider.of(context)) - .watchSessionActivities(session.id), + return StreamBuilder( + initialData: session, + stream: SessionsDao(Provider.of(context)) + .watchSession(session.id), builder: (context, snapshot) { + // return StreamBuilder>( + // stream: ActivitiesDao(Provider.of(context)) + // .watchSessionActivities(session.id), + // builder: (context, snapshot) { if (snapshot.hasData) { - final activities = snapshot.data!; - return Scaffold( floatingActionButtonLocation: ExpandableFab.location, floatingActionButton: ExpandableFab( @@ -82,6 +86,26 @@ class _SessionViewState extends State { blur: 10, ), children: [ + FloatingActionButton.extended( + icon: const Icon(Icons.edit_outlined), + label: Text('Add Achievement'), + onPressed: () { + showEditorSheet( + context, + AchievementEditor( + session: session, callback: resetState)); + }, + ), + FloatingActionButton.extended( + icon: const Icon(Icons.edit_outlined), + label: Text('Edit'), + onPressed: () { + showEditorSheet( + context, + SessionEditor( + session: session, callback: resetState)); + }, + ), FloatingActionButton.extended( icon: const Icon(Icons.history_outlined), label: Text('Restart'), @@ -116,16 +140,6 @@ class _SessionViewState extends State { } }, ), - FloatingActionButton.extended( - icon: const Icon(Icons.edit_outlined), - label: Text('Edit'), - onPressed: () { - showEditorSheet( - context, - SessionEditor( - session: session, callback: resetState)); - }, - ), ]), body: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -144,7 +158,7 @@ class _SessionViewState extends State { style: const TextStyle( fontSize: 25, fontWeight: FontWeight.bold), title())), - SessionViewAchievements(session: session), + SessionViewAchievements(session: session, callback: resetState), Padding( padding: const EdgeInsets.only(left: 15, right: 15), child: Text( @@ -163,7 +177,7 @@ class _SessionViewState extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold), 'Activites:')), - SessionViewActivities(activities: activities), + SessionViewActivities(session: session), ], )); } else { diff --git a/lib/widgets/sessions/session_view_achievements.dart b/lib/widgets/sessions/session_view_achievements.dart index 38c7164..19137b4 100644 --- a/lib/widgets/sessions/session_view_achievements.dart +++ b/lib/widgets/sessions/session_view_achievements.dart @@ -1,8 +1,8 @@ import 'dart:convert'; +import 'package:drift/drift.dart' hide Column; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:sendtrain/daos/session_activities_dao.dart'; import 'package:sendtrain/daos/sessions_dao.dart'; import 'package:sendtrain/database/database.dart'; import 'package:sendtrain/extensions/string_extensions.dart'; @@ -11,33 +11,25 @@ import 'package:sendtrain/widgets/achievements/achievement_editor.dart'; import 'package:sendtrain/widgets/builders/dialogs.dart'; class SessionViewAchievements extends StatelessWidget { - const SessionViewAchievements({super.key, required this.session}); + const SessionViewAchievements( + {super.key, required this.session, this.callback}); final Session session; + final Function? callback; - // List getAchievements(List sessionActivities) { - // List achievements = []; - - // for (int i = 0; i < sessionActivities.length; i++) { - // final SessionActivity sessionActivity = sessionActivities[i]; - // final List? saAchievments = sessionActivity.achievements?.split(','); - - // if (saAchievments != null) { - // saAchievments.forEach((achievement) => achievements.add(achievement)); - // } - // } - - // return achievements; - // } - - void resetState() { - // setState(() {}); + Session updateAchievements(int index, List achievements) { + achievements.removeAt(index); + return session.copyWith( + achievements: Value(json.encode(achievements))); } @override Widget build(BuildContext context) { Widget content; - if (session.achievements == null) { + final AppDatabase db = Provider.of(context, listen: false); + List achievements = json.decode(session.achievements ?? "[]"); + + if (achievements.isEmpty) { content = Padding( padding: const EdgeInsets.only(left: 10, right: 5), child: ActionChip( @@ -45,11 +37,11 @@ class SessionViewAchievements extends StatelessWidget { avatar: const Icon(Icons.check_circle_outline), label: Text(maxLines: 1, 'Add Achievements!'), onPressed: () { - showEditorSheet(context, AchievementEditor(callback: resetState)); + showEditorSheet(context, + AchievementEditor(session: session, callback: callback)); }, )); } else { - final achievements = json.decode(session.achievements!); content = ListView.builder( scrollDirection: Axis.horizontal, padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), @@ -60,15 +52,23 @@ class SessionViewAchievements extends StatelessWidget { child: ActionChip( visualDensity: VisualDensity.compact, avatar: const Icon(Icons.check_circle_outline), - label: Text(maxLines: 1, achievements[index].toString().toTitleCase()), - onPressed: () { - showUpdateDialog( + label: Text( + maxLines: 1, + achievements[index].toString().toTitleCase()), + onPressed: () async { + // remove the achievement at index + // then update session + Session newSession = + updateAchievements(index, achievements); + await showUpdateDialog( 'Achievement Removal', 'Would you like to remove this achievement?', - context, - SessionsDao( - Provider.of(context, listen: false)), - session); + context, () { + SessionsDao(db).replace(newSession); + if (callback != null) { + callback!(); + } + }); })); }, ); @@ -81,59 +81,101 @@ class SessionViewAchievements extends StatelessWidget { child: SizedBox(height: 40, child: content)), ], ); - // return StreamBuilder>( - // stream: SessionActivitiesDao(Provider.of(context)) - // .fromSessionId(session.id).asStream(), - // builder: (context, snapshot) { - // if (snapshot.hasData) { - // final sessionActivities = snapshot.data!; - // final achievements = getAchievements(sessionActivities); - - // Widget content; - // if (achievements.isEmpty) { - // content = Padding( - // padding: const EdgeInsets.only(left: 10, right: 5), - // child: ActionChip( - // visualDensity: VisualDensity.compact, - // avatar: const Icon(Icons.check_circle_outline), - // label: Text(maxLines: 1, 'Add Achievements!'), - // onPressed: () { - // showEditorSheet(context, AchievementEditor(achievements: achievements, callback: resetState)); - // }, - // )); - // } else { - // content = ListView.builder( - // scrollDirection: Axis.horizontal, - // padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), - // itemCount: achievements.length, - // itemBuilder: (BuildContext context, int index) { - // return Padding( - // padding: const EdgeInsets.only(right: 5), - // child: ActionChip( - // visualDensity: VisualDensity.compact, - // avatar: const Icon(Icons.check_circle_outline), - // label: Text( - // maxLines: 1, achievements[index].toTitleCase()), - // onPressed: () { - // // remove achievements - // }, - // )); - // }, - // ); - // } - - // return Column( - // children: [ - // Padding( - // padding: const EdgeInsets.only(bottom: 10), - // child: SizedBox(height: 40, child: content)), - // ], - // ); - // } else { - // return Padding( - // padding: EdgeInsets.all(15), - // child: CircularProgressIndicator()); - // } - // }); } } + + +// class SessionViewAchievements extends StatefulWidget { +// const SessionViewAchievements({super.key, required this.session}); + +// final Session session; + +// @override +// State createState() => +// _SessionViewAchievementsState(); +// } + +// class _SessionViewAchievementsState extends State { +// late final AppDatabase db; +// late Session session; +// late List achievements; + +// @override +// void initState() { +// super.initState(); +// db = Provider.of(context, listen: false); +// session = widget.session; +// achievements = json.decode(session.achievements!); +// } + +// void resetState(int sessionId) async { +// Session updatedSession = +// await SessionsDao(Provider.of(context, listen: false)) +// .find(sessionId); + +// setState(() { +// session = updatedSession; +// achievements = json.decode(session.achievements!); +// }); +// } + +// Session updateAchievements(int index) { +// achievements.removeAt(index); +// return session.copyWith( +// achievements: Value(json.encode(achievements))); +// } + +// @override +// Widget build(BuildContext context) { +// Widget content; +// if (achievements.isEmpty) { +// content = Padding( +// padding: const EdgeInsets.only(left: 10, right: 5), +// child: ActionChip( +// visualDensity: VisualDensity.compact, +// avatar: const Icon(Icons.check_circle_outline), +// label: Text(maxLines: 1, 'Add Achievements!'), +// onPressed: () { +// showEditorSheet(context, +// AchievementEditor(session: session, callback: resetState)); +// }, +// )); +// } else { +// content = ListView.builder( +// scrollDirection: Axis.horizontal, +// padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), +// itemCount: achievements.length, +// itemBuilder: (BuildContext context, int index) { +// return Padding( +// padding: const EdgeInsets.only(right: 5), +// child: ActionChip( +// visualDensity: VisualDensity.compact, +// avatar: const Icon(Icons.check_circle_outline), +// label: Text( +// maxLines: 1, +// achievements[index].toString().toTitleCase()), +// onPressed: () async { +// // remove the achievement at index +// // then update session +// Session newSession = updateAchievements(index); +// await showUpdateDialog( +// 'Achievement Removal', +// 'Would you like to remove this achievement?', +// context, +// SessionsDao(db), +// newSession, +// resetState); +// })); +// }, +// ); +// } + +// return Column( +// children: [ +// Padding( +// padding: const EdgeInsets.only(bottom: 10), +// child: SizedBox(height: 40, child: content)), +// ], +// ); +// } +// } diff --git a/lib/widgets/sessions/session_view_activities.dart b/lib/widgets/sessions/session_view_activities.dart index 274f991..e4f3de1 100644 --- a/lib/widgets/sessions/session_view_activities.dart +++ b/lib/widgets/sessions/session_view_activities.dart @@ -1,29 +1,40 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:sendtrain/daos/activities_dao.dart'; import 'package:sendtrain/database/database.dart'; import 'package:sendtrain/widgets/activities/activity_card.dart'; +import 'package:sendtrain/widgets/generic/elements/generic_progress_indicator.dart'; class SessionViewActivities extends StatefulWidget { - const SessionViewActivities({super.key, required this.activities }); + const SessionViewActivities({super.key, required this.session}); - final List activities; + final Session session; @override State createState() => _SessionViewActivitiesState(); } class _SessionViewActivitiesState extends State { - @override Widget build(BuildContext context) { - return Expanded( - child: ListView.builder( - // shrinkWrap: true, - padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), - itemCount: widget.activities.length, - itemBuilder: (BuildContext context, int index) { - return ActivityCard( - activity: widget.activities[index]); - }, - )); + return StreamBuilder>( + stream: ActivitiesDao(Provider.of(context)) + .watchSessionActivities(widget.session.id), + builder: (context, snapshot) { + if (snapshot.hasData) { + final activities = snapshot.data!; + return Expanded( + child: ListView.builder( + // shrinkWrap: true, + padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), + itemCount: activities.length, + itemBuilder: (BuildContext context, int index) { + return ActivityCard(activity: activities[index]); + }, + )); + } else { + return GenericProgressIndicator(); + } + }); } -} \ No newline at end of file +} diff --git a/lib/widgets/sessions/session_view_media.dart b/lib/widgets/sessions/session_view_media.dart index 05b8406..eed4df5 100644 --- a/lib/widgets/sessions/session_view_media.dart +++ b/lib/widgets/sessions/session_view_media.dart @@ -14,18 +14,10 @@ class SessionViewMedia extends StatefulWidget { } class _SessionViewMediaState extends State { - // late final Session session; - void resetState() { setState(() {}); } - // @override - // initState() async { - // super.initState(); - - // } - @override Widget build(BuildContext context) { return FutureBuilder>( @@ -35,8 +27,8 @@ class _SessionViewMediaState extends State { if (snapshot.hasData) { final mediaItems = snapshot.data!; - List mediaCards = List.generate( - mediaItems.length, (i) => MediaCard(media: mediaItems[i], callback: resetState)); + List mediaCards = List.generate(mediaItems.length, + (i) => MediaCard(media: mediaItems[i], callback: resetState)); return Column( children: [