diff --git a/TODO.md b/TODO.md index 5a4f019..5c7f1de 100644 --- a/TODO.md +++ b/TODO.md @@ -48,4 +48,6 @@ - [R] d4rtCode is a text area with dart syntax highligthing - [R] At the botton, a button allows to test the edited Formula, launching a FormulaScreen - [ ] When _FormulaScreenState._evaluateFormula() detect an error, instead of show an SnackBar, show a ExpansionTile with "⚠️ There were an error. Show details..." with the details of the exception. The ExpansionTile will be invisible if there is no error. +- [R] When FormulaEditor._save formula, ensure formula is updated in the initial FormulaList +- [ ] Refresh FormulaList each time it gets focus, so formulas are updated from corpus - [ ] Investigate https://pub.dev/packages/quantity diff --git a/lib/ai/formula_editor.dart b/lib/ai/formula_editor.dart index ff7805e..159306d 100644 --- a/lib/ai/formula_editor.dart +++ b/lib/ai/formula_editor.dart @@ -5,6 +5,8 @@ import 'package:flutter_markdown_plus/flutter_markdown_plus.dart'; import 'package:markdown/markdown.dart' as markdown; import '../formula_models.dart'; import '../corpus.dart'; +import '../database/database_service.dart'; +import '../service_locator.dart'; import 'formula_screen.dart'; import 'unit_dropdown.dart'; @@ -13,11 +15,13 @@ import 'unit_dropdown.dart'; class FormulaEditor extends StatefulWidget { final Formula formula; final Corpus corpus; + final Function(Formula)? onSave; // Callback when formula is saved const FormulaEditor({ super.key, required this.formula, required this.corpus, + this.onSave, }); @override @@ -181,7 +185,7 @@ class _FormulaEditorState extends State { } } - void _saveFormula() { + Future _saveFormula() async { if (!_validateFormula()) { return; } @@ -189,14 +193,34 @@ class _FormulaEditorState extends State { final formula = _buildFormula(); if (formula == null) return; - // For now, just show a success message - // In a real implementation, this would save to database - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Formula "${formula.name}" saved successfully!'), - backgroundColor: Theme.of(context).colorScheme.primary, - ), - ); + try { + final database = getDatabase(); + + // Update corpus in memory + widget.corpus.updateFormula(formula); + + // Update database + final updated = await database.updateFormula(formula); + + if (!updated) { + // If formula wasn't found (e.g., name changed), add it as new + await database.addFormula(formula); + } + + // Call the onSave callback if provided + widget.onSave?.call(formula); + + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Formula "${formula.name}" saved successfully!'), + backgroundColor: Theme.of(context).colorScheme.primary, + ), + ); + } catch (e, stack) { + print('Error saving formula: $e\n$stack'); + _showErrorDialog('Error saving formula: $e'); + } } void _showErrorDialog(String message) { diff --git a/lib/ai/formula_list.dart b/lib/ai/formula_list.dart index a9f6675..9689384 100644 --- a/lib/ai/formula_list.dart +++ b/lib/ai/formula_list.dart @@ -9,12 +9,10 @@ import 'package:share_plus/share_plus.dart'; class FormulaList extends StatefulWidget { final Corpus corpus; - final List formulas; const FormulaList({ super.key, required this.corpus, - required this.formulas, }); @override @@ -44,9 +42,9 @@ class _FormulaListState extends State { } List get _filteredFormulas { - if (_searchQuery.isEmpty) return widget.formulas; + if (_searchQuery.isEmpty) return widget.corpus.getFormulas(); - return widget.formulas.where((formula) { + return widget.corpus.getFormulas().where((formula) { final nameMatch = formula.name.toLowerCase().contains(_searchQuery); final tagMatch = formula.tags.any((tag) => tag.toLowerCase().contains(_searchQuery)); return nameMatch || tagMatch; @@ -119,14 +117,14 @@ class _FormulaListState extends State { context: context, builder: (BuildContext context) { return AlertDialog( - title: Text('Error'), + title: const Text('Error'), content: Text(message), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, - child: Text('OK'), + child: const Text('OK'), ), ], ); diff --git a/lib/ai/formula_screen.dart b/lib/ai/formula_screen.dart index dc1089f..56758b6 100644 --- a/lib/ai/formula_screen.dart +++ b/lib/ai/formula_screen.dart @@ -214,6 +214,12 @@ class _FormulaScreenState extends State { builder: (context) => FormulaEditor( formula: widget.formula, corpus: widget.corpus, + onSave: (updatedFormula) { + // Refresh the screen after saving + setState(() { + // The corpus has been updated, refresh the displayed formula + }); + }, ), ), ); diff --git a/lib/corpus.dart b/lib/corpus.dart index b2f7cf2..e04ee88 100644 --- a/lib/corpus.dart +++ b/lib/corpus.dart @@ -63,6 +63,27 @@ class Corpus{ return _allFormulas.get(name); } + /// Updates a formula in the corpus + void updateFormula(Formula formula) { + if (!_allFormulas.containsKey(formula.name)) { + throw ArgumentError("Formula not found: ${formula.name}"); + } + + // Remove old tags + final oldFormula = _allFormulas[formula.name]!; + for (final tag in oldFormula.tags) { + _tags[tag]?.removeWhere((f) => f.name == formula.name); + } + + // Update the formula + _allFormulas[formula.name] = formula; + + // Add new tags + for (final tag in formula.tags) { + _tags[tag]?.add(formula); + } + } + final Multimap _baseToUnits = Multimap.create(); final Map _allUnits = {}; diff --git a/lib/database/database_service.dart b/lib/database/database_service.dart index 06dc034..4ccf2de 100644 --- a/lib/database/database_service.dart +++ b/lib/database/database_service.dart @@ -35,4 +35,59 @@ extension CorpusDatabaseExtension on FormulasDatabase { await insertFormulaElement(element.toStringLiteral()); } } + + // Method to update a formula in the database by name + Future updateFormula(models.Formula formula) async { + final elements = await getAllFormulaElements(); + + for (final element in elements) { + try { + final parsed = models.parseCorpusElements('[${element.elementText}]'); + if (parsed.isNotEmpty && parsed.first is models.Formula) { + final existingFormula = parsed.first as models.Formula; + if (existingFormula.name == formula.name) { + // Update this element + await updateFormulaElement( + element.id, + formula.toStringLiteral() + ); + return true; + } + } + } catch (e) { + print('Error parsing database element during update: $e'); + continue; + } + } + + return false; // Formula not found + } + + // Method to add a new formula to the database + Future addFormula(models.Formula formula) async { + await insertFormulaElement(formula.toStringLiteral()); + } + + // Method to delete a formula from the database by name + Future deleteFormula(String formulaName) async { + final elements = await getAllFormulaElements(); + + for (final element in elements) { + try { + final parsed = models.parseCorpusElements('[${element.elementText}]'); + if (parsed.isNotEmpty && parsed.first is models.Formula) { + final existingFormula = parsed.first as models.Formula; + if (existingFormula.name == formulaName) { + await deleteFormulaElement(element.id); + return true; + } + } + } catch (e) { + print('Error parsing database element during delete: $e'); + continue; + } + } + + return false; // Formula not found + } } diff --git a/lib/main.dart b/lib/main.dart index a1e6684..3fc772f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -62,8 +62,7 @@ class _CorpusLoaderState extends State { return Scaffold( appBar: AppBar(title: const Text('Formulas')), body: FormulaList( - corpus: corpus, - formulas: snapshot.data!.getFormulas(), + corpus: snapshot.data!, ), ); }