Merge branch 'feature/save-edited-formulas'

This commit is contained in:
Álvaro González 2026-03-06 08:29:00 +01:00
commit 0d20542825
7 changed files with 122 additions and 17 deletions

View file

@ -48,4 +48,6 @@
- [R] d4rtCode is a text area with dart syntax highligthing - [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 - [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. - [ ] 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 - [ ] Investigate https://pub.dev/packages/quantity

View file

@ -5,6 +5,8 @@ import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
import 'package:markdown/markdown.dart' as markdown; import 'package:markdown/markdown.dart' as markdown;
import '../formula_models.dart'; import '../formula_models.dart';
import '../corpus.dart'; import '../corpus.dart';
import '../database/database_service.dart';
import '../service_locator.dart';
import 'formula_screen.dart'; import 'formula_screen.dart';
import 'unit_dropdown.dart'; import 'unit_dropdown.dart';
@ -13,11 +15,13 @@ import 'unit_dropdown.dart';
class FormulaEditor extends StatefulWidget { class FormulaEditor extends StatefulWidget {
final Formula formula; final Formula formula;
final Corpus corpus; final Corpus corpus;
final Function(Formula)? onSave; // Callback when formula is saved
const FormulaEditor({ const FormulaEditor({
super.key, super.key,
required this.formula, required this.formula,
required this.corpus, required this.corpus,
this.onSave,
}); });
@override @override
@ -181,7 +185,7 @@ class _FormulaEditorState extends State<FormulaEditor> {
} }
} }
void _saveFormula() { Future<void> _saveFormula() async {
if (!_validateFormula()) { if (!_validateFormula()) {
return; return;
} }
@ -189,14 +193,34 @@ class _FormulaEditorState extends State<FormulaEditor> {
final formula = _buildFormula(); final formula = _buildFormula();
if (formula == null) return; if (formula == null) return;
// For now, just show a success message try {
// In a real implementation, this would save to database 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( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Formula "${formula.name}" saved successfully!'), content: Text('Formula "${formula.name}" saved successfully!'),
backgroundColor: Theme.of(context).colorScheme.primary, 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) { void _showErrorDialog(String message) {

View file

@ -9,12 +9,10 @@ import 'package:share_plus/share_plus.dart';
class FormulaList extends StatefulWidget { class FormulaList extends StatefulWidget {
final Corpus corpus; final Corpus corpus;
final List<Formula> formulas;
const FormulaList({ const FormulaList({
super.key, super.key,
required this.corpus, required this.corpus,
required this.formulas,
}); });
@override @override
@ -44,9 +42,9 @@ class _FormulaListState extends State<FormulaList> {
} }
List<Formula> get _filteredFormulas { List<Formula> 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 nameMatch = formula.name.toLowerCase().contains(_searchQuery);
final tagMatch = formula.tags.any((tag) => tag.toLowerCase().contains(_searchQuery)); final tagMatch = formula.tags.any((tag) => tag.toLowerCase().contains(_searchQuery));
return nameMatch || tagMatch; return nameMatch || tagMatch;
@ -119,14 +117,14 @@ class _FormulaListState extends State<FormulaList> {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: Text('Error'), title: const Text('Error'),
content: Text(message), content: Text(message),
actions: <Widget>[ actions: <Widget>[
TextButton( TextButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Text('OK'), child: const Text('OK'),
), ),
], ],
); );

View file

@ -214,6 +214,12 @@ class _FormulaScreenState extends State<FormulaScreen> {
builder: (context) => FormulaEditor( builder: (context) => FormulaEditor(
formula: widget.formula, formula: widget.formula,
corpus: widget.corpus, corpus: widget.corpus,
onSave: (updatedFormula) {
// Refresh the screen after saving
setState(() {
// The corpus has been updated, refresh the displayed formula
});
},
), ),
), ),
); );

View file

@ -63,6 +63,27 @@ class Corpus{
return _allFormulas.get(name); 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<String, String> _baseToUnits = Multimap.create(); final Multimap<String, String> _baseToUnits = Multimap.create();
final Map<String, UnitSpec> _allUnits = {}; final Map<String, UnitSpec> _allUnits = {};

View file

@ -35,4 +35,59 @@ extension CorpusDatabaseExtension on FormulasDatabase {
await insertFormulaElement(element.toStringLiteral()); await insertFormulaElement(element.toStringLiteral());
} }
} }
// Method to update a formula in the database by name
Future<bool> 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<void> addFormula(models.Formula formula) async {
await insertFormulaElement(formula.toStringLiteral());
}
// Method to delete a formula from the database by name
Future<bool> 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
}
} }

View file

@ -62,8 +62,7 @@ class _CorpusLoaderState extends State<CorpusLoader> {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Formulas')), appBar: AppBar(title: const Text('Formulas')),
body: FormulaList( body: FormulaList(
corpus: corpus, corpus: snapshot.data!,
formulas: snapshot.data!.getFormulas(),
), ),
); );
} }