achievement crud complete

This commit is contained in:
Joshua Burman 2025-01-03 18:20:11 -05:00
parent 2206720810
commit 32826abcea
12 changed files with 293 additions and 212 deletions

View File

@ -8,6 +8,7 @@ class SessionsDao extends DatabaseAccessor<AppDatabase> with _$SessionsDaoMixin
SessionsDao(super.db);
Future<Session> find(int id) => (select(sessions)..where((session) => session.id.equals(id) )).getSingle();
Stream<Session> watchSession(int id) => (select(sessions)..where((session) => session.id.equals(id) )).watchSingle();
Future<List<Session>> all() => select(sessions).get();
Stream<List<Session>> watch() => select(sessions).watch();
Future createOrUpdate(SessionsCompanion session) => into(sessions).insertOnConflictUpdate(session);

View File

@ -9,7 +9,7 @@ showMediaDetailWidget(BuildContext context, MediaItem media) {
showEditorSheet(BuildContext context, Widget widget) {
showModalBottomSheet<void>(
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,

View File

@ -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<AchievementEditor> createState() => _AchievementEditorState();
// }
// class _AchievementEditorState extends State<AchievementEditor> {
class AchievementEditor extends StatelessWidget {
AchievementEditor({super.key, required this.session, this.callback});
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController tec = TextEditingController();
final Session session;
final Function? callback;
@override
State<AchievementEditor> 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: <Widget>[
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<String>(json.encode(achievements)));
class _AchievementEditorState extends State<AchievementEditor> {
@override
Widget build(BuildContext context, ) {
return Text('this is a test');
SessionsDao(Provider.of<AppDatabase>(context,
listen: false))
.replace(updatedSession);
Navigator.pop(_formKey.currentContext!, 'Submit');
if (callback != null) {
await callback!();
}
}))
])
])));
}
}
}

View File

@ -24,8 +24,7 @@ class ActivityCard extends StatefulWidget {
class ActivityCardState extends State<ActivityCard> {
@override
Widget build(BuildContext context) {
final ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context);
final ActivityTimerModel atm = Provider.of<ActivityTimerModel>(context);
return FutureBuilder<List<MediaItem>>(
future: MediaItemsDao(Provider.of<AppDatabase>(context))
@ -48,7 +47,8 @@ class ActivityCardState extends State<ActivityCard> {
ListTile(
// visualDensity: VisualDensity(horizontal: VisualDensity.maximumDensity),
leading: CardImage(
image: findMediaByType(mediaItems, MediaType.image)),
image:
findMediaByType(mediaItems, MediaType.image)),
title: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
if (atm.activity?.id == widget.activity.id) {
@ -76,15 +76,14 @@ class ActivityCardState extends State<ActivityCard> {
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<AppDatabase>(
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<AppDatabase>(
context,
listen: false))
.remove(widget.activity);
}).then((result) {
setState(() {});
});
},

View File

@ -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);
}

View File

@ -16,41 +16,6 @@ class MediaCard extends StatelessWidget {
final MediaItem media;
final Function? callback;
// @override
// State<MediaCard> createState() => _MediaCardState();
// }
// class _MediaCardState extends State<MediaCard> {
// // 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<VideoPlayerController> 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<AppDatabase>(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<AppDatabase>(context, listen: false))
.remove(media);
}).then((result) {
if (callback != null) {
callback!();
}
}),
onPressed: () => showMediaDetailWidget(context, media),
child: const ListTile(

View File

@ -80,13 +80,13 @@ class _SessionCardFullState extends State<SessionCardFull> {
icon: Icon(Icons.close_rounded),
onPressed: () {
showRemovalDialog(
'Session Removal',
'Would you like to permanently remove this session?',
context,
SessionsDao(Provider.of<AppDatabase>(context,
listen: false)),
session)
.then((result) {
'Session Removal',
'Would you like to permanently remove this session?',
context, () {
SessionsDao(
Provider.of<AppDatabase>(context, listen: false))
.remove(session);
}).then((result) {
setState(() {});
});
},

View File

@ -281,7 +281,7 @@ class _SessionEditorState extends State<SessionEditor> {
if (widget.callback != null) {
await widget
.callback!(widget.session!.id);
.callback!();
}
})
}

View File

@ -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<SessionView> {
return title;
}
void resetState(int sessionId) async {
void resetState() async {
Session updatedSession =
await SessionsDao(Provider.of<AppDatabase>(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<SessionView> {
@override
Widget build(BuildContext context) {
return StreamBuilder<List<Activity>>(
stream: ActivitiesDao(Provider.of<AppDatabase>(context))
.watchSessionActivities(session.id),
return StreamBuilder<Session>(
initialData: session,
stream: SessionsDao(Provider.of<AppDatabase>(context))
.watchSession(session.id),
builder: (context, snapshot) {
// return StreamBuilder<List<Activity>>(
// stream: ActivitiesDao(Provider.of<AppDatabase>(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<SessionView> {
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<SessionView> {
}
},
),
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<SessionView> {
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<SessionView> {
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Activites:')),
SessionViewActivities(activities: activities),
SessionViewActivities(session: session),
],
));
} else {

View File

@ -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<String> getAchievements(List<SessionActivity> sessionActivities) {
// List<String> 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<String>(json.encode(achievements)));
}
@override
Widget build(BuildContext context) {
Widget content;
if (session.achievements == null) {
final AppDatabase db = Provider.of<AppDatabase>(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<AppDatabase>(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<List<SessionActivity>>(
// stream: SessionActivitiesDao(Provider.of<AppDatabase>(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<SessionViewAchievements> createState() =>
// _SessionViewAchievementsState();
// }
// class _SessionViewAchievementsState extends State<SessionViewAchievements> {
// late final AppDatabase db;
// late Session session;
// late List achievements;
// @override
// void initState() {
// super.initState();
// db = Provider.of<AppDatabase>(context, listen: false);
// session = widget.session;
// achievements = json.decode(session.achievements!);
// }
// void resetState(int sessionId) async {
// Session updatedSession =
// await SessionsDao(Provider.of<AppDatabase>(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<String>(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)),
// ],
// );
// }
// }

View File

@ -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<Activity> activities;
final Session session;
@override
State<SessionViewActivities> createState() => _SessionViewActivitiesState();
}
class _SessionViewActivitiesState extends State<SessionViewActivities> {
@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<List<Activity>>(
stream: ActivitiesDao(Provider.of<AppDatabase>(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();
}
});
}
}
}

View File

@ -14,18 +14,10 @@ class SessionViewMedia extends StatefulWidget {
}
class _SessionViewMediaState extends State<SessionViewMedia> {
// late final Session session;
void resetState() {
setState(() {});
}
// @override
// initState() async {
// super.initState();
// }
@override
Widget build(BuildContext context) {
return FutureBuilder<List<MediaItem>>(
@ -35,8 +27,8 @@ class _SessionViewMediaState extends State<SessionViewMedia> {
if (snapshot.hasData) {
final mediaItems = snapshot.data!;
List<Widget> mediaCards = List.generate(
mediaItems.length, (i) => MediaCard(media: mediaItems[i], callback: resetState));
List<Widget> mediaCards = List.generate(mediaItems.length,
(i) => MediaCard(media: mediaItems[i], callback: resetState));
return Column(
children: [