calorimeter/lib/storage/storage.dart
Marco a7a7f44050 Make scanned widgets appear in the list instead of putting the info into
the EnterFoodWidget.

1. Make scanned foods appear in the list of foods
2. Remove Controller for entering food

This commit removes the EnterFoodController that was used to put a
scanned food into the EnterFoodWidget.
This is now unnecessary because scanning a product will be distributed
via the FoodBLoC.
2024-09-24 14:41:42 +02:00

223 lines
5.1 KiB
Dart

import 'dart:developer';
import 'dart:io';
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
import 'package:path_provider/path_provider.dart';
import 'package:universal_platform/universal_platform.dart';
class FoodStorage {
static late FoodStorage _instance;
late String path;
final Map<String, int> _foodLookupDatabase = {};
FoodStorage._create();
static Future<FoodStorage> create() async {
var storage = FoodStorage._create();
Directory dir = Directory('');
if (UniversalPlatform.isDesktop) {
dir = await getApplicationCacheDirectory();
} else if (UniversalPlatform.isAndroid) {
dir = await getApplicationDocumentsDirectory();
}
storage.path = dir.path;
_instance = storage;
return _instance;
}
static FoodStorage getInstance() => _instance;
Future<List<FoodEntryState>> getEntriesForDate(DateTime date) async {
List<FoodEntryState> entries = [];
var filePath = '$path/${date.year}/${date.month}/${date.day}';
var file = File(filePath);
var exists = await file.exists();
if (!exists) return [];
var lines = await file.readAsLines();
for (var line in lines) {
var fields = line.splitWithIgnore(',', ignoreIn: '"');
int mass = 0;
int kcalPerMass = 0;
try {
mass = int.parse(fields[2]);
} catch (e) {
mass = double.parse(fields[2]).toInt();
}
try {
kcalPerMass = int.parse(fields[3]);
} catch (e) {
kcalPerMass = double.parse(fields[3]).toInt();
}
var entry = FoodEntryState(
name: fields[1].replaceAll('"', ""),
mass: mass,
kcalPerMass: kcalPerMass,
waitingForNetwork: false,
);
entries.add(entry);
}
return entries;
}
Future<void> writeEntriesForDate(
DateTime date, List<FoodEntryState> foodEntries) async {
var filePath = '$path/${date.year}/${date.month}/${date.day}';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
await file.create(recursive: true);
}
String fullString = '';
for (var entry in foodEntries) {
fullString += '${entry.toString()}\n';
}
await file.writeAsString(fullString);
}
Future<void> updateLimit(double limit) async {
var filePath = '$path/limit';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
await file.create();
}
await file.writeAsString(limit.toString());
}
Future<double> readLimit() async {
var filePath = '$path/limit';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
return 2000;
}
var line = await file.readAsLines();
double limit;
try {
limit = double.parse(line[0]);
} catch (e) {
limit = 2000;
}
return limit;
}
Future<String> readBrightness() async {
var filePath = '$path/brightness';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
return 'dark';
}
var line = await file.readAsLines();
if (line.isEmpty || (line[0] != 'dark' && line[0] != 'light')) {
return 'dark';
}
return line[0];
}
Future<void> writeBrightness(String brightness) async {
var filePath = '$path/brightness';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
file.create();
}
await file.writeAsString(brightness);
}
Future<void> buildFoodLookupDatabase() async {
// get a list of dates of the last 365 days
var dates = List<DateTime>.generate(365, (idx) {
var durationToPast = Duration(days: idx);
return DateTime.now().subtract(durationToPast);
});
for (var date in dates.reversed) {
addFoodEntryToLookupDatabaseFor(date);
}
}
Future<void> addFoodEntryToLookupDatabaseFor(DateTime date) async {
var entriesForDate = await getEntriesForDate(date);
for (var entry in entriesForDate) {
_foodLookupDatabase[entry.name] = entry.kcalPerMass;
log("Added entry: ${entry.name}/${entry.kcalPerMass}");
}
}
void addFoodEntryToLookupDatabase(FoodEntryState entry) {
_foodLookupDatabase[entry.name] = entry.kcalPerMass;
log("Added entry: ${entry.name}/${entry.kcalPerMass}");
}
Map<String, int> get getFoodEntryLookupDatabase => _foodLookupDatabase;
}
extension SplitWithIgnore on String {
List<String> splitWithIgnore(String delimiter, {String? ignoreIn}) {
List<String> parts = [];
if (ignoreIn == null) {
return split(delimiter);
}
int index = -1;
int indexCharAfterDelimiter = 0;
bool inIgnore = false;
for (var rune in runes) {
var char = String.fromCharCode(rune);
index += 1;
if (char == ignoreIn) {
inIgnore = !inIgnore;
continue;
}
if (inIgnore) {
continue;
}
if (char == delimiter) {
parts.add(substring(indexCharAfterDelimiter, index));
indexCharAfterDelimiter = index + 1;
}
if (index + 1 == length) {
parts.add(substring(indexCharAfterDelimiter, length));
}
}
return parts;
}
}