small refactor, better dao thigns

This commit is contained in:
Joshua Burman 2024-12-21 17:51:24 -05:00
parent 3153bf13f9
commit 1234a300e1
9 changed files with 152 additions and 182 deletions

View File

@ -11,8 +11,8 @@ class ActivitiesDao extends DatabaseAccessor<AppDatabase> with _$ActivitiesDaoMi
return await select(activities).get(); return await select(activities).get();
} }
Future<List<Activity>> find(int id) async { Future<Activity> find(int id) async {
return await (select(activities)..where((activity) => activity.id.equals(id) )).get(); return await (select(activities)..where((activity) => activity.id.equals(id) )).getSingle();
} }
Future<List<Activity>> sessionActivities(int id) async { Future<List<Activity>> sessionActivities(int id) async {

View File

@ -11,11 +11,31 @@ class MediaItemsDao extends DatabaseAccessor<AppDatabase> with _$MediaItemsDaoMi
return await select(mediaItems).get(); return await select(mediaItems).get();
} }
Future<List<MediaItem>> find(int id) async { Future<MediaItem> find(int id) async {
return await (select(mediaItems)..where((mediaItem) => mediaItem.id.equals(id) )).get(); return await (select(mediaItems)..where((mediaItem) => mediaItem.id.equals(id) )).getSingle();
} }
Future<List<MediaItem>> mediaItemsFromSession(Session session) async { Future<List<MediaItem>> fromActivity(Activity activity) async {
final result = select(db.objectMediaItems).join(
[
innerJoin(
db.mediaItems,
db.mediaItems.id.equalsExp(db.objectMediaItems.mediaId),
),
],
)
..where(
db.objectMediaItems.objectType.equals(ObjectType.activities.name))
..where(db.objectMediaItems.objectId.equals(activity.id));
final mediaItems = (await result.get())
.map((e) => e.readTable(db.mediaItems))
.toList();
return mediaItems;
}
Future<List<MediaItem>> fromSession(Session session) async {
final result = select(db.objectMediaItems).join( final result = select(db.objectMediaItems).join(
[ [
innerJoin( innerJoin(

View File

@ -11,11 +11,11 @@ class SessionActivitiesDao extends DatabaseAccessor<AppDatabase> with _$SessionA
return await select(sessionActivities).get(); return await select(sessionActivities).get();
} }
Future<List<SessionActivity>> find(int id) async { Future<SessionActivity> find(int id) async {
return await (select(sessionActivities)..where((sessionActivity) => sessionActivity.id.equals(id) )).get(); return await (select(sessionActivities)..where((sessionActivity) => sessionActivity.id.equals(id) )).getSingle();
} }
Future<List<SessionActivity>> sessionActivitiesBySessionId(int id) async { Future<List<SessionActivity>> fromSessionId(int id) async {
final result = db.managers.sessionActivities final result = db.managers.sessionActivities
.filter((sessionActivity) => sessionActivity.sessionId.id(id)); .filter((sessionActivity) => sessionActivity.sessionId.id(id));

View File

@ -11,7 +11,7 @@ class SessionsDao extends DatabaseAccessor<AppDatabase> with _$SessionsDaoMixin
return await select(sessions).get(); return await select(sessions).get();
} }
Future<List<Session>> find(int id) async { Future<Session> find(int id) async {
return await (select(sessions)..where((session) => session.id.equals(id) )).get(); return await (select(sessions)..where((session) => session.id.equals(id) )).getSingle();
} }
} }

View File

@ -30,12 +30,13 @@ class ActivityTimerModel with ChangeNotifier {
double get progress => _progress; double get progress => _progress;
int get totalTime => _totalTime; int get totalTime => _totalTime;
void setup(ActivityModel activityModel) { void setup(ActivityModel activityModel, Activity activity) {
if (_activityModel == null || activityModel.id != _activityModel?.id) { if (_activityModel == null || activityModel.id != _activityModel?.id) {
_periodicTimer?.cancel(); _periodicTimer?.cancel();
_progress = 0; _progress = 0;
_isc = null; _isc = null;
_activityModel = activityModel; _activityModel = activityModel;
_activity = activity;
_sets = activityModel.actions[0].items(); _sets = activityModel.actions[0].items();
_currentActionNum = 0; _currentActionNum = 0;
_currentSetNum = 0; _currentSetNum = 0;

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:sendtrain/classes/media.dart'; import 'package:sendtrain/daos/media_items_dao.dart';
import 'package:sendtrain/database/database.dart'; import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/models/activity_model.dart'; import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/activity_timer_model.dart'; import 'package:sendtrain/models/activity_timer_model.dart';
@ -30,97 +30,113 @@ class ActivityCardState extends State<ActivityCard> {
final ActivityTimerModel atm = final ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: false); Provider.of<ActivityTimerModel>(context, listen: false);
return Card( return FutureBuilder<List<MediaItem>>(
color: atm.activity?.id == widget.activity.id future: MediaItemsDao(Provider.of<AppDatabase>(context))
? Theme.of(context).colorScheme.primaryContainer .fromActivity(widget.data),
: Theme.of(context).colorScheme.surfaceContainerLow, builder: (context, snapshot) {
clipBehavior: Clip.hardEdge, if (snapshot.hasData) {
child: InkWell( List<MediaItem> mediaItems = snapshot.data!;
onTap: () => showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5), return Card(
transitionDuration: const Duration(milliseconds: 220), color: atm.activity?.id == widget.activity.id
transitionBuilder: (BuildContext context, ? Theme.of(context).colorScheme.primaryContainer
Animation<double> animation, : Theme.of(context).colorScheme.surfaceContainerLow,
Animation<double> secondaryAnimation, clipBehavior: Clip.hardEdge,
Widget child) { child: InkWell(
Animation<Offset> custom = Tween<Offset>( onTap: () => showGeneralDialog(
begin: const Offset(0.0, 1.0), barrierColor: Colors.black.withOpacity(0.5),
end: const Offset(0.0, 0.0)) transitionDuration: const Duration(milliseconds: 220),
.animate(animation); transitionBuilder: (BuildContext context,
return SlideTransition( Animation<double> animation,
position: custom, Animation<double> secondaryAnimation,
child: Dialog.fullscreen( Widget child) {
child: ActivityView(activity: widget.activity))); Animation<Offset> custom = Tween<Offset>(
}, begin: const Offset(0.0, 1.0),
barrierDismissible: true, end: const Offset(0.0, 0.0))
barrierLabel: '', .animate(animation);
context: context, return SlideTransition(
pageBuilder: (context, animation1, animation2) { position: custom,
return Container(); child: Dialog.fullscreen(
}), child: ActivityView(
child: Column( activityModel: widget.activity,
mainAxisSize: MainAxisSize.min, activity: widget.data)));
children: <Widget>[ },
ListTile( barrierDismissible: true,
leading: Padding( barrierLabel: '',
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), context: context,
child: Container( pageBuilder: (context, animation1, animation2) {
padding: EdgeInsets.only(top: 5, bottom: 5), return Container();
width: 60, }),
decoration: BoxDecoration( child: Column(
image: DecorationImage( mainAxisSize: MainAxisSize.min,
fit: BoxFit.cover, children: <Widget>[
image: findMediaByType( ListTile(
widget.activity.actions[0].media, 'image')), leading: Padding(
// color: Colors.blue, padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
borderRadius: child: Container(
const BorderRadius.all(Radius.elliptical(10, 10)), padding: EdgeInsets.only(top: 5, bottom: 5),
), width: 60,
)), decoration: BoxDecoration(
title: Consumer<ActivityTimerModel>( image: DecorationImage(
builder: (context, atm, child) { fit: BoxFit.cover,
if (atm.activity?.id == widget.activity.id) { image:
return Text( findMediaByType(mediaItems, 'image')),
maxLines: 1, // color: Colors.blue,
"${widget.data.title} (${formattedTime(atm.totalTime)})"); borderRadius: const BorderRadius.all(
} else { Radius.elliptical(10, 10)),
return Text(maxLines: 1, widget.data.title); ),
} )),
}, title: Consumer<ActivityTimerModel>(
), builder: (context, atm, child) {
subtitle: Text(maxLines: 2, widget.data.description), if (atm.activity?.id == widget.activity.id) {
trailing: IconButton( return Text(
visualDensity: VisualDensity.compact, maxLines: 1,
icon: Icon(Icons.close_rounded), "${widget.data.title} (${formattedTime(atm.totalTime)})");
onPressed: () { } else {
showAdaptiveDialog( return Text(maxLines: 1, widget.data.title);
context: context, }
builder: (BuildContext context) => AlertDialog( },
title: const Text('Activity Removal'), ),
content: const Text('Would you like to permanently remove this activity from the current session?'), subtitle: Text(maxLines: 2, widget.data.description),
actions: <Widget>[ trailing: IconButton(
TextButton( visualDensity: VisualDensity.compact,
onPressed: () => Navigator.pop(context, 'Cancel'), icon: Icon(Icons.close_rounded),
child: const Text('Cancel'), onPressed: () {
), showAdaptiveDialog(
TextButton( context: context,
onPressed: () => Navigator.pop(context, 'OK'), builder: (BuildContext context) => AlertDialog(
child: const Text('OK'), title: const Text('Activity Removal'),
), content: const Text(
], 'Would you like to permanently remove this activity from the current session?'),
), actions: <Widget>[
); TextButton(
}, onPressed: () =>
Navigator.pop(context, 'Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () =>
Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
},
)),
],
)), )),
], );
)), } else {
); return CircularProgressIndicator();
}
});
} }
ImageProvider findMediaByType(List<Media>? media, String type) { ImageProvider findMediaByType(List<MediaItem> media, String type) {
var found = media?.where((m) => m.type == 'image'); Iterable<MediaItem>? found = media.where((m) => m.type == MediaType.image);
if (found != null) { if (found.isNotEmpty) {
return NetworkImage(found.first.reference); return NetworkImage(found.first.reference);
} else { } else {
// Element is not found // Element is not found

View File

@ -1,16 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart'; import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/models/activity_model.dart'; import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/activity_timer_model.dart'; import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/widgets/activity_action_view.dart'; import 'package:sendtrain/widgets/activity_action_view.dart';
import 'package:sendtrain/widgets/media_card.dart'; import 'package:sendtrain/widgets/activity_view_categories.dart';
import 'package:sendtrain/widgets/activity_view_media.dart';
class ActivityView extends StatefulWidget { class ActivityView extends StatefulWidget {
const ActivityView({super.key, required this.activity}); const ActivityView(
final ActivityModel activity; {super.key, required this.activityModel, required this.activity});
final ActivityModel activityModel;
final Activity activity;
@override @override
State<ActivityView> createState() => _ActivityViewState(); State<ActivityView> createState() => _ActivityViewState();
@ -19,11 +21,12 @@ class ActivityView extends StatefulWidget {
class _ActivityViewState extends State<ActivityView> { class _ActivityViewState extends State<ActivityView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ActivityModel activity = widget.activity; final ActivityModel activityModel = widget.activityModel;
final Activity activity = widget.activity;
ActivityTimerModel atm = ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: false); Provider.of<ActivityTimerModel>(context, listen: false);
atm.setup(activity); atm.setup(activityModel, activity);
return Scaffold( return Scaffold(
floatingActionButtonLocation: ExpandableFab.location, floatingActionButtonLocation: ExpandableFab.location,
@ -63,15 +66,15 @@ class _ActivityViewState extends State<ActivityView> {
maxLines: 1, maxLines: 1,
style: const TextStyle( style: const TextStyle(
fontSize: 25, fontWeight: FontWeight.bold), fontSize: 25, fontWeight: FontWeight.bold),
activity.title)), activityModel.title)),
ActivityViewCategories(categories: activity.categories), ActivityViewCategories(categories: activityModel.categories),
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 0, bottom: 20, left: 15, right: 15), top: 0, bottom: 20, left: 15, right: 15),
child: Text( child: Text(
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: const TextStyle(fontSize: 15), style: const TextStyle(fontSize: 15),
activity.description)), activityModel.description)),
ActivityViewMedia(activity: activity), ActivityViewMedia(activity: activity),
const Padding( const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10), padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
@ -139,77 +142,7 @@ class _ActivityViewState extends State<ActivityView> {
semanticsLabel: 'Activity Progress', semanticsLabel: 'Activity Progress',
); );
})), })),
ActivityActionView(action: activity.actions[0]), ActivityActionView(action: activityModel.actions[0]),
])); ]));
} }
} }
class ActivityViewCategories extends StatelessWidget {
const ActivityViewCategories({super.key, this.categories});
final List<String>? categories;
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: SizedBox(
height: 40,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: categories?.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, '${categories?[index]}'),
onPressed: () {},
));
},
))),
],
);
}
}
class ActivityViewMedia extends StatelessWidget {
const ActivityViewMedia({super.key, this.activity});
final ActivityModel? activity;
@override
Widget build(BuildContext context) {
List<Media> media = [];
for (ActivityAction action in activity!.actions) {
if (action.media!.isNotEmpty) {
media.addAll(action.media as Iterable<Media>);
}
}
return Text("media!");
// List<Widget> mediaCards =
// List.generate(media.length, (i) => MediaCard(media: media[i]));
// return Column(
// children: [
// SizedBox(
// width: double.infinity,
// height: 100,
// child: GridView.count(
// padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
// scrollDirection: Axis.horizontal,
// crossAxisSpacing: 5,
// mainAxisSpacing: 5,
// crossAxisCount: 1,
// children: mediaCards))
// ],
// );
}
}

View File

@ -27,7 +27,7 @@ class SessionViewAchievements extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<List<SessionActivity>>( return FutureBuilder<List<SessionActivity>>(
future: SessionActivitiesDao(Provider.of<AppDatabase>(context)) future: SessionActivitiesDao(Provider.of<AppDatabase>(context))
.sessionActivitiesBySessionId(session.id), .fromSessionId(session.id),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
final sessionActivities = snapshot.data!; final sessionActivities = snapshot.data!;

View File

@ -13,7 +13,7 @@ class SessionViewMedia extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<List<MediaItem>>( return FutureBuilder<List<MediaItem>>(
future: MediaItemsDao(Provider.of<AppDatabase>(context)) future: MediaItemsDao(Provider.of<AppDatabase>(context))
.mediaItemsFromSession(session), .fromSession(session),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
final mediaItems = snapshot.data!; final mediaItems = snapshot.data!;