diameter/lib/screens/log/log_event/log_event_detail.dart
2022-03-21 01:07:29 +01:00

512 lines
20 KiB
Dart

import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/date_time_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
class LogEventDetailScreen extends StatefulWidget {
static const String routeName = '/log-event';
final int logEntryId;
final int endLogEntryId;
final int id;
final DateTime? suggestedDate;
const LogEventDetailScreen(
{Key? key, this.logEntryId = 0, this.endLogEntryId = 0, this.id = 0, this.suggestedDate})
: super(key: key);
@override
_LogEventDetailScreenState createState() => _LogEventDetailScreenState();
}
class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
LogEvent? _logEvent;
bool _isNew = true;
bool _isSaving = false;
List<BolusProfile> _bolusProfiles = [];
List<BasalProfile> _basalProfiles = [];
final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>();
final ScrollController _scrollController = ScrollController();
late DateTime _time;
DateTime? _endTime;
final _timeController = TextEditingController(text: '');
final _endTimeController = TextEditingController(text: '');
final _dateController = TextEditingController(text: '');
final _endDateController = TextEditingController(text: '');
final _reminderDurationController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
LogEventType? _eventType;
final _eventTypeController = TextEditingController(text: '');
bool _hasEndTime = false;
BolusProfile? _bolusProfile;
BasalProfile? _basalProfile;
final _bolusProfileController = TextEditingController(text: '');
final _basalProfileController = TextEditingController(text: '');
List<LogEventType> _logEventTypes = [];
@override
void initState() {
super.initState();
reload();
_bolusProfiles = BolusProfile.getAll();
_basalProfiles = BasalProfile.getAll();
if (widget.id != 0) {
_reminderDurationController.text =
(_logEvent!.reminderDuration ?? '').toString();
_hasEndTime = _logEvent!.hasEndTime;
_notesController.text = _logEvent!.notes ?? '';
_eventType = _logEvent!.eventType.target;
_eventTypeController.text = (_eventType ?? '').toString();
_basalProfile = _logEvent!.basalProfile.target;
_basalProfileController.text = (_basalProfile ?? '').toString();
_bolusProfile = _logEvent!.bolusProfile.target;
_bolusProfileController.text = (_bolusProfile ?? '').toString();
_time = _logEvent!.time;
_endTime = _logEvent!.endTime;
} else {
_time = widget.suggestedDate ?? DateTime.now();
}
_logEventTypes = LogEventType.getAll();
updateTime();
updateEndTime();
}
@override
void dispose() {
_scrollController.dispose();
_timeController.dispose();
_endTimeController.dispose();
_dateController.dispose();
_endDateController.dispose();
_reminderDurationController.dispose();
_notesController.dispose();
_eventTypeController.dispose();
_bolusProfileController.dispose();
_basalProfileController.dispose();
super.dispose();
}
void reload({String? message}) {
if (widget.id != 0) {
setState(() {
_logEvent = LogEvent.get(widget.id);
});
}
_isNew = _logEvent == null;
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
void updateTime() {
_timeController.text = DateTimeUtils.displayTime(_time);
_dateController.text = DateTimeUtils.displayDate(_time);
}
void updateEndTime() {
_endTimeController.text = DateTimeUtils.displayTime(_endTime);
_endDateController.text = DateTimeUtils.displayDate(_endTime);
}
void updateBasalProfile(BasalProfile? value) {
setState(() {
_basalProfile = value;
_basalProfileController.text = (_basalProfile ?? '').toString();
});
}
void updateBolusProfile(BolusProfile? value) {
setState(() {
_bolusProfile = value;
_bolusProfileController.text = (_bolusProfile ?? '').toString();
});
}
void onSelectEventType(LogEventType? eventType) {
setState(() {
_eventType = eventType;
_eventTypeController.text = (_eventType ?? '').toString();
});
if (eventType != null) {
setState(() {
_hasEndTime = eventType.hasEndTime;
if (eventType.defaultReminderDuration != null) {
_reminderDurationController.text =
eventType.defaultReminderDuration.toString();
}
});
if (eventType.basalProfile.target != null) {
updateBasalProfile(eventType.basalProfile.target);
}
if (eventType.bolusProfile.target != null) {
updateBolusProfile(eventType.bolusProfile.target);
}
}
}
Future<void> checkIfActiveEventOfTypeExistsBeforeSaving() async {
if (_eventType != null &&
LogEvent.eventTypeExistsForTime(_eventType!.id, _time)) {
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: const Text(
'An Event of this type is already active within the set time frame. What would you like to do?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, 'DISCARD'),
child: const Text('DISCARD'),
),
TextButton(
onPressed: () => Navigator.pop(context, 'EDIT'),
child: const Text('KEEP EDITING'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, 'SAVE'),
child: const Text('SAVE'),
)
],
);
}).then((value) async {
if (value == 'DISCARD') {
Navigator.pop(context);
} else if (value == 'SAVE') {
onSave();
}
});
} else {
onSave();
}
}
void onSave() {
LogEvent event = LogEvent(
id: widget.id,
time: _time,
endTime: _endTime,
hasEndTime: _hasEndTime,
reminderDuration: int.tryParse(_reminderDurationController.text),
notes: _notesController.text,
);
event.eventType.target = _eventType;
event.basalProfile.target = _basalProfile;
event.bolusProfile.target = _bolusProfile;
LogEvent.put(event);
Navigator.pop(context, ['${_isNew ? 'New' : ''} Event Saved', event]);
}
void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_logEventForm.currentState!.validate()) {
await checkIfActiveEventOfTypeExistsBeforeSaving();
}
setState(() {
_isSaving = false;
});
}
void handleCancelAction() {
if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew &&
(_notesController.text != '' ||
_eventType != null ||
_hasEndTime)) ||
(!_isNew &&
(_notesController.text != (_logEvent!.notes ?? '') ||
_eventType != _logEvent!.eventType.target ||
_hasEndTime != _logEvent!.hasEndTime)))) {
DialogUtils.showCancelConfirmationDialog(
context: context,
isNew: _isNew,
onSave: handleSaveAction,
);
} else {
Navigator.pop(context);
}
}
@override
Widget build(BuildContext context) {
final now = DateTime.now();
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Event' : 'Edit Event'),
),
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
body: Scrollbar(
controller: _scrollController,
child: SingleChildScrollView(
controller: _scrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FormWrapper(
formState: _logEventForm,
fields: [
AutoCompleteDropdownButton<LogEventType>(
controller: _eventTypeController,
selectedItem: _eventType,
label: 'Event Type',
items: _logEventTypes,
onChanged: onSelectEventType,
),
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _time,
label: _hasEndTime ? 'Start Date' : 'Date',
controller: _dateController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_time = DateTime(newTime.year, newTime.month,
newTime.day, _time.hour, _time.minute);
});
updateTime();
}
},
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(_time),
label: _hasEndTime ? 'Start Time' : 'Time',
controller: _timeController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_time = DateTime(_time.year, _time.month,
_time.day, newTime.hour, newTime.minute);
});
updateTime();
}
},
),
),
),
],
),
BooleanFormField(
value: _hasEndTime,
onChanged: (value) {
setState(() {
_hasEndTime = value;
});
},
label: 'has end time',
),
Column(
children: _hasEndTime
? [
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _endTime ?? now,
label: 'End Date',
controller: _endDateController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_endTime = DateTime(
newTime.year,
newTime.month,
newTime.day,
_endTime?.hour ?? 0,
_endTime?.minute ?? 0);
});
updateEndTime();
}
},
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(
_endTime ?? now),
label: 'End Time',
controller: _endTimeController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_endTime = DateTime(
_endTime?.year ?? now.year,
_endTime?.month ?? now.month,
_endTime?.day ?? now.day,
newTime.hour,
newTime.minute);
});
updateEndTime();
}
},
),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: TextFormField(
controller: _reminderDurationController,
keyboardType:
const TextInputType.numberWithOptions(),
decoration: InputDecoration(
labelText: 'Default Reminder Duration',
suffixText: ' min',
enabled: _hasEndTime,
),
),
),
Row(
children: [
Expanded(
child: AutoCompleteDropdownButton<
BolusProfile>(
controller: _bolusProfileController,
selectedItem: _bolusProfile,
label: 'Bolus Profile',
items: _bolusProfiles,
onChanged: updateBolusProfile,
),
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => _bolusProfile ==
null
? const BolusProfileDetailScreen()
: BolusProfileDetailScreen(
id: _basalProfile!.id),
),
).then((result) {
updateBolusProfile(result?[1]);
reload(message: result?[0]);
});
},
icon: Icon(_bolusProfile == null
? Icons.add
: Icons.edit),
),
],
),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Row(
children: [
Expanded(
child: AutoCompleteDropdownButton<
BasalProfile>(
controller: _basalProfileController,
selectedItem: _basalProfile,
label: 'Basal Profile',
items: _basalProfiles,
onChanged: updateBasalProfile,
),
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => _basalProfile ==
null
? const BasalProfileDetailScreen()
: BasalProfileDetailScreen(
id: _basalProfile!.id),
),
).then((result) {
updateBasalProfile(result?[1]);
reload(message: result?[0]);
});
},
icon: Icon(_basalProfile == null
? Icons.add
: Icons.edit),
),
],
),
)
]
: []),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
),
keyboardType: TextInputType.multiline,
minLines: 2,
maxLines: 5,
),
],
),
],
),
),
),
bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction,
onAction: _isSaving ? null : handleSaveAction,
),
);
}
}