add information to log; separate log events from log entry
This commit is contained in:
parent
796265b048
commit
e42ad61f44
18
TODO
18
TODO
@ -9,22 +9,18 @@ MAIN TASKS:
|
||||
☐ replace active profile picking mode with simple dropdown
|
||||
☐ indicate both the default rate and the currently active one (according to event)
|
||||
|
||||
Log Overview:
|
||||
☐ display more information
|
||||
☐ apply target color settings to glucose
|
||||
☐ total bolus and carbs per entry
|
||||
|
||||
Log Entry:
|
||||
☐ check for multiple active events with temporary basal/bolus profiles (smallest covered time frame counts)
|
||||
☐ provide splitting functionality for overlapping events
|
||||
☐ provide percentage functionality for delayed bolus
|
||||
☐ get rid of useless cancellation warnings
|
||||
|
||||
Events:
|
||||
☐ when adding an event later on, prompt if existing boli should be recalculated
|
||||
☐ show event start AND end times in list
|
||||
|
||||
Settings:
|
||||
☐ add objectbox class and use instead of shared preferences
|
||||
☐ add fields for preferred date and time formats
|
||||
☐ add fields for glucose target (as map of cutoff glucose and colors)
|
||||
☐ add option to hide warning dialogs on cancel or event stop
|
||||
|
||||
FUTURE TASKS:
|
||||
General/Framework:
|
||||
@ -39,14 +35,20 @@ FUTURE TASKS:
|
||||
☐ find a better way to work with multiple measurements (or disable it?)
|
||||
Log Overview:
|
||||
☐ add pagination
|
||||
☐ apply target color settings to glucose
|
||||
Event Types:
|
||||
☐ add colors as indicators for log entries (and later graphs in reports)
|
||||
☐ implement reminders as push notifications
|
||||
Settings:
|
||||
☐ add fields for preferred date and time formats
|
||||
☐ add fields for glucose target (as map of cutoff glucose and colors)
|
||||
☐ add option to hide warning dialogs on cancel or event stop
|
||||
☐ add option to hide extra customization options (ie. changing pre calculated values)
|
||||
☐ add setting for decimal places
|
||||
|
||||
Archive:
|
||||
✔ separate events from log entries @done(21-12-01 23:37) @project(MAIN TASKS.Events)
|
||||
✔ show total bolus and carbs per entry @done(21-12-01 19:50) @project(MAIN TASKS.Log Overview)
|
||||
✔ display boli correctly @done(21-11-30 04:14) @project(MAIN TASKS.Log Entry)
|
||||
✔ replace meal and glucose boli with logbolus entities @done(21-11-30 03:56) @project(MAIN TASKS.Log Entry)
|
||||
✔ adjust/debug active events view @done(21-11-26 22:54) @project(MAIN TASKS.Log Overview)
|
||||
|
@ -115,7 +115,7 @@ class _DateTimeFormFieldState extends State<DateTimeFormField> {
|
||||
context: context,
|
||||
initialDate: widget.date,
|
||||
firstDate: widget.minDate ?? DateTime(2000, 1, 1),
|
||||
lastDate: widget.maxDate ?? DateTime.now(),
|
||||
lastDate: widget.maxDate ?? DateTime.now().add(const Duration(days: 365)),
|
||||
);
|
||||
widget.onChanged(newTime);
|
||||
},
|
||||
|
@ -3,12 +3,12 @@ import 'package:diameter/object_box.dart';
|
||||
import 'package:diameter/screens/accuracy_detail.dart';
|
||||
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
||||
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
||||
import 'package:diameter/screens/log/active_log_event_list.dart';
|
||||
import 'package:diameter/screens/log/log.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_entry.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event_type_list.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_list.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_type_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_type_list.dart';
|
||||
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
||||
import 'package:diameter/screens/meal/meal_category_list.dart';
|
||||
import 'package:diameter/screens/meal/meal_detail.dart';
|
||||
@ -54,8 +54,8 @@ Future<void> main() async {
|
||||
Routes.logEntry: (context) => const LogEntryScreen(),
|
||||
Routes.logEvent: (context) => const LogEventDetailScreen(),
|
||||
Routes.logEventTypes: (context) => const LogEventTypeListScreen(),
|
||||
Routes.logEventType: (context) => const LogEventTypeDetailScreen(),
|
||||
Routes.activeEvents: (context) => const ActiveEventListScreen(),
|
||||
Routes.logEventType: (context) => const EventTypeDetailScreen(),
|
||||
Routes.events: (context) => const LogEventListScreen(),
|
||||
Routes.accuracies: (context) => const AccuracyListScreen(),
|
||||
Routes.accuracy: (context) => const AccuracyDetailScreen(),
|
||||
Routes.meals: (context) => const MealListScreen(),
|
||||
|
@ -56,8 +56,16 @@ class LogBolus {
|
||||
return builder.build().find();
|
||||
}
|
||||
|
||||
static double getTotalBolusForEntry(int id) {
|
||||
QueryBuilder<LogBolus> builder = box.query(LogBolus_.deleted.equals(false));
|
||||
builder.link(LogBolus_.logEntry, LogEntry_.id.equals(id));
|
||||
return builder.build().property(LogBolus_.units).sum();
|
||||
}
|
||||
|
||||
static bool glucoseBolusForEntryExists(int id) {
|
||||
QueryBuilder<LogBolus> builder = box.query(LogBolus_.deleted.equals(false).and(LogBolus_.mgPerDlCorrection.notNull()));
|
||||
QueryBuilder<LogBolus> builder = box.query(LogBolus_.deleted
|
||||
.equals(false)
|
||||
.and(LogBolus_.mgPerDlCorrection.notNull()));
|
||||
builder.link(LogBolus_.logEntry, LogEntry_.id.equals(id));
|
||||
return builder.build().find().isNotEmpty;
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import 'package:diameter/main.dart';
|
||||
import 'package:diameter/models/basal_profile.dart';
|
||||
import 'package:diameter/models/bolus_profile.dart';
|
||||
import 'package:diameter/models/log_entry.dart';
|
||||
import 'package:diameter/models/log_event_type.dart';
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
import 'package:diameter/objectbox.g.dart' show LogEvent_, LogEntry_;
|
||||
import 'package:diameter/objectbox.g.dart' show LogEvent_;
|
||||
|
||||
@Entity(uid: 4303325892753185970)
|
||||
class LogEvent {
|
||||
@ -22,8 +21,6 @@ class LogEvent {
|
||||
String? notes;
|
||||
|
||||
// relations
|
||||
final logEntry = ToOne<LogEntry>();
|
||||
final endLogEntry = ToOne<LogEntry>();
|
||||
final eventType = ToOne<LogEventType>();
|
||||
final bolusProfile = ToOne<BolusProfile>();
|
||||
final basalProfile = ToOne<BasalProfile>();
|
||||
@ -73,18 +70,21 @@ class LogEvent {
|
||||
return [];
|
||||
}
|
||||
|
||||
static List<LogEvent> getAllForEntry(int id) {
|
||||
QueryBuilder<LogEvent> builder = box.query(LogEvent_.deleted.equals(false))
|
||||
..order(LogEvent_.time);
|
||||
builder.link(LogEvent_.logEntry, LogEntry_.id.equals(id));
|
||||
return builder.build().find();
|
||||
static Map<DateTime, List<LogEvent>> getDailyEntryMap() {
|
||||
Map<DateTime, List<LogEvent>> dateMap = <DateTime, List<LogEvent>>{};
|
||||
|
||||
QueryBuilder<LogEvent> allByDate = box
|
||||
.query(LogEvent_.deleted.equals(false))
|
||||
..order(LogEvent_.time, flags: Order.descending);
|
||||
List<LogEvent> events = allByDate.build().find();
|
||||
DateTime? date;
|
||||
|
||||
for (LogEvent event in events) {
|
||||
date = DateTime.utc(event.time.year, event.time.month, event.time.day);
|
||||
dateMap.putIfAbsent(date, () => <LogEvent>[]).add(event);
|
||||
}
|
||||
|
||||
static List<LogEvent> getAllEndedByEntry(int id) {
|
||||
QueryBuilder<LogEvent> builder = box.query(LogEvent_.deleted.equals(false))
|
||||
..order(LogEvent_.time);
|
||||
builder.link(LogEvent_.endLogEntry, LogEntry_.id.equals(id));
|
||||
return builder.build().find();
|
||||
return dateMap;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -59,6 +59,12 @@ class LogMeal {
|
||||
return builder.build().find();
|
||||
}
|
||||
|
||||
static double getTotalCarbsForEntry(int id) {
|
||||
QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false));
|
||||
builder.link(LogMeal_.logEntry, LogEntry_.id.equals(id));
|
||||
return builder.build().property(LogMeal_.carbsPerPortion).sum();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return value;
|
||||
|
@ -8,10 +8,10 @@ import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
||||
import 'package:diameter/screens/bolus/bolus_profile_list.dart';
|
||||
import 'package:diameter/screens/log/log.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_entry.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/active_log_event_list.dart';
|
||||
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event_type_list.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_list.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_type_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_type_list.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
|
||||
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
||||
import 'package:diameter/screens/meal/meal_category_list.dart';
|
||||
@ -40,10 +40,10 @@ class Routes {
|
||||
static const String logEvent = LogEventDetailScreen.routeName;
|
||||
static const String logMeal = LogMealDetailScreen.routeName;
|
||||
static const List<String> logEntryRoutes = [logEntry, logEvent, logMeal];
|
||||
static const String logEventType = LogEventTypeDetailScreen.routeName;
|
||||
static const String logEventType = EventTypeDetailScreen.routeName;
|
||||
static const String logEventTypes = LogEventTypeListScreen.routeName;
|
||||
static const List<String> logEventTypeRoutes = [logEventType, logEventTypes];
|
||||
static const String activeEvents = ActiveEventListScreen.routeName;
|
||||
static const String events = LogEventListScreen.routeName;
|
||||
|
||||
static const String meal = MealDetailScreen.routeName;
|
||||
static const String meals = MealListScreen.routeName;
|
||||
@ -108,12 +108,12 @@ class _NavigationState extends State<Navigation> {
|
||||
selected: Routes.logEntryRoutes.contains(widget.currentLocation),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Active Events'),
|
||||
title: const Text('Log Events'),
|
||||
leading: const Icon(Icons.event),
|
||||
onTap: () {
|
||||
selectDestination(Routes.activeEvents);
|
||||
selectDestination(Routes.events);
|
||||
},
|
||||
selected: widget.currentLocation == Routes.activeEvents,
|
||||
selected: widget.currentLocation == Routes.events,
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Meals'),
|
||||
|
@ -240,22 +240,6 @@
|
||||
"name": "notes",
|
||||
"type": 9
|
||||
},
|
||||
{
|
||||
"id": "6:7838546213550447420",
|
||||
"name": "logEntryId",
|
||||
"type": 11,
|
||||
"flags": 520,
|
||||
"indexId": "3:3670661188280692002",
|
||||
"relationTarget": "LogEntry"
|
||||
},
|
||||
{
|
||||
"id": "7:8031421171668506924",
|
||||
"name": "endLogEntryId",
|
||||
"type": 11,
|
||||
"flags": 520,
|
||||
"indexId": "4:7379712902406481832",
|
||||
"relationTarget": "LogEntry"
|
||||
},
|
||||
{
|
||||
"id": "8:2514297323717317184",
|
||||
"name": "eventTypeId",
|
||||
@ -818,7 +802,10 @@
|
||||
"retiredEntityUids": [
|
||||
3095978685310268382
|
||||
],
|
||||
"retiredIndexUids": [],
|
||||
"retiredIndexUids": [
|
||||
3670661188280692002,
|
||||
7379712902406481832
|
||||
],
|
||||
"retiredPropertyUids": [
|
||||
3455702077061719523,
|
||||
1048198814030724077,
|
||||
@ -832,7 +819,9 @@
|
||||
1568597071506264632,
|
||||
8795268969829293398,
|
||||
3247926313599127440,
|
||||
8789440370359282572
|
||||
8789440370359282572,
|
||||
7838546213550447420,
|
||||
8031421171668506924
|
||||
],
|
||||
"retiredRelationUids": [],
|
||||
"version": 1
|
||||
|
@ -262,20 +262,6 @@ final _entities = <ModelEntity>[
|
||||
name: 'notes',
|
||||
type: 9,
|
||||
flags: 0),
|
||||
ModelProperty(
|
||||
id: const IdUid(6, 7838546213550447420),
|
||||
name: 'logEntryId',
|
||||
type: 11,
|
||||
flags: 520,
|
||||
indexId: const IdUid(3, 3670661188280692002),
|
||||
relationTarget: 'LogEntry'),
|
||||
ModelProperty(
|
||||
id: const IdUid(7, 8031421171668506924),
|
||||
name: 'endLogEntryId',
|
||||
type: 11,
|
||||
flags: 520,
|
||||
indexId: const IdUid(4, 7379712902406481832),
|
||||
relationTarget: 'LogEntry'),
|
||||
ModelProperty(
|
||||
id: const IdUid(8, 2514297323717317184),
|
||||
name: 'eventTypeId',
|
||||
@ -831,7 +817,7 @@ ModelDefinition getObjectBoxModel() {
|
||||
lastRelationId: const IdUid(0, 0),
|
||||
lastSequenceId: const IdUid(0, 0),
|
||||
retiredEntityUids: const [3095978685310268382],
|
||||
retiredIndexUids: const [],
|
||||
retiredIndexUids: const [3670661188280692002, 7379712902406481832],
|
||||
retiredPropertyUids: const [
|
||||
3455702077061719523,
|
||||
1048198814030724077,
|
||||
@ -845,7 +831,9 @@ ModelDefinition getObjectBoxModel() {
|
||||
1568597071506264632,
|
||||
8795268969829293398,
|
||||
3247926313599127440,
|
||||
8789440370359282572
|
||||
8789440370359282572,
|
||||
7838546213550447420,
|
||||
8031421171668506924
|
||||
],
|
||||
retiredRelationUids: const [],
|
||||
modelVersion: 5,
|
||||
@ -1056,13 +1044,8 @@ ModelDefinition getObjectBoxModel() {
|
||||
}),
|
||||
LogEvent: EntityDefinition<LogEvent>(
|
||||
model: _entities[5],
|
||||
toOneRelations: (LogEvent object) => [
|
||||
object.logEntry,
|
||||
object.endLogEntry,
|
||||
object.eventType,
|
||||
object.bolusProfile,
|
||||
object.basalProfile
|
||||
],
|
||||
toOneRelations: (LogEvent object) =>
|
||||
[object.eventType, object.bolusProfile, object.basalProfile],
|
||||
toManyRelations: (LogEvent object) => {},
|
||||
getId: (LogEvent object) => object.id,
|
||||
setId: (LogEvent object, int id) {
|
||||
@ -1077,8 +1060,6 @@ ModelDefinition getObjectBoxModel() {
|
||||
fbb.addInt64(2, object.endTime?.millisecondsSinceEpoch);
|
||||
fbb.addBool(3, object.hasEndTime);
|
||||
fbb.addOffset(4, notesOffset);
|
||||
fbb.addInt64(5, object.logEntry.targetId);
|
||||
fbb.addInt64(6, object.endLogEntry.targetId);
|
||||
fbb.addInt64(7, object.eventType.targetId);
|
||||
fbb.addBool(8, object.deleted);
|
||||
fbb.addInt64(9, object.bolusProfile.targetId);
|
||||
@ -1107,12 +1088,6 @@ ModelDefinition getObjectBoxModel() {
|
||||
.vTableGetNullable(buffer, rootOffset, 26),
|
||||
notes: const fb.StringReader()
|
||||
.vTableGetNullable(buffer, rootOffset, 12));
|
||||
object.logEntry.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 14, 0);
|
||||
object.logEntry.attach(store);
|
||||
object.endLogEntry.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0);
|
||||
object.endLogEntry.attach(store);
|
||||
object.eventType.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
|
||||
object.eventType.attach(store);
|
||||
@ -1731,33 +1706,25 @@ class LogEvent_ {
|
||||
static final notes =
|
||||
QueryStringProperty<LogEvent>(_entities[5].properties[4]);
|
||||
|
||||
/// see [LogEvent.logEntry]
|
||||
static final logEntry =
|
||||
QueryRelationToOne<LogEvent, LogEntry>(_entities[5].properties[5]);
|
||||
|
||||
/// see [LogEvent.endLogEntry]
|
||||
static final endLogEntry =
|
||||
QueryRelationToOne<LogEvent, LogEntry>(_entities[5].properties[6]);
|
||||
|
||||
/// see [LogEvent.eventType]
|
||||
static final eventType =
|
||||
QueryRelationToOne<LogEvent, LogEventType>(_entities[5].properties[7]);
|
||||
QueryRelationToOne<LogEvent, LogEventType>(_entities[5].properties[5]);
|
||||
|
||||
/// see [LogEvent.deleted]
|
||||
static final deleted =
|
||||
QueryBooleanProperty<LogEvent>(_entities[5].properties[8]);
|
||||
QueryBooleanProperty<LogEvent>(_entities[5].properties[6]);
|
||||
|
||||
/// see [LogEvent.bolusProfile]
|
||||
static final bolusProfile =
|
||||
QueryRelationToOne<LogEvent, BolusProfile>(_entities[5].properties[9]);
|
||||
QueryRelationToOne<LogEvent, BolusProfile>(_entities[5].properties[7]);
|
||||
|
||||
/// see [LogEvent.basalProfile]
|
||||
static final basalProfile =
|
||||
QueryRelationToOne<LogEvent, BasalProfile>(_entities[5].properties[10]);
|
||||
QueryRelationToOne<LogEvent, BasalProfile>(_entities[5].properties[8]);
|
||||
|
||||
/// see [LogEvent.reminderDuration]
|
||||
static final reminderDuration =
|
||||
QueryIntegerProperty<LogEvent>(_entities[5].properties[11]);
|
||||
QueryIntegerProperty<LogEvent>(_entities[5].properties[9]);
|
||||
}
|
||||
|
||||
/// [LogEventType] entity fields to define ObjectBox queries.
|
||||
|
@ -1,74 +0,0 @@
|
||||
import 'package:diameter/models/log_event.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_event_list.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
|
||||
class ActiveEventListScreen extends StatefulWidget {
|
||||
static const String routeName = '/active-log-events';
|
||||
const ActiveEventListScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ActiveEventListScreenState createState() => _ActiveEventListScreenState();
|
||||
}
|
||||
|
||||
|
||||
class _ActiveEventListScreenState extends State<ActiveEventListScreen> {
|
||||
List<LogEvent> _activeEvents = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_activeEvents = LogEvent.getAllActiveForTime(DateTime.now());
|
||||
});
|
||||
|
||||
setState(() {
|
||||
if (message != null) {
|
||||
var snackBar = SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
ScaffoldMessenger.of(context)
|
||||
..removeCurrentSnackBar()
|
||||
..showSnackBar(snackBar);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void handleAddNewEvent() async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return const LogEventDetailScreen();
|
||||
},
|
||||
),
|
||||
).then((message) => reload(message: message));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Active Events'),
|
||||
actions: <Widget>[
|
||||
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||
],
|
||||
),
|
||||
drawer:
|
||||
const Navigation(currentLocation: ActiveEventListScreen.routeName),
|
||||
body: LogEventListScreen(activeEvents: _activeEvents),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: handleAddNewEvent,
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
import 'package:diameter/components/dialogs.dart';
|
||||
import 'package:diameter/config.dart';
|
||||
import 'package:diameter/models/log_bolus.dart';
|
||||
import 'package:diameter/models/log_entry.dart';
|
||||
import 'package:diameter/models/log_meal.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_entry.dart';
|
||||
import 'package:diameter/settings.dart';
|
||||
import 'package:diameter/utils/date_time_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@ -63,10 +66,7 @@ class _LogScreenState extends State<LogScreen> {
|
||||
appBar: AppBar(
|
||||
title: const Text('Log Entries'),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
onPressed: reload,
|
||||
icon: const Icon(Icons.refresh)
|
||||
),
|
||||
IconButton(onPressed: reload, icon: const Icon(Icons.refresh)),
|
||||
],
|
||||
),
|
||||
drawer: const Navigation(currentLocation: LogScreen.routeName),
|
||||
@ -74,13 +74,15 @@ class _LogScreenState extends State<LogScreen> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: _logEntryDailyMap.isNotEmpty ? SingleChildScrollView(
|
||||
child: _logEntryDailyMap.isNotEmpty
|
||||
? SingleChildScrollView(
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: _logEntryDailyMap.length,
|
||||
itemBuilder: (context, dateIndex) {
|
||||
List<DateTime> dateList = _logEntryDailyMap.keys.toList();
|
||||
List<DateTime> dateList =
|
||||
_logEntryDailyMap.keys.toList();
|
||||
final date = dateList[dateIndex];
|
||||
final entryList = _logEntryDailyMap[date];
|
||||
return ListBody(
|
||||
@ -92,6 +94,12 @@ class _LogScreenState extends State<LogScreen> {
|
||||
itemCount: entryList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final logEntry = entryList[index];
|
||||
double bolus =
|
||||
LogBolus.getTotalBolusForEntry(
|
||||
logEntry.id);
|
||||
double carbs =
|
||||
LogMeal.getTotalCarbsForEntry(
|
||||
logEntry.id);
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
@ -101,48 +109,134 @@ class _LogScreenState extends State<LogScreen> {
|
||||
LogEntryScreen(
|
||||
id: logEntry.id),
|
||||
),
|
||||
).then((message) => reload(
|
||||
message: message));
|
||||
).then((message) =>
|
||||
reload(message: message));
|
||||
},
|
||||
title: Text(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
DateTimeUtils.displayTime(
|
||||
logEntry.time)),
|
||||
// ignore: todo
|
||||
// TODO: add more data (event, glucose color...)
|
||||
subtitle: Text(logEntry
|
||||
logEntry.time),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: logEntry
|
||||
.mgPerDl !=
|
||||
null
|
||||
? '${logEntry.mgPerDl.toString()} mg/dl'
|
||||
: ''),
|
||||
null &&
|
||||
(glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
|
||||
glucoseDisplayMode ==
|
||||
GlucoseDisplayMode
|
||||
.both ||
|
||||
glucoseDisplayMode ==
|
||||
GlucoseDisplayMode
|
||||
.bothForList)
|
||||
? [
|
||||
Text(logEntry
|
||||
.mgPerDl
|
||||
.toString()),
|
||||
const Text(
|
||||
'mg/dl',
|
||||
textScaleFactor:
|
||||
0.75,
|
||||
),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: logEntry
|
||||
.mmolPerL !=
|
||||
null &&
|
||||
(glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
|
||||
glucoseDisplayMode ==
|
||||
GlucoseDisplayMode
|
||||
.both ||
|
||||
glucoseDisplayMode ==
|
||||
GlucoseDisplayMode
|
||||
.bothForList)
|
||||
? [
|
||||
Text(logEntry
|
||||
.mmolPerL
|
||||
.toString()),
|
||||
const Text(
|
||||
'mmol/l',
|
||||
textScaleFactor:
|
||||
0.75,
|
||||
),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: (bolus > 0)
|
||||
? [
|
||||
Text(bolus
|
||||
.toStringAsPrecision(
|
||||
3)),
|
||||
const Text('U',
|
||||
textScaleFactor:
|
||||
0.75),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: (carbs > 0)
|
||||
? [
|
||||
Text(carbs
|
||||
.toStringAsPrecision(
|
||||
3)),
|
||||
Text(
|
||||
nutritionMeasurement ==
|
||||
NutritionMeasurement
|
||||
.grams
|
||||
? 'g carbs'
|
||||
: nutritionMeasurement ==
|
||||
NutritionMeasurement
|
||||
.ounces
|
||||
? 'oz carbs'
|
||||
: '',
|
||||
textScaleFactor:
|
||||
0.75),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () =>
|
||||
handleDeleteAction(
|
||||
logEntry),
|
||||
icon: const Icon(
|
||||
Icons.delete,
|
||||
handleDeleteAction(logEntry),
|
||||
icon: const Icon(Icons.delete,
|
||||
color: Colors.blue),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
) : Container(),
|
||||
})
|
||||
: Container(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
) : const Center(
|
||||
)
|
||||
: const Center(
|
||||
child: Text('You have not created any Log Entries yet!'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// ignore: todo
|
||||
// TODO: add button for active events
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
|
@ -4,13 +4,10 @@ import 'package:diameter/components/forms.dart';
|
||||
import 'package:diameter/config.dart';
|
||||
import 'package:diameter/models/log_bolus.dart';
|
||||
import 'package:diameter/models/log_entry.dart';
|
||||
import 'package:diameter/models/log_event.dart';
|
||||
import 'package:diameter/models/log_meal.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_bolus_list.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_event_list.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_meal_list.dart';
|
||||
import 'package:diameter/settings.dart';
|
||||
@ -31,15 +28,13 @@ class LogEntryScreen extends StatefulWidget {
|
||||
class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
LogEntry? _logEntry;
|
||||
List<LogMeal> _logMeals = [];
|
||||
List<LogEvent> _logEvents = [];
|
||||
List<LogEvent> _activeEvents = [];
|
||||
List<LogBolus> _logBoli = [];
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
|
||||
final GlobalKey<FormState> logEntryForm = GlobalKey<FormState>();
|
||||
|
||||
DateTime _time = DateTime.now();
|
||||
late DateTime _time;
|
||||
|
||||
final _timeController = TextEditingController(text: '');
|
||||
final _dateController = TextEditingController(text: '');
|
||||
@ -49,7 +44,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
|
||||
late FloatingActionButton addMealButton;
|
||||
late FloatingActionButton addBolusButton;
|
||||
late FloatingActionButton addEventButton;
|
||||
late IconButton refreshButton;
|
||||
late IconButton closeButton;
|
||||
late DetailBottomRow detailBottomRow;
|
||||
@ -74,11 +68,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
child: const Icon(Icons.add),
|
||||
);
|
||||
|
||||
addEventButton = FloatingActionButton(
|
||||
onPressed: handleAddNewEvent,
|
||||
child: const Icon(Icons.add),
|
||||
);
|
||||
|
||||
refreshButton = IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: reload,
|
||||
@ -103,6 +92,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
_mgPerDlController.text = (_logEntry!.mgPerDl ?? '').toString();
|
||||
_mmolPerLController.text = (_logEntry!.mmolPerL ?? '').toString();
|
||||
_notesController.text = _logEntry!.notes ?? '';
|
||||
} else {
|
||||
_time = DateTime.now();
|
||||
}
|
||||
|
||||
updateTime();
|
||||
@ -113,8 +104,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
setState(() {
|
||||
_logEntry = LogEntry.get(widget.id);
|
||||
_logMeals = LogMeal.getAllForEntry(widget.id);
|
||||
_logEvents = LogEvent.getAllForEntry(widget.id);
|
||||
_activeEvents = LogEvent.getAllActiveForTime(_logEntry?.time).where((event) => !_logEvents.any((logEvent) => logEvent.id == event.id)).toList();
|
||||
_logBoli = LogBolus.getAllForEntry(widget.id);
|
||||
});
|
||||
_isNew = _logEntry == null;
|
||||
@ -230,17 +219,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
).then((message) => reload(message: message));
|
||||
}
|
||||
|
||||
void handleAddNewEvent() async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return LogEventDetailScreen(logEntryId: _logEntry!.id);
|
||||
},
|
||||
),
|
||||
).then((message) => reload(message: message));
|
||||
}
|
||||
|
||||
void renderTabButtons(index) {
|
||||
if (_logEntry != null) {
|
||||
setState(() {
|
||||
@ -255,11 +233,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
appBarActions = [refreshButton, closeButton];
|
||||
bottomNav = null;
|
||||
break;
|
||||
case 3:
|
||||
actionButton = addEventButton;
|
||||
appBarActions = [refreshButton, closeButton];
|
||||
bottomNav = null;
|
||||
break;
|
||||
default:
|
||||
actionButton = null;
|
||||
appBarActions = [closeButton];
|
||||
@ -272,7 +245,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: _isNew ? 1 : 4,
|
||||
length: _isNew ? 1 : 3,
|
||||
child: Builder(builder: (BuildContext context) {
|
||||
final TabController tabController = DefaultTabController.of(context)!;
|
||||
tabController.addListener(() {
|
||||
@ -442,11 +415,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
logEntry: _logEntry!, logMeals: _logMeals, reload: reload));
|
||||
tabs.add(LogBolusListScreen(
|
||||
logEntry: _logEntry!, logBoli: _logBoli, reload: reload));
|
||||
tabs.add(LogEventListScreen(
|
||||
logEntry: _logEntry!,
|
||||
logEvents: _logEvents,
|
||||
activeEvents: _activeEvents,
|
||||
reload: reload));
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
@ -459,7 +427,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
Tab(text: 'GENERAL'),
|
||||
Tab(text: 'MEALS'),
|
||||
Tab(text: 'BOLI'),
|
||||
Tab(text: 'EVENTS'),
|
||||
],
|
||||
),
|
||||
actions: appBarActions,
|
||||
|
@ -1,228 +0,0 @@
|
||||
import 'package:diameter/components/detail.dart';
|
||||
import 'package:diameter/components/dialogs.dart';
|
||||
import 'package:diameter/components/dropdown.dart';
|
||||
import 'package:diameter/components/forms.dart';
|
||||
import 'package:diameter/config.dart';
|
||||
import 'package:diameter/models/basal_profile.dart';
|
||||
import 'package:diameter/models/bolus_profile.dart';
|
||||
import 'package:diameter/models/log_entry.dart';
|
||||
import 'package:diameter/models/log_event.dart';
|
||||
import 'package:diameter/models/log_event_type.dart';
|
||||
import 'package:diameter/navigation.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;
|
||||
|
||||
const LogEventDetailScreen(
|
||||
{Key? key, this.logEntryId = 0, this.endLogEntryId = 0, this.id = 0})
|
||||
: 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 _reminderDurationController = TextEditingController(text: '');
|
||||
final _notesController = TextEditingController(text: '');
|
||||
LogEventType? _eventType;
|
||||
bool _hasEndTime = false;
|
||||
BolusProfile? _bolusProfile;
|
||||
BasalProfile? _basalProfile;
|
||||
|
||||
List<LogEventType> _logEventTypes = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
reload();
|
||||
|
||||
_bolusProfiles = BolusProfile.getAll();
|
||||
_basalProfiles = BasalProfile.getAll();
|
||||
|
||||
if (_logEvent != null) {
|
||||
_reminderDurationController.text =
|
||||
(_logEvent!.reminderDuration ?? '').toString();
|
||||
_hasEndTime = _logEvent!.hasEndTime;
|
||||
_notesController.text = _logEvent!.notes ?? '';
|
||||
_eventType = _logEvent!.eventType.target;
|
||||
_basalProfile = _logEvent!.basalProfile.target;
|
||||
_bolusProfile = _logEvent!.bolusProfile.target;
|
||||
}
|
||||
|
||||
_logEventTypes = LogEventType.getAll();
|
||||
}
|
||||
|
||||
void reload() {
|
||||
if (widget.id != 0) {
|
||||
setState(() {
|
||||
_logEvent = LogEvent.get(widget.id);
|
||||
});
|
||||
}
|
||||
_isNew = _logEvent == null;
|
||||
}
|
||||
|
||||
void onSelectEventType(LogEventType eventType) {
|
||||
setState(() {
|
||||
_eventType = eventType;
|
||||
_hasEndTime = eventType.hasEndTime;
|
||||
if (eventType.basalProfile.target != null) {
|
||||
_basalProfile = eventType.basalProfile.target;
|
||||
}
|
||||
if (eventType.bolusProfile.target != null) {
|
||||
_bolusProfile = eventType.bolusProfile.target;
|
||||
}
|
||||
if (eventType.defaultReminderDuration != null) {
|
||||
_reminderDurationController.text =
|
||||
eventType.defaultReminderDuration.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
setState(() {
|
||||
_isSaving = true;
|
||||
});
|
||||
if (_logEventForm.currentState!.validate()) {
|
||||
LogEvent event = LogEvent(
|
||||
id: widget.id,
|
||||
time: LogEntry.get(widget.logEntryId)?.time ?? DateTime.now(),
|
||||
hasEndTime: _hasEndTime,
|
||||
reminderDuration: int.tryParse(_reminderDurationController.text),
|
||||
notes: _notesController.text,
|
||||
);
|
||||
if (widget.logEntryId != 0) {
|
||||
event.logEntry.targetId = widget.logEntryId;
|
||||
} else {
|
||||
event.logEntry.target = LogEntry(time: DateTime.now());
|
||||
}
|
||||
event.eventType.target = _eventType;
|
||||
event.basalProfile.target = _basalProfile;
|
||||
event.bolusProfile.target = _bolusProfile;
|
||||
LogEvent.put(event);
|
||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Event Saved');
|
||||
}
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
});
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
if (showConfirmationDialogOnCancel &&
|
||||
((_isNew &&
|
||||
(_notesController.text != '' ||
|
||||
_eventType != null ||
|
||||
_hasEndTime)) ||
|
||||
(!_isNew &&
|
||||
(_notesController.text != (_logEvent!.notes ?? '') ||
|
||||
_eventType != _logEvent!.eventType.target ||
|
||||
_hasEndTime != _logEvent!.hasEndTime)))) {
|
||||
Dialogs.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: _isNew,
|
||||
onSave: handleSaveAction,
|
||||
);
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_isNew ? 'New Event' : 'Edit Event'),
|
||||
),
|
||||
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
FormWrapper(
|
||||
formState: _logEventForm,
|
||||
fields: [
|
||||
AutoCompleteDropdownButton<LogEventType>(
|
||||
selectedItem: _eventType,
|
||||
label: 'Event Type',
|
||||
items: _logEventTypes,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
onSelectEventType(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
BooleanFormField(
|
||||
value: _hasEndTime,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_hasEndTime = value;
|
||||
});
|
||||
},
|
||||
label: 'has end time',
|
||||
),
|
||||
Column(
|
||||
children: _hasEndTime ? [
|
||||
TextFormField(
|
||||
controller: _reminderDurationController,
|
||||
keyboardType: const TextInputType.numberWithOptions(),
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Default Reminder Duration',
|
||||
suffixText: ' min',
|
||||
enabled: _hasEndTime,
|
||||
),
|
||||
),
|
||||
AutoCompleteDropdownButton<BolusProfile>(
|
||||
selectedItem: _bolusProfile,
|
||||
label: 'Bolus Profile',
|
||||
items: _bolusProfiles,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_bolusProfile = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
AutoCompleteDropdownButton<BasalProfile>(
|
||||
selectedItem: _basalProfile,
|
||||
label: 'Basal Profile',
|
||||
items: _basalProfiles,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_basalProfile = value;
|
||||
});
|
||||
},
|
||||
)
|
||||
] : []),
|
||||
TextFormField(
|
||||
controller: _notesController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Notes',
|
||||
alignLabelWithHint: true,
|
||||
),
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
366
lib/screens/log/log_event/log_event_detail.dart
Normal file
366
lib/screens/log/log_event/log_event_detail.dart
Normal file
@ -0,0 +1,366 @@
|
||||
import 'package:diameter/components/detail.dart';
|
||||
import 'package:diameter/components/dialogs.dart';
|
||||
import 'package:diameter/components/dropdown.dart';
|
||||
import 'package:diameter/components/forms.dart';
|
||||
import 'package:diameter/config.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/navigation.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;
|
||||
|
||||
const LogEventDetailScreen(
|
||||
{Key? key, this.logEntryId = 0, this.endLogEntryId = 0, this.id = 0})
|
||||
: 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>();
|
||||
|
||||
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;
|
||||
bool _hasEndTime = false;
|
||||
BolusProfile? _bolusProfile;
|
||||
BasalProfile? _basalProfile;
|
||||
|
||||
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;
|
||||
_basalProfile = _logEvent!.basalProfile.target;
|
||||
_bolusProfile = _logEvent!.bolusProfile.target;
|
||||
_time = _logEvent!.time;
|
||||
_endTime = _logEvent!.endTime;
|
||||
} else {
|
||||
_time = DateTime.now();
|
||||
}
|
||||
|
||||
_logEventTypes = LogEventType.getAll();
|
||||
|
||||
updateTime();
|
||||
updateEndTime();
|
||||
}
|
||||
|
||||
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 onSelectEventType(LogEventType eventType) {
|
||||
setState(() {
|
||||
_eventType = eventType;
|
||||
_hasEndTime = eventType.hasEndTime;
|
||||
if (eventType.basalProfile.target != null) {
|
||||
_basalProfile = eventType.basalProfile.target;
|
||||
}
|
||||
if (eventType.bolusProfile.target != null) {
|
||||
_bolusProfile = eventType.bolusProfile.target;
|
||||
}
|
||||
if (eventType.defaultReminderDuration != null) {
|
||||
_reminderDurationController.text =
|
||||
eventType.defaultReminderDuration.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
setState(() {
|
||||
_isSaving = true;
|
||||
});
|
||||
if (_logEventForm.currentState!.validate()) {
|
||||
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');
|
||||
}
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
});
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
if (showConfirmationDialogOnCancel &&
|
||||
((_isNew &&
|
||||
(_notesController.text != '' ||
|
||||
_eventType != null ||
|
||||
_hasEndTime)) ||
|
||||
(!_isNew &&
|
||||
(_notesController.text != (_logEvent!.notes ?? '') ||
|
||||
_eventType != _logEvent!.eventType.target ||
|
||||
_hasEndTime != _logEvent!.hasEndTime)))) {
|
||||
Dialogs.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: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
FormWrapper(
|
||||
formState: _logEventForm,
|
||||
fields: [
|
||||
AutoCompleteDropdownButton<LogEventType>(
|
||||
selectedItem: _eventType,
|
||||
label: 'Event Type',
|
||||
items: _logEventTypes,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
onSelectEventType(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
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();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
TextFormField(
|
||||
controller: _reminderDurationController,
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(),
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Default Reminder Duration',
|
||||
suffixText: ' min',
|
||||
enabled: _hasEndTime,
|
||||
),
|
||||
),
|
||||
AutoCompleteDropdownButton<BolusProfile>(
|
||||
selectedItem: _bolusProfile,
|
||||
label: 'Bolus Profile',
|
||||
items: _bolusProfiles,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_bolusProfile = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
AutoCompleteDropdownButton<BasalProfile>(
|
||||
selectedItem: _basalProfile,
|
||||
label: 'Basal Profile',
|
||||
items: _basalProfiles,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_basalProfile = value;
|
||||
});
|
||||
},
|
||||
)
|
||||
]
|
||||
: []),
|
||||
TextFormField(
|
||||
controller: _notesController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Notes',
|
||||
alignLabelWithHint: true,
|
||||
),
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,31 +1,35 @@
|
||||
import 'package:diameter/components/dialogs.dart';
|
||||
import 'package:diameter/config.dart';
|
||||
import 'package:diameter/models/log_entry.dart';
|
||||
import 'package:diameter/models/log_event.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_detail.dart';
|
||||
import 'package:diameter/utils/date_time_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
|
||||
class LogEventListScreen extends StatefulWidget {
|
||||
final LogEntry? logEntry;
|
||||
final List<LogEvent> logEvents;
|
||||
final List<LogEvent> activeEvents;
|
||||
final Function()? reload;
|
||||
|
||||
const LogEventListScreen(
|
||||
{Key? key, this.logEntry, this.logEvents = const [], this.activeEvents = const [], this.reload})
|
||||
: super(key: key);
|
||||
static const String routeName = '/log-events';
|
||||
const LogEventListScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LogEventListScreenState createState() => _LogEventListScreenState();
|
||||
}
|
||||
|
||||
class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
void reload({String? message}) {
|
||||
if (widget.reload != null) {
|
||||
widget.reload!();
|
||||
List<LogEvent> _activeEvents = [];
|
||||
late Map<DateTime, List<LogEvent>> _logEventDailyMap;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_activeEvents = LogEvent.getAllActiveForTime(DateTime.now());
|
||||
_logEventDailyMap = LogEvent.getDailyEntryMap();
|
||||
});
|
||||
|
||||
setState(() {
|
||||
if (message != null) {
|
||||
var snackBar = SnackBar(
|
||||
@ -39,12 +43,22 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void handleAddNewEvent() async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return const LogEventDetailScreen();
|
||||
},
|
||||
),
|
||||
).then((message) => reload(message: message));
|
||||
}
|
||||
|
||||
void handleEditAction(LogEvent event) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LogEventDetailScreen(
|
||||
logEntryId: widget.logEntry?.id ?? 0,
|
||||
id: event.id,
|
||||
),
|
||||
),
|
||||
@ -70,8 +84,6 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
|
||||
void onStop(LogEvent event) async {
|
||||
event.endTime = DateTime.now();
|
||||
event.endLogEntry.target =
|
||||
LogEntry.get(widget.logEntry?.id ?? 0) ?? LogEntry(time: DateTime.now());
|
||||
LogEvent.put(event);
|
||||
reload(message: 'Event ended');
|
||||
}
|
||||
@ -89,21 +101,41 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Log Events'),
|
||||
actions: <Widget>[
|
||||
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||
],
|
||||
),
|
||||
drawer: const Navigation(currentLocation: LogEventListScreen.routeName),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: (widget.logEvents.isNotEmpty ||
|
||||
widget.activeEvents.isNotEmpty)
|
||||
child: _logEventDailyMap.isNotEmpty
|
||||
? SingleChildScrollView(
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: _logEventDailyMap.length,
|
||||
itemBuilder: (context, dateIndex) {
|
||||
List<DateTime?> dateList = <DateTime?>[null] +
|
||||
_logEventDailyMap.keys.toList();
|
||||
final date = dateList[dateIndex];
|
||||
final eventList = date != null ? _logEventDailyMap[date] : _activeEvents;
|
||||
return ListBody(
|
||||
children: [
|
||||
Text(DateTimeUtils.displayDate(date, fallback: 'Active Events')),
|
||||
eventList != null && eventList.isNotEmpty
|
||||
? ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.logEvents.length +
|
||||
widget.activeEvents.length,
|
||||
itemCount: eventList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final event = (widget.logEvents +
|
||||
widget.activeEvents)[index];
|
||||
final event = eventList[index];
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
handleEditAction(event);
|
||||
@ -138,13 +170,21 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
],
|
||||
),
|
||||
);
|
||||
})
|
||||
: Center(
|
||||
child: Text(widget.logEntry == null ? 'There are no active Events!'
|
||||
: 'You have not added any Events to this Log Entry yet!'),
|
||||
}) : Container(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
) : const Center(
|
||||
child: Text('There are no Events!'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: handleAddNewEvent,
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -9,17 +9,17 @@ import 'package:diameter/models/log_event_type.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LogEventTypeDetailScreen extends StatefulWidget {
|
||||
class EventTypeDetailScreen extends StatefulWidget {
|
||||
static const String routeName = '/log-event-type';
|
||||
final int id;
|
||||
const LogEventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||
const EventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LogEventTypeDetailScreenState createState() =>
|
||||
_LogEventTypeDetailScreenState();
|
||||
_EventTypeDetailScreenState createState() =>
|
||||
_EventTypeDetailScreenState();
|
||||
}
|
||||
|
||||
class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
||||
class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
||||
LogEventType? _logEventType;
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
@ -120,7 +120,7 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
||||
title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value),
|
||||
),
|
||||
drawer:
|
||||
const Navigation(currentLocation: LogEventTypeDetailScreen.routeName),
|
||||
const Navigation(currentLocation: EventTypeDetailScreen.routeName),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
@ -1,6 +1,6 @@
|
||||
import 'package:diameter/models/log_event_type.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_type_detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LogEventTypeListScreen extends StatefulWidget {
|
||||
@ -60,7 +60,7 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
LogEventTypeDetailScreen(
|
||||
EventTypeDetailScreen(
|
||||
id: logEventType.id),
|
||||
),
|
||||
).then((message) => reload(message: message));
|
||||
@ -73,10 +73,8 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
LogEventType.remove(logEventType.id);
|
||||
// await logEventType.delete().then((_) {
|
||||
reload(
|
||||
message: 'Log Event Type deleted');
|
||||
// });
|
||||
},
|
||||
icon: const Icon(Icons.delete,
|
||||
color: Colors.blue),
|
||||
@ -96,7 +94,7 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const LogEventTypeDetailScreen(),
|
||||
builder: (context) => const EventTypeDetailScreen(),
|
||||
),
|
||||
).then((message) => reload(message: message));
|
||||
},
|
Loading…
Reference in New Issue
Block a user