media item and session images and location management, also refactoring and DRYing up code
This commit is contained in:
parent
5f628d6b48
commit
10332ec8be
@ -39,7 +39,7 @@ class MediaItemsDao extends DatabaseAccessor<AppDatabase>
|
||||
return mediaItems;
|
||||
}
|
||||
|
||||
Future<List<MediaItem>> fromSession(Session session) async {
|
||||
Future<List<MediaItem>> fromSession(int sessionId) async {
|
||||
final result = select(db.objectMediaItems).join(
|
||||
[
|
||||
innerJoin(
|
||||
@ -49,7 +49,7 @@ class MediaItemsDao extends DatabaseAccessor<AppDatabase>
|
||||
],
|
||||
)
|
||||
..where(db.objectMediaItems.objectType.equals(ObjectType.sessions.name))
|
||||
..where(db.objectMediaItems.objectId.equals(session.id));
|
||||
..where(db.objectMediaItems.objectId.equals(sessionId));
|
||||
|
||||
final mediaItems =
|
||||
(await result.get()).map((e) => e.readTable(db.mediaItems)).toList();
|
||||
@ -75,4 +75,9 @@ class MediaItemsDao extends DatabaseAccessor<AppDatabase>
|
||||
return mediaItems;
|
||||
});
|
||||
}
|
||||
|
||||
Future removeAll(Iterable<int> mediaItemIds) {
|
||||
return (delete(mediaItems)..where((table) => table.id.isIn(mediaItemIds)))
|
||||
.go();
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,9 @@ class SessionsDao extends DatabaseAccessor<AppDatabase> with _$SessionsDaoMixin
|
||||
SessionsDao(super.db);
|
||||
|
||||
Future<Session> find(int id) => (select(sessions)..where((session) => session.id.equals(id) )).getSingle();
|
||||
Future<List<Session>> all() => select(sessions).get();
|
||||
Future<List<Session>> all() => (select(sessions)..orderBy([(session) => OrderingTerm(expression: session.createdAt, mode: OrderingMode.desc)])).get();
|
||||
Stream<List<Session>> watch() => select(sessions).watch();
|
||||
Future createOrUpdate(SessionsCompanion session) => into(sessions).insertOnConflictUpdate(session);
|
||||
// Future replace(Session session) => update(sessions).replace(session);
|
||||
Future replace(Session session) => update(sessions).replace(session);
|
||||
Future remove(Session session) => delete(sessions).delete(session);
|
||||
|
||||
|
||||
}
|
@ -35,7 +35,7 @@ class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase() : super(_openConnection());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 6;
|
||||
int get schemaVersion => 7;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
@ -141,7 +141,7 @@ class ObjectMediaItems extends Table {
|
||||
dateTime().withDefault(Variable(DateTime.now()))();
|
||||
}
|
||||
|
||||
enum MediaType { youtube, image }
|
||||
enum MediaType { youtube, image, location }
|
||||
|
||||
class MediaItems extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
|
@ -2398,6 +2398,16 @@ abstract class _$AppDatabase extends GeneratedDatabase {
|
||||
late final $MediaItemsTable mediaItems = $MediaItemsTable(this);
|
||||
late final $ObjectMediaItemsTable objectMediaItems =
|
||||
$ObjectMediaItemsTable(this);
|
||||
late final SessionsDao sessionsDao = SessionsDao(this as AppDatabase);
|
||||
late final ActivitiesDao activitiesDao = ActivitiesDao(this as AppDatabase);
|
||||
late final MediaItemsDao mediaItemsDao = MediaItemsDao(this as AppDatabase);
|
||||
late final ObjectMediaItemsDao objectMediaItemsDao =
|
||||
ObjectMediaItemsDao(this as AppDatabase);
|
||||
late final SessionActivitiesDao sessionActivitiesDao =
|
||||
SessionActivitiesDao(this as AppDatabase);
|
||||
late final ActivityActionsDao activityActionsDao =
|
||||
ActivityActionsDao(this as AppDatabase);
|
||||
late final ActionsDao actionsDao = ActionsDao(this as AppDatabase);
|
||||
@override
|
||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
||||
|
@ -1,13 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sendtrain/database/database.dart';
|
||||
|
||||
ImageProvider findMediaByType(List<MediaItem> media, String type) {
|
||||
Iterable<MediaItem>? found = media.where((m) => m.type == MediaType.image);
|
||||
ImageProvider findMediaByType(List<MediaItem> media, MediaType type) {
|
||||
Iterable<MediaItem>? found = media.where((m) => m.type == type);
|
||||
Image image;
|
||||
|
||||
if (found.isNotEmpty) {
|
||||
return NetworkImage(found.first.reference);
|
||||
} else {
|
||||
// Element is not found
|
||||
return const AssetImage('assets/images/placeholder.jpg');
|
||||
}
|
||||
}
|
||||
if (found.isNotEmpty) {
|
||||
image = Image.network(found.first.reference);
|
||||
} else {
|
||||
// Element is not found
|
||||
image = Image.asset('assets/images/placeholder.jpg');
|
||||
}
|
||||
|
||||
return image.image;
|
||||
}
|
||||
|
17
lib/helpers/widget_helpers.dart
Normal file
17
lib/helpers/widget_helpers.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sendtrain/database/database.dart';
|
||||
import 'package:sendtrain/widgets/media/media_details.dart';
|
||||
|
||||
showMediaDetailWidget(BuildContext context, MediaItem media) {
|
||||
showModalBottomSheet<void>(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
context: context,
|
||||
showDragHandle: true,
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (BuildContext context) {
|
||||
return MediaDetails(media: media);
|
||||
});
|
||||
}
|
@ -6,7 +6,7 @@ import 'package:sendtrain/widgets/screens/activities_screen.dart';
|
||||
import 'package:sendtrain/widgets/screens/sessions_screen.dart';
|
||||
// ignore: unused_import
|
||||
import 'package:sendtrain/database/seed.dart';
|
||||
import 'package:sendtrain/widgets/sessions/session_creator.dart';
|
||||
import 'package:sendtrain/widgets/sessions/session_editor.dart';
|
||||
|
||||
class SendTrain extends StatelessWidget {
|
||||
const SendTrain({super.key});
|
||||
@ -108,7 +108,7 @@ class _AppState extends State<App> {
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (BuildContext context) {
|
||||
return SessionCreator();
|
||||
return SessionEditor();
|
||||
});
|
||||
},
|
||||
label: const Text('New Session'),
|
||||
|
@ -48,7 +48,7 @@ class ActivityCardState extends State<ActivityCard> {
|
||||
ListTile(
|
||||
// visualDensity: VisualDensity(horizontal: VisualDensity.maximumDensity),
|
||||
leading: CardImage(
|
||||
image: findMediaByType(mediaItems, 'image')),
|
||||
image: findMediaByType(mediaItems, MediaType.image)),
|
||||
title: Consumer<ActivityTimerModel>(
|
||||
builder: (context, atm, child) {
|
||||
if (atm.activity?.id == widget.activity.id) {
|
||||
|
@ -21,6 +21,7 @@ class CardImage extends StatelessWidget {
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: image,
|
||||
onError: (error, stackTrace) => AssetImage('assets/images/placeholder.jpg')
|
||||
// color: Colors.blue,
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.elliptical(8, 8)),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sendtrain/database/database.dart';
|
||||
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
|
||||
import 'package:sendtrain/helpers/widget_helpers.dart';
|
||||
|
||||
class MediaCard extends StatelessWidget {
|
||||
const MediaCard({super.key, required this.media});
|
||||
@ -9,15 +9,10 @@ class MediaCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
YoutubePlayerController controller = YoutubePlayerController(
|
||||
initialVideoId: media.reference,
|
||||
flags: const YoutubePlayerFlags(
|
||||
autoPlay: false, mute: true, showLiveFullscreenButton: false));
|
||||
|
||||
DecorationImage mediaImage(MediaItem media) {
|
||||
String image = '';
|
||||
|
||||
if (media.type == MediaType.image) {
|
||||
if (media.type == MediaType.image || media.type == MediaType.location) {
|
||||
image = media.reference;
|
||||
} else if (media.type == MediaType.youtube) {
|
||||
image = 'https://img.youtube.com/vi/${media.reference}/0.jpg';
|
||||
@ -26,19 +21,6 @@ class MediaCard extends StatelessWidget {
|
||||
return DecorationImage(image: NetworkImage(image), fit: BoxFit.cover);
|
||||
}
|
||||
|
||||
Widget mediaItem(MediaItem media) {
|
||||
if (media.type == MediaType.image) {
|
||||
return Image(image: NetworkImage(media.reference));
|
||||
} else if (media.type == MediaType.youtube) {
|
||||
return YoutubePlayer(
|
||||
controller: controller,
|
||||
aspectRatio: 16 / 9,
|
||||
);
|
||||
}
|
||||
|
||||
return const Image(image: AssetImage('assets/images/placeholder.jpg'));
|
||||
}
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
@ -50,42 +32,7 @@ class MediaCard extends StatelessWidget {
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
shadowColor: const Color.fromARGB(0, 255, 255, 255),
|
||||
child: TextButton(
|
||||
onPressed: () => showModalBottomSheet<void>(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
context: context,
|
||||
showDragHandle: true,
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(15, 0, 15, 15),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: mediaItem(media),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(15, 0, 15, 15),
|
||||
child: Text(
|
||||
media.description,
|
||||
style: const TextStyle(fontSize: 20),
|
||||
)),
|
||||
const Divider(
|
||||
indent: 20,
|
||||
endIndent: 20,
|
||||
)
|
||||
]));
|
||||
// const Text(
|
||||
// 'Comments',
|
||||
// style: TextStyle(fontSize: 20),
|
||||
// ),
|
||||
}),
|
||||
onPressed: () => showMediaDetailWidget(context, media),
|
||||
child: const ListTile(
|
||||
title: Text(''),
|
||||
))));
|
||||
|
28
lib/widgets/media/media_content.dart
Normal file
28
lib/widgets/media/media_content.dart
Normal file
@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sendtrain/database/database.dart';
|
||||
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
|
||||
|
||||
class MediaContent extends StatelessWidget {
|
||||
const MediaContent({super.key, required this.media});
|
||||
|
||||
final MediaItem media;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
YoutubePlayerController controller = YoutubePlayerController(
|
||||
initialVideoId: media.reference,
|
||||
flags: const YoutubePlayerFlags(
|
||||
autoPlay: false, mute: true, showLiveFullscreenButton: false));
|
||||
|
||||
if (media.type == MediaType.image || media.type == MediaType.location) {
|
||||
return Image(image: NetworkImage(media.reference));
|
||||
} else if (media.type == MediaType.youtube) {
|
||||
return YoutubePlayer(
|
||||
controller: controller,
|
||||
aspectRatio: 16 / 9,
|
||||
);
|
||||
}
|
||||
|
||||
return const Image(image: AssetImage('assets/images/placeholder.jpg'));
|
||||
}
|
||||
}
|
35
lib/widgets/media/media_details.dart
Normal file
35
lib/widgets/media/media_details.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sendtrain/database/database.dart';
|
||||
import 'package:sendtrain/widgets/media/media_content.dart';
|
||||
|
||||
class MediaDetails extends StatelessWidget {
|
||||
const MediaDetails({super.key, required this.media});
|
||||
|
||||
final MediaItem media;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(15, 0, 15, 15),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: MediaContent(media: media),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(15, 0, 15, 15),
|
||||
child: Text(
|
||||
media.description,
|
||||
style: const TextStyle(fontSize: 20),
|
||||
)),
|
||||
const Divider(
|
||||
indent: 20,
|
||||
endIndent: 20,
|
||||
)
|
||||
]));
|
||||
}
|
||||
}
|
@ -5,10 +5,12 @@ import 'package:sendtrain/database/database.dart';
|
||||
import 'package:sendtrain/extensions/string_extensions.dart';
|
||||
import 'package:sendtrain/helpers/date_time_helpers.dart';
|
||||
import 'package:sendtrain/helpers/media_helpers.dart';
|
||||
import 'package:sendtrain/helpers/widget_helpers.dart';
|
||||
import 'package:sendtrain/widgets/builders/dialogs.dart';
|
||||
import 'package:sendtrain/widgets/generic/elements/card_content.dart';
|
||||
import 'package:sendtrain/widgets/generic/elements/card_image.dart';
|
||||
import 'package:sendtrain/widgets/sessions/session_view.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class SessionCardFull extends StatefulWidget {
|
||||
const SessionCardFull(
|
||||
@ -22,6 +24,9 @@ class SessionCardFull extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SessionCardFullState extends State<SessionCardFull> {
|
||||
late final List<MediaItem> mediaItems;
|
||||
late final MediaItem? sessionImage;
|
||||
late final Session session;
|
||||
|
||||
String sessionTitle(Session session) {
|
||||
String title = session.title.toTitleCase();
|
||||
@ -32,10 +37,16 @@ class _SessionCardFullState extends State<SessionCardFull> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Session session = widget.session;
|
||||
final List<MediaItem> mediaItems = widget.mediaItems;
|
||||
initState() {
|
||||
super.initState();
|
||||
session = widget.session;
|
||||
mediaItems = widget.mediaItems;
|
||||
sessionImage = mediaItems
|
||||
.firstWhereOrNull((mediaItem) => mediaItem.type == MediaType.location);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
color: (session.status == SessionStatus.started)
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
@ -44,6 +55,7 @@ class _SessionCardFullState extends State<SessionCardFull> {
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: InkWell(
|
||||
splashColor: Colors.deepPurple,
|
||||
onLongPress: () => showMediaDetailWidget(context, sessionImage!),
|
||||
onTap: () =>
|
||||
showGenericDialog(SessionView(session: session), context),
|
||||
child: Column(
|
||||
@ -52,7 +64,7 @@ class _SessionCardFullState extends State<SessionCardFull> {
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 8),
|
||||
leading: CardImage(
|
||||
image: findMediaByType(mediaItems, 'image'),
|
||||
image: findMediaByType(mediaItems, MediaType.location),
|
||||
padding: EdgeInsets.only(left: 5, top: 5)),
|
||||
title: Text(maxLines: 1, sessionTitle(session)),
|
||||
subtitle: Text(
|
||||
|
@ -3,8 +3,10 @@ import 'package:sendtrain/database/database.dart';
|
||||
import 'package:sendtrain/extensions/string_extensions.dart';
|
||||
import 'package:sendtrain/helpers/date_time_helpers.dart';
|
||||
import 'package:sendtrain/helpers/media_helpers.dart';
|
||||
import 'package:sendtrain/helpers/widget_helpers.dart';
|
||||
import 'package:sendtrain/widgets/builders/dialogs.dart';
|
||||
import 'package:sendtrain/widgets/sessions/session_view.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class SessionCardSmall extends StatefulWidget {
|
||||
const SessionCardSmall(
|
||||
@ -18,11 +20,20 @@ class SessionCardSmall extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SessionCardSmallState extends State<SessionCardSmall> {
|
||||
late final List<MediaItem> mediaItems;
|
||||
late final MediaItem? sessionImage;
|
||||
late final Session session;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
session = widget.session;
|
||||
mediaItems = widget.mediaItems;
|
||||
sessionImage = mediaItems.firstWhereOrNull((mediaItem) => mediaItem.type == MediaType.location);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Session session = widget.session;
|
||||
final List<MediaItem> mediaItems = widget.mediaItems;
|
||||
|
||||
return Card(
|
||||
color: (session.status == SessionStatus.started)
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
@ -31,7 +42,9 @@ class _SessionCardSmallState extends State<SessionCardSmall> {
|
||||
// overlayColor: MaterialStateColor(Colors.deepPurple as int),
|
||||
splashColor: Colors.deepPurple,
|
||||
borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)),
|
||||
onTap: () => showGenericDialog(SessionView(session: session), context),
|
||||
onLongPress: () => showMediaDetailWidget(context, sessionImage!),
|
||||
onTap: () =>
|
||||
showGenericDialog(SessionView(session: session), context),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
// color: const Color.fromARGB(47, 0, 0, 0),
|
||||
@ -39,7 +52,7 @@ class _SessionCardSmallState extends State<SessionCardSmall> {
|
||||
image: DecorationImage(
|
||||
colorFilter: ColorFilter.mode(
|
||||
Color.fromARGB(220, 41, 39, 39), BlendMode.hardLight),
|
||||
image: findMediaByType(mediaItems, 'image'),
|
||||
image: findMediaByType(mediaItems, MediaType.location),
|
||||
fit: BoxFit.cover),
|
||||
),
|
||||
child: Align(
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:drift/drift.dart' hide Column;
|
||||
import 'package:flutter/material.dart';
|
||||
@ -14,14 +13,15 @@ import 'package:sendtrain/widgets/generic/elements/form_search_input.dart';
|
||||
import 'package:sendtrain/widgets/generic/elements/form_text_input.dart';
|
||||
import 'package:sendtrain/widgets/sessions/session_view.dart';
|
||||
|
||||
class SessionCreator extends StatefulWidget {
|
||||
const SessionCreator({super.key, this.data, this.session});
|
||||
class SessionEditor extends StatefulWidget {
|
||||
const SessionEditor({super.key, this.data, this.session, this.callback});
|
||||
|
||||
final Session? session;
|
||||
final Map<String, dynamic>? data;
|
||||
final Function? callback;
|
||||
|
||||
@override
|
||||
State<SessionCreator> createState() => _SessionCreatorState();
|
||||
State<SessionEditor> createState() => _SessionEditorState();
|
||||
}
|
||||
|
||||
// used to pass the result of the found image back to current context...
|
||||
@ -30,8 +30,10 @@ class SessionPayload {
|
||||
String? address;
|
||||
}
|
||||
|
||||
class _SessionCreatorState extends State<SessionCreator> {
|
||||
class _SessionEditorState extends State<SessionEditor> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
late AppDatabase db;
|
||||
String editorType = 'Create';
|
||||
|
||||
final Map<String, TextEditingController> sessionCreateController = {
|
||||
'name': TextEditingController(),
|
||||
@ -44,45 +46,78 @@ class _SessionCreatorState extends State<SessionCreator> {
|
||||
|
||||
final SessionPayload sessionPayload = SessionPayload();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
db = Provider.of<AppDatabase>(context, listen: false);
|
||||
|
||||
// if we're editing a session, we'll want to populate it with the appropriate values
|
||||
if (widget.session != null) {
|
||||
editorType = 'Edit';
|
||||
final Session session = widget.session!;
|
||||
sessionCreateController['name']?.text = session.title;
|
||||
sessionCreateController['content']?.text = session.content;
|
||||
sessionCreateController['status']?.text = session.status.name;
|
||||
sessionCreateController['date']?.text =
|
||||
DateFormat('yyyy-MM-dd').format(session.date!);
|
||||
if (session.address != null) {
|
||||
sessionCreateController['address']?.text = session.address!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future createSession(context) async {
|
||||
Map<Symbol, Value> payload = {
|
||||
Symbol('title'): Value<String>(sessionCreateController['name']!.text),
|
||||
Symbol('content'):
|
||||
Value<String>(sessionCreateController['content']!.text),
|
||||
Symbol('status'): Value<SessionStatus>(SessionStatus.pending),
|
||||
// we want to maintain existing status during update
|
||||
Symbol('status'): widget.session != null
|
||||
? Value<SessionStatus>(widget.session!.status)
|
||||
: Value<SessionStatus>(SessionStatus.pending),
|
||||
Symbol('date'): Value<DateTime>(
|
||||
DateTime.parse(sessionCreateController['date']!.text)),
|
||||
};
|
||||
|
||||
// if a session exists we'll want to update it
|
||||
// so the payload needs the session id
|
||||
if (widget.session != null) {
|
||||
payload[Symbol('id')] = Value<int>(widget.session!.id);
|
||||
}
|
||||
|
||||
// optional params
|
||||
if (sessionCreateController['address']!.text.isNotEmpty) {
|
||||
payload[Symbol('address')] =
|
||||
Value<String>(sessionCreateController['address']!.text);
|
||||
}
|
||||
|
||||
return await SessionsDao(Provider.of<AppDatabase>(context, listen: false))
|
||||
return await SessionsDao(db)
|
||||
.createOrUpdate(Function.apply(SessionsCompanion.new, [], payload));
|
||||
}
|
||||
|
||||
Future createSessionMedia(context, sessionId) async {
|
||||
List<MediaItem> deletedMedia =
|
||||
await MediaItemsDao(db).fromSession(sessionId)
|
||||
..where((mediaItem) => mediaItem.type == MediaType.location);
|
||||
|
||||
await MediaItemsDao(db).removeAll(deletedMedia.map((m) => m.id));
|
||||
|
||||
if (sessionPayload.photoUri != null) {
|
||||
MediaItemsCompanion mediaItem = MediaItemsCompanion(
|
||||
title: Value('Location Image'),
|
||||
description: Value(sessionPayload.address!),
|
||||
reference: Value(sessionPayload.photoUri!),
|
||||
type: Value(MediaType.image));
|
||||
type: Value(MediaType.location));
|
||||
|
||||
return await MediaItemsDao(
|
||||
Provider.of<AppDatabase>(context, listen: false))
|
||||
.createOrUpdate(mediaItem).then((id) async {
|
||||
ObjectMediaItemsCompanion omi = ObjectMediaItemsCompanion(
|
||||
objectId: Value(sessionId),
|
||||
objectType: Value(ObjectType.sessions),
|
||||
mediaId: Value(id),
|
||||
);
|
||||
return await MediaItemsDao(db).createOrUpdate(mediaItem).then((id) async {
|
||||
ObjectMediaItemsCompanion omi = ObjectMediaItemsCompanion(
|
||||
objectId: Value(sessionId),
|
||||
objectType: Value(ObjectType.sessions),
|
||||
mediaId: Value(id),
|
||||
);
|
||||
|
||||
await ObjectMediaItemsDao(Provider.of<AppDatabase>(context, listen: false)).createOrUpdate(omi);
|
||||
});
|
||||
await ObjectMediaItemsDao(db).createOrUpdate(omi);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,8 +126,6 @@ class _SessionCreatorState extends State<SessionCreator> {
|
||||
sessionCreateController['date']!.text =
|
||||
DateFormat('yyyy-MM-dd').format(DateTime.now());
|
||||
|
||||
AppDatabase db = Provider.of<AppDatabase>(context, listen: false);
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(15, 0, 15, 15),
|
||||
child: Form(
|
||||
@ -103,7 +136,7 @@ class _SessionCreatorState extends State<SessionCreator> {
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: Text('Create Session',
|
||||
child: Text('$editorType Session',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleLarge)),
|
||||
FormTextInput(
|
||||
@ -166,19 +199,43 @@ class _SessionCreatorState extends State<SessionCreator> {
|
||||
if (_formKey.currentState!.validate())
|
||||
{
|
||||
await createSession(_formKey.currentContext)
|
||||
.then((id) async => {
|
||||
await createSessionMedia(
|
||||
_formKey.currentContext, id),
|
||||
SessionsDao(db).find(id).then(
|
||||
(session) => showGenericDialog(
|
||||
SessionView(
|
||||
session: session),
|
||||
_formKey
|
||||
.currentContext!)),
|
||||
Navigator.pop(
|
||||
_formKey.currentContext!,
|
||||
'Submit')
|
||||
})
|
||||
.then((sessionId) async {
|
||||
int currentSessionId = sessionId;
|
||||
|
||||
// dirft weirdly doesn't return the proper id if
|
||||
// an upsert ends up bein an update, so we'll
|
||||
// set the id to the provided session for an update
|
||||
if (widget.session != null) {
|
||||
currentSessionId = widget.session!.id;
|
||||
}
|
||||
|
||||
// if we've found a photo add it to media!
|
||||
if (sessionPayload.photoUri != null) {
|
||||
await createSessionMedia(
|
||||
_formKey.currentContext,
|
||||
currentSessionId);
|
||||
}
|
||||
|
||||
// if session is null it's new so we show the dialog
|
||||
// otherwise the dialog is already open, so no need
|
||||
if (widget.session == null) {
|
||||
SessionsDao(db)
|
||||
.find(currentSessionId)
|
||||
.then((session) =>
|
||||
showGenericDialog(
|
||||
SessionView(
|
||||
session: session),
|
||||
_formKey.currentContext!));
|
||||
}
|
||||
|
||||
Navigator.pop(
|
||||
_formKey.currentContext!, 'Submit');
|
||||
|
||||
if (widget.callback != null) {
|
||||
await widget
|
||||
.callback!(widget.session!.id);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
child: Text('Submit')))
|
@ -5,9 +5,11 @@ import 'package:intl/date_symbol_data_local.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:sendtrain/daos/activities_dao.dart';
|
||||
import 'package:sendtrain/daos/sessions_dao.dart';
|
||||
import 'package:sendtrain/database/database.dart';
|
||||
import 'package:sendtrain/extensions/string_extensions.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';
|
||||
import 'package:sendtrain/widgets/sessions/session_view_activities.dart';
|
||||
import 'package:sendtrain/widgets/sessions/session_view_media.dart';
|
||||
@ -22,13 +24,45 @@ class SessionView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SessionViewState extends State<SessionView> {
|
||||
final _fabKey = GlobalKey<ExpandableFabState>();
|
||||
late Session session;
|
||||
late DateFormat dateFormat;
|
||||
|
||||
String title() {
|
||||
String title = session.title.toTitleCase();
|
||||
|
||||
if (session.address != null) {
|
||||
title = "$title @ ${session.address}";
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
void resetState(int sessionId) async {
|
||||
Session updatedSession =
|
||||
await SessionsDao(Provider.of<AppDatabase>(context, listen: false))
|
||||
.find(sessionId);
|
||||
|
||||
final state = _fabKey.currentState;
|
||||
if (state != null) {
|
||||
state.toggle();
|
||||
}
|
||||
|
||||
setState(() {
|
||||
session = updatedSession;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
initializeDateFormatting('en');
|
||||
dateFormat = DateFormat('yyyy-MM-dd');
|
||||
session = widget.session;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Session session = widget.session;
|
||||
|
||||
initializeDateFormatting('en');
|
||||
final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
|
||||
|
||||
return StreamBuilder<List<Activity>>(
|
||||
stream: ActivitiesDao(Provider.of<AppDatabase>(context))
|
||||
.watchSessionActivities(session.id),
|
||||
@ -39,6 +73,7 @@ class _SessionViewState extends State<SessionView> {
|
||||
return Scaffold(
|
||||
floatingActionButtonLocation: ExpandableFab.location,
|
||||
floatingActionButton: ExpandableFab(
|
||||
key: _fabKey,
|
||||
distance: 70,
|
||||
type: ExpandableFabType.up,
|
||||
overlayStyle: ExpandableFabOverlayStyle(
|
||||
@ -49,17 +84,55 @@ class _SessionViewState extends State<SessionView> {
|
||||
FloatingActionButton.extended(
|
||||
icon: const Icon(Icons.history_outlined),
|
||||
label: Text('Restart'),
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
Session newSession =
|
||||
session.copyWith(status: SessionStatus.pending);
|
||||
|
||||
SessionsDao(Provider.of<AppDatabase>(context,
|
||||
listen: false))
|
||||
.replace(newSession);
|
||||
|
||||
final state = _fabKey.currentState;
|
||||
if (state != null) {
|
||||
state.toggle();
|
||||
}
|
||||
},
|
||||
),
|
||||
FloatingActionButton.extended(
|
||||
icon: const Icon(Icons.done_all_outlined),
|
||||
label: Text('Done'),
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
Session newSession =
|
||||
session.copyWith(status: SessionStatus.completed);
|
||||
|
||||
SessionsDao(Provider.of<AppDatabase>(context,
|
||||
listen: false))
|
||||
.replace(newSession);
|
||||
|
||||
final state = _fabKey.currentState;
|
||||
if (state != null) {
|
||||
state.toggle();
|
||||
}
|
||||
},
|
||||
),
|
||||
FloatingActionButton.extended(
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
label: Text('Edit'),
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
showModalBottomSheet<void>(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
context: context,
|
||||
showDragHandle: true,
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (BuildContext context) {
|
||||
return SessionEditor(
|
||||
session: session, callback: resetState);
|
||||
});
|
||||
},
|
||||
),
|
||||
]),
|
||||
body: Column(
|
||||
@ -78,7 +151,7 @@ class _SessionViewState extends State<SessionView> {
|
||||
maxLines: 1,
|
||||
style: const TextStyle(
|
||||
fontSize: 25, fontWeight: FontWeight.bold),
|
||||
session.title.toTitleCase())),
|
||||
title())),
|
||||
SessionViewAchievements(session: session),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15),
|
||||
|
@ -13,7 +13,7 @@ class SessionViewMedia extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<List<MediaItem>>(
|
||||
future: MediaItemsDao(Provider.of<AppDatabase>(context))
|
||||
.fromSession(session),
|
||||
.fromSession(session.id),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final mediaItems = snapshot.data!;
|
||||
|
Loading…
x
Reference in New Issue
Block a user