diff --git a/client/pom.xml b/client/pom.xml
index b9eb186..753a004 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -128,6 +128,11 @@
logback-classic
1.5.20
+
+ org.slf4j
+ slf4j-api
+ 2.0.17
+
diff --git a/client/src/main/java/client/MyModule.java b/client/src/main/java/client/MyModule.java
index 661b2df..6cd6fb9 100644
--- a/client/src/main/java/client/MyModule.java
+++ b/client/src/main/java/client/MyModule.java
@@ -15,6 +15,7 @@
*/
package client;
+import client.model.ApplicationDataViewModel;
import client.scenes.FoodpalApplicationCtrl;
import client.scenes.SearchBarCtrl;
import client.scenes.nutrition.NutritionDetailsCtrl;
@@ -58,5 +59,6 @@ public class MyModule implements Module {
binder.bind(new TypeLiteral>() {}).toInstance(
new WebSocketDataService<>()
);
+ binder.bind(ApplicationDataViewModel.class).in(Scopes.SINGLETON);
}
}
\ No newline at end of file
diff --git a/client/src/main/java/client/model/ApplicationDataViewModel.java b/client/src/main/java/client/model/ApplicationDataViewModel.java
new file mode 100644
index 0000000..0fd0ceb
--- /dev/null
+++ b/client/src/main/java/client/model/ApplicationDataViewModel.java
@@ -0,0 +1,41 @@
+package client.model;
+
+import commons.Recipe;
+import javafx.beans.property.ListProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleListProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.ObservableList;
+
+import java.util.List;
+import java.util.Optional;
+
+public class ApplicationDataViewModel {
+ private final ListProperty recipes = new SimpleListProperty<>();
+ private final ListProperty ingredients = new SimpleListProperty<>();
+ public ApplicationDataViewModel() {}
+ public ApplicationDataViewModel(ObservableList recipes, ObservableList ingredients) {
+ this.recipes.set(recipes);
+ this.ingredients.set(ingredients);
+ }
+ public Optional getRecipeViewById(Long id) {
+ return recipes.get().stream().filter(r -> r.getId().equals(id)).findFirst();
+ }
+ public Optional getIngredientViewById(Long id) {
+ return ingredients.get().stream().filter(i -> i.idProperty().get() == id).findFirst();
+ }
+
+ public ObservableList getRecipes() {
+ return recipes.get();
+ }
+
+ public ObservableList getIngredients() {
+ return ingredients.get();
+ }
+ public void setRecipes(List recipes) {
+ this.recipes.setAll(recipes);
+ }
+ public void setIngredients(List ingredients) {
+ this.ingredients.setAll(ingredients);
+ }
+}
diff --git a/client/src/main/java/client/model/BaseViewModel.java b/client/src/main/java/client/model/BaseViewModel.java
new file mode 100644
index 0000000..0eda2b8
--- /dev/null
+++ b/client/src/main/java/client/model/BaseViewModel.java
@@ -0,0 +1,6 @@
+package client.model;
+
+public abstract class BaseViewModel {
+ public abstract void updateFrom(T item);
+ public abstract T into();
+}
diff --git a/client/src/main/java/client/model/FormalIngredientViewModel.java b/client/src/main/java/client/model/FormalIngredientViewModel.java
new file mode 100644
index 0000000..f012655
--- /dev/null
+++ b/client/src/main/java/client/model/FormalIngredientViewModel.java
@@ -0,0 +1,66 @@
+package client.model;
+
+import commons.FormalIngredient;
+import commons.RecipeIngredient;
+import javafx.beans.binding.Bindings;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.LongProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleLongProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+public class FormalIngredientViewModel extends RecipeIngredientViewModel {
+ private final LongProperty id = new SimpleLongProperty();
+ private final ObjectProperty ingredientView = new SimpleObjectProperty<>();
+ private final DoubleProperty amount = new SimpleDoubleProperty();
+ private final StringProperty unitSuffix = new SimpleStringProperty();
+ private final DoubleProperty kcal = new SimpleDoubleProperty();
+ public FormalIngredientViewModel(FormalIngredient ingredient) {
+ updateFrom(ingredient);
+ kcal.bind(Bindings.createDoubleBinding(() -> {
+ System.out.println(ingredientView.get().getKcal());
+ System.out.println("evaluating kcal: " + ingredientView.get().getKcal() * amount.get());
+ return ingredientView.get().getKcal() * amount.get();
+ }));
+ }
+ @Override
+ public void updateFrom(FormalIngredient item) {
+ id.set(item.getId());
+ ingredientView.set(new IngredientViewModel(item.getIngredient()));
+ amount.set(item.getAmount());
+ unitSuffix.set(item.getUnitSuffix());
+ }
+
+ @Override
+ public FormalIngredient into() {
+ return new FormalIngredient();
+ }
+
+ @Override
+ public RecipeIngredient getReal() {
+ return null;
+ }
+
+ @Override
+ public RecipeIngredient getScaled() {
+ return null;
+ }
+
+ @Override
+ public void setReal(RecipeIngredient real) {
+
+ }
+
+ @Override
+ public void setScale(Number scale) {
+
+ }
+
+ @Override
+ public DoubleProperty kcalProperty() {
+ return kcal;
+ }
+}
diff --git a/client/src/main/java/client/Ingredient/IngredientViewModel.java b/client/src/main/java/client/model/IngredientViewModel.java
similarity index 93%
rename from client/src/main/java/client/Ingredient/IngredientViewModel.java
rename to client/src/main/java/client/model/IngredientViewModel.java
index 799c69f..6f87311 100644
--- a/client/src/main/java/client/Ingredient/IngredientViewModel.java
+++ b/client/src/main/java/client/model/IngredientViewModel.java
@@ -1,4 +1,4 @@
-package client.Ingredient;
+package client.model;
import commons.Ingredient;
import javafx.beans.binding.Bindings;
@@ -12,7 +12,7 @@ import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
-public class IngredientViewModel {
+public class IngredientViewModel extends BaseViewModel {
private final LongProperty id = new SimpleLongProperty();
private final StringProperty name = new SimpleStringProperty();
private final DoubleProperty protein = new SimpleDoubleProperty();
@@ -22,7 +22,7 @@ public class IngredientViewModel {
private final ReadOnlyDoubleWrapper kcal = new ReadOnlyDoubleWrapper();
public IngredientViewModel() {
DoubleBinding computeKcal = Bindings.createDoubleBinding(
- () -> toIngredient().kcalPer100g(),
+ () -> into().kcalPer100g(),
protein, carbs, fat
);
@@ -61,7 +61,7 @@ public class IngredientViewModel {
return carbs;
}
- public Ingredient toIngredient() {
+ public Ingredient into() {
Ingredient i = new Ingredient();
i.setId(id.get());
i.setName(name.get());
diff --git a/client/src/main/java/client/model/RecipeIngredientViewModel.java b/client/src/main/java/client/model/RecipeIngredientViewModel.java
new file mode 100644
index 0000000..c1f7f64
--- /dev/null
+++ b/client/src/main/java/client/model/RecipeIngredientViewModel.java
@@ -0,0 +1,9 @@
+package client.model;
+
+import commons.RecipeIngredient;
+import javafx.beans.property.DoubleProperty;
+
+public abstract class RecipeIngredientViewModel
+ extends BaseViewModel implements ScaleAware {
+ public abstract DoubleProperty kcalProperty();
+}
diff --git a/client/src/main/java/client/model/RecipeViewModel.java b/client/src/main/java/client/model/RecipeViewModel.java
new file mode 100644
index 0000000..6640c47
--- /dev/null
+++ b/client/src/main/java/client/model/RecipeViewModel.java
@@ -0,0 +1,112 @@
+package client.model;
+
+import commons.FormalIngredient;
+import commons.Recipe;
+import commons.RecipeIngredient;
+import commons.VagueIngredient;
+import javafx.beans.Observable;
+import javafx.beans.binding.Bindings;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ListProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleListProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+import java.util.List;
+
+public class RecipeViewModel extends BaseViewModel implements ScaleAware {
+ // Immutable
+ private final Long id;
+
+ // Mutable values
+ private final StringProperty name = new SimpleStringProperty();
+ private final StringProperty locale = new SimpleStringProperty();
+ private final ListProperty> ingredients = new SimpleListProperty<>(FXCollections.observableArrayList(
+ item -> new Observable[] { item.kcalProperty() }
+ ));
+ private final ObjectProperty> steps = new SimpleObjectProperty<>();
+ private final DoubleProperty kcal = new SimpleDoubleProperty();
+ private final DoubleProperty scale = new SimpleDoubleProperty();
+ public RecipeViewModel(Recipe recipe) {
+ this.id = recipe.getId();
+ this.kcal.bind(Bindings.createDoubleBinding(() -> {
+ List> list = ingredients.get();
+ System.out.println(list);
+ if (list == null || list.isEmpty()) {
+ return 0.0;
+ }
+ System.out.println("Compiled kcal double binding");
+ return list.stream().mapToDouble(i -> i.kcalProperty().get()).sum();
+ }, ingredients));
+ updateFrom(recipe);
+ }
+ @Override
+ public void updateFrom(Recipe item) {
+ name.setValue(item.getName());
+ locale.setValue(item.getLocale());
+ List> list = item.getIngredients().stream().>map(recipeIngredient -> switch (recipeIngredient) {
+ case FormalIngredient f -> new FormalIngredientViewModel(f);
+ case VagueIngredient v -> new VagueIngredientViewModel(v);
+ default -> throw new IllegalStateException("Unexpected value: " + recipeIngredient);
+ }).toList();
+ ingredients.setAll(list);
+ steps.setValue(FXCollections.observableList(item.getPreparationSteps()));
+ }
+
+ @Override
+ public Recipe into() {
+ return new Recipe(
+ id, name.getValue(), locale.getValue(),
+ ingredients.stream().map(i -> (RecipeIngredient) i.into()).toList(),
+ steps.getValue());
+ }
+
+ @Override
+ public Recipe getReal() {
+ return into();
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ @Override
+ public Recipe getScaled() {
+ return into().scaleBy(scale.get());
+ }
+
+ @Override
+ public void setReal(Recipe real) {
+ updateFrom(real);
+ }
+
+ @Override
+ public void setScale(Number scale) {
+ this.scale.setValue(scale);
+ }
+
+ public StringProperty nameProperty() {
+ return name;
+ }
+
+ public StringProperty localeProperty() {
+ return locale;
+ }
+ public ListProperty> ingredientsProperty() {
+ return ingredients;
+ }
+ public ObjectProperty> stepsProperty() {
+ return steps;
+ }
+ public DoubleProperty kcalProperty() {
+ return kcal;
+ }
+ public DoubleProperty scaleProperty() {
+ return scale;
+ }
+}
diff --git a/client/src/main/java/client/model/ScaleAware.java b/client/src/main/java/client/model/ScaleAware.java
new file mode 100644
index 0000000..c5e7bf9
--- /dev/null
+++ b/client/src/main/java/client/model/ScaleAware.java
@@ -0,0 +1,12 @@
+package client.model;
+
+/**
+ * An interface describing a value that has a real and scaled counterpart.
+ * @param
+ */
+public interface ScaleAware {
+ T getReal();
+ T getScaled();
+ void setReal(T real);
+ void setScale(Number scale);
+}
diff --git a/client/src/main/java/client/model/VagueIngredientViewModel.java b/client/src/main/java/client/model/VagueIngredientViewModel.java
new file mode 100644
index 0000000..98f24c3
--- /dev/null
+++ b/client/src/main/java/client/model/VagueIngredientViewModel.java
@@ -0,0 +1,47 @@
+package client.model;
+
+import commons.RecipeIngredient;
+import commons.VagueIngredient;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+public class VagueIngredientViewModel extends RecipeIngredientViewModel {
+ private final SimpleObjectProperty vagueIngredient = new SimpleObjectProperty<>();
+ public VagueIngredientViewModel(VagueIngredient ingredient) {
+ this.vagueIngredient.set(ingredient);
+ }
+ @Override
+ public void updateFrom(VagueIngredient item) {
+
+ }
+
+ @Override
+ public VagueIngredient into() {
+ return vagueIngredient.get();
+ }
+
+ @Override
+ public DoubleProperty kcalProperty() {
+ return null;
+ }
+
+ @Override
+ public RecipeIngredient getReal() {
+ return null;
+ }
+
+ @Override
+ public RecipeIngredient getScaled() {
+ return null;
+ }
+
+ @Override
+ public void setReal(RecipeIngredient real) {
+
+ }
+
+ @Override
+ public void setScale(Number scale) {
+
+ }
+}
diff --git a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java
index d276137..9abcf9f 100644
--- a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java
+++ b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java
@@ -10,6 +10,8 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
import client.exception.InvalidModificationException;
+import client.model.ApplicationDataViewModel;
+import client.model.RecipeViewModel;
import client.scenes.nutrition.NutritionViewCtrl;
import client.scenes.recipe.RecipeDetailCtrl;
@@ -51,7 +53,7 @@ public class FoodpalApplicationCtrl implements LocaleAware {
private final LocaleManager localeManager;
private final WebSocketDataService dataService;
private final Logger logger = Logger.getLogger(FoodpalApplicationCtrl.class.getName());
-
+ private final ApplicationDataViewModel applicationDataViewModel;
@FXML
private RecipeDetailCtrl recipeDetailController;
@@ -98,7 +100,8 @@ public class FoodpalApplicationCtrl implements LocaleAware {
WebSocketUtils webSocketUtils,
LocaleManager localeManager,
ConfigService configService,
- WebSocketDataService recipeDataService
+ WebSocketDataService recipeDataService,
+ ApplicationDataViewModel applicationDataViewModel
) {
this.server = server;
this.webSocketUtils = webSocketUtils;
@@ -106,6 +109,7 @@ public class FoodpalApplicationCtrl implements LocaleAware {
this.configService = configService;
this.dataService = recipeDataService;
+ this.applicationDataViewModel = applicationDataViewModel;
setupDataService();
logger.info("WebSocket processor initialized.");
@@ -290,6 +294,7 @@ public class FoodpalApplicationCtrl implements LocaleAware {
searchBarController.getFilter(),
this.configService.getConfig().getRecipeLanguages()
);
+ applicationDataViewModel.setRecipes(recipes.stream().map(RecipeViewModel::new).toList());
} catch (IOException | InterruptedException e) {
recipes = Collections.emptyList();
String msg = "Failed to load recipes: " + e.getMessage();
diff --git a/client/src/main/java/client/scenes/nutrition/NutritionDetailsCtrl.java b/client/src/main/java/client/scenes/nutrition/NutritionDetailsCtrl.java
index 25b7bfb..28afbdb 100644
--- a/client/src/main/java/client/scenes/nutrition/NutritionDetailsCtrl.java
+++ b/client/src/main/java/client/scenes/nutrition/NutritionDetailsCtrl.java
@@ -1,6 +1,7 @@
package client.scenes.nutrition;
-import client.Ingredient.IngredientViewModel;
+import client.model.ApplicationDataViewModel;
+import client.model.IngredientViewModel;
import client.exception.IllegalInputFormatException;
import client.utils.LocaleAware;
import client.utils.LocaleManager;
@@ -32,6 +33,7 @@ public class NutritionDetailsCtrl implements LocaleAware {
private final ServerUtils server;
private final SimpleObjectProperty ingredient =
new SimpleObjectProperty<>(new IngredientViewModel());
+ private ApplicationDataViewModel applicationDataViewModel;
private final Logger logger = Logger.getLogger(this.getClass().getName());
public Label ingredientName;
@@ -51,10 +53,12 @@ public class NutritionDetailsCtrl implements LocaleAware {
@Inject
public NutritionDetailsCtrl(
LocaleManager manager,
- ServerUtils server
+ ServerUtils server,
+ ApplicationDataViewModel applicationDataViewModel
) {
this.manager = manager;
this.server = server;
+ this.applicationDataViewModel = applicationDataViewModel;
}
@Override
@@ -77,6 +81,9 @@ public class NutritionDetailsCtrl implements LocaleAware {
Ingredient newIngredient = server.updateIngredient(updateIngredient());
Platform.runLater(() -> {
this.ingredient.get().updateFrom(newIngredient);
+ applicationDataViewModel.getIngredientViewById(newIngredient.getId())
+ .orElseThrow(() -> new RuntimeException("error!"))
+ .updateFrom(newIngredient);
logger.info("Updated ingredient to " + newIngredient);
});
} catch (IllegalInputFormatException e) {
@@ -106,7 +113,7 @@ public class NutritionDetailsCtrl implements LocaleAware {
setVisible(true);
}
private Ingredient updateIngredient() throws IllegalInputFormatException {
- Ingredient current = this.ingredient.get().toIngredient();
+ Ingredient current = this.ingredient.get().into();
try {
double f = Double.parseDouble(this.fatInputElement.getText());
double p = Double.parseDouble(this.proteinInputElement.getText());
diff --git a/client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java b/client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java
index 608ac44..ec1cb58 100644
--- a/client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java
+++ b/client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java
@@ -1,6 +1,8 @@
package client.scenes.recipe;
import client.exception.UpdateException;
+import client.model.ApplicationDataViewModel;
+import client.model.RecipeViewModel;
import client.scenes.FoodpalApplicationCtrl;
import client.utils.Config;
import client.utils.ConfigService;
@@ -18,6 +20,8 @@ import java.nio.file.Path;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+
+import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
@@ -32,8 +36,8 @@ import javafx.scene.text.Font;
import javafx.stage.DirectoryChooser;
/**
- * Controller for the recipe detail view.
- * Manages displaying and editing recipe details such as name, ingredients, and
+ * Controller for the recipeViewModel detail view.
+ * Manages displaying and editing recipeViewModel details such as name, ingredients, and
* steps.
*/
public class RecipeDetailCtrl implements LocaleAware {
@@ -42,6 +46,8 @@ public class RecipeDetailCtrl implements LocaleAware {
private final FoodpalApplicationCtrl appCtrl;
private final ConfigService configService;
private final WebSocketDataService webSocketDataService;
+ private final ApplicationDataViewModel applicationDataViewModel;
+ public Label kcalLabel;
@FXML
private IngredientListCtrl ingredientListController;
@@ -49,7 +55,7 @@ public class RecipeDetailCtrl implements LocaleAware {
@FXML
private RecipeStepListCtrl stepListController;
- private Recipe recipe;
+ private SimpleObjectProperty recipeViewModel = new SimpleObjectProperty<>();
@Inject
public RecipeDetailCtrl(
@@ -57,12 +63,14 @@ public class RecipeDetailCtrl implements LocaleAware {
ServerUtils server,
FoodpalApplicationCtrl appCtrl,
ConfigService configService,
- WebSocketDataService webSocketDataService) {
+ WebSocketDataService webSocketDataService,
+ ApplicationDataViewModel applicationDataViewModel) {
this.localeManager = localeManager;
this.server = server;
this.appCtrl = appCtrl;
this.configService = configService;
this.webSocketDataService = webSocketDataService;
+ this.applicationDataViewModel = applicationDataViewModel;
}
@FXML
@@ -92,9 +100,9 @@ public class RecipeDetailCtrl implements LocaleAware {
/**
* Convenience method for a frequently needed operation to retrieve the
- * currently selected recipe.
+ * currently selected recipeViewModel.
*
- * @return The currently selected recipe in the parent recipe list.
+ * @return The currently selected recipeViewModel in the parent recipeViewModel list.
*/
private Optional getSelectedRecipe() {
return Optional.ofNullable(
@@ -112,19 +120,19 @@ public class RecipeDetailCtrl implements LocaleAware {
}
/**
- * Refreshes the recipe list from the server.
+ * Refreshes the recipeViewModel list from the server.
*
- * @throws IOException Upon invalid recipe response.
+ * @throws IOException Upon invalid recipeViewModel response.
* @throws InterruptedException Upon request interruption.
*
* @see FoodpalApplicationCtrl#refresh()
*/
- private void refresh() throws IOException, InterruptedException {
+ public void refresh() throws IOException, InterruptedException {
this.appCtrl.refresh();
}
/**
- * Toggles the visibility of the recipe details screen.
+ * Toggles the visibility of the recipeViewModel details screen.
*
* @param visible Whether the details screen should be visible.
*/
@@ -133,17 +141,16 @@ public class RecipeDetailCtrl implements LocaleAware {
}
/**
- * Sets the currently viewed recipe in the detail view.
+ * Sets the currently viewed recipeViewModel in the detail view.
*
- * @param recipe The recipe to view.
+ * @param recipe The recipeViewModel to view.
*/
public void setCurrentlyViewedRecipe(Recipe recipe) {
if (recipe == null) {
return;
}
- this.recipe = recipe;
-
+ this.recipeViewModel.set(new RecipeViewModel(recipe));
this.showName(recipe.getName());
this.ingredientListController.refetchFromRecipe(recipe);
this.stepListController.refetchFromRecipe(recipe);
@@ -152,11 +159,11 @@ public class RecipeDetailCtrl implements LocaleAware {
}
/**
- * Create a callback that takes in the selected recipe and a helper type
- * and updates the recipe based on that.
+ * Create a callback that takes in the selected recipeViewModel and a helper type
+ * and updates the recipeViewModel based on that.
* Also, posts the update to the server.
*
- * @param recipeConsumer The helper function to use when updating the recipe -
+ * @param recipeConsumer The helper function to use when updating the recipeViewModel -
* how to update it
* @return The created callback to use in a setUpdateCallback or related
* function
@@ -165,7 +172,7 @@ public class RecipeDetailCtrl implements LocaleAware {
return recipes -> {
Recipe selectedRecipe = this.getSelectedRecipe().orElseThrow(
() -> new NullPointerException(
- "Null recipe whereas ingredients are edited"));
+ "Null recipeViewModel whereas ingredients are edited"));
recipeConsumer.accept(selectedRecipe, recipes);
@@ -174,9 +181,10 @@ public class RecipeDetailCtrl implements LocaleAware {
webSocketDataService.add(selectedRecipe.getId(), recipe -> {
int idx = getParentRecipeList().getItems().indexOf(selectedRecipe);
getParentRecipeList().getItems().set(idx, recipe);
+ recipeViewModel.get().updateFrom(recipe);
});
} catch (IOException | InterruptedException e) {
- throw new UpdateException("Unable to update recipe to server for " +
+ throw new UpdateException("Unable to update recipeViewModel to server for " +
selectedRecipe);
}
};
@@ -190,15 +198,16 @@ public class RecipeDetailCtrl implements LocaleAware {
// Initialize callback for ingredient list updates
this.ingredientListController.setUpdateCallback(
- this.createUpdateRecipeCallback(Recipe::setIngredients));
+ this.createUpdateRecipeCallback(Recipe::setIngredients)
+ );
// Ditto, for step list updates
this.stepListController.setUpdateCallback(
this.createUpdateRecipeCallback(Recipe::setPreparationSteps));
}
/**
- * Revised edit recipe control flow, deprecates the use of a separate
- * AddNameCtrl. This is automagically called when a new recipe is created,
+ * Revised edit recipeViewModel control flow, deprecates the use of a separate
+ * AddNameCtrl. This is automagically called when a new recipeViewModel is created,
* making for a more seamless UX.
* Public for reference by ApplicationCtrl.
*/
@@ -213,14 +222,15 @@ public class RecipeDetailCtrl implements LocaleAware {
}
String newName = edit.getText();
- this.recipe.setName(newName);
-
+ this.recipeViewModel.get().nameProperty().setValue(newName);
+ Recipe r = this.recipeViewModel.get().into();
+ System.out.println(r.toDetailedString());
try {
- server.updateRecipe(this.recipe);
+ server.updateRecipe(r);
// this.refresh();
} catch (IOException | InterruptedException e) {
// throw a nice blanket UpdateException
- throw new UpdateException("Error occurred when updating recipe name!");
+ throw new UpdateException("Error occurred when updating recipeViewModel name!");
}
this.showName(edit.getText());
@@ -231,7 +241,7 @@ public class RecipeDetailCtrl implements LocaleAware {
}
/**
- * Remove the selected recipe from the server and refresh the recipe list.
+ * Remove the selected recipeViewModel from the server and refresh the recipeViewModel list.
* Internally calls {@link FoodpalApplicationCtrl#removeSelectedRecipe()}.
*/
@FXML
@@ -241,17 +251,17 @@ public class RecipeDetailCtrl implements LocaleAware {
/**
* Refreshes the favourite button state based on whether the currently viewed
- * recipe is marked as a favourite in the application configuration.
+ * recipeViewModel is marked as a favourite in the application configuration.
*/
public void refreshFavouriteButton() {
- if (recipe == null) {
+ if (recipeViewModel == null || recipeViewModel.get() == null) {
favouriteButton.setDisable(true);
favouriteButton.setText("☆");
return;
}
favouriteButton.setDisable(false);
- favouriteButton.setText(this.getConfig().isFavourite(recipe.getId()) ? "★"
+ favouriteButton.setText(this.getConfig().isFavourite(recipeViewModel.get().getReal().getId()) ? "★"
: "☆");
}
@@ -262,8 +272,8 @@ public class RecipeDetailCtrl implements LocaleAware {
*/
@FXML
private void printRecipe() {
- if (recipe == null) {
- return; // Do nothing if no recipe selected
+ if (recipeViewModel == null) {
+ return; // Do nothing if no recipeViewModel selected
}
// Open directory chooser
@@ -278,9 +288,9 @@ public class RecipeDetailCtrl implements LocaleAware {
}
// Ask for filename
- TextInputDialog dialog = new TextInputDialog(recipe.getName() + ".txt");
+ TextInputDialog dialog = new TextInputDialog(recipeViewModel.get().getReal().getName() + ".txt");
dialog.setTitle("Save Recipe");
- dialog.setHeaderText("Enter filename for the recipe");
+ dialog.setHeaderText("Enter filename for the recipeViewModel");
dialog.setContentText("Filename:");
Optional result = dialog.showAndWait();
@@ -292,18 +302,18 @@ public class RecipeDetailCtrl implements LocaleAware {
}
// Use PrintExportService methods
- String recipeText = PrintExportService.buildRecipeText(recipe);
+ String recipeText = PrintExportService.buildRecipeText(recipeViewModel.get().getReal());
Path dirPath = selectedDirectory.toPath();
PrintExportService.exportToFile(recipeText, dirPath, filename);
}
}
/**
- * Toggles the favourite status of the currently viewed recipe in the
+ * Toggles the favourite status of the currently viewed recipeViewModel in the
* application configuration and writes the changes to disk.
*/
@FXML
private void toggleFavourite() {
- long id = this.recipe.getId();
+ long id = this.recipeViewModel.get().getReal().getId();
Config config = this.getConfig();
if (config.isFavourite(id)) {
@@ -337,16 +347,16 @@ public class RecipeDetailCtrl implements LocaleAware {
}
/**
- * Switch the recipe's language.
+ * Switch the recipeViewModel's language.
*/
@FXML
void changeLanguage() {
- recipe.setLocale(this.langSelector.getValue());
+ recipeViewModel.get().getReal().setLocale(this.langSelector.getValue());
try {
- server.updateRecipe(this.recipe);
+ server.updateRecipe(this.recipeViewModel.get().getReal());
} catch (IOException | InterruptedException e) {
- throw new UpdateException("Error occurred when updating recipe locale!");
+ throw new UpdateException("Error occurred when updating recipeViewModel locale!");
}
}
@@ -365,7 +375,11 @@ public class RecipeDetailCtrl implements LocaleAware {
@Override
public void initializeComponents() {
initStepsIngredientsList();
-
+ recipeViewModel.addListener((_, ov, nv) -> {
+ if (nv != null) {
+ kcalLabel.textProperty().bind(recipeViewModel.get().kcalProperty().asString("%.1f kcal"));
+ }
+ });
langSelector.getItems().addAll("en", "nl", "pl");
}
}
diff --git a/client/src/main/resources/client/scenes/recipe/RecipeDetailView.fxml b/client/src/main/resources/client/scenes/recipe/RecipeDetailView.fxml
index dd5f2ba..639e823 100644
--- a/client/src/main/resources/client/scenes/recipe/RecipeDetailView.fxml
+++ b/client/src/main/resources/client/scenes/recipe/RecipeDetailView.fxml
@@ -35,4 +35,5 @@
+
diff --git a/commons/src/main/java/commons/FormalIngredient.java b/commons/src/main/java/commons/FormalIngredient.java
index 88d713a..c05dc18 100644
--- a/commons/src/main/java/commons/FormalIngredient.java
+++ b/commons/src/main/java/commons/FormalIngredient.java
@@ -76,4 +76,9 @@ public class FormalIngredient extends RecipeIngredient implements Scalable {
// PRIMARY Key, unique id for recipe, generated automatically.
@Id
@@ -191,5 +191,16 @@ public class Recipe {
", preparationSteps=" + preparationSteps +
'}';
}
+ public double kcal() {
+ return this.ingredients.stream().mapToDouble(RecipeIngredient::kcal).sum();
+ }
+ @Override
+ public Recipe scaleBy(double factor) {
+ return new Recipe(id, name, locale, ingredients.stream().map(ri -> switch (ri) {
+ case FormalIngredient f -> f.scaleBy(factor);
+ case VagueIngredient v -> v;
+ default -> throw new IllegalArgumentException("Unexpected child class of RecipeIngredient!");
+ }).toList(), preparationSteps);
+ }
}
\ No newline at end of file
diff --git a/commons/src/main/java/commons/RecipeIngredient.java b/commons/src/main/java/commons/RecipeIngredient.java
index 33d9b75..1563a47 100644
--- a/commons/src/main/java/commons/RecipeIngredient.java
+++ b/commons/src/main/java/commons/RecipeIngredient.java
@@ -92,4 +92,5 @@ public abstract class RecipeIngredient {
public int hashCode() {
return Objects.hash(id, ingredient);
}
+ public abstract double kcal();
}
diff --git a/commons/src/main/java/commons/Scalable.java b/commons/src/main/java/commons/Scalable.java
index f2a3981..fc234ae 100644
--- a/commons/src/main/java/commons/Scalable.java
+++ b/commons/src/main/java/commons/Scalable.java
@@ -1,8 +1,8 @@
package commons;
-public interface Scalable {
- default IngredientType scaleBy(int numPortions) {
+public interface Scalable {
+ default T scaleBy(int numPortions) {
return scaleBy((double) numPortions);
};
- IngredientType scaleBy(double factor);
+ T scaleBy(double factor);
}
diff --git a/commons/src/main/java/commons/VagueIngredient.java b/commons/src/main/java/commons/VagueIngredient.java
index efa2fc3..9facfd0 100644
--- a/commons/src/main/java/commons/VagueIngredient.java
+++ b/commons/src/main/java/commons/VagueIngredient.java
@@ -50,4 +50,9 @@ public class VagueIngredient extends RecipeIngredient {
public int hashCode() {
return Objects.hashCode(description);
}
+
+ @Override
+ public double kcal() {
+ return 0;
+ }
}