refactor: make callbacks clean

This commit is contained in:
Natalia Cholewa 2026-01-05 16:05:27 +01:00
commit ee66b643ec

View file

@ -9,6 +9,10 @@ import client.utils.LocaleManager;
import client.utils.ServerUtils;
import com.google.inject.Inject;
import commons.Recipe;
import java.io.IOException;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
@ -19,12 +23,10 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import java.io.IOException;
import java.util.Optional;
/**
* Controller for the recipe detail view.
* Manages displaying and editing recipe details such as name, ingredients, and steps.
* Manages displaying and editing recipe details such as name, ingredients, and
* steps.
*/
public class RecipeDetailCtrl implements LocaleAware {
private final LocaleManager localeManager;
@ -41,8 +43,7 @@ public class RecipeDetailCtrl implements LocaleAware {
private Recipe recipe;
@Inject
public RecipeDetailCtrl(LocaleManager localeManager,
ServerUtils server,
public RecipeDetailCtrl(LocaleManager localeManager, ServerUtils server,
FoodpalApplicationCtrl appCtrl,
ConfigService configService) {
this.localeManager = localeManager;
@ -74,15 +75,14 @@ public class RecipeDetailCtrl implements LocaleAware {
}
/**
* Convenience method for a frequently needed operation to retrieve the currently
* selected recipe.
* Convenience method for a frequently needed operation to retrieve the
* currently selected recipe.
*
* @return The currently selected recipe in the parent recipe list.
*/
private Optional<Recipe> getSelectedRecipe() {
return Optional.ofNullable(
this.getParentRecipeList().getSelectionModel().getSelectedItem()
);
this.getParentRecipeList().getSelectionModel().getSelectedItem());
}
/**
@ -135,45 +135,50 @@ public class RecipeDetailCtrl implements LocaleAware {
}
/**
* Initializes the ingredient and step list controllers with update callbacks.
* Create a callback that takes in the selected recipe and a helper type
* and updates the recipe based on that.
* Also, posts the update to the server.
*
* @param recipeConsumer The helper function to use when updating the recipe -
* how to update it
* @return The created callback to use in a setUpdateCallback or related
* function
*/
private void initStepsIngredientsList() {
// Initialize callback for ingredient list updates
this.ingredientListController.setUpdateCallback(newList -> {
var maybeSelected = this.getSelectedRecipe();
if (maybeSelected.isEmpty()) { // Safely handle edge case
throw new NullPointerException("Null recipe whereas ingredients are edited");
}
private <T> Consumer<T> createUpdateRecipeCallback(BiConsumer<Recipe, T> recipeConsumer) {
return recipes -> {
Recipe selectedRecipe = this.getSelectedRecipe().orElseThrow(
() -> new NullPointerException(
"Null recipe whereas ingredients are edited"));
recipeConsumer.accept(selectedRecipe, recipes);
Recipe selectedRecipe = maybeSelected.get();
selectedRecipe.setIngredients(newList);
try { // propagate changes to server
server.updateRecipe(selectedRecipe);
} catch (IOException | InterruptedException e) {
throw new UpdateException("Unable to update recipe to server for " + selectedRecipe);
throw new UpdateException("Unable to update recipe to server for " +
selectedRecipe);
}
});
// Ditto, for step list updates
this.stepListController.setUpdateCallback(newList -> {
var maybeSelected = this.getSelectedRecipe();
if (maybeSelected.isEmpty()) { // Safely handle edge case
throw new NullPointerException("Null recipe whereas ingredients are edited");
}
Recipe selectedRecipe = maybeSelected.get();
selectedRecipe.setPreparationSteps(newList);
try { // propagate changes to server
server.updateRecipe(selectedRecipe);
} catch (IOException | InterruptedException e) {
throw new UpdateException("Unable to update recipe to server for " + selectedRecipe);
}
});
};
}
/**
* Revised edit recipe control flow, deprecates the use of a separate AddNameCtrl.
* This is automagically called when a new recipe is created, making for a more seamless UX.
* Initializes the ingredient and step list controllers with update callbacks.
*/
private void initStepsIngredientsList() {
// code does NOT get nicer than this :pray:
// Initialize callback for ingredient list updates
this.ingredientListController.setUpdateCallback(
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,
* making for a more seamless UX.
*/
@FXML
private void editRecipeTitle() {
@ -222,8 +227,8 @@ 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.
* Refreshes the favourite button state based on whether the currently viewed
* recipe is marked as a favourite in the application configuration.
*/
public void refreshFavouriteButton() {
if (recipe == null) {
@ -233,14 +238,13 @@ public class RecipeDetailCtrl implements LocaleAware {
}
favouriteButton.setDisable(false);
favouriteButton.setText(
this.getConfig().isFavourite(recipe.getId()) ? "" : ""
);
favouriteButton.setText(this.getConfig().isFavourite(recipe.getId()) ? ""
: "");
}
/**
* Toggles the favourite status of the currently viewed recipe in the application configuration
* and writes the changes to disk.
* Toggles the favourite status of the currently viewed recipe in the
* application configuration and writes the changes to disk.
*/
@FXML
private void toggleFavourite() {