merge main into feature/client-side-search
This commit is contained in:
commit
96eb19d74c
18 changed files with 528 additions and 138 deletions
|
|
@ -4,7 +4,6 @@ package client.scenes;
|
|||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import client.exception.UpdateException;
|
||||
import client.scenes.recipe.IngredientListCtrl;
|
||||
|
|
@ -23,7 +22,6 @@ import commons.ws.Topics;
|
|||
import commons.ws.messages.Message;
|
||||
import jakarta.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
|
|
@ -48,18 +46,6 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
public VBox detailsScreen;
|
||||
public HBox editableTitleArea;
|
||||
|
||||
|
||||
// all of these aren't used with only my part of the code
|
||||
// everything in the top bar ===
|
||||
@FXML
|
||||
private Button flagEnButton; //already here for advanced stuff
|
||||
|
||||
@FXML
|
||||
private Button flagNlButton; //already here for advanced stuff
|
||||
|
||||
@FXML
|
||||
private Button flagPlButton; //already here for advanced stuff
|
||||
|
||||
// everything in the left lane
|
||||
@FXML
|
||||
public Label recipesLabel;
|
||||
|
|
@ -113,6 +99,23 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
|
||||
initializeWebSocket();
|
||||
}
|
||||
private void initRecipeList() {
|
||||
// Show recipe name in the list
|
||||
recipeList.setCellFactory(list -> new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(Recipe item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText(empty || item == null ? "" : item.getName());
|
||||
}
|
||||
});
|
||||
// When your selection changes, update details in the panel
|
||||
recipeList.getSelectionModel().selectedItemProperty().addListener(
|
||||
(obs, oldRecipe, newRecipe) -> {
|
||||
showRecipeDetails(newRecipe);
|
||||
updateFavouriteButton(newRecipe);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Inject
|
||||
void setSearchBarCtrl(SearchBarCtrl searchBarCtrl) {
|
||||
|
|
@ -165,11 +168,7 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeComponents() {
|
||||
config = configService.getConfig();
|
||||
// TODO Reduce code duplication??
|
||||
private void initStepsIngredientsList() {
|
||||
// Initialize callback for ingredient list updates
|
||||
this.ingredientListCtrl.setUpdateCallback(newList -> {
|
||||
Recipe selectedRecipe = recipeList.getSelectionModel().getSelectedItem();
|
||||
|
|
@ -199,21 +198,12 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
throw new UpdateException("Unable to update recipe to server for " + selectedRecipe);
|
||||
}
|
||||
});
|
||||
// Show recipe name in the list
|
||||
recipeList.setCellFactory(list -> new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(Recipe item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText(empty || item == null ? "" : item.getName());
|
||||
}
|
||||
});
|
||||
// When your selection changes, update details in the panel
|
||||
recipeList.getSelectionModel().selectedItemProperty().addListener(
|
||||
(obs, oldRecipe, newRecipe) -> {
|
||||
showRecipeDetails(newRecipe);
|
||||
updateFavouriteButton(newRecipe);
|
||||
}
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public void initializeComponents() {
|
||||
config = configService.getConfig();
|
||||
initStepsIngredientsList();
|
||||
initRecipeList();
|
||||
|
||||
// Double-click to go to detail screen
|
||||
recipeList.setOnMouseClicked(event -> {
|
||||
|
|
@ -222,7 +212,6 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
openSelectedRecipe();
|
||||
}
|
||||
});
|
||||
|
||||
this.initializeSearchBar();
|
||||
refresh();
|
||||
updateFavouriteButton(recipeList.getSelectionModel().getSelectedItem());
|
||||
|
|
@ -247,13 +236,6 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
printRecipeButton.setText(getLocaleString("menu.button.print"));
|
||||
|
||||
recipesLabel.setText(getLocaleString("menu.label.recipes"));
|
||||
|
||||
// TODO propagate ResourceBundle lang changes to nested controller
|
||||
// ingredientsLabel.setText(getLocaleString("menu.label.ingredients"));
|
||||
// preparationLabel.setText(getLocaleString("menu.label.preparation"));
|
||||
|
||||
// addIngredientButton.setText(getLocaleString("menu.button.add.ingredient"));
|
||||
// addPreparationStepButton.setText(getLocaleString("menu.button.add.step"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -378,15 +360,6 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
editableTitleArea.getChildren().add(edit);
|
||||
edit.requestFocus();
|
||||
}
|
||||
|
||||
// Language buttons
|
||||
@FXML
|
||||
private void switchLocale(ActionEvent event) {
|
||||
Button button = (Button)event.getSource();
|
||||
String lang = (String)button.getUserData();
|
||||
localeManager.setLocale(Locale.of(lang));
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void makePrintable() {
|
||||
System.out.println("Recipe printed");
|
||||
|
|
|
|||
100
client/src/main/java/client/scenes/LangSelectMenuCtrl.java
Normal file
100
client/src/main/java/client/scenes/LangSelectMenuCtrl.java
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
package client.scenes;
|
||||
|
||||
import client.utils.LocaleAware;
|
||||
import client.utils.LocaleManager;
|
||||
import com.google.inject.Inject;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The language selection menu controller.
|
||||
* This needs to implement {@link LocaleAware LocaleAware} so that the
|
||||
* <code>getLocaleString(String)</code> function is available.
|
||||
*/
|
||||
public class LangSelectMenuCtrl implements LocaleAware {
|
||||
public ComboBox<String> langSelectMenu;
|
||||
private final LocaleManager manager;
|
||||
|
||||
@Inject
|
||||
public LangSelectMenuCtrl(LocaleManager manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the locale of the application depending on which button the user selects in the list.
|
||||
* @param event The action event triggered by the user.
|
||||
*/
|
||||
@FXML
|
||||
private void switchLocale(ActionEvent event) {
|
||||
String lang = langSelectMenu.getSelectionModel().getSelectedItem();
|
||||
manager.setLocale(Locale.of(lang));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeComponents() {
|
||||
langSelectMenu.getItems().setAll("en", "pl", "nl");
|
||||
langSelectMenu.setConverter(new StringConverter<String>() {
|
||||
@Override
|
||||
public String toString(String s) {
|
||||
if (s == null) {
|
||||
return "";
|
||||
}
|
||||
return getLocaleString("lang." + s + ".display");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromString(String s) {
|
||||
return s;
|
||||
}
|
||||
});
|
||||
langSelectMenu.setCellFactory(list -> new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(String item, boolean empty) {
|
||||
final int IMAGE_HEIGHT = 32;
|
||||
final int HBOX_SPACING = 10;
|
||||
super.updateItem(item, empty);
|
||||
if (item == null || empty) {
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
return;
|
||||
}
|
||||
InputStream imageStream = getClass().getResourceAsStream("/flag_" + item + ".png");
|
||||
if (imageStream == null) {
|
||||
setGraphic(new HBox(new Label(getLocaleString("lang." + item + ".display"))));
|
||||
return;
|
||||
}
|
||||
Image img = new Image(imageStream);
|
||||
ImageView imageView = new ImageView(img);
|
||||
imageView.setFitHeight(IMAGE_HEIGHT);
|
||||
imageView.setFitWidth(IMAGE_HEIGHT);
|
||||
setGraphic(
|
||||
new HBox(
|
||||
HBOX_SPACING,
|
||||
imageView,
|
||||
new Label(getLocaleString("lang." + item + ".display"))
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateText() {
|
||||
// doesn't do anything; the text doesn't need to be updated.
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocaleManager getLocaleManager() {
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
67
client/src/main/java/client/utils/PrintExportService.java
Normal file
67
client/src/main/java/client/utils/PrintExportService.java
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package client.utils;
|
||||
|
||||
import commons.Recipe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class PrintExportService {
|
||||
|
||||
/**
|
||||
* Builds the String with all the recipe data in a human-readable format and returns said string.
|
||||
* @param recipe - Recipe Object that needs to be converted
|
||||
* @return - String Result that is the converted recipe object
|
||||
*/
|
||||
public static String buildRecipeText(Recipe recipe){
|
||||
String result = "Title: " + recipe.getName() + "\nRecipe ID: " + recipe.getId() + "\n" +
|
||||
"Ingredients: "; //Starts the string with name and recipe ID
|
||||
for(int i =0; i<recipe.getIngredients().size();i++){ // For loop adding ingredients one by one
|
||||
result += recipe.getIngredients().get(i) + ", ";
|
||||
}
|
||||
result += "\nSteps:\n";
|
||||
for(int i =0; i<recipe.getPreparationSteps().size();i++){ // Preparation Steps separated by new lines
|
||||
result += (i+1) + ": " + recipe.getPreparationSteps().get(i) + "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that checks if the directory path of the selected directory is valid and throws and error.
|
||||
* if not valid
|
||||
* @param path - Path to directory
|
||||
*/
|
||||
public static void validateFolder(Path path){
|
||||
if (path == null) { //Null path value
|
||||
throw new IllegalArgumentException("Path is empty");
|
||||
}
|
||||
if(!Files.exists(path)){ //If Folder doesn't exist
|
||||
throw new IllegalArgumentException("Folder does not exist");
|
||||
}
|
||||
if(!Files.isDirectory(path)){ // If folder is not directory
|
||||
throw new IllegalArgumentException("Given path is not a folder");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that exports a plain-text String of recipe data, into a newly created file from
|
||||
* a selected directory provided as a path to the method and names said file based on the
|
||||
* nameOfFile param.
|
||||
* @param recipeData - String containing recipe data that will be written to file
|
||||
* @param dirFilePath - Path to Folder
|
||||
* @param nameOfFile - Name of file to be created
|
||||
*/
|
||||
public static void exportToFile(String recipeData, Path dirFilePath, String nameOfFile){
|
||||
validateFolder(dirFilePath); //runs validate Folder method to check if Directory path is valid
|
||||
Path filePath = dirFilePath.resolve(nameOfFile); //Creates a file with name provided in directory and stores path in PATH object
|
||||
try {
|
||||
Files.writeString(filePath, recipeData); // Writes the recipe data into the File.
|
||||
}
|
||||
catch (IOException e){
|
||||
System.err.println("An error occurred while writing to the file");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<?import javafx.scene.control.ComboBox?>
|
||||
<BorderPane prefHeight="800.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.FoodpalApplicationCtrl">
|
||||
|
||||
<!-- TOP BAR -->
|
||||
|
|
@ -23,58 +24,12 @@
|
|||
<padding>
|
||||
<Insets bottom="10" left="10" right="10" top="10" />
|
||||
</padding>
|
||||
<GridPane>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="198.0" minWidth="10.0" prefWidth="130.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="95.0" minWidth="2.0" prefWidth="70.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="54.0" minHeight="10.0" prefHeight="54.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="25.0" minHeight="6.0" prefHeight="6.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
|
||||
<Label prefHeight="56.0" prefWidth="158.0" text="FoodPal">
|
||||
<font>
|
||||
<Font name="System Bold" size="29.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<ButtonBar prefHeight="40.0" prefWidth="200.0" GridPane.rowIndex="2">
|
||||
<buttons>
|
||||
<ToolBar prefHeight="35.0" prefWidth="123.0">
|
||||
<items>
|
||||
<Button fx:id="flagEnButton" minWidth="33.0" onAction="#switchLocale" userData="en" prefHeight="25.0" prefWidth="33.0" text="EN" />
|
||||
<Button fx:id="flagNlButton" onAction="#switchLocale" userData="nl" text="NL" />
|
||||
<Button fx:id="flagPlButton" onAction="#switchLocale" userData="pl" text="PL" />
|
||||
</items>
|
||||
</ToolBar>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</children>
|
||||
</GridPane>
|
||||
|
||||
<Pane HBox.hgrow="ALWAYS" />
|
||||
|
||||
<HBox spacing="5" />
|
||||
<GridPane prefHeight="90.0" prefWidth="114.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="315.0" minWidth="10.0" prefWidth="32.1666259765625" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="315.0" minWidth="10.0" prefWidth="24.8333740234375" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="173.0" minWidth="10.0" prefWidth="28.6666259765625" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="95.0" minWidth="10.0" prefWidth="34.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
|
||||
<Button fx:id="refreshButton" onAction="#refresh" prefHeight="25.0" prefWidth="34.0" text="⟳" GridPane.columnIndex="3" GridPane.rowIndex="2" />
|
||||
</children>
|
||||
</GridPane>
|
||||
</HBox>
|
||||
<Label prefHeight="56.0" prefWidth="158.0" text="FoodPal">
|
||||
<font>
|
||||
<Font name="System Bold" size="29.0" />
|
||||
</font>
|
||||
</Label>
|
||||
</HBox>
|
||||
</top>
|
||||
|
||||
<!-- LEFT: RECIPE LIST -->
|
||||
|
|
@ -83,7 +38,8 @@
|
|||
<padding>
|
||||
<Insets bottom="10" left="10" right="10" top="10" />
|
||||
</padding>
|
||||
|
||||
<fx:include source="LangSelect.fxml" />
|
||||
<Button fx:id="refreshButton" onAction="#refresh" prefHeight="25.0" prefWidth="34.0" text="⟳" GridPane.columnIndex="3" GridPane.rowIndex="2" />
|
||||
<Label fx:id="recipesLabel" text="Recipes">
|
||||
<font>
|
||||
<Font name="System Bold" size="15.0" />
|
||||
|
|
|
|||
16
client/src/main/resources/client/scenes/LangSelect.fxml
Normal file
16
client/src/main/resources/client/scenes/LangSelect.fxml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.*?>
|
||||
<?import java.util.*?>
|
||||
<?import javafx.scene.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
<ComboBox
|
||||
fx:id="langSelectMenu"
|
||||
xmlns="http://javafx.com/javafx/25"
|
||||
xmlns:fx="http://javafx.com/fxml/1"
|
||||
onAction="#switchLocale"
|
||||
fx:controller="client.scenes.LangSelectMenuCtrl"
|
||||
>
|
||||
</ComboBox>
|
||||
BIN
client/src/main/resources/flag_en.png
Normal file
BIN
client/src/main/resources/flag_en.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 995 B |
BIN
client/src/main/resources/flag_nl.png
Normal file
BIN
client/src/main/resources/flag_nl.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 266 B |
BIN
client/src/main/resources/flag_pl.png
Normal file
BIN
client/src/main/resources/flag_pl.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 241 B |
|
|
@ -24,3 +24,7 @@ menu.button.remove.step=Remove Step
|
|||
menu.button.edit=Edit
|
||||
menu.button.clone=Clone
|
||||
menu.button.print=Print recipe
|
||||
|
||||
lang.en.display=English
|
||||
lang.nl.display=Dutch
|
||||
lang.pl.display=Polish
|
||||
|
|
@ -25,4 +25,7 @@ menu.button.edit=Edit
|
|||
menu.button.clone=Clone
|
||||
menu.button.print=Print recipe
|
||||
|
||||
menu.search=Search...
|
||||
menu.search=Search...
|
||||
lang.en.display=English
|
||||
lang.nl.display=Nederlands
|
||||
lang.pl.display=Polski
|
||||
|
|
|
|||
|
|
@ -25,4 +25,7 @@ menu.button.edit=Bewerken
|
|||
menu.button.clone=Dupliceren
|
||||
menu.button.print=Recept afdrukken
|
||||
|
||||
menu.search=Zoeken...
|
||||
menu.search=Zoeken...
|
||||
lang.en.display=English
|
||||
lang.nl.display=Nederlands
|
||||
lang.pl.display=Polski
|
||||
|
|
|
|||
|
|
@ -25,4 +25,7 @@ menu.button.edit=Edytuj
|
|||
menu.button.clone=Duplikuj
|
||||
menu.button.print=Drukuj przepis
|
||||
|
||||
menu.search=Szukaj...
|
||||
menu.search=Szukaj...
|
||||
lang.en.display=English
|
||||
lang.nl.display=Nederlands
|
||||
lang.pl.display=Polski
|
||||
|
|
|
|||
70
client/src/test/java/client/scenes/PrintExportTest.java
Normal file
70
client/src/test/java/client/scenes/PrintExportTest.java
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package client.scenes;
|
||||
|
||||
import client.utils.PrintExportService;
|
||||
import client.utils.ServerUtils;
|
||||
import commons.Recipe;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class PrintExportTest {
|
||||
static ServerUtils dv = new ServerUtils();
|
||||
@BeforeEach
|
||||
public void setup(){
|
||||
Assumptions.assumeTrue(dv.isServerAvailable(), "Server not available");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRecipeTextTest(){
|
||||
List<String> ingredients = new ArrayList<>();
|
||||
ingredients.add("Banana");
|
||||
ingredients.add("Bread");
|
||||
final long testRecipeId = 1234L;
|
||||
List<String> preparationSteps = new ArrayList<>();
|
||||
preparationSteps.add("Mix Ingredients");
|
||||
preparationSteps.add("Heat in Oven");
|
||||
Recipe recipe1 = new Recipe(testRecipeId, "Banana Bread", ingredients, preparationSteps);
|
||||
|
||||
assertEquals("""
|
||||
Title: Banana Bread
|
||||
Recipe ID: 1234
|
||||
Ingredients: Banana, Bread,\s
|
||||
Steps:
|
||||
1: Mix Ingredients
|
||||
2: Heat in Oven
|
||||
""", PrintExportService.buildRecipeText(recipe1));
|
||||
|
||||
}
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
@Test
|
||||
public void validateFolderWithValidFolderTest(){
|
||||
assertDoesNotThrow(() -> PrintExportService.validateFolder(tempDir));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateFolderWithNullPathTest(){
|
||||
IllegalArgumentException i = assertThrows(IllegalArgumentException.class,
|
||||
()->PrintExportService.validateFolder(null));
|
||||
assertEquals("Path is empty", i.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateFolderWithFilePathTest() throws IOException {
|
||||
Path filePath = Files.createFile(tempDir.resolve("TestFile"));
|
||||
IllegalArgumentException i = assertThrows(IllegalArgumentException.class,
|
||||
()->PrintExportService.validateFolder(filePath));
|
||||
assertEquals("Given path is not a folder", i.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue