import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:pizzaplanner/util.dart'; import 'package:pizzaplanner/entities/PizzaRecipe/ingredient.dart'; import 'package:pizzaplanner/entities/PizzaRecipe/recipe_step.dart'; import 'package:pizzaplanner/entities/PizzaRecipe/recipe_substep.dart'; import 'package:yaml/yaml.dart'; part 'pizza_recipe.g.dart'; @HiveType(typeId: 0) class PizzaRecipe extends HiveObject { static const String hiveName = "PizzaRecipes"; @HiveField(0) String name; @HiveField(1) String description; @HiveField(2) List ingredients; @HiveField(3) List recipeSteps; // Using this because deleting it from the box does weird things. // It seems to 'null' the item that you delete, and then reduce the size/length // cutting off the last item @HiveField(4) bool deleted = false; @HiveField(5) String? imgUrl; PizzaRecipe(this.name, this.description, this.ingredients, this.recipeSteps, {String? image}){ imgUrl = image; } String getShortDescriptionString(){ if (description.length < 150) { // TODO 150? return description; } final endOfLineIndex = description.indexOf(RegExp("[.]|\$")) + 1; if (endOfLineIndex >= 150){ final first150 = description.substring(0, 150); return "$first150..."; } return description.substring(0, endOfLineIndex); } Table getIngredientsTable(int pizzaCount, int doughBallSize) { return Table( border: TableBorder.all(), columnWidths: const { 0: FlexColumnWidth(2), 1: FlexColumnWidth(), 2: FlexColumnWidth(2), }, children: [ const TableRow( children: [ TableCell(child: Center(child: Text("Ingredient"))), TableCell(child: Center(child: Text("Per Ball"))), TableCell(child: Center(child: Text("Total"))), ] ) ] + ingredients.map((ingredient) => ingredient.getIngredientTableRow(pizzaCount, doughBallSize)) .toList() ); } int getStepsCompleted(){ var stepCount = 0; for (final recipeStep in recipeSteps) { if (!recipeStep.completed) { return stepCount; } stepCount++; } return stepCount; } static Future fromYamlAsset(String yamlPath) async{ final String yamlString = await loadAsset(yamlPath); return fromYaml(yamlString); } static Future fromYaml(String yamlString) async{ return fromParsedYaml(loadYaml(yamlString) as YamlMap); } static Future fromParsedYaml(YamlMap recipe) async { if (recipe.containsKey("recipe")){ recipe = recipe["recipe"] as YamlMap; } final String name = recipe["name"] as String; final String? image = recipe.containsKey("image") ? recipe["image"] as String : null; final String description = recipe["description"] as String; final YamlList ingredients = recipe["ingredients"] as YamlList; final List newIngredients = ingredients.map( (ingredient) => Ingredient( ingredient["name"] as String, ingredient["unit"] as String, ingredient["value"] as double )).toList(); final YamlList steps = recipe["steps"] as YamlList; final newRecipeSteps = List.generate(steps.length, (i) { final YamlMap step = steps[i] as YamlMap; final String stepName = step["name"] as String; final String stepDescription = step["description"] as String; String waitUnit = "none"; String waitDescription = ""; int waitMin = 0; int waitMax = 0; if (step.containsKey("wait")) { final YamlMap waitMap = step["wait"] as YamlMap; waitDescription = waitMap["description"] as String; waitUnit = waitMap["unit"] as String; waitMin = waitMap["min"] as int; waitMax = waitMap["max"] as int; } final YamlList subSteps = step.containsKey("substeps") ? step["substeps"] as YamlList : YamlList(); final newSubSteps = List.generate(subSteps.length, (j) { final subStep = subSteps[j]; return RecipeSubStep( subStep["name"] as String, subStep["description"] as String ); }); return RecipeStep( stepName, stepDescription, waitDescription, waitUnit, waitMin, waitMax, newSubSteps ); }); return PizzaRecipe( name, description, newIngredients, newRecipeSteps, image: image ); } String toYaml(){ final yaml = StringBuffer("recipe:\n"); // Name yaml.writeln(indent(1, 'name: "$name"')); // image url if (imgUrl != null){ yaml.writeln(indent(1, 'image: "$imgUrl"')); } // Description yaml.writeln(indent(1, 'description: >')); for (final line in description.split("\n")){ yaml.writeln(indent(2, line)); } // Ingredients yaml.writeln(indent(1, 'ingredients:')); for (final ingredient in ingredients){ yaml.writeln(indent(2, "- name: ${ingredient.name}")); yaml.writeln(indent(3, "unit: ${ingredient.unit}")); yaml.writeln(indent(3, "value: ${ingredient.value}")); } // Steps if (recipeSteps.isNotEmpty){ yaml.writeln(indent(1, "steps:")); } for (final recipeStep in recipeSteps) { yaml.writeln(indent(2, "- name: ${recipeStep.name}")); // Wait if (recipeStep.waitUnit != "none"){ yaml.writeln(indent(3, "wait:")); yaml.writeln(indent(4, "description: >")); for (final line in recipeStep.waitDescription.split("\n")) { yaml.writeln(indent(5, line)); } yaml.writeln(indent(4, "unit: ${recipeStep.waitUnit}")); yaml.writeln(indent(4, "min: ${recipeStep.waitMin}")); yaml.writeln(indent(4, "max: ${recipeStep.waitMax}")); } // description yaml.writeln(indent(3, "description: >")); for (final line in recipeStep.description.split("\n")) { yaml.writeln(indent(4, line)); } // Steps if (recipeStep.subSteps.isNotEmpty){ yaml.writeln(indent(3, "substeps:")); } for (final subStep in recipeStep.subSteps){ yaml.writeln(indent(4, "- name: ${subStep.name}")); yaml.writeln(indent(5, "description: >")); for (final line in subStep.description.split("\n")) { yaml.writeln(indent(6, line)); } } } return yaml.toString(); } String indent(int indent, String s){ return "${' '*indent*2}$s"; } Duration getMinDuration(){ return Duration(seconds: recipeSteps.map((recipeStep) => recipeStep.getWaitMinInSeconds()).reduce((a, b) => a+b)); } Duration getMaxDuration(){ return Duration(seconds: recipeSteps.map((recipeStep) => recipeStep.getWaitMaxInSeconds()).reduce((a, b) => a+b)); } Duration getCurrentDuration(){ return Duration(seconds: recipeSteps.map((recipeStep) => recipeStep.getCurrentWaitInSeconds()).reduce((a, b) => a+b)); } @override String toString() { return "PizzaRecipe($name, ${ingredients.length}, ${recipeSteps.length})"; } Table getStepTimeTable(DateTime startTime) { final List stepRows = []; DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(startTime.millisecondsSinceEpoch); for (final recipeStep in recipeSteps.reversed) { final Duration stepWaitDuration = Duration(seconds: recipeStep.getCurrentWaitInSeconds()); stepRows.add( TableRow( children: [ TableCell(child: Center(child: Text(recipeStep.name))), TableCell(child: Center(child: Text(getDateFormat().format(dateTime.subtract(stepWaitDuration))))) ] ) ); dateTime = dateTime.subtract(stepWaitDuration); } return Table( columnWidths: const { 0: FlexColumnWidth(), 1: FlexColumnWidth(), }, children: [ const TableRow( children: [ TableCell(child: Center(child: Text("Step"))), TableCell(child: Center(child: Text("When"))), ] ) ] + stepRows.reversed.toList() ); } }