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 client.utils.ServerUtils;
import com.google.inject.Inject; import com.google.inject.Inject;
import commons.Recipe; 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.fxml.FXML;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
@ -19,12 +23,10 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.scene.text.Font; import javafx.scene.text.Font;
import java.io.IOException;
import java.util.Optional;
/** /**
* Controller for the recipe detail view. * 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 { public class RecipeDetailCtrl implements LocaleAware {
private final LocaleManager localeManager; private final LocaleManager localeManager;
@ -41,8 +43,7 @@ public class RecipeDetailCtrl implements LocaleAware {
private Recipe recipe; private Recipe recipe;
@Inject @Inject
public RecipeDetailCtrl(LocaleManager localeManager, public RecipeDetailCtrl(LocaleManager localeManager, ServerUtils server,
ServerUtils server,
FoodpalApplicationCtrl appCtrl, FoodpalApplicationCtrl appCtrl,
ConfigService configService) { ConfigService configService) {
this.localeManager = localeManager; this.localeManager = localeManager;
@ -74,15 +75,14 @@ public class RecipeDetailCtrl implements LocaleAware {
} }
/** /**
* Convenience method for a frequently needed operation to retrieve the currently * Convenience method for a frequently needed operation to retrieve the
* selected recipe. * currently selected recipe.
* *
* @return The currently selected recipe in the parent recipe list. * @return The currently selected recipe in the parent recipe list.
*/ */
private Optional<Recipe> getSelectedRecipe() { private Optional<Recipe> getSelectedRecipe() {
return Optional.ofNullable( 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() { private <T> Consumer<T> createUpdateRecipeCallback(BiConsumer<Recipe, T> recipeConsumer) {
// Initialize callback for ingredient list updates return recipes -> {
this.ingredientListController.setUpdateCallback(newList -> { Recipe selectedRecipe = this.getSelectedRecipe().orElseThrow(
var maybeSelected = this.getSelectedRecipe(); () -> new NullPointerException(
if (maybeSelected.isEmpty()) { // Safely handle edge case "Null recipe whereas ingredients are edited"));
throw new NullPointerException("Null recipe whereas ingredients are edited");
} recipeConsumer.accept(selectedRecipe, recipes);
Recipe selectedRecipe = maybeSelected.get();
selectedRecipe.setIngredients(newList);
try { // propagate changes to server try { // propagate changes to server
server.updateRecipe(selectedRecipe); server.updateRecipe(selectedRecipe);
} catch (IOException | InterruptedException e) { } 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. * Initializes the ingredient and step list controllers with update callbacks.
* This is automagically called when a new recipe is created, making for a more seamless UX. */
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 @FXML
private void editRecipeTitle() { private void editRecipeTitle() {
@ -222,8 +227,8 @@ public class RecipeDetailCtrl implements LocaleAware {
} }
/** /**
* Refreshes the favourite button state based on whether the currently viewed recipe * Refreshes the favourite button state based on whether the currently viewed
* is marked as a favourite in the application configuration. * recipe is marked as a favourite in the application configuration.
*/ */
public void refreshFavouriteButton() { public void refreshFavouriteButton() {
if (recipe == null) { if (recipe == null) {
@ -233,14 +238,13 @@ public class RecipeDetailCtrl implements LocaleAware {
} }
favouriteButton.setDisable(false); favouriteButton.setDisable(false);
favouriteButton.setText( favouriteButton.setText(this.getConfig().isFavourite(recipe.getId()) ? ""
this.getConfig().isFavourite(recipe.getId()) ? "" : "" : "");
);
} }
/** /**
* Toggles the favourite status of the currently viewed recipe in the application configuration * Toggles the favourite status of the currently viewed recipe in the
* and writes the changes to disk. * application configuration and writes the changes to disk.
*/ */
@FXML @FXML
private void toggleFavourite() { private void toggleFavourite() {
@ -255,7 +259,7 @@ public class RecipeDetailCtrl implements LocaleAware {
configService.save(); configService.save();
//instant ui update // instant ui update
appCtrl.applyRecipeFilterAndKeepSelection(); appCtrl.applyRecipeFilterAndKeepSelection();
this.getParentRecipeList().refresh(); this.getParentRecipeList().refresh();
refreshFavouriteButton(); refreshFavouriteButton();