288 lines
12 KiB
Dart
288 lines
12 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:drift/drift.dart' hide Column;
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:sendtrain/daos/media_items_dao.dart';
|
|
import 'package:sendtrain/daos/object_media_items_dao.dart';
|
|
import 'package:sendtrain/daos/sessions_dao.dart';
|
|
import 'package:sendtrain/database/database.dart';
|
|
import 'package:sendtrain/widgets/builders/dialogs.dart';
|
|
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 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<SessionEditor> createState() => _SessionEditorState();
|
|
}
|
|
|
|
// used to pass the result of the found image back to current context...
|
|
class SessionPayload {
|
|
String? photoUri;
|
|
String? address;
|
|
List<PlatformFile>? files;
|
|
}
|
|
|
|
class _SessionEditorState extends State<SessionEditor> {
|
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
|
late AppDatabase db;
|
|
String editorType = 'Create';
|
|
|
|
final Map<String, TextEditingController> sessionCreateController = {
|
|
'name': TextEditingController(),
|
|
'content': TextEditingController(),
|
|
'status': TextEditingController(),
|
|
'date': TextEditingController(),
|
|
'address': TextEditingController(),
|
|
'media': TextEditingController(),
|
|
};
|
|
|
|
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),
|
|
// 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(db)
|
|
.createOrUpdate(Function.apply(SessionsCompanion.new, [], payload));
|
|
}
|
|
|
|
Future deleteSessionMedia(int sessionId, MediaType mediaType) async {
|
|
List<MediaItem> deletedMedia =
|
|
(await MediaItemsDao(db).fromSession(sessionId))
|
|
.where((mediaItem) => mediaItem.type == mediaType)
|
|
.toList();
|
|
|
|
for (int i = 0; i < deletedMedia.length; i++) {
|
|
await MediaItemsDao(db).remove(deletedMedia[i]);
|
|
}
|
|
}
|
|
|
|
Future createSessionMedia(
|
|
title,
|
|
sessionId,
|
|
description,
|
|
reference,
|
|
mediaType,
|
|
) async {
|
|
// if (sessionPayload.photoUri != null) {
|
|
MediaItemsCompanion mediaItem = MediaItemsCompanion(
|
|
title: Value(title),
|
|
description: Value(description),
|
|
reference: Value(reference.toString()),
|
|
type: Value(mediaType));
|
|
|
|
return await MediaItemsDao(db).createOrUpdate(mediaItem).then((id) async {
|
|
ObjectMediaItemsCompanion omi = ObjectMediaItemsCompanion(
|
|
objectId: Value(sessionId),
|
|
objectType: Value(ObjectType.sessions),
|
|
mediaId: Value(id),
|
|
);
|
|
|
|
await ObjectMediaItemsDao(db).createOrUpdate(omi);
|
|
});
|
|
// }
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
sessionCreateController['date']!.text =
|
|
DateFormat('yyyy-MM-dd').format(DateTime.now());
|
|
|
|
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('$editorType Session',
|
|
textAlign: TextAlign.center,
|
|
style: Theme.of(context).textTheme.titleLarge)),
|
|
FormTextInput(
|
|
controller: sessionCreateController['name']!,
|
|
title: 'Title'),
|
|
FormTextInput(
|
|
controller: sessionCreateController['content']!,
|
|
title: 'Description',
|
|
icon: Icon(Icons.description_rounded),
|
|
maxLines: 10),
|
|
FormTextInput(
|
|
controller: sessionCreateController['date']!,
|
|
title: 'Date',
|
|
icon: Icon(Icons.date_range_rounded),
|
|
onTap: () {
|
|
showDatePicker(
|
|
context: context,
|
|
initialDate: DateTime.now(),
|
|
firstDate: DateTime.now(),
|
|
lastDate: DateTime.now().add(Duration(days: 365)))
|
|
.then((date) {
|
|
if (date != null) {
|
|
sessionCreateController['date']?.text =
|
|
DateFormat('yyyy-MM-dd').format(date);
|
|
}
|
|
});
|
|
}),
|
|
FormSearchInput(
|
|
sessionController: sessionCreateController['address']!,
|
|
optionalPayload: sessionPayload),
|
|
Padding(
|
|
padding: EdgeInsets.only(top: 10, bottom: 10),
|
|
child: TextFormField(
|
|
readOnly: true,
|
|
decoration: InputDecoration(
|
|
prefixIcon: Icon(Icons.image_rounded),
|
|
filled: true,
|
|
border: OutlineInputBorder(
|
|
borderSide: BorderSide.none,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
labelText: 'Select Media (optional)',
|
|
),
|
|
controller: sessionCreateController['media'],
|
|
onTap: () async {
|
|
FilePickerResult? result = await FilePicker.platform
|
|
.pickFiles(
|
|
allowMultiple: true, type: FileType.media);
|
|
|
|
if (result != null) {
|
|
List<PlatformFile> files = result.files;
|
|
sessionCreateController['media']!.text =
|
|
files.map((file) => file.name).toString();
|
|
sessionPayload.files = files;
|
|
}
|
|
})),
|
|
Row(mainAxisAlignment: MainAxisAlignment.end, children: [
|
|
Padding(
|
|
padding: EdgeInsets.only(top: 10),
|
|
child: FilledButton(
|
|
onPressed: () async => {
|
|
if (_formKey.currentState!.validate())
|
|
{
|
|
await createSession(_formKey.currentContext)
|
|
.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 deleteSessionMedia(
|
|
currentSessionId,
|
|
MediaType.location);
|
|
await createSessionMedia(
|
|
'Location Image',
|
|
currentSessionId,
|
|
sessionPayload.address,
|
|
sessionPayload.photoUri,
|
|
MediaType.location);
|
|
}
|
|
|
|
// if we've selected files to save, save them
|
|
if (sessionPayload.files != null) {
|
|
for (int i = 0;
|
|
i < sessionPayload.files!.length;
|
|
i++) {
|
|
PlatformFile file =
|
|
sessionPayload.files![i];
|
|
Uint8List fileBytes =
|
|
await file.xFile.readAsBytes();
|
|
await createSessionMedia(
|
|
'Local Media',
|
|
currentSessionId,
|
|
file.name,
|
|
base64Encode(fileBytes),
|
|
MediaType.localImage);
|
|
}
|
|
}
|
|
|
|
// 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')))
|
|
]),
|
|
],
|
|
)));
|
|
}
|
|
}
|