chore: etc
This commit is contained in:
parent
ecccebe7c5
commit
895aecba3d
19 changed files with 404 additions and 55 deletions
|
|
@ -128,6 +128,11 @@
|
|||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.5.20</version> <!-- or latest compatible version -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.17</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
|||
|
|
@ -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<WebSocketDataService<Long, Ingredient>>() {}).toInstance(
|
||||
new WebSocketDataService<>()
|
||||
);
|
||||
binder.bind(ApplicationDataViewModel.class).in(Scopes.SINGLETON);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<RecipeViewModel> recipes = new SimpleListProperty<>();
|
||||
private final ListProperty<IngredientViewModel> ingredients = new SimpleListProperty<>();
|
||||
public ApplicationDataViewModel() {}
|
||||
public ApplicationDataViewModel(ObservableList<RecipeViewModel> recipes, ObservableList<IngredientViewModel> ingredients) {
|
||||
this.recipes.set(recipes);
|
||||
this.ingredients.set(ingredients);
|
||||
}
|
||||
public Optional<RecipeViewModel> getRecipeViewById(Long id) {
|
||||
return recipes.get().stream().filter(r -> r.getId().equals(id)).findFirst();
|
||||
}
|
||||
public Optional<IngredientViewModel> getIngredientViewById(Long id) {
|
||||
return ingredients.get().stream().filter(i -> i.idProperty().get() == id).findFirst();
|
||||
}
|
||||
|
||||
public ObservableList<RecipeViewModel> getRecipes() {
|
||||
return recipes.get();
|
||||
}
|
||||
|
||||
public ObservableList<IngredientViewModel> getIngredients() {
|
||||
return ingredients.get();
|
||||
}
|
||||
public void setRecipes(List<RecipeViewModel> recipes) {
|
||||
this.recipes.setAll(recipes);
|
||||
}
|
||||
public void setIngredients(List<IngredientViewModel> ingredients) {
|
||||
this.ingredients.setAll(ingredients);
|
||||
}
|
||||
}
|
||||
6
client/src/main/java/client/model/BaseViewModel.java
Normal file
6
client/src/main/java/client/model/BaseViewModel.java
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package client.model;
|
||||
|
||||
public abstract class BaseViewModel<T> {
|
||||
public abstract void updateFrom(T item);
|
||||
public abstract T into();
|
||||
}
|
||||
|
|
@ -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<FormalIngredient> {
|
||||
private final LongProperty id = new SimpleLongProperty();
|
||||
private final ObjectProperty<IngredientViewModel> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Ingredient> {
|
||||
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());
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package client.model;
|
||||
|
||||
import commons.RecipeIngredient;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
|
||||
public abstract class RecipeIngredientViewModel<T>
|
||||
extends BaseViewModel<T> implements ScaleAware<RecipeIngredient> {
|
||||
public abstract DoubleProperty kcalProperty();
|
||||
}
|
||||
112
client/src/main/java/client/model/RecipeViewModel.java
Normal file
112
client/src/main/java/client/model/RecipeViewModel.java
Normal file
|
|
@ -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<Recipe> implements ScaleAware<Recipe> {
|
||||
// Immutable
|
||||
private final Long id;
|
||||
|
||||
// Mutable values
|
||||
private final StringProperty name = new SimpleStringProperty();
|
||||
private final StringProperty locale = new SimpleStringProperty();
|
||||
private final ListProperty<RecipeIngredientViewModel<?>> ingredients = new SimpleListProperty<>(FXCollections.observableArrayList(
|
||||
item -> new Observable[] { item.kcalProperty() }
|
||||
));
|
||||
private final ObjectProperty<ObservableList<String>> 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<RecipeIngredientViewModel<?>> 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<RecipeIngredientViewModel<?>> list = item.getIngredients().stream().<RecipeIngredientViewModel<?>>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<RecipeIngredientViewModel<?>> ingredientsProperty() {
|
||||
return ingredients;
|
||||
}
|
||||
public ObjectProperty<ObservableList<String>> stepsProperty() {
|
||||
return steps;
|
||||
}
|
||||
public DoubleProperty kcalProperty() {
|
||||
return kcal;
|
||||
}
|
||||
public DoubleProperty scaleProperty() {
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
12
client/src/main/java/client/model/ScaleAware.java
Normal file
12
client/src/main/java/client/model/ScaleAware.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package client.model;
|
||||
|
||||
/**
|
||||
* An interface describing a value that has a real and scaled counterpart.
|
||||
* @param <T>
|
||||
*/
|
||||
public interface ScaleAware<T> {
|
||||
T getReal();
|
||||
T getScaled();
|
||||
void setReal(T real);
|
||||
void setScale(Number scale);
|
||||
}
|
||||
|
|
@ -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<VagueIngredient> {
|
||||
private final SimpleObjectProperty<VagueIngredient> 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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Long, Recipe> 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<Long, Recipe> recipeDataService
|
||||
WebSocketDataService<Long, Recipe> 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();
|
||||
|
|
|
|||
|
|
@ -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<IngredientViewModel> 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());
|
||||
|
|
|
|||
|
|
@ -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<Long, Recipe> 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> recipeViewModel = new SimpleObjectProperty<>();
|
||||
|
||||
@Inject
|
||||
public RecipeDetailCtrl(
|
||||
|
|
@ -57,12 +63,14 @@ public class RecipeDetailCtrl implements LocaleAware {
|
|||
ServerUtils server,
|
||||
FoodpalApplicationCtrl appCtrl,
|
||||
ConfigService configService,
|
||||
WebSocketDataService<Long, Recipe> webSocketDataService) {
|
||||
WebSocketDataService<Long, Recipe> 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<Recipe> 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<String> 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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,4 +35,5 @@
|
|||
<!-- Preparation -->
|
||||
<fx:include source="RecipeStepList.fxml" fx:id="stepList"
|
||||
VBox.vgrow="ALWAYS" maxWidth="Infinity" />
|
||||
<Label>Total energy in this recipe: </Label><Label fx:id="kcalLabel" /><Label>kcal</Label>
|
||||
</VBox>
|
||||
|
|
|
|||
|
|
@ -76,4 +76,9 @@ public class FormalIngredient extends RecipeIngredient implements Scalable<Forma
|
|||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), amount, unitSuffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double kcal() {
|
||||
return ingredient.kcalPer100g() * amountInBaseUnit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import java.util.Objects;
|
|||
// TABLE named recipes
|
||||
@Entity
|
||||
@Table(name = "recipes")
|
||||
public class Recipe {
|
||||
public class Recipe implements Scalable<Recipe> {
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -92,4 +92,5 @@ public abstract class RecipeIngredient {
|
|||
public int hashCode() {
|
||||
return Objects.hash(id, ingredient);
|
||||
}
|
||||
public abstract double kcal();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package commons;
|
||||
|
||||
public interface Scalable<IngredientType extends RecipeIngredient> {
|
||||
default IngredientType scaleBy(int numPortions) {
|
||||
public interface Scalable<T> {
|
||||
default T scaleBy(int numPortions) {
|
||||
return scaleBy((double) numPortions);
|
||||
};
|
||||
IngredientType scaleBy(double factor);
|
||||
T scaleBy(double factor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,4 +50,9 @@ public class VagueIngredient extends RecipeIngredient {
|
|||
public int hashCode() {
|
||||
return Objects.hashCode(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double kcal() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue