feat: new UX for add/edit/update recipes
Revised adding recipes to have untitled recipes with inline name editing (name edit box autofocused upon recipe creation)
This commit is contained in:
parent
805c8fd6ea
commit
d42e6ff74c
3 changed files with 96 additions and 12 deletions
|
|
@ -0,0 +1,7 @@
|
||||||
|
package client.exception;
|
||||||
|
|
||||||
|
public class UpdateException extends RuntimeException {
|
||||||
|
public UpdateException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,20 +4,30 @@ package client.scenes;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import client.exception.UpdateException;
|
||||||
|
import client.utils.DefaultRecipeFactory;
|
||||||
import client.utils.ServerUtils;
|
import client.utils.ServerUtils;
|
||||||
import commons.Recipe;
|
import commons.Recipe;
|
||||||
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
public class FoodpalApplicationCtrl {
|
public class FoodpalApplicationCtrl {
|
||||||
private final MainCtrl mainCtrl;
|
private final MainCtrl mainCtrl;
|
||||||
private final ServerUtils server;
|
private final ServerUtils server;
|
||||||
|
|
||||||
|
public VBox detailsScreen;
|
||||||
|
public HBox editableTitleArea;
|
||||||
|
|
||||||
|
|
||||||
// all of these aren't used with only my part of the code
|
// all of these aren't used with only my part of the code
|
||||||
// everything in the top bar ===
|
// everything in the top bar ===
|
||||||
|
|
@ -103,10 +113,13 @@ public class FoodpalApplicationCtrl {
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
private void showName(String name) {
|
||||||
|
editableTitleArea.getChildren().clear();
|
||||||
|
editableTitleArea.getChildren().add(new Label(name));
|
||||||
|
}
|
||||||
// till the all the code from everyone is implemented for now to not have errors
|
// till the all the code from everyone is implemented for now to not have errors
|
||||||
private void showRecipeDetails(Recipe newRecipe) {
|
private void showRecipeDetails(Recipe newRecipe) {
|
||||||
recipeNameLabel.setText(newRecipe.getName());
|
showName(newRecipe.getName());
|
||||||
ingredientsListView.getItems().setAll(newRecipe.getIngredients());
|
ingredientsListView.getItems().setAll(newRecipe.getIngredients());
|
||||||
preparationListView.getItems().setAll(newRecipe.getPreparationSteps());
|
preparationListView.getItems().setAll(newRecipe.getPreparationSteps());
|
||||||
|
|
||||||
|
|
@ -123,17 +136,40 @@ public class FoodpalApplicationCtrl {
|
||||||
// Select first recipe in the list by default
|
// Select first recipe in the list by default
|
||||||
if (!recipes.isEmpty()) {
|
if (!recipes.isEmpty()) {
|
||||||
recipeList.getSelectionModel().selectFirst();
|
recipeList.getSelectionModel().selectFirst();
|
||||||
|
detailsScreen.visibleProperty().set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void printError(String msg) {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||||
|
alert.setContentText(msg);
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a recipe, by going to a different scene, there you insert the title and bam recipe created
|
* Adds a recipe, by providing a default name "Untitled recipe (n)"
|
||||||
|
* The UX flow is now:
|
||||||
|
* <ol>
|
||||||
|
* <li>User creates an untitled recipe</li>
|
||||||
|
* <li>The application autofocuses on the edit box (not exactly automagically yet)</li>
|
||||||
|
* <li>User edits the name to whatever they want</li>
|
||||||
|
* <li>Profit??</li>
|
||||||
|
* </ol>
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void addRecipe() {
|
private void addRecipe() {
|
||||||
// Navigate to "create recipe" screen (should be like a pop-up or new screen or just another place in the app)
|
// a default factory provides the value
|
||||||
mainCtrl.showAddName();
|
Recipe newRecipe = DefaultRecipeFactory.getDefaultRecipe();
|
||||||
|
try {
|
||||||
|
server.addRecipe(newRecipe);
|
||||||
|
refresh();
|
||||||
|
// the list focuses on the new recipe
|
||||||
|
// otherwise strange issues occur when the autofocus on edit box is called
|
||||||
|
recipeList.getFocusModel().focus(recipeList.getItems().indexOf(newRecipe));
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
printError("Error occurred when adding recipe!");
|
||||||
|
}
|
||||||
|
// Calls edit after the recipe has been created, seamless name editing
|
||||||
|
editRecipeTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|
@ -145,7 +181,10 @@ public class FoodpalApplicationCtrl {
|
||||||
|
|
||||||
server.deleteRecipe(selected.getId());
|
server.deleteRecipe(selected.getId());
|
||||||
|
|
||||||
recipeList.getItems().remove(selected);
|
// prefer a global refresh (or WS-based update upon its impl)
|
||||||
|
// rather than recipeList.getItems().remove(selected);
|
||||||
|
// to maintain single source of truth from the server.
|
||||||
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|
@ -154,15 +193,38 @@ public class FoodpalApplicationCtrl {
|
||||||
if (selected == null) {
|
if (selected == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Let MainCtrl open the full detail screen
|
detailsScreen.visibleProperty().set(true);
|
||||||
mainCtrl.showAddName(); //I had showrecipedetail but intelij says showoverview
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
// TODO: someone else todo
|
editableTitleArea.getChildren().clear();
|
||||||
// For now reuse openSelectedRecipe()
|
TextField edit = new TextField();
|
||||||
openSelectedRecipe();
|
edit.setOnKeyPressed(event -> {
|
||||||
|
if (event.getCode() != KeyCode.ENTER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String newName = edit.getText();
|
||||||
|
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||||
|
if (selected == null) {
|
||||||
|
// edge case, prob won't happen but best to handle it later
|
||||||
|
throw new NullPointerException("No recipe selected while name was changed!");
|
||||||
|
}
|
||||||
|
selected.setName(newName);
|
||||||
|
try {
|
||||||
|
server.updateRecipe(selected);
|
||||||
|
refresh();
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
// throw a nice blanket UpdateException
|
||||||
|
throw new UpdateException("Error occurred when updating recipe name!");
|
||||||
|
}
|
||||||
|
showName(edit.getText());
|
||||||
|
});
|
||||||
|
editableTitleArea.getChildren().add(edit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
15
client/src/main/java/client/utils/DefaultRecipeFactory.java
Normal file
15
client/src/main/java/client/utils/DefaultRecipeFactory.java
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package client.utils;
|
||||||
|
|
||||||
|
import commons.Recipe;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DefaultRecipeFactory {
|
||||||
|
public static Recipe getDefaultRecipe() {
|
||||||
|
return new Recipe(
|
||||||
|
null,
|
||||||
|
"Untitled recipe",
|
||||||
|
List.of(),
|
||||||
|
List.of());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue