Merge branch 'main' of https://git.sudo.ca/spinel/diameter into main

This commit is contained in:
spinel 2022-03-21 01:23:04 +01:00
commit 481dc60996
12 changed files with 2773 additions and 1704 deletions

View File

@ -68,4 +68,19 @@ class LogEntry {
String toString() {
return DateTimeUtils.displayDateTime(time);
}
static List<LogEntry> getAllForDate(DateTime date) {
DateTime startOfDay = DateTime(date.year, date.month, date.day);
DateTime endOfDay = startOfDay.add(const Duration(days: 1));
QueryBuilder<LogEntry> builder = box.query(LogEntry_.deleted.equals(false))
..order(LogEntry_.time, flags: Order.descending);
return builder.build().find().where((entry) {
return (entry.time.compareTo(startOfDay) >= 0 && entry.time.isBefore(endOfDay));
}).toList();
}
@override
String toString() {
return DateTimeUtils.displayDateTime(time);
}
}

View File

@ -44,7 +44,6 @@ class LogEvent {
// methods
static LogEvent? get(int id) => box.get(id);
static List<LogEvent> getAll() => box.getAll();
static void put(LogEvent logEvent) => box.put(logEvent);
static void remove(int id) {

File diff suppressed because it is too large Load Diff

View File

@ -1234,7 +1234,7 @@ ModelDefinition getObjectBoxModel() {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = Bolus(
final object = LogEventType(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 20, false),
@ -1250,190 +1250,6 @@ ModelDefinition getObjectBoxModel() {
.vTableGetNullable(buffer, rootOffset, 14),
mmolPerL: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 16));
object.bolusProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
object.bolusProfile.attach(store);
return object;
}),
BolusProfile: EntityDefinition<BolusProfile>(
model: _entities[3],
toOneRelations: (BolusProfile object) => [],
toManyRelations: (BolusProfile object) => {},
getId: (BolusProfile object) => object.id,
setId: (BolusProfile object, int id) {
object.id = id;
},
objectToFB: (BolusProfile object, fb.Builder fbb) {
final nameOffset = fbb.writeString(object.name);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(6);
fbb.addInt64(0, object.id);
fbb.addOffset(1, nameOffset);
fbb.addBool(2, object.active);
fbb.addOffset(3, notesOffset);
fbb.addBool(4, object.deleted);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = BolusProfile(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 12, false),
name: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''),
active:
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 10));
return object;
}),
LogEntry: EntityDefinition<LogEntry>(
model: _entities[4],
toOneRelations: (LogEntry object) => [],
toManyRelations: (LogEntry object) => {},
getId: (LogEntry object) => object.id,
setId: (LogEntry object, int id) {
object.id = id;
},
objectToFB: (LogEntry object, fb.Builder fbb) {
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(11);
fbb.addInt64(0, object.id);
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
fbb.addInt64(2, object.mgPerDl);
fbb.addFloat64(3, object.mmolPerL);
fbb.addOffset(7, notesOffset);
fbb.addBool(8, object.deleted);
fbb.addFloat64(9, object.glucoseTrend);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = LogEntry(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 20, false),
time: DateTime.fromMillisecondsSinceEpoch(
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
mgPerDl: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 8),
mmolPerL: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 10),
glucoseTrend: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 22),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18));
return object;
}),
LogEvent: EntityDefinition<LogEvent>(
model: _entities[5],
toOneRelations: (LogEvent object) =>
[object.eventType, object.bolusProfile, object.basalProfile],
toManyRelations: (LogEvent object) => {},
getId: (LogEvent object) => object.id,
setId: (LogEvent object, int id) {
object.id = id;
},
objectToFB: (LogEvent object, fb.Builder fbb) {
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(13);
fbb.addInt64(0, object.id);
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
fbb.addInt64(2, object.endTime?.millisecondsSinceEpoch);
fbb.addBool(3, object.hasEndTime);
fbb.addOffset(4, notesOffset);
fbb.addInt64(7, object.eventType.targetId);
fbb.addBool(8, object.deleted);
fbb.addInt64(9, object.bolusProfile.targetId);
fbb.addInt64(10, object.basalProfile.targetId);
fbb.addInt64(11, object.reminderDuration);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final endTimeValue =
const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 8);
final object = LogEvent(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 20, false),
time: DateTime.fromMillisecondsSinceEpoch(
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
endTime: endTimeValue == null
? null
: DateTime.fromMillisecondsSinceEpoch(endTimeValue),
hasEndTime: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 10, false),
reminderDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 26),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
object.eventType.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
object.eventType.attach(store);
object.bolusProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
object.bolusProfile.attach(store);
object.basalProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0);
object.basalProfile.attach(store);
return object;
}),
LogEventType: EntityDefinition<LogEventType>(
model: _entities[6],
toOneRelations: (LogEventType object) =>
[object.bolusProfile, object.basalProfile],
toManyRelations: (LogEventType object) => {},
getId: (LogEventType object) => object.id,
setId: (LogEventType object, int id) {
object.id = id;
},
objectToFB: (LogEventType object, fb.Builder fbb) {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(9);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addBool(2, object.hasEndTime);
fbb.addInt64(3, object.defaultReminderDuration);
fbb.addOffset(4, notesOffset);
fbb.addBool(5, object.deleted);
fbb.addInt64(6, object.bolusProfile.targetId);
fbb.addInt64(7, object.basalProfile.targetId);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = LogEventType(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 14, false),
value: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''),
hasEndTime:
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
defaultReminderDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 10),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
object.bolusProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0);
object.bolusProfile.attach(store);
@ -1487,6 +1303,353 @@ ModelDefinition getObjectBoxModel() {
final rootOffset = buffer.derefObject(0);
final object = LogMeal(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 36, false),
value:
const fb.StringReader().vTableGet(buffer, rootOffset, 6, ''),
amount:
const fb.Float64Reader().vTableGet(buffer, rootOffset, 38, 0),
carbsRatio: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 8),
portionSize: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 10),
totalCarbs: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 40),
notes: const fb.StringReader()
.vTableGetNullable(buffer, rootOffset, 20))
..bolus = const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 14);
object.logEntry.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
object.logEntry.attach(store);
object.meal.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0);
object.meal.attach(store);
object.mealSource.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 26, 0);
object.mealSource.attach(store);
object.mealCategory.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 28, 0);
object.mealCategory.attach(store);
object.mealPortionType.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 30, 0);
object.mealPortionType.attach(store);
object.portionSizeAccuracy.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 32, 0);
object.portionSizeAccuracy.attach(store);
object.carbsRatioAccuracy.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 34, 0);
object.carbsRatioAccuracy.attach(store);
return object;
}),
Meal: EntityDefinition<Meal>(
model: _entities[8],
toOneRelations: (Meal object) => [
object.mealSource,
object.mealCategory,
object.mealPortionType,
object.portionSizeAccuracy,
object.carbsRatioAccuracy
],
toManyRelations: (Meal object) => {},
getId: (Meal object) => object.id,
setId: (Meal object, int id) {
object.id = id;
},
objectToFB: (Meal object, fb.Builder fbb) {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(16);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addFloat64(2, object.carbsRatio);
fbb.addFloat64(3, object.portionSize);
fbb.addFloat64(4, object.carbsPerPortion);
fbb.addInt64(5, object.delayedBolusDuration);
fbb.addOffset(7, notesOffset);
fbb.addInt64(8, object.mealSource.targetId);
fbb.addInt64(9, object.mealCategory.targetId);
fbb.addInt64(10, object.mealPortionType.targetId);
fbb.addInt64(11, object.portionSizeAccuracy.targetId);
fbb.addInt64(12, object.carbsRatioAccuracy.targetId);
fbb.addBool(13, object.deleted);
fbb.addFloat64(14, object.delayedBolusPercentage);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = Meal(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 30, false),
value:
const fb.StringReader().vTableGet(buffer, rootOffset, 6, ''),
carbsRatio: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 8),
portionSize: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 10),
carbsPerPortion: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 12),
delayedBolusDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 14),
delayedBolusPercentage: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 32),
notes: const fb.StringReader()
.vTableGetNullable(buffer, rootOffset, 18));
object.mealSource.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0);
object.mealSource.attach(store);
object.mealCategory.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
object.mealCategory.attach(store);
object.mealPortionType.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0);
object.mealPortionType.attach(store);
object.portionSizeAccuracy.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 26, 0);
object.portionSizeAccuracy.attach(store);
object.carbsRatioAccuracy.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 28, 0);
object.carbsRatioAccuracy.attach(store);
return object;
}),
BolusProfile: EntityDefinition<BolusProfile>(
model: _entities[3],
toOneRelations: (BolusProfile object) => [],
toManyRelations: (BolusProfile object) => {},
getId: (BolusProfile object) => object.id,
setId: (BolusProfile object, int id) {
object.id = id;
},
objectToFB: (MealCategory object, fb.Builder fbb) {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(6);
fbb.addInt64(0, object.id);
fbb.addOffset(1, nameOffset);
fbb.addBool(2, object.active);
fbb.addOffset(3, notesOffset);
fbb.addBool(4, object.deleted);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = MealCategory(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 12, false),
name: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''),
active:
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 10));
return object;
}),
LogEntry: EntityDefinition<LogEntry>(
model: _entities[4],
toOneRelations: (LogEntry object) => [],
toManyRelations: (LogEntry object) => {},
getId: (LogEntry object) => object.id,
setId: (LogEntry object, int id) {
object.id = id;
},
objectToFB: (MealPortionType object, fb.Builder fbb) {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(11);
fbb.addInt64(0, object.id);
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
fbb.addInt64(2, object.mgPerDl);
fbb.addFloat64(3, object.mmolPerL);
fbb.addOffset(7, notesOffset);
fbb.addBool(8, object.deleted);
fbb.addFloat64(9, object.glucoseTrend);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = MealPortionType(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 20, false),
time: DateTime.fromMillisecondsSinceEpoch(
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
mgPerDl: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 8),
mmolPerL: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 10),
glucoseTrend: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 22),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18));
return object;
}),
LogEvent: EntityDefinition<LogEvent>(
model: _entities[5],
toOneRelations: (LogEvent object) =>
[object.eventType, object.bolusProfile, object.basalProfile],
toManyRelations: (LogEvent object) => {},
getId: (LogEvent object) => object.id,
setId: (LogEvent object, int id) {
object.id = id;
},
objectToFB: (MealSource object, fb.Builder fbb) {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(13);
fbb.addInt64(0, object.id);
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
fbb.addInt64(2, object.endTime?.millisecondsSinceEpoch);
fbb.addBool(3, object.hasEndTime);
fbb.addOffset(4, notesOffset);
fbb.addInt64(7, object.eventType.targetId);
fbb.addBool(8, object.deleted);
fbb.addInt64(9, object.bolusProfile.targetId);
fbb.addInt64(10, object.basalProfile.targetId);
fbb.addInt64(11, object.reminderDuration);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = MealSource(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 20, false),
time: DateTime.fromMillisecondsSinceEpoch(
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
endTime: endTimeValue == null
? null
: DateTime.fromMillisecondsSinceEpoch(endTimeValue),
hasEndTime: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 10, false),
reminderDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 26),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
object.eventType.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
object.eventType.attach(store);
object.bolusProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
object.bolusProfile.attach(store);
object.basalProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0);
object.basalProfile.attach(store);
return object;
}),
LogEventType: EntityDefinition<LogEventType>(
model: _entities[6],
toOneRelations: (LogEventType object) =>
[object.bolusProfile, object.basalProfile],
toManyRelations: (LogEventType object) => {},
getId: (LogEventType object) => object.id,
setId: (LogEventType object, int id) {
object.id = id;
},
objectToFB: (LogBolus object, fb.Builder fbb) {
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(9);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addBool(2, object.hasEndTime);
fbb.addInt64(3, object.defaultReminderDuration);
fbb.addOffset(4, notesOffset);
fbb.addBool(5, object.deleted);
fbb.addInt64(6, object.bolusProfile.targetId);
fbb.addInt64(7, object.basalProfile.targetId);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = LogBolus(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 14, false),
value: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''),
hasEndTime:
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
defaultReminderDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 10),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
object.bolusProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0);
object.bolusProfile.attach(store);
object.basalProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
object.basalProfile.attach(store);
return object;
}),
LogMeal: EntityDefinition<LogMeal>(
model: _entities[7],
toOneRelations: (LogMeal object) => [
object.logEntry,
object.meal,
object.mealSource,
object.mealCategory,
object.mealPortionType,
object.portionSizeAccuracy,
object.carbsRatioAccuracy
],
toManyRelations: (LogMeal object) => {},
getId: (LogMeal object) => object.id,
setId: (LogMeal object, int id) {
object.id = id;
},
objectToFB: (Accuracy object, fb.Builder fbb) {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(20);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addFloat64(2, object.carbsRatio);
fbb.addFloat64(3, object.portionSize);
fbb.addFloat64(5, object.bolus);
fbb.addOffset(8, notesOffset);
fbb.addInt64(9, object.logEntry.targetId);
fbb.addInt64(10, object.meal.targetId);
fbb.addInt64(11, object.mealSource.targetId);
fbb.addInt64(12, object.mealCategory.targetId);
fbb.addInt64(13, object.mealPortionType.targetId);
fbb.addInt64(14, object.portionSizeAccuracy.targetId);
fbb.addInt64(15, object.carbsRatioAccuracy.targetId);
fbb.addBool(16, object.deleted);
fbb.addFloat64(17, object.amount);
fbb.addFloat64(18, object.totalCarbs);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = Accuracy(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 36, false),

View File

@ -188,7 +188,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
}
Future<void> checkIfActiveEventOfTypeExistsBeforeSaving() async {
if (_isNew && _eventType != null &&
if (_eventType != null &&
LogEvent.eventTypeExistsForTime(_eventType!.id, _time)) {
await showDialog(
context: context,

View File

@ -0,0 +1,366 @@
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/settings.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 {
static const String routeName = '/log-events';
const LogEventListScreen({Key? key}) : super(key: key);
@override
_LogEventListScreenState createState() => _LogEventListScreenState();
}
class _LogEventListScreenState extends State<LogEventListScreen> {
List<LogEvent> _activeEvents = [];
late List<LogEvent> _logEvents;
final ScrollController _scrollController = ScrollController();
final TextEditingController _dateController = TextEditingController(text: '');
late DateTime _date;
bool _showActive = true;
@override
void initState() {
super.initState();
_date = DateTime.now();
_dateController.text = DateTimeUtils.displayDate(_date);
reload();
}
@override
void dispose() {
_scrollController.dispose();
_dateController.dispose();
super.dispose();
}
void reload({String? message}) {
setState(() {
_activeEvents = LogEvent.getAllActiveForTime(DateTime.now());
_logEvents = LogEvent.getAllForDate(_date);
});
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
void handleAddNewEvent() async {
final now = DateTime.now();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogEventDetailScreen(
suggestedDate: _date.isAtSameMomentAs(DateTime(now.year, now.month, now.day)) ? now : _date,
);
},
),
).then((result) => reload(message: result?[0]));
}
void handleEditAction(LogEvent event) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LogEventDetailScreen(
id: event.id,
),
),
).then((result) => reload(message: result?[0]));
}
void onDelete(LogEvent logEvent) {
LogEvent.remove(logEvent.id);
reload(message: 'Event deleted');
}
void handleDeleteAction(LogEvent logEvent) async {
if (Settings.get().showConfirmationDialogOnDelete) {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(logEvent),
message: 'Are you sure you want to delete this Event?',
);
} else {
onDelete(logEvent);
}
}
void onStop(LogEvent event) async {
event.endTime = DateTime.now();
LogEvent.put(event);
reload(message: 'Event ended');
}
void handleStopAction(LogEvent event) async {
if (Settings.get().showConfirmationDialogOnStopEvent) {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onStop(event),
message: 'Are you sure you want to end this Event?',
confirmationLabel: 'END EVENT',
);
} else {
onStop(event);
}
}
void onChangeDate(DateTime? date) {
if (date != null) {
setState(() {
_date = DateTime(date.year, date.month, date.day);
_dateController.text = DateTimeUtils.displayDate(date);
});
reload();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Log Events'),
actions: <Widget>[
IconButton(
onPressed: () => onChangeDate(DateTime.now()),
icon: const Icon(Icons.today)),
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
],
),
drawer: const Navigation(currentLocation: LogEventListScreen.routeName),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () => setState(() {
_showActive = !_showActive;
}),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Text(
'ACTIVE EVENTS',
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center,
),
),
Icon(_showActive
? Icons.expand_less
: Icons.expand_more),
],
),
),
),
!_showActive ? Container() :
_activeEvents.isNotEmpty
? ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(10.0),
itemCount: _activeEvents.length,
itemBuilder: (context, index) {
LogEvent event = _activeEvents[index];
return Card(
child: ListTile(
onTap: () {
handleEditAction(event);
},
title: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
DateTimeUtils.displayDateTime(event.time),
),
const SizedBox(width: 24),
Expanded(
child: Text(
(event.title ??
event.eventType.target?.value ??
'')
.toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
event.hasEndTime && event.endTime == null
? IconButton(
icon: const Icon(
Icons.stop,
color: Colors.blue,
),
onPressed: () => handleStopAction(event),
)
: const SizedBox(width: 50),
IconButton(
icon: const Icon(
Icons.edit,
color: Colors.blue,
),
onPressed: () => handleEditAction(event),
),
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
),
onPressed: () => handleDeleteAction(event),
),
],
),
),
);
},
)
: const Center(
child: Text('There are no Active Events!'),
),
const Padding(
padding: EdgeInsets.all(10.0),
child: Divider(),
),
Row(
children: [
IconButton(
onPressed: _date.isAtSameMomentAs(DateTime(2000, 1, 1))
? null
: () =>
onChangeDate(_date.subtract(const Duration(days: 1))),
icon: const Icon(Icons.arrow_back),
),
Expanded(
child: GestureDetector(
onTap: () async {
final newTime = await showDatePicker(
context: context,
initialDate: _date,
firstDate: DateTime(2000, 1, 1),
lastDate: DateTime.now().add(const Duration(days: 365)),
);
onChangeDate(newTime);
},
child: Expanded(
child: Text(
DateTimeUtils.displayDate(_date).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center,
),
),
),
),
IconButton(
onPressed:
_date.add(const Duration(days: 1)).isBefore(DateTime.now())
? () => onChangeDate(_date.add(const Duration(days: 1)))
: null,
icon: const Icon(Icons.arrow_forward),
),
],
),
Expanded(
child: _logEvents.isNotEmpty
? Scrollbar(
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
shrinkWrap: true,
padding: const EdgeInsets.all(10.0),
itemCount: _logEvents.length,
itemBuilder: (context, index) {
LogEvent event = _logEvents[index];
return Card(
child: ListTile(
onTap: () {
handleEditAction(event);
},
title: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
DateTimeUtils.displayTime(event.isEndEvent
? event.endTime
: event.time),
),
const SizedBox(width: 24),
Expanded(
child: Text(
(event.title ??
event.eventType.target?.value ??
'')
.toUpperCase(),
style:
Theme.of(context).textTheme.subtitle2,
),
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
event.hasEndTime && event.endTime == null
? IconButton(
icon: const Icon(
Icons.stop,
color: Colors.blue,
),
onPressed: () =>
handleStopAction(event),
)
: const SizedBox(width: 50),
IconButton(
icon: const Icon(
Icons.edit,
color: Colors.blue,
),
onPressed: () => handleEditAction(event),
),
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
),
onPressed: () => handleDeleteAction(event),
),
],
),
),
);
},
))
: const Center(
child: Text('There are no Events for that date!'),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: handleAddNewEvent,
child: const Icon(Icons.add),
),
);
}
}

View File

@ -0,0 +1,304 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/duration_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_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:flutter/material.dart';
class EventTypeDetailScreen extends StatefulWidget {
static const String routeName = '/log-event-type';
final int id;
const EventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
@override
_EventTypeDetailScreenState createState() => _EventTypeDetailScreenState();
}
class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
LogEventType? _logEventType;
bool _isNew = true;
bool _isSaving = false;
List<BolusProfile> _bolusProfiles = [];
List<BasalProfile> _basalProfiles = [];
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
final ScrollController _scrollController = ScrollController();
final _valueController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
bool _hasEndTime = false;
int _defaultReminderDuration = 0;
BolusProfile? _bolusProfile;
BasalProfile? _basalProfile;
final _bolusProfileController = TextEditingController(text: '');
final _basalProfileController = TextEditingController(text: '');
@override
void initState() {
super.initState();
reload();
_bolusProfiles = BolusProfile.getAll();
_basalProfiles = BasalProfile.getAll();
if (_logEventType != null) {
_valueController.text = _logEventType!.value;
_defaultReminderDuration =
_logEventType!.defaultReminderDuration ?? 0;
_hasEndTime = _logEventType!.hasEndTime;
_notesController.text = _logEventType!.notes ?? '';
_basalProfile = _logEventType!.basalProfile.target;
_basalProfileController.text = (_basalProfile ?? '').toString();
_bolusProfile = _logEventType!.bolusProfile.target;
_bolusProfileController.text = (_bolusProfile ?? '').toString();
}
}
@override
void dispose() {
_scrollController.dispose();
_valueController.dispose();
_notesController.dispose();
_bolusProfileController.dispose();
_basalProfileController.dispose();
super.dispose();
}
void reload({String? message}) {
if (widget.id != 0) {
setState(() {
_logEventType = LogEventType.get(widget.id);
});
}
_isNew = _logEventType == null;
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
void updateBasalProfile(BasalProfile? value) {
setState(() {
_basalProfile = value;
_basalProfileController.text = (_basalProfile ?? '').toString();
});
}
void updateBolusProfile(BolusProfile? value) {
setState(() {
_bolusProfile = value;
_bolusProfileController.text = (_bolusProfile ?? '').toString();
});
}
void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_logEventTypeForm.currentState!.validate()) {
LogEventType eventType = LogEventType(
id: widget.id,
value: _valueController.text,
notes: _notesController.text,
defaultReminderDuration: _defaultReminderDuration,
hasEndTime: _hasEndTime,
);
eventType.basalProfile.target = _basalProfile;
eventType.bolusProfile.target = _bolusProfile;
LogEventType.put(eventType);
Navigator.pop(
context, ['${_isNew ? 'New' : ''} Log Event Type Saved', eventType]);
}
setState(() {
_isSaving = false;
});
}
void handleCancelAction() {
bool isNew = _logEventType == null;
if (Settings.get().showConfirmationDialogOnCancel &&
((isNew &&
(_valueController.text != '' ||
_defaultReminderDuration != 0 ||
_notesController.text != '' ||
_hasEndTime)) ||
(!isNew &&
(_valueController.text != _logEventType!.value ||
_defaultReminderDuration !=
_logEventType!.defaultReminderDuration ||
_notesController.text != (_logEventType!.notes ?? '') ||
_hasEndTime != _logEventType!.hasEndTime)))) {
DialogUtils.showCancelConfirmationDialog(
context: context,
isNew: isNew,
onSave: handleSaveAction,
);
} else {
Navigator.pop(context);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value),
),
drawer:
const Navigation(currentLocation: EventTypeDetailScreen.routeName),
body: Scrollbar(
controller: _scrollController,
child: SingleChildScrollView(
controller: _scrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FormWrapper(formState: _logEventTypeForm, fields: [
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
}
return null;
},
),
BooleanFormField(
value: _hasEndTime,
label: 'has end time',
onChanged: (value) {
setState(() {
_hasEndTime = value;
});
},
),
Column(
children: _hasEndTime
? [
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: DurationFormField(
minutes: _defaultReminderDuration,
label: 'Default Reminder Duration',
onChanged: (value) => _defaultReminderDuration = value ?? 0,
showSteppers: true,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Row(
children: [
Expanded(
child: AutoCompleteDropdownButton<
BolusProfile>(
selectedItem: _bolusProfile,
controller: _bolusProfileController,
label: 'Bolus Profile',
items: _bolusProfiles,
onChanged: updateBolusProfile,
),
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => _bolusProfile ==
null
? const BolusProfileDetailScreen()
: BolusProfileDetailScreen(
id: _bolusProfile!.id),
),
).then((result) {
setState(() {
updateBolusProfile(result?[1]);
});
reload(message: result?[0]);
});
},
icon: Icon(_bolusProfile == null
? Icons.add
: Icons.edit),
),
],
),
),
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,
),
);
}
}

View File

@ -0,0 +1,123 @@
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_event/log_event_type_detail.dart';
import 'package:flutter/material.dart';
class LogEventTypeListScreen extends StatefulWidget {
static const String routeName = '/log-event-types';
const LogEventTypeListScreen({Key? key}) : super(key: key);
@override
_LogEventTypeListScreenState createState() => _LogEventTypeListScreenState();
}
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
List<LogEventType> _logEventTypes = [];
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
reload();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void reload({String? message}) {
setState(() {
_logEventTypes = LogEventType.getAll();
});
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]),
drawer:
const Navigation(currentLocation: LogEventTypeListScreen.routeName),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: _logEventTypes.isNotEmpty
? Scrollbar(
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.all(10.0),
itemCount: _logEventTypes.length,
itemBuilder: (context, index) {
final logEventType = _logEventTypes[index];
return Card(
child: ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EventTypeDetailScreen(
id: logEventType.id),
),
).then((result) => reload(message: result?[0]));
},
title: Text(
logEventType.value.toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
subtitle: Text(logEventType.notes ?? ''),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () async {
LogEventType.remove(logEventType.id);
reload(message: 'Log Event Type deleted');
},
icon: const Icon(Icons.delete,
color: Colors.blue),
)
],
),
),
);
},
),
)
: const Center(
child:
Text('You have not created any Log Event Types yet!'),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const EventTypeDetailScreen(),
),
).then((result) => reload(message: result?[0]));
},
child: const Icon(Icons.add),
),
);
}
}

View File

@ -1,386 +0,0 @@
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/screens/log/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 {
static const String routeName = '/log-events';
const LogEventListScreen({Key? key}) : super(key: key);
@override
_LogEventListScreenState createState() => _LogEventListScreenState();
}
class _LogEventListScreenState extends State<LogEventListScreen> {
List<LogEvent> _activeEvents = [];
late List<LogEvent> _logEvents;
final ScrollController _scrollController = ScrollController();
final TextEditingController _dateController = TextEditingController(text: '');
late DateTime _date;
bool _showActive = true;
String? swipeDirection;
@override
void initState() {
super.initState();
_date = DateTime.now();
_dateController.text = DateTimeUtils.displayDate(_date);
reload();
}
@override
void dispose() {
_scrollController.dispose();
_dateController.dispose();
super.dispose();
}
void reload({String? message}) {
setState(() {
_activeEvents = LogEvent.getAllActiveForTime(DateTime.now());
_logEvents = LogEvent.getAllForDate(_date);
});
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 _date.isAtSameMomentAs(DateTimeUtils.today())
? const LogEventDetailScreen()
: LogEventDetailScreen(
suggestedDate: _date,
);
},
),
).then((result) => reload(message: result?[0]));
}
void handleEditAction(LogEvent event) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LogEventDetailScreen(
id: event.id,
),
),
).then((result) => reload(message: result?[0]));
}
void onDelete(LogEvent logEvent) {
LogEvent.remove(logEvent.id);
reload(message: 'Event deleted');
}
void handleDeleteAction(LogEvent logEvent) async {
if (Settings.get().showConfirmationDialogOnDelete) {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(logEvent),
message: 'Are you sure you want to delete this Event?',
);
} else {
onDelete(logEvent);
}
}
void onStop(LogEvent event) async {
event.endTime = DateTime.now();
LogEvent.put(event);
reload(message: 'Event ended');
}
void handleStopAction(LogEvent event) async {
if (Settings.get().showConfirmationDialogOnStopEvent) {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onStop(event),
message: 'Are you sure you want to end this Event?',
confirmationLabel: 'END EVENT',
);
} else {
onStop(event);
}
}
void onChangeDate(DateTime? date) {
if (date != null) {
setState(() {
_date = DateTime(date.year, date.month, date.day);
_dateController.text = DateTimeUtils.displayDate(date);
});
reload();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Log Events'),
actions: <Widget>[
IconButton(
onPressed: () => onChangeDate(DateTime.now()),
icon: const Icon(Icons.today)),
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
],
),
drawer: const Navigation(currentLocation: LogEventListScreen.routeName),
body: GestureDetector(
onPanUpdate: (details) {
swipeDirection = details.delta.dx < 0 ? 'left' : 'right';
},
onPanEnd: (details) {
if (swipeDirection == null) {
return;
}
if (swipeDirection == 'right' &&
!_date.isAtSameMomentAs(DateTime(2000, 1, 1))) {
onChangeDate(_date.subtract(const Duration(days: 1)));
}
if (swipeDirection == 'left' &&
_date.add(const Duration(days: 1)).isBefore(DateTime.now())) {
onChangeDate(_date.add(const Duration(days: 1)));
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () => setState(() {
_showActive = !_showActive;
}),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Text(
'ACTIVE EVENTS',
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center,
),
),
Icon(_showActive ? Icons.expand_less : Icons.expand_more),
],
),
),
),
!_showActive
? Container()
: _activeEvents.isNotEmpty
? ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(10.0),
itemCount: _activeEvents.length,
itemBuilder: (context, index) {
LogEvent event = _activeEvents[index];
return Card(
child: ListTile(
onTap: () {
handleEditAction(event);
},
title: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
DateTimeUtils.displayDateTime(event.time),
),
const SizedBox(width: 24),
Expanded(
child: Text(
(event.title ??
event.eventType.target?.value ??
'')
.toUpperCase(),
style:
Theme.of(context).textTheme.subtitle2,
),
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
event.hasEndTime && event.endTime == null
? IconButton(
icon: const Icon(
Icons.stop,
color: Colors.blue,
),
onPressed: () =>
handleStopAction(event),
)
: const SizedBox(width: 50),
IconButton(
icon: const Icon(
Icons.edit,
color: Colors.blue,
),
onPressed: () => handleEditAction(event),
),
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
),
onPressed: () => handleDeleteAction(event),
),
],
),
),
);
},
)
: const Center(
child: Text('There are no Active Events!'),
),
const Padding(
padding: EdgeInsets.all(10.0),
child: Divider(),
),
Row(
children: [
IconButton(
onPressed: _date.isAtSameMomentAs(DateTime(2000, 1, 1))
? null
: () =>
onChangeDate(_date.subtract(const Duration(days: 1))),
icon: const Icon(Icons.arrow_back),
),
Expanded(
child: GestureDetector(
onTap: () async {
final newTime = await showDatePicker(
context: context,
initialDate: _date,
firstDate: DateTime(2000, 1, 1),
lastDate: DateTime.now().add(const Duration(days: 365)),
);
onChangeDate(newTime);
},
child: Text(
DateTimeUtils.displayDate(_date).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center,
),
),
),
IconButton(
onPressed: _date
.add(const Duration(days: 1))
.isBefore(DateTime.now())
? () => onChangeDate(_date.add(const Duration(days: 1)))
: null,
icon: const Icon(Icons.arrow_forward),
),
],
),
Expanded(
child: _logEvents.isNotEmpty
? Scrollbar(
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
shrinkWrap: true,
padding: const EdgeInsets.all(10.0),
itemCount: _logEvents.length,
itemBuilder: (context, index) {
LogEvent event = _logEvents[index];
return Card(
child: ListTile(
onTap: () {
handleEditAction(event);
},
title: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
DateTimeUtils.displayTime(event.isEndEvent
? event.endTime
: event.time),
),
const SizedBox(width: 24),
Expanded(
child: Text(
(event.title ??
event.eventType.target?.value ??
'')
.toUpperCase(),
style:
Theme.of(context).textTheme.subtitle2,
),
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
event.hasEndTime && event.endTime == null
? IconButton(
icon: const Icon(
Icons.stop,
color: Colors.blue,
),
onPressed: () =>
handleStopAction(event),
)
: const SizedBox(width: 50),
IconButton(
icon: const Icon(
Icons.edit,
color: Colors.blue,
),
onPressed: () => handleEditAction(event),
),
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
),
onPressed: () => handleDeleteAction(event),
),
],
),
),
);
},
))
: const Center(
child: Text('There are no Events for that date!'),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: handleAddNewEvent,
child: const Icon(Icons.add),
),
);
}
}

View File

@ -0,0 +1,322 @@
import 'package:diameter/components/detail.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/ingredient.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/recipe.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_detail.dart';
import 'package:flutter/material.dart';
class RecipeDetailScreen extends StatefulWidget {
static const String routeName = '/recipe';
final int id;
const RecipeDetailScreen({Key? key, this.id = 0}) : super(key: key);
@override
_RecipeDetailScreenState createState() => _RecipeDetailScreenState();
}
class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
Recipe? _recipe;
List<Ingredient> _ingredients = [];
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _recipeForm = GlobalKey<FormState>();
final ScrollController _scrollController = ScrollController();
final _nameController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
double _servings = 1;
final List<TextEditingController> _ingredientControllers = [];
List<Meal> _meals = [];
@override
void initState() {
super.initState();
reload();
_meals = Meal.getAll();
if (_recipe != null) {
_nameController.text = _recipe!.name;
_servings = _recipe!.servings ?? 1;
_notesController.text = _recipe!.notes ?? '';
if (_ingredients.isNotEmpty) {
for (Ingredient ingredient in _ingredients) {
_ingredientControllers.add(
TextEditingController(text: ingredient.ingredient.target?.value));
}
}
}
}
void reload({String? message}) {
if (widget.id != 0) {
setState(() {
_recipe = Recipe.get(widget.id);
_ingredients = Ingredient.getAllForRecipe(widget.id);
});
}
_isNew = _recipe == null;
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
void onAddIngredient() {
final newIngredient = Ingredient(amount: 0);
setState(() {
newIngredient.recipe.target = _recipe;
_ingredients.add(newIngredient);
_ingredientControllers.add(TextEditingController(text: ''));
});
}
void handleSaveAction({bool close = false}) async {
setState(() {
_isSaving = true;
});
if (_recipeForm.currentState!.validate()) {
Recipe recipe = Recipe(
id: widget.id,
name: _nameController.text,
servings: _servings,
notes: _notesController.text,
);
Recipe.put(recipe);
List<Ingredient> ingredients = _ingredients.map((ingredient) {
if (ingredient.id != 0 &&
(!ingredient.ingredient.hasValue || ingredient.amount == 0)) {
ingredient.deleted = true;
}
return ingredient;
}).toList();
ingredients.retainWhere((ingredient) {
return ingredient.id != 0 ||
(ingredient.amount > 0 && ingredient.ingredient.hasValue);
});
Ingredient.putMany(ingredients);
if (close) {
Navigator.pop(context, ['${_isNew ? 'New' : ''} Recipe Saved', recipe]);
} else {
if (_isNew) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RecipeDetailScreen(id: recipe.id),
),
).then((result) => Navigator.pop(context, result));
} else {
reload(message: 'Recipe saved');
}
}
setState(() {
_isSaving = false;
});
}
}
void handleCancelAction() {
if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew &&
(_nameController.text != '' ||
_servings != 1 ||
_notesController.text != '')) ||
(!_isNew &&
(_nameController.text != _recipe!.name ||
_servings != _recipe!.servings ||
_notesController.text != (_recipe!.notes ?? ''))))) {
DialogUtils.showCancelConfirmationDialog(
context: context,
isNew: _isNew,
onSave: handleSaveAction,
);
} else {
Navigator.pop(context);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Recipe' : _recipe!.name),
),
drawer: const Navigation(currentLocation: RecipeDetailScreen.routeName),
body: Scrollbar(
controller: _scrollController,
child: SingleChildScrollView(
controller: _scrollController,
child: Column(
children: <Widget>[
FormWrapper(
formState: _recipeForm,
fields: [
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Name',
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
}
return null;
},
),
// NumberFormField(
// value: _servings,
// label: 'Servings',
// suffix: ' portions',
// min: 0,
// onChanged: (value) {
// if (value != null && value >= 0) {
// setState(() {
// _servings = value.toDouble();
// });
// }
// },
// ),
TextFormField(
keyboardType: TextInputType.multiline,
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
),
minLines: 2,
maxLines: 5,
),
const Divider(),
GestureDetector(
onTap: onAddIngredient,
child: Row(
children: [
Text(
'INGREDIENTS',
style: Theme.of(context).textTheme.subtitle2,
),
const Spacer(),
IconButton(
onPressed: onAddIngredient,
icon: const Icon(Icons.add),
),
],
),
),
],
),
!_isNew && _ingredients.isNotEmpty
? ListBody(
children: _ingredients.map((item) {
final ingredient = item.ingredient.target;
final index = _ingredients.indexOf(item);
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0, vertical: 5.0),
child: Column(
children: <Widget>[
Row(
children: [
Expanded(
child: AutoCompleteDropdownButton<Meal>(
controller: _ingredientControllers[index],
selectedItem: ingredient,
label: 'Meal Category',
items: _meals,
onChanged: (value) {
setState(() {
_ingredients[index]
.ingredient
.target = value;
_ingredientControllers[index].text =
value?.value ?? '';
});
},
),
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ingredient == null
? const MealDetailScreen()
: MealDetailScreen(
id: ingredient.id),
),
).then((result) {
_ingredients[index].ingredient.target =
result?[1];
_ingredientControllers[index].text =
result?[1].value ?? '';
reload(message: result?[0]);
});
},
icon: Icon(ingredient == null
? Icons.add
: Icons.edit),
),
],
),
// Padding(
// padding: const EdgeInsets.only(top: 10.0),
// child: NumberFormField(
// controller:
// _ingredients[index].amount,
// label: 'Amount',
// suffix: Settings.nutritionMeasurementSuffix,
// min: 0,
// onChanged: (value) {
// if (value != null && value >= 0) {
// setState(() {
// _ingredients[index].amount = value.toDouble();
// });
// }
// },
// ),
// ),
],
),
);
}).toList(),
)
: Center(
child: Text(_isNew
? 'Save the Recipe in order to add ingredients!'
: 'You have not added any Ingredients yet!'),
)
],
),
),
),
bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction,
onAction: _isSaving ? null : handleSaveAction,
onMiddleAction: _isSaving ? null : () => handleSaveAction(close: true),
),
);
}
}

View File

@ -0,0 +1,200 @@
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/ingredient.dart';
import 'package:diameter/models/recipe.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/recipe/recipe_detail.dart';
import 'package:flutter/material.dart';
class RecipeListScreen extends StatefulWidget {
static const String routeName = '/recipes';
const RecipeListScreen({Key? key}) : super(key: key);
@override
_RecipeListScreenState createState() => _RecipeListScreenState();
}
class _RecipeListScreenState extends State<RecipeListScreen> {
List<Recipe> _recipes = [];
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
reload();
}
void reload({String? message}) {
setState(() {
_recipes = Recipe.getAll();
});
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
void onDelete(Recipe recipe) {
Recipe.remove(recipe.id);
reload(message: 'Recipe deleted');
}
void handleDeleteAction(Recipe recipe) async {
if (Settings.get().showConfirmationDialogOnDelete) {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(recipe),
message: 'Are you sure you want to delete this Recipe?',
);
} else {
onDelete(recipe);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Recipes'), actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]),
drawer: const Navigation(currentLocation: RecipeListScreen.routeName),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: _recipes.isNotEmpty
? Scrollbar(
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.all(10.0),
itemCount: _recipes.length,
itemBuilder: (context, index) {
final recipe = _recipes[index];
final carbsRatio =
Ingredient.getCarbsRatioForRecipe(recipe.id);
final carbsPerPortion = Recipe.getCarbsPerPortion(recipe.id);
return Card(
child: ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
RecipeDetailScreen(id: recipe.id),
),
).then((result) => reload(message: result?[0]));
},
title: Text(
recipe.name.toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
subtitle: Padding(
padding:
const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
children: [
Column(
children: [
Text(recipe.notes ?? ''),
],
),
Expanded(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children:
((carbsRatio ?? 0) > 0)
? [
Text(carbsRatio!.toString()),
const Text('% carbs',
textScaleFactor: 0.75),
]
: [],
),
),
Expanded(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children:
(recipe.servings != null)
? [
Text(recipe.servings!
.toStringAsPrecision(3)),
const Text('servings',
textScaleFactor: 0.75),
]
: [],
),
),
Expanded(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children:
((carbsPerPortion ?? 0) > 0)
? [
Text(carbsPerPortion!
.toStringAsPrecision(3)),
Text(
'${Settings.nutritionMeasurementSuffix} carbs per serving',
textScaleFactor: 0.75),
]
: [],
),
),
],
),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
onPressed: () => handleDeleteAction(recipe),
icon: const Icon(Icons.delete,
color: Colors.blue),
)
],
),
),
);
},
),
)
: const Center(
child: Text('You have not created any Recipes yet!'),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RecipeDetailScreen(),
),
).then((result) => reload(message: result?[0]));
},
child: const Icon(Icons.add),
),
);
}
}

View File

@ -249,11 +249,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
@ -275,13 +270,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
http:
dependency: transitive
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.4"
http_multi_server:
dependency: transitive
description: