diff --git a/pom.xml b/pom.xml
index 6fcd2fa..865755d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.starlight
tabemonpal
- 1.0-ALPHA
+ 1.1-ALPHA
jar
@@ -99,6 +99,12 @@
17.0.10
test
+
+ org.awaitility
+ awaitility
+ 4.2.0
+ test
+
@@ -144,40 +150,14 @@
-
- org.openjfx
- javafx-maven-plugin
- 0.0.8
-
- com.starlight.App
-
- --add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED
- --add-opens=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.glass.ui=ALL-UNNAMED
- --add-opens=javafx.graphics/com.sun.glass.ui=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.javafx.util=ALL-UNNAMED
- --add-exports=javafx.base/com.sun.javafx.logging=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.glass.ui=org.testfx
- --add-opens=javafx.graphics/com.sun.glass.ui.monocle=ALL-UNNAMED
- --add-exports=javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED
- --add-opens=javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED
- --add-opens=javafx.graphics/com.sun.glass.ui.monocle.input.devices=ALL-UNNAMED
- --add-opens=javafx.graphics/com.sun.glass.ui.monocle=org.testfx
- --add-exports=javafx.graphics/com.sun.glass.ui.monocle=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.glass.ui=ALL-UNNAMED,org.testfx.monocle
- --add-opens=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.glass.ui.monocle.input=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.glass.ui.monocle.input.devices=ALL-UNNAMED
- --add-opens=javafx.graphics/com.sun.javafx.application.HostServicesDelegate=ALL-UNNAMED
-
-
-
org.apache.maven.plugins
maven-surefire-plugin
3.0.0-M9
+
+ false
+
--add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED
--add-opens=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED
@@ -185,25 +165,19 @@
--add-opens=javafx.graphics/com.sun.glass.ui=ALL-UNNAMED
--add-exports=javafx.graphics/com.sun.javafx.util=ALL-UNNAMED
--add-exports=javafx.base/com.sun.javafx.logging=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.glass.ui=org.testfx
--add-opens=javafx.graphics/com.sun.glass.ui.monocle=ALL-UNNAMED
+ --add-exports=javafx.graphics/com.sun.glass.ui.monocle=ALL-UNNAMED
--add-exports=javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED
--add-opens=javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED
- --add-opens=javafx.graphics/com.sun.glass.ui.monocle.input.devices=ALL-UNNAMED
- --add-opens=javafx.graphics/com.sun.glass.ui.monocle=org.testfx
- --add-exports=javafx.graphics/com.sun.glass.ui.monocle=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.glass.ui=ALL-UNNAMED,org.testfx.monocle
- --add-opens=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED
- --add-exports=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED
--add-exports=javafx.graphics/com.sun.glass.ui.monocle.input=ALL-UNNAMED
--add-exports=javafx.graphics/com.sun.glass.ui.monocle.input.devices=ALL-UNNAMED
- --add-opens=javafx.graphics/com.sun.javafx.application.HostServicesDelegate=ALL-UNNAMED
- --add-opens=com.starlight/com.starlight=ALL-UNNAMED
- --add-opens=com.starlight/com.starlight.api=ALL-UNNAMED
- --add-opens=com.starlight/com.starlight.model=ALL-UNNAMED
- --add-opens=com.starlight/com.starlight.util=ALL-UNNAMED
- --add-opens=com.starlight/com.starlight.controller=ALL-UNNAMED
+ -Dtestfx.headless=true -Dprism.order=sw -Dprism.verbose=false -Djava.awt.headless=true
+
+ true
+ sw
+ true
+
diff --git a/src/main/java/com/starlight/api/ApiClient.java b/src/main/java/com/starlight/api/ApiClient.java
index 3cc7a26..89e1e8f 100644
--- a/src/main/java/com/starlight/api/ApiClient.java
+++ b/src/main/java/com/starlight/api/ApiClient.java
@@ -53,8 +53,8 @@ public User login(String emailOrUsername, String password) throws ApiException {
conn.setDoOutput(true);
User creds = new User();
- creds.email = emailOrUsername;
- creds.password = password;
+ creds.setEmail(emailOrUsername);
+ creds.setPassword(password);
writeXmlToConnection(conn, creds);
@@ -85,9 +85,9 @@ public User register(String username, String email, String password) throws ApiE
conn.setDoOutput(true);
User newUser = new User();
- newUser.username = username;
- newUser.email = email;
- newUser.password = password;
+ newUser.setUsername(username);
+ newUser.setEmail(email);
+ newUser.setPassword(password);
writeXmlToConnection(conn, newUser);
diff --git a/src/main/java/com/starlight/api/UserApiServer.java b/src/main/java/com/starlight/api/UserApiServer.java
index b83e494..ef3bec9 100644
--- a/src/main/java/com/starlight/api/UserApiServer.java
+++ b/src/main/java/com/starlight/api/UserApiServer.java
@@ -114,11 +114,11 @@ public void handle(HttpExchange exchange) throws IOException {
User creds = (User) xstream.fromXML(body);
// First check local users (admin and user accounts)
- String loginIdentifier = creds.email; // This could be username or email
- User localUser = localRepository.validateCredentials(loginIdentifier, creds.password);
+ String loginIdentifier = creds.getEmail(); // This could be username or email
+ User localUser = localRepository.validateCredentials(loginIdentifier, creds.getPassword());
if (localUser != null) {
- logger.log(Level.INFO, "Local user authenticated: {0}", localUser.username);
+ logger.log(Level.INFO, "Local user authenticated: {0}", localUser.getUsername());
sendXml(exchange, 200, xstream.toXML(localUser));
return;
}
@@ -128,9 +128,9 @@ public void handle(HttpExchange exchange) throws IOException {
// Check for either email or username match
Optional match = users.stream()
- .filter(u -> ((u.email != null && u.email.equals(creds.email)) ||
- (u.username != null && u.username.equals(creds.email))) &&
- u.password != null && u.password.equals(creds.password))
+ .filter(u -> ((u.getEmail() != null && u.getEmail().equals(creds.getEmail())) ||
+ (u.getUsername() != null && u.getUsername().equals(creds.getEmail()))) &&
+ u.getPassword() != null && u.getPassword().equals(creds.getPassword()))
.findFirst();
if (match.isPresent()) {
@@ -162,12 +162,12 @@ public void handle(HttpExchange exchange) throws IOException {
User newUser = (User) xstream.fromXML(body);
// Set default profile picture for new users
- if (newUser.profilepicture == null || newUser.profilepicture.trim().isEmpty()) {
- newUser.profilepicture = "src/main/resources/com/starlight/images/dummy/profiledefault.png";
+ if (newUser.getProfilepicture() == null || newUser.getProfilepicture().trim().isEmpty()) {
+ newUser.setProfilepicture("src/main/resources/com/starlight/images/dummy/profiledefault.png");
}
List users = repository.loadUsers(false);
- boolean exists = users.stream().anyMatch(u -> newUser.email.equals(u.email));
+ boolean exists = users.stream().anyMatch(u -> newUser.getEmail().equals(u.getEmail()));
if (exists) {
sendXml(exchange, 409, "");
return;
@@ -240,7 +240,7 @@ private void handleGetUser(HttpExchange exchange, String username) throws IOExce
// Fallback to regular repository users
List users = repository.loadUsers();
Optional user = users.stream()
- .filter(u -> u.username != null && u.username.equals(username))
+ .filter(u -> u.getUsername() != null && u.getUsername().equals(username))
.findFirst();
if (user.isPresent()) {
@@ -303,7 +303,7 @@ private void updateUserInRepository(HttpExchange exchange, String username, User
for (int i = 0; i < users.size(); i++) {
User existingUser = users.get(i);
- if (existingUser.username != null && existingUser.username.equals(username)) {
+ if (existingUser.getUsername() != null && existingUser.getUsername().equals(username)) {
updateUserFields(existingUser, updated);
repository.saveUsers(users);
sendXml(exchange, 200, xstream.toXML(existingUser));
@@ -318,9 +318,9 @@ private void updateUserInRepository(HttpExchange exchange, String username, User
* Updates user fields from the updated user object
*/
private void updateUserFields(User existingUser, User updated) {
- if (updated.email != null) existingUser.email = updated.email;
- if (updated.password != null) existingUser.password = updated.password;
- if (updated.birthDay != null) existingUser.birthDay = updated.birthDay;
+ if (updated.getEmail() != null) existingUser.setEmail(updated.getEmail());
+ if (updated.getPassword() != null) existingUser.setPassword(updated.getPassword());
+ if (updated.getBirthDay() != null) existingUser.setBirthDay(updated.getBirthDay());
}
/**
diff --git a/src/main/java/com/starlight/controller/AuthorizationController.java b/src/main/java/com/starlight/controller/AuthorizationController.java
index bb2b3a1..d05d885 100644
--- a/src/main/java/com/starlight/controller/AuthorizationController.java
+++ b/src/main/java/com/starlight/controller/AuthorizationController.java
@@ -50,13 +50,17 @@ public void initialize(URL location, ResourceBundle resources) {
* Shows the login view in the right section of the BorderPane.
*/
public void showLoginView() {
- authPanel.setRight(loginView);
+ if (authPanel != null && loginView != null) {
+ authPanel.setRight(loginView);
+ }
}
/**
* Shows the register view in the right section of the BorderPane.
*/
public void showRegisterView() {
- authPanel.setRight(registerView);
+ if (authPanel != null && registerView != null) {
+ authPanel.setRight(registerView);
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/com/starlight/controller/CommunityController.java b/src/main/java/com/starlight/controller/CommunityController.java
index 1507707..33c13cd 100644
--- a/src/main/java/com/starlight/controller/CommunityController.java
+++ b/src/main/java/com/starlight/controller/CommunityController.java
@@ -127,8 +127,8 @@ public String getProfilePictureForUser(String username) {
List users = userRepository.loadUsers();
for (User user : users) {
- if (username.equals(user.username)) {
- return user.profilepicture;
+ if (username.equals(user.getUsername())) {
+ return user.getProfilepicture();
}
}
return null;
@@ -142,10 +142,10 @@ public String getDisplayUsernameForUser(String username) {
List users = userRepository.loadUsers();
for (User user : users) {
- if (username.equals(user.username)) {
+ if (username.equals(user.getUsername())) {
// Return fullname if available, otherwise return username
- return user.fullname != null && !user.fullname.trim().isEmpty()
- ? user.fullname : user.username;
+ return user.getFullname() != null && !user.getFullname().trim().isEmpty()
+ ? user.getFullname() : user.getUsername();
}
}
return username; // fallback to original username if not found
@@ -170,12 +170,12 @@ private void loadPosts() {
for (int i = 0; i < posts.size(); i++) {
Post p = posts.get(i);
- String tits = p.title;
- String usr = p.username;
- String desc = p.description;
- String image = p.image;
- String time = p.uploadtime;
- String likes = p.likecount;
+ String tits = p.getTitle();
+ String usr = p.getUsername();
+ String desc = p.getDescription();
+ String image = p.getImage();
+ String time = p.getUploadtime();
+ String likes = p.getLikecount();
// Get profile picture and display username from UserData.xml instead of Post data
String pp = getProfilePictureForUser(usr);
@@ -193,7 +193,7 @@ private void loadPosts() {
c.likecounter.setText(likes);
// Set comment count (default to 0 if not set)
- String commentCount = p.commentcount != null ? p.commentcount : "0";
+ String commentCount = p.getCommentcount() != null ? p.getCommentcount() : "0";
c.commentcounter.setText(commentCount);
// Set the post data for the controller
@@ -203,8 +203,8 @@ private void loadPosts() {
c.setMainController(main);
// Initialize isLiked field if not set
- if (p.isLiked == null) {
- p.isLiked = "false";
+ if (p.getIsLiked() == null) {
+ p.setIsLiked("false");
}
// Load profile picture
@@ -250,31 +250,31 @@ private void loadDailyPosts() {
Post post = shuffledPosts.get(i);
switch (i) {
case 0:
- dailytitle1.setText(post.title);
- starrating.setText(post.rating);
- dailylikecounter.setText(post.likecount);
- ImageUtils.loadImage(dailyphoto1, post.image, ImageUtils.DEFAULT_MISSING_IMAGE);
+ dailytitle1.setText(post.getTitle());
+ starrating.setText(post.getRating());
+ dailylikecounter.setText(post.getLikecount());
+ ImageUtils.loadImage(dailyphoto1, post.getImage(), ImageUtils.DEFAULT_MISSING_IMAGE);
ImageUtils.scaleToFit(dailyphoto1, 280, 174, 30);
break;
case 1:
- dailytitle2.setText(post.title);
- starrating2.setText(post.rating);
- dailylikecounter2.setText(post.likecount);
- ImageUtils.loadImage(dailyphoto2, post.image, ImageUtils.DEFAULT_MISSING_IMAGE);
+ dailytitle2.setText(post.getTitle());
+ starrating2.setText(post.getRating());
+ dailylikecounter2.setText(post.getLikecount());
+ ImageUtils.loadImage(dailyphoto2, post.getImage(), ImageUtils.DEFAULT_MISSING_IMAGE);
ImageUtils.scaleToFit(dailyphoto2, 280, 174, 30);
break;
case 2:
- dailytitle3.setText(post.title);
- starrating3.setText(post.rating);
- dailylikecounter3.setText(post.likecount);
- ImageUtils.loadImage(dailyphoto3, post.image, ImageUtils.DEFAULT_MISSING_IMAGE);
+ dailytitle3.setText(post.getTitle());
+ starrating3.setText(post.getRating());
+ dailylikecounter3.setText(post.getLikecount());
+ ImageUtils.loadImage(dailyphoto3, post.getImage(), ImageUtils.DEFAULT_MISSING_IMAGE);
ImageUtils.scaleToFit(dailyphoto3, 280, 174, 30);
break;
case 3:
- dailytitle4.setText(post.title);
- starrating4.setText(post.rating);
- dailylikecounter4.setText(post.likecount);
- ImageUtils.loadImage(dailyphoto4, post.image, ImageUtils.DEFAULT_MISSING_IMAGE);
+ dailytitle4.setText(post.getTitle());
+ starrating4.setText(post.getRating());
+ dailylikecounter4.setText(post.getLikecount());
+ ImageUtils.loadImage(dailyphoto4, post.getImage(), ImageUtils.DEFAULT_MISSING_IMAGE);
ImageUtils.scaleToFit(dailyphoto4, 280, 174, 30);
break;
default:
diff --git a/src/main/java/com/starlight/controller/ConsultController.java b/src/main/java/com/starlight/controller/ConsultController.java
index bee8239..26c12ac 100644
--- a/src/main/java/com/starlight/controller/ConsultController.java
+++ b/src/main/java/com/starlight/controller/ConsultController.java
@@ -182,7 +182,7 @@ private void addBubble(String message, boolean isUser, boolean saveToHistory) {
// Save to chat history if enabled and not a welcome message
if (saveToHistory && historyManager != null && currentUser != null && !message.contains("Welcome Pal!")) {
- historyManager.addMessage(message, isUser, currentUser.username);
+ historyManager.addMessage(message, isUser, currentUser.getUsername());
}
@@ -334,8 +334,8 @@ private String getBotAvatarPath() {
* Gets the avatar path for the current user.
*/
private String getUserAvatarPath() {
- if (currentUser != null && currentUser.profilepicture != null && !currentUser.profilepicture.isEmpty()) {
- return currentUser.profilepicture;
+ if (currentUser != null && currentUser.getProfilepicture() != null && !currentUser.getProfilepicture().isEmpty()) {
+ return currentUser.getProfilepicture();
}
// Return the same format as used in UserData.xml for default avatar
@@ -487,7 +487,7 @@ public void initialize(URL url, ResourceBundle rb) {
// Start chat history session if user is available
if (currentUser != null && historyManager != null) {
- historyManager.startSession(currentUser.username);
+ historyManager.startSession(currentUser.getUsername());
}
// Load previous chat history if available
@@ -601,7 +601,7 @@ private void startLoadingAnimation(Text dotsText) {
private void loadRecentChatHistory() {
if (!canLoadHistory()) return;
try {
- var histories = historyManager.loadUserHistory(currentUser.username);
+ var histories = historyManager.loadUserHistory(currentUser.getUsername());
int startIndex = computeHistoryStartIndex(histories);
if (histories.size() <= startIndex) return; // Nothing to show
@@ -645,10 +645,10 @@ private void showPreviousSessionHeader(com.starlight.model.ChatHistory history)
private void appendRecentMessages(java.util.List messages, int startIndex) {
for (int i = startIndex; i < messages.size(); i++) {
var msg = messages.get(i);
- if (msg != null && msg.content != null) {
- String content = msg.content.trim();
+ if (msg != null && msg.getContent() != null) {
+ String content = msg.getContent().trim();
if (!content.isEmpty()) {
- addBubble(content, msg.isUser, false);
+ addBubble(content, msg.isUser(), false);
}
}
}
@@ -702,7 +702,7 @@ public void endCurrentSession() {
*/
public void startNewSession() {
if (currentUser != null && historyManager != null) {
- historyManager.startSession(currentUser.username);
+ historyManager.startSession(currentUser.getUsername());
logger.info("New chat session started");
}
}
diff --git a/src/main/java/com/starlight/controller/CreatePostController.java b/src/main/java/com/starlight/controller/CreatePostController.java
index ebfd265..5b38435 100644
--- a/src/main/java/com/starlight/controller/CreatePostController.java
+++ b/src/main/java/com/starlight/controller/CreatePostController.java
@@ -71,6 +71,8 @@ public class CreatePostController implements Initializable {
private final PostDataRepository repository = new PostDataRepository();
private final ChatbotAPI chatbotAPI = new ChatbotAPI();
private final NutritionParser nutritionParser = new NutritionParser();
+ private static final int MAX_RETRIES = 3;
+ private static final int RETRY_DELAY_MS = 2000;
private MainController mainController;
@@ -126,25 +128,25 @@ public void initialize(URL url, ResourceBundle rb) {
// Create a new Post object
Post newPost = new Post();
- newPost.uuid = UUID.randomUUID().toString();
- newPost.username = Session.getCurrentUser().username;
- newPost.profilepicture = "src/main/resources/com/starlight/images/dummy/2.png";
- newPost.title = postTitle;
- newPost.description = postDescription;
- newPost.ingredients = postIngredients;
- newPost.directions = postDirections;
- newPost.uploadtime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+ newPost.setUuid(UUID.randomUUID().toString());
+ newPost.setUsername(Session.getCurrentUser().getUsername());
+ newPost.setProfilepicture("src/main/resources/com/starlight/images/dummy/2.png");
+ newPost.setTitle(postTitle);
+ newPost.setDescription(postDescription);
+ newPost.setIngredients(postIngredients);
+ newPost.setDirections(postDirections);
+ newPost.setUploadtime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
try {
String storedPath = copyImageToUserDir(selectedImage);
- newPost.image = storedPath != null ? storedPath : selectedImage.getAbsolutePath();
+ newPost.setImage(storedPath != null ? storedPath : selectedImage.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
- newPost.image = selectedImage.getAbsolutePath();
+ newPost.setImage(selectedImage.getAbsolutePath());
}
- newPost.likecount = "0";
- newPost.commentcount = "0";
- newPost.isLiked = "false";
- newPost.rating = "0.0";
+ newPost.setLikecount("0");
+ newPost.setCommentcount("0");
+ newPost.setIsLiked("false");
+ newPost.setRating("0.0");
// Close the current dialog first
Stage currentStage = (Stage) submit.getScene().getWindow();
@@ -189,7 +191,7 @@ private String formatTextAsVerticalLineSeparated(String text) {
*/
private String copyImageToUserDir(File image) {
try {
- String username = Session.getCurrentUser() != null ? Session.getCurrentUser().username : "unknown";
+ String username = Session.getCurrentUser() != null ? Session.getCurrentUser().getUsername() : "unknown";
return com.starlight.util.FileSystemManager.copyFileToUserDirectoryWithUniqueFilename(image, username);
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to copy image to user directory: {0}", new Object[]{e.getMessage()});
@@ -204,263 +206,229 @@ private String copyImageToUserDir(File image) {
*/
private void showProcessingAndAnalyzeNutrition(Post newPost, String ingredients) {
try {
- // Create and show processing dialog as a modal popup
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/starlight/view/processingDialog.fxml"));
- Parent root = loader.load();
-
- ProcessingDialogController processingController = loader.getController();
-
- Stage dialogStage = new Stage();
- dialogStage.setTitle("Creating Post");
- dialogStage.initModality(Modality.APPLICATION_MODAL);
- dialogStage.setScene(new Scene(root));
- dialogStage.setResizable(false);
-
- // Set the stage reference in the controller
- processingController.setDialogStage(dialogStage);
-
- // Center the dialog
- dialogStage.centerOnScreen();
-
- // Show the dialog (non-blocking)
- dialogStage.show();
-
- // Create background task for AI analysis
- Task analysisTask = new Task() {
- @Override
- protected Void call() throws Exception {
- final int MAX_RETRIES = 3;
- int attempt = 0;
- Exception lastException = null;
- boolean isApiKeyIssue = false;
- boolean isNetworkIssue = false;
-
- while (attempt < MAX_RETRIES) {
- attempt++;
- final int currentAttempt = attempt; // Final copy for lambda use
-
- try {
- if (logger.isLoggable(Level.INFO)) {
- logger.info(java.text.MessageFormat.format(
- "Analyzing nutrition facts for ingredients (attempt {0}/{1}): {2}",
- currentAttempt, MAX_RETRIES, ingredients));
- }
-
- // Update status on UI thread
- Platform.runLater(() -> {
- try {
- if (currentAttempt == 1) {
- processingController.updateStatus("Analyzing nutrition facts...");
- } else {
- processingController.updateStatus("Analyzing nutrition facts... (retry " + currentAttempt + "/" + MAX_RETRIES + ")");
- }
- } catch (Exception e) {
- logger.log(Level.WARNING, "Failed to update processing status: " + e.getMessage());
- }
- });
-
- String nutritionResponse = chatbotAPI.analyzeNutritionFacts(ingredients);
- newPost.nutrition = nutritionParser.parseNutritionFromResponse(nutritionResponse);
-
- // Check if we got valid nutrition data
- if (newPost.nutrition != null && newPost.nutrition.ingredient != null && !newPost.nutrition.ingredient.isEmpty()) {
- logger.info(java.text.MessageFormat.format(
- "Nutrition analysis completed successfully on attempt {0}", currentAttempt));
-
- // Update status to show success
- Platform.runLater(() -> {
- processingController.updateStatus("Nutrition analysis completed successfully!");
- });
-
- return null; // Success - exit the retry loop
- } else {
- throw new Exception("Received empty or invalid nutrition data from AI");
- } } catch (Exception e) {
- lastException = e;
- String errorMessage = e.getMessage() != null ? e.getMessage().toLowerCase() : "";
-
- // Detect specific error types
- if (errorMessage.contains("api key") || errorMessage.contains("configure your openai api key")) {
- isApiKeyIssue = true;
- logger.log(Level.WARNING, "API Key issue detected: {0}", new Object[]{e.getMessage()});
- logger.log(Level.WARNING, EXCEPTION_DETAILS, e);
- break; // No point retrying API key issues
- } else if (errorMessage.contains("network") || errorMessage.contains("connection") ||
- errorMessage.contains("timeout") || errorMessage.contains("unreachable") ||
- errorMessage.contains("failed to get response")) {
- isNetworkIssue = true;
- logger.log(Level.WARNING, "Network issue detected on attempt {0}: {1}",
- new Object[]{currentAttempt, e.getMessage()});
- logger.log(Level.WARNING, EXCEPTION_DETAILS, e);
- } else {
- logger.log(Level.WARNING, "Nutrition analysis attempt {0} failed: {1}",
- new Object[]{currentAttempt, e.getMessage()});
- logger.log(Level.WARNING, EXCEPTION_DETAILS, e);
- }
-
- if (currentAttempt < MAX_RETRIES && !isApiKeyIssue) {
- // Wait a bit before retrying (unless it's an API key issue)
- Platform.runLater(() -> {
- try {
- processingController.updateStatus("Analysis failed, retrying... (" + (currentAttempt + 1) + "/" + MAX_RETRIES + ")");
- } catch (Exception ex) {
- logger.log(Level.WARNING, "Failed to update retry status: " + ex.getMessage());
- }
- });
-
- try {
- Thread.sleep(2000); // Wait 2 seconds before retry
- } catch (InterruptedException ie) {
- Thread.currentThread().interrupt();
- throw new Exception("Analysis interrupted", ie);
- }
- }
- }
- }
-
- // All retries failed or specific issue detected
- final boolean finalIsApiKeyIssue = isApiKeyIssue;
- final boolean finalIsNetworkIssue = isNetworkIssue;
- final Exception finalException = lastException;
-
- Platform.runLater(() -> {
- try {
- if (finalIsApiKeyIssue) {
- logger.log(Level.WARNING, "Skipping nutrition analysis - API key not configured");
- processingController.updateStatus("API key not configured. Using default values...");
- showPopupDialog("apikey");
- } else if (finalIsNetworkIssue) {
- logger.log(Level.WARNING, "Skipping nutrition analysis - network issues detected");
- processingController.updateStatus("Network issues detected. Using default values...");
- showPopupDialog("network");
- } else {
- logger.log(Level.SEVERE, "All " + MAX_RETRIES + " nutrition analysis attempts failed. Last error: " +
- (finalException != null ? finalException.getMessage() : "Unknown error"));
- processingController.updateStatus("Nutrition analysis failed. Using default values...");
- showPopupDialog("analysis_failed");
- }
- } catch (Exception e) {
- logger.log(Level.WARNING, "Failed to update error status or show warning: " + e.getMessage());
- }
- });
-
- // Use fallback nutrition data
- newPost.nutrition = nutritionParser.parseNutritionFromResponse(null); // Creates fallback nutrition
-
- return null;
- }
-
- @Override
- protected void succeeded() {
- Platform.runLater(() -> {
- try {
- // Update final status
- processingController.updateStatus("Saving post...");
-
- // Save the new post
- List posts = repository.loadPosts();
- posts.add(0, newPost);
- repository.savePosts(posts);
-
- success = true;
- logger.info("Post created and saved successfully");
-
- // Close the processing dialog
- processingController.closeDialog();
-
- // Show success popup
- showResultDialog("post_created_success");
-
- // Refresh the community page instead of navigating away
- if (mainController != null) {
- mainController.refreshCommunityPage();
- }
-
- } catch (Exception e) {
- logger.log(Level.SEVERE, "Failed to save post or refresh community page: " + e.getMessage(), e);
-
- // Close dialog and show failure message
- try {
- processingController.closeDialog();
- showResultDialog("post_creation_failed");
- if (mainController != null) {
- mainController.refreshCommunityPage();
- }
- } catch (Exception fallbackEx) {
- logger.log(Level.SEVERE, "Fallback navigation failed: " + fallbackEx.getMessage(), fallbackEx);
- }
- }
- });
- }
-
- @Override
- protected void failed() {
- Platform.runLater(() -> {
- logger.log(Level.SEVERE, "Nutrition analysis task failed completely", getException());
-
- // Still try to save post without nutrition data
- try {
- processingController.updateStatus("Saving post with default values...");
- newPost.nutrition = nutritionParser.parseNutritionFromResponse(null); // Creates fallback nutrition
- List posts = repository.loadPosts();
- posts.add(0, newPost);
- repository.savePosts(posts);
- success = true;
-
- // Close the processing dialog
- processingController.closeDialog();
-
- // Show success popup (post was still created)
- showResultDialog("post_created_success");
-
- // Refresh the community page
- if (mainController != null) {
- mainController.refreshCommunityPage();
- }
- } catch (Exception e) {
- logger.log(Level.SEVERE, "Failed to save post: " + e.getMessage(), e);
-
- // Close the processing dialog and show failure message
- try {
- processingController.closeDialog();
- showResultDialog("post_creation_failed");
- if (mainController != null) {
- mainController.refreshCommunityPage();
- }
- } catch (Exception ex) {
- logger.log(Level.SEVERE, "Failed to close dialog or refresh community page: " + ex.getMessage(), ex);
- }
- }
- });
- }
- };
-
- // Start the task in a background thread
- Thread analysisThread = new Thread(analysisTask);
- analysisThread.setDaemon(true);
- analysisThread.start();
-
+ ProcessingDialogController processingController = createAndShowProcessingDialog();
+ startNutritionAnalysis(newPost, ingredients, processingController);
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to show processing screen: {0}", new Object[]{e.getMessage()});
logger.log(Level.SEVERE, EXCEPTION_DETAILS, e);
-
- // Fallback: try to save post and navigate directly
- try {
- newPost.nutrition = nutritionParser.parseNutritionFromResponse(null); // Creates fallback nutrition
- List posts = repository.loadPosts();
- posts.add(0, newPost);
- repository.savePosts(posts);
- success = true;
- if (mainController != null) {
- mainController.refreshCommunityPage();
- }
- } catch (Exception fallbackEx) {
- logger.log(Level.SEVERE, "Fallback save and navigation failed: {0}", new Object[]{fallbackEx.getMessage()});
- logger.log(Level.SEVERE, EXCEPTION_DETAILS, fallbackEx);
+ fallbackSavePost(newPost);
+ }
+ }
+
+ private ProcessingDialogController createAndShowProcessingDialog() throws Exception {
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/starlight/view/processingDialog.fxml"));
+ Parent root = loader.load();
+ ProcessingDialogController controller = loader.getController();
+ Stage dialogStage = new Stage();
+ dialogStage.setTitle("Creating Post");
+ dialogStage.initModality(Modality.APPLICATION_MODAL);
+ dialogStage.setScene(new Scene(root));
+ dialogStage.setResizable(false);
+ controller.setDialogStage(dialogStage);
+ dialogStage.centerOnScreen();
+ dialogStage.show();
+ return controller;
+ }
+
+ private void startNutritionAnalysis(Post newPost, String ingredients, ProcessingDialogController processingController) {
+ Task analysisTask = new Task<>() {
+ @Override
+ protected Void call() {
+ AnalysisOutcome outcome = performNutritionAnalysisWithRetries(newPost, ingredients, processingController);
+ handlePostAnalysisOutcome(outcome, newPost, processingController);
+ return null;
+ }
+
+ @Override
+ protected void succeeded() {
+ Platform.runLater(() -> finalizeSuccessfulPostCreation(newPost, processingController));
+ }
+
+ @Override
+ protected void failed() {
+ Platform.runLater(() -> finalizeFailedAnalysis(newPost, processingController, getException()));
+ }
+ };
+ Thread analysisThread = new Thread(analysisTask);
+ analysisThread.setDaemon(true);
+ analysisThread.start();
+ }
+
+ private AnalysisOutcome performNutritionAnalysisWithRetries(Post newPost, String ingredients, ProcessingDialogController processingController) {
+ Exception lastException = null;
+ for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
+ AnalysisOutcome outcome = attemptNutritionAnalysisAttempt(newPost, ingredients, processingController, attempt);
+ if (outcome.type == AnalysisOutcome.Type.SUCCESS) return outcome;
+ if (outcome.type == AnalysisOutcome.Type.API_KEY || outcome.type == AnalysisOutcome.Type.NETWORK || attempt == MAX_RETRIES) {
+ return outcome;
}
+ lastException = outcome.exception;
+ scheduleRetryStatusUpdate(processingController, attempt + 1);
+ sleepQuietly(RETRY_DELAY_MS);
}
+ return AnalysisOutcome.failure(lastException);
}
-
+
+ private AnalysisOutcome attemptNutritionAnalysisAttempt(Post newPost, String ingredients, ProcessingDialogController controller, int attempt) {
+ try {
+ logInfo("Analyzing nutrition facts for ingredients (attempt {0}/{1}): {2}", attempt, MAX_RETRIES, ingredients);
+ updateStatus(controller, attempt == 1 ? "Analyzing nutrition facts..." : java.text.MessageFormat.format("Analyzing nutrition facts... (retry {0}/{1})", attempt, MAX_RETRIES));
+ String response = chatbotAPI.analyzeNutritionFacts(ingredients);
+ newPost.setNutrition(nutritionParser.parseNutritionFromResponse(response));
+ if (hasValidNutrition(newPost)) {
+ logInfo("Nutrition analysis completed successfully on attempt {0}", attempt);
+ updateStatus(controller, "Nutrition analysis completed successfully!");
+ return AnalysisOutcome.success();
+ }
+ throw new InvalidNutritionDataException("Empty or invalid nutrition data");
+ } catch (Exception e) {
+ String msg = e.getMessage() != null ? e.getMessage().toLowerCase() : "";
+ if (isApiKeyIssue(msg)) return AnalysisOutcome.apiKeyIssue(e);
+ if (isNetworkIssue(msg)) return AnalysisOutcome.networkIssue(e);
+ return AnalysisOutcome.failure(e);
+ }
+ }
+
+ private void handlePostAnalysisOutcome(AnalysisOutcome outcome, Post newPost, ProcessingDialogController controller) {
+ if (outcome.type == AnalysisOutcome.Type.SUCCESS) {
+ return;
+ }
+ Platform.runLater(() -> {
+ switch (outcome.type) {
+ case API_KEY:
+ logWarn("Skipping nutrition analysis - API key not configured");
+ controller.updateStatus("API key not configured. Using default values...");
+ showPopupDialog(POPUP_APIKEY);
+ break;
+ case NETWORK:
+ logWarn("Skipping nutrition analysis - network issues detected");
+ controller.updateStatus("Network issues detected. Using default values...");
+ showPopupDialog(POPUP_NETWORK);
+ break;
+ case FAILURE:
+ logSevere("All nutrition analysis attempts failed. Last error: {0}", outcome.exception != null ? outcome.exception.getMessage() : "Unknown");
+ controller.updateStatus("Nutrition analysis failed. Using default values...");
+ showPopupDialog(POPUP_ANALYSIS_FAILED);
+ break;
+ case SUCCESS:
+ default:
+ break; // no action
+ }
+ newPost.setNutrition(nutritionParser.parseNutritionFromResponse(null));
+ });
+ }
+
+ private boolean hasValidNutrition(Post post) {
+ return post.getNutrition() != null && post.getNutrition().getIngredient() != null && !post.getNutrition().getIngredient().isEmpty();
+ }
+
+ private boolean isApiKeyIssue(String msg) {
+ return msg.contains("api key") || msg.contains("configure your openai api key");
+ }
+
+ private boolean isNetworkIssue(String msg) {
+ return msg.contains(POPUP_NETWORK) || msg.contains("connection") || msg.contains("timeout") || msg.contains("unreachable") || msg.contains("failed to get response");
+ }
+
+ private void scheduleRetryStatusUpdate(ProcessingDialogController controller, int nextAttempt) {
+ Platform.runLater(() -> updateStatus(controller, java.text.MessageFormat.format("Analysis failed, retrying... ({0}/{1})", nextAttempt, MAX_RETRIES)));
+ }
+
+ private void sleepQuietly(int ms) {
+ try { Thread.sleep(ms); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); }
+ }
+
+ private void finalizeSuccessfulPostCreation(Post newPost, ProcessingDialogController controller) {
+ try {
+ updateStatus(controller, "Saving post...");
+ saveNewPost(newPost);
+ success = true;
+ controller.closeDialog();
+ showResultDialog(RESULT_POST_CREATED_SUCCESS);
+ refreshCommunity();
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, () -> "Failed to save post or refresh community page: " + e.getMessage());
+ logger.log(Level.SEVERE, EXCEPTION_DETAILS, e);
+ handleFinalizeFailure(controller);
+ }
+ }
+
+ private void finalizeFailedAnalysis(Post newPost, ProcessingDialogController controller, Throwable error) {
+ logger.log(Level.SEVERE, "Nutrition analysis task failed completely", error);
+ try {
+ updateStatus(controller, "Saving post with default values...");
+ newPost.setNutrition(nutritionParser.parseNutritionFromResponse(null));
+ saveNewPost(newPost);
+ success = true;
+ controller.closeDialog();
+ showResultDialog(RESULT_POST_CREATED_SUCCESS);
+ refreshCommunity();
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, () -> "Failed to save post: " + e.getMessage());
+ logger.log(Level.SEVERE, EXCEPTION_DETAILS, e);
+ handleFinalizeFailure(controller);
+ }
+ }
+
+ private void handleFinalizeFailure(ProcessingDialogController controller) {
+ try {
+ controller.closeDialog();
+ showResultDialog(RESULT_POST_CREATION_FAILED);
+ refreshCommunity();
+ } catch (Exception fallbackEx) {
+ logger.log(Level.SEVERE, () -> "Fallback navigation failed: " + fallbackEx.getMessage());
+ }
+ }
+
+ private void saveNewPost(Post post) {
+ List posts = repository.loadPosts();
+ posts.add(0, post);
+ repository.savePosts(posts);
+ }
+
+ private void refreshCommunity() {
+ if (mainController != null) {
+ mainController.refreshCommunityPage();
+ }
+ }
+
+ private void fallbackSavePost(Post newPost) {
+ try {
+ newPost.setNutrition(nutritionParser.parseNutritionFromResponse(null));
+ saveNewPost(newPost);
+ success = true;
+ refreshCommunity();
+ } catch (Exception fallbackEx) {
+ logger.log(Level.SEVERE, () -> "Fallback save and navigation failed: " + fallbackEx.getMessage());
+ logger.log(Level.SEVERE, EXCEPTION_DETAILS, fallbackEx);
+ }
+ }
+
+ private void logWarn(String pattern, Object... args) { if (logger.isLoggable(Level.WARNING)) logger.log(Level.WARNING, java.text.MessageFormat.format(pattern, args)); }
+ private void logSevere(String pattern, Object... args) { if (logger.isLoggable(Level.SEVERE)) logger.log(Level.SEVERE, java.text.MessageFormat.format(pattern, args)); }
+ private void logInfo(String pattern, Object... args) { if (logger.isLoggable(Level.INFO)) logger.log(Level.INFO, java.text.MessageFormat.format(pattern, args)); }
+
+ private void updateStatus(ProcessingDialogController controller, String message) {
+ try { controller.updateStatus(message); } catch (Exception ignored) { /* dialog may be closed */ }
+ }
+
+ private static class AnalysisOutcome {
+ enum Type { SUCCESS, API_KEY, NETWORK, FAILURE }
+ final Type type;
+ final Exception exception;
+ private AnalysisOutcome(Type type, Exception exception) { this.type = type; this.exception = exception; }
+ static AnalysisOutcome success() { return new AnalysisOutcome(Type.SUCCESS, null); }
+ static AnalysisOutcome apiKeyIssue(Exception e) { return new AnalysisOutcome(Type.API_KEY, e); }
+ static AnalysisOutcome networkIssue(Exception e) { return new AnalysisOutcome(Type.NETWORK, e); }
+ static AnalysisOutcome failure(Exception e) { return new AnalysisOutcome(Type.FAILURE, e); }
+ }
+ private static class InvalidNutritionDataException extends Exception { InvalidNutritionDataException(String msg) { super(msg); } }
+ private static final String POPUP_APIKEY = "apikey";
+ private static final String POPUP_NETWORK = "network";
+ private static final String POPUP_ANALYSIS_FAILED = "analysis_failed";
+ private static final String RESULT_POST_CREATED_SUCCESS = "post_created_success";
+ private static final String RESULT_POST_CREATION_FAILED = "post_creation_failed";
+ private static final String RESULT_NUTRITION_ANALYSIS_SUCCESS = "nutrition_analysis_success";
/**
* Shows a popup dialog for nutrition analysis issues
*/
@@ -473,18 +441,10 @@ private void showPopupDialog(String warningType) {
// Set appropriate warning message based on type
switch (warningType) {
- case "apikey":
- controller.setApiKeyWarning();
- break;
- case "network":
- controller.setNetworkWarning();
- break;
- case "analysis_failed":
- controller.setAnalysisFailedWarning();
- break;
- default:
- controller.setMessage("An error occurred during nutrition analysis. Using default values.");
- break;
+ case POPUP_APIKEY -> controller.setApiKeyWarning();
+ case POPUP_NETWORK -> controller.setNetworkWarning();
+ case POPUP_ANALYSIS_FAILED -> controller.setAnalysisFailedWarning();
+ default -> controller.setMessage("An error occurred during nutrition analysis. Using default values.");
}
Stage dialogStage = new Stage();
@@ -519,18 +479,10 @@ private void showResultDialog(String resultType) {
// Set appropriate message based on result type
switch (resultType) {
- case "post_created_success":
- controller.setPostCreatedSuccess();
- break;
- case "post_creation_failed":
- controller.setPostCreationFailed();
- break;
- case "nutrition_analysis_success":
- controller.setNutritionAnalysisSuccess();
- break;
- default:
- controller.setMessage("Operation completed.");
- break;
+ case RESULT_POST_CREATED_SUCCESS -> controller.setPostCreatedSuccess();
+ case RESULT_POST_CREATION_FAILED -> controller.setPostCreationFailed();
+ case RESULT_NUTRITION_ANALYSIS_SUCCESS -> controller.setNutritionAnalysisSuccess();
+ default -> controller.setMessage("Operation completed.");
}
Stage dialogStage = new Stage();
diff --git a/src/main/java/com/starlight/controller/DeleteDialogController.java b/src/main/java/com/starlight/controller/DeleteDialogController.java
index 518f63e..8acb6cb 100644
--- a/src/main/java/com/starlight/controller/DeleteDialogController.java
+++ b/src/main/java/com/starlight/controller/DeleteDialogController.java
@@ -69,7 +69,7 @@ private void handleDeletePost(Post postToDelete) {
// Remove the post from the repository
try {
List posts = repository.loadPosts();
- posts.removeIf(post -> post.uuid != null && post.uuid.equals(postToDelete.uuid));
+ posts.removeIf(post -> post.getUuid() != null && post.getUuid().equals(postToDelete.getUuid()));
repository.savePosts(posts);
confirmed = true;
diff --git a/src/main/java/com/starlight/controller/EditPostController.java b/src/main/java/com/starlight/controller/EditPostController.java
index 6ad6c54..d020eba 100644
--- a/src/main/java/com/starlight/controller/EditPostController.java
+++ b/src/main/java/com/starlight/controller/EditPostController.java
@@ -79,15 +79,14 @@ public void setPost(Post post) {
*/
private void populateFields() {
if (currentPost == null) return;
+ title.setText(currentPost.getTitle() != null ? currentPost.getTitle() : "");
+ description.setText(currentPost.getDescription() != null ? currentPost.getDescription() : "");
+ ingredients.setText(currentPost.getIngredients() != null ? currentPost.getIngredients() : "");
+ directions.setText(currentPost.getDirections() != null ? currentPost.getDirections() : "");
- title.setText(currentPost.title != null ? currentPost.title : "");
- description.setText(currentPost.description != null ? currentPost.description : "");
- ingredients.setText(currentPost.ingredients != null ? currentPost.ingredients : "");
- directions.setText(currentPost.directions != null ? currentPost.directions : "");
-
// Set image status
- if (currentPost.image != null && !currentPost.image.isEmpty()) {
- pickerstatus.setText("Current: " + new File(currentPost.image).getName());
+ if (currentPost.getImage() != null && !currentPost.getImage().isEmpty()) {
+ pickerstatus.setText("Current: " + new File(currentPost.getImage()).getName());
} else {
pickerstatus.setText(NO_IMAGE_SELECTED);
}
@@ -98,7 +97,7 @@ private void populateFields() {
*/
private String copyImageToUserDir(File image) {
try {
- String username = Session.getCurrentUser() != null ? Session.getCurrentUser().username : "unknown";
+ String username = Session.getCurrentUser() != null ? Session.getCurrentUser().getUsername() : "unknown";
return com.starlight.util.FileSystemManager.copyFileToUserDirectoryWithUniqueFilename(image, username);
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to copy image to user directory: {0}", new Object[]{e.getMessage()});
@@ -141,8 +140,8 @@ private void setupImagePicker() {
/** If current post has an image keep showing it, else show no image message. */
private void restoreOrShowNoImage() {
- if (currentPost != null && currentPost.image != null && !currentPost.image.isEmpty()) {
- pickerstatus.setText("Current: " + new File(currentPost.image).getName());
+ if (currentPost != null && currentPost.getImage() != null && !currentPost.getImage().isEmpty()) {
+ pickerstatus.setText("Current: " + new File(currentPost.getImage()).getName());
} else {
pickerstatus.setText(NO_IMAGE_SELECTED);
}
@@ -179,20 +178,20 @@ private boolean validateFormInputs() {
private boolean isEmpty(TextArea area) { return area.getText() == null || area.getText().isEmpty(); }
private void applyFormToPost() {
- currentPost.title = title.getText();
- currentPost.description = description.getText();
- currentPost.ingredients = ingredients.getText();
- currentPost.directions = directions.getText();
+ currentPost.setTitle(title.getText());
+ currentPost.setDescription(description.getText());
+ currentPost.setIngredients(ingredients.getText());
+ currentPost.setDirections(directions.getText());
}
private void handleImageUpdate() {
if (selectedImage == null) return;
try {
String storedPath = copyImageToUserDir(selectedImage);
- currentPost.image = storedPath != null ? storedPath : selectedImage.getAbsolutePath();
+ currentPost.setImage(storedPath != null ? storedPath : selectedImage.getAbsolutePath());
} catch (Exception e) {
logger.log(Level.WARNING, "Image copy failed, using original path: {0}", new Object[]{e.getMessage()});
- currentPost.image = selectedImage.getAbsolutePath();
+ currentPost.setImage(selectedImage.getAbsolutePath());
}
}
@@ -216,7 +215,7 @@ private void persistPostAndShowResult() {
private void replacePostInList(List posts, Post updated) {
for (int i = 0; i < posts.size(); i++) {
Post p = posts.get(i);
- if (p.uuid != null && p.uuid.equals(updated.uuid)) {
+ if (p.getUuid() != null && p.getUuid().equals(updated.getUuid())) {
posts.set(i, updated);
return;
}
diff --git a/src/main/java/com/starlight/controller/EditProfileController.java b/src/main/java/com/starlight/controller/EditProfileController.java
index 8828af3..f31cc9d 100644
--- a/src/main/java/com/starlight/controller/EditProfileController.java
+++ b/src/main/java/com/starlight/controller/EditProfileController.java
@@ -38,8 +38,15 @@
*/
public class EditProfileController implements Initializable {
private static final Logger logger = Logger.getLogger(EditProfileController.class.getName());
+
+ // Dialog result type constants
+ private static final String ACCOUNT_UPDATED_SUCCESS = "account_updated_success";
+ private static final String ACCOUNT_UPDATE_FAILED = "account_update_failed";
+ private static final String ACCOUNT_DELETED_SUCCESS = "account_deleted_success";
+ private static final String ACCOUNT_DELETION_FAILED = "account_deletion_failed";
+
@FXML
- private ImageView Image;
+ private ImageView image;
@FXML
private MFXButton imagepicker;
@@ -68,17 +75,17 @@ public class EditProfileController implements Initializable {
public void setUser(User user) {
this.currentUser = user;
- if (welcomeLabel != null) welcomeLabel.setText("Hello, " + user.username);
- if (emailField != null) emailField.setText(user.email);
- if (passwordField != null) passwordField.setText(user.password);
- if (birthDayPicker != null && user.birthDay != null && !user.birthDay.isEmpty()) {
- birthDayPicker.setValue(java.time.LocalDate.parse(user.birthDay));
+ if (welcomeLabel != null) welcomeLabel.setText("Hello, " + user.getUsername());
+ if (emailField != null) emailField.setText(user.getEmail());
+ if (passwordField != null) passwordField.setText(user.getPassword());
+ if (birthDayPicker != null && user.getBirthDay() != null && !user.getBirthDay().isEmpty()) {
+ birthDayPicker.setValue(java.time.LocalDate.parse(user.getBirthDay()));
}
// Load and scale profile image using ImageUtils
- if (Image != null) {
- ImageUtils.loadImage(Image, user.profilepicture, ImageUtils.DEFAULT_MISSING_IMAGE);
- ImageUtils.scaleToFit(Image, 170, 170, 200);
+ if (image != null) {
+ ImageUtils.loadImage(image, user.getProfilepicture(), ImageUtils.DEFAULT_MISSING_IMAGE);
+ ImageUtils.scaleToFit(image, 170, 170, 200);
}
}
@@ -100,10 +107,10 @@ public void setPreviousStage(Stage stage) {
*/
private void loadCurrentUserProfileImage() {
User currentSessionUser = Session.getCurrentUser();
- if (currentSessionUser != null && Image != null) {
+ if (currentSessionUser != null && image != null) {
// Load profile image using ImageUtils
- ImageUtils.loadImage(Image, currentSessionUser.profilepicture, ImageUtils.DEFAULT_MISSING_IMAGE);
- ImageUtils.scaleToFit(Image, 170, 170, 85); // Circular profile image with rounded corners
+ ImageUtils.loadImage(image, currentSessionUser.getProfilepicture(), ImageUtils.DEFAULT_MISSING_IMAGE);
+ ImageUtils.scaleToFit(image, 170, 170, 85); // Circular profile image with rounded corners
}
}
@@ -143,7 +150,7 @@ private void updateProfileImage(File selectedFile) {
// Copy the selected file to the user's directory
String copiedFilePath = com.starlight.util.FileSystemManager.copyFileToUserDirectoryWithUniqueFilename(
- selectedFile, currentSessionUser.username);
+ selectedFile, currentSessionUser.getUsername());
if (copiedFilePath == null) {
logger.warning("Failed to copy image file to user directory");
@@ -151,19 +158,19 @@ private void updateProfileImage(File selectedFile) {
}
// Update the current user's profile picture path
- currentSessionUser.profilepicture = copiedFilePath;
+ currentSessionUser.setProfilepicture(copiedFilePath);
// Also update currentUser if it's set
if (currentUser != null) {
- currentUser.profilepicture = copiedFilePath;
+ currentUser.setProfilepicture(copiedFilePath);
}
// Update the UserData XML file
com.starlight.repository.UserDataRepository repo = new com.starlight.repository.UserDataRepository();
java.util.List users = repo.loadUsers();
for (com.starlight.model.User u : users) {
- if (u.username.equals(currentSessionUser.username)) {
- u.profilepicture = copiedFilePath;
+ if (u.getUsername().equals(currentSessionUser.getUsername())) {
+ u.setProfilepicture(copiedFilePath);
break;
}
}
@@ -172,9 +179,9 @@ private void updateProfileImage(File selectedFile) {
// Refresh the profile image display
loadCurrentUserProfileImage();
- logger.info("Profile image updated successfully: " + copiedFilePath);
+ logger.info(() -> "Profile image updated successfully: " + copiedFilePath);
} catch (Exception e) {
- logger.warning("Failed to update profile image: " + e.getMessage());
+ logger.warning(() -> "Failed to update profile image: " + e.getMessage());
e.printStackTrace();
}
}
@@ -189,15 +196,15 @@ public void initialize(URL url, ResourceBundle rb) {
String newPass = passwordField.getText();
String birth = birthDayPicker.getValue() != null ? birthDayPicker.getValue().toString() : null;
try {
- URL endpoint = new URL("http://localhost:8000/users/" + currentUser.username);
+ URL endpoint = new URL("http://localhost:8000/users/" + currentUser.getUsername());
HttpURLConnection conn = (HttpURLConnection) endpoint.openConnection();
conn.setRequestMethod("PUT");
conn.setRequestProperty("Content-Type", "application/xml");
conn.setDoOutput(true);
User u = new User();
- u.email = newEmail;
- u.password = newPass;
- u.birthDay = birth;
+ u.setEmail(newEmail);
+ u.setPassword(newPass);
+ u.setBirthDay(birth);
XStream xs = new XStream(new DomDriver());
xs.allowTypesByWildcard(new String[]{"com.starlight.model.*"});
xs.alias("user", User.class);
@@ -207,14 +214,15 @@ public void initialize(URL url, ResourceBundle rb) {
}
if (conn.getResponseCode() == 200) {
logger.info("User updated successfully");
- showResultDialog("account_updated_success");
+ showResultDialog(ACCOUNT_UPDATED_SUCCESS);
} else {
- logger.info("Update failed: " + conn.getResponseCode());
- showResultDialog("account_update_failed");
+ int responseCode = conn.getResponseCode();
+ logger.info(() -> "Update failed: " + responseCode);
+ showResultDialog(ACCOUNT_UPDATE_FAILED);
}
} catch (Exception e) {
- logger.log(Level.SEVERE, "Failed to update user: " + e.getMessage(), e);
- showResultDialog("account_update_failed");
+ logger.log(Level.SEVERE, "Failed to update user: {0}", e.getMessage());
+ showResultDialog(ACCOUNT_UPDATE_FAILED);
}
});
@@ -223,7 +231,7 @@ public void initialize(URL url, ResourceBundle rb) {
User currentSessionUser = Session.getCurrentUser();
if (currentSessionUser != null && welcomeLabel != null) {
- welcomeLabel.setText("Hello, " + currentSessionUser.username);
+ welcomeLabel.setText("Hello, " + currentSessionUser.getUsername());
}
// Load the current user's profile image
@@ -242,18 +250,18 @@ private void onUpdate(javafx.event.ActionEvent event) {
com.starlight.repository.UserDataRepository repo = new com.starlight.repository.UserDataRepository();
java.util.List users = repo.loadUsers();
for (com.starlight.model.User u : users) {
- if (u.username.equals(currentUser.username)) {
- u.email = email;
- u.password = password;
- u.birthDay = birthDay != null ? birthDay.toString() : null;
+ if (u.getUsername().equals(currentUser.getUsername())) {
+ u.setEmail(email);
+ u.setPassword(password);
+ u.setBirthDay(birthDay != null ? birthDay.toString() : null);
break;
}
}
repo.saveUsers(users);
logger.info("User updated successfully");
- currentUser.email = email;
- currentUser.password = password;
- currentUser.birthDay = birthDay != null ? birthDay.toString() : null;
+ currentUser.setEmail(email);
+ currentUser.setPassword(password);
+ currentUser.setBirthDay(birthDay != null ? birthDay.toString() : null);
// Refresh the profile image in case it was updated
loadCurrentUserProfileImage();
@@ -264,11 +272,11 @@ public void onDelete(javafx.event.ActionEvent event) {
try {
// Remove user from UserDataRepository using the new deleteUser method
com.starlight.repository.UserDataRepository repo = new com.starlight.repository.UserDataRepository();
- boolean userDeleted = repo.deleteUser(currentUser.username);
+ boolean userDeleted = repo.deleteUser(currentUser.getUsername());
if (!userDeleted) {
logger.warning("User could not be deleted.");
- showResultDialog("account_deletion_failed");
+ showResultDialog(ACCOUNT_DELETION_FAILED);
return;
}
@@ -281,7 +289,7 @@ public void onDelete(javafx.event.ActionEvent event) {
}
// Show success message first
- showResultDialog("account_deleted_success");
+ showResultDialog(ACCOUNT_DELETED_SUCCESS);
// Load the authorization view
FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/starlight/view/Authorization.fxml"));
@@ -300,8 +308,8 @@ public void onDelete(javafx.event.ActionEvent event) {
logger.info("User account deleted successfully");
} catch (IOException e) {
- logger.log(Level.WARNING, "Failed to load authorization view: " + e.getMessage(), e);
- showResultDialog("account_deletion_failed");
+ logger.log(Level.WARNING, "Failed to load authorization view: {0}", e.getMessage());
+ showResultDialog(ACCOUNT_DELETION_FAILED);
// Fallback to previous behavior if authorization view can't be loaded
Stage currentStage = (Stage) deleteaccbutton.getScene().getWindow();
@@ -313,8 +321,8 @@ public void onDelete(javafx.event.ActionEvent event) {
previousStage.requestFocus();
}
} catch (Exception e) {
- logger.log(Level.SEVERE, "Failed to delete account: " + e.getMessage(), e);
- showResultDialog("account_deletion_failed");
+ logger.log(Level.SEVERE, "Failed to delete account: {0}", e.getMessage());
+ showResultDialog(ACCOUNT_DELETION_FAILED);
}
}
@@ -330,16 +338,16 @@ private void showResultDialog(String resultType) {
// Set appropriate message based on result type
switch (resultType) {
- case "account_updated_success":
+ case ACCOUNT_UPDATED_SUCCESS:
controller.setAccountUpdatedSuccess();
break;
- case "account_update_failed":
+ case ACCOUNT_UPDATE_FAILED:
controller.setAccountUpdateFailed();
break;
- case "account_deleted_success":
+ case ACCOUNT_DELETED_SUCCESS:
controller.setAccountDeletedSuccess();
break;
- case "account_deletion_failed":
+ case ACCOUNT_DELETION_FAILED:
controller.setAccountDeletionFailed();
break;
default:
@@ -360,7 +368,7 @@ private void showResultDialog(String resultType) {
dialogStage.showAndWait();
} catch (Exception e) {
- logger.log(Level.SEVERE, "Failed to show result dialog: " + e.getMessage(), e);
+ logger.log(Level.SEVERE, "Failed to show result dialog: {0}", e.getMessage());
}
}
diff --git a/src/main/java/com/starlight/controller/LoginViewController.java b/src/main/java/com/starlight/controller/LoginViewController.java
index 314a840..eeb06f7 100644
--- a/src/main/java/com/starlight/controller/LoginViewController.java
+++ b/src/main/java/com/starlight/controller/LoginViewController.java
@@ -98,7 +98,7 @@ private void performLogin() {
try {
User logged = apiClient.login(em, pass);
Session.setCurrentUser(logged);
- logger.info("Login success for user: " + logged.username);
+ logger.info("Login success for user: " + logged.getUsername());
// Ensure we redirect to main.fxml after successful login
App.loadMainWithSplash();
diff --git a/src/main/java/com/starlight/controller/MainController.java b/src/main/java/com/starlight/controller/MainController.java
index a3010bb..37787ea 100644
--- a/src/main/java/com/starlight/controller/MainController.java
+++ b/src/main/java/com/starlight/controller/MainController.java
@@ -6,6 +6,7 @@
import java.util.logging.Logger;
import com.starlight.model.Post;
+import java.util.function.Consumer;
import com.starlight.util.Session;
import io.github.palexdev.materialfx.controls.MFXButton;
@@ -75,9 +76,8 @@ public class MainController implements Initializable {
private NavbarController navbarController;
private MFXButton currentActiveButton;
-
- private Post currentPost; // Current post for navigation to post view
-
+ // PostController setup handled via openPost initializer when needed
+
@FXML
private BorderPane bp;
@@ -117,42 +117,42 @@ void home(MouseEvent event) {
/** Handles navigation to the achievement view. */
@FXML
void achievement(MouseEvent event) {
- loadPage("underDevelopment");
+ loadPage(PAGE_UNDER_DEVELOPMENT);
selected(achievement);
}
/** Handles navigation to the community view. */
@FXML
void community(MouseEvent event) {
- loadPage("community");
+ loadPage(PAGE_COMMUNITY);
selected(community);
}
/** Handles navigation to the consult view. */
@FXML
void consult(MouseEvent event) {
- loadPage("consult");
+ loadPage(PAGE_CONSULT);
selected(consult);
}
/** Handles navigation to the games view. */
@FXML
void games(MouseEvent event) {
- loadPage("underDevelopment");
+ loadPage(PAGE_UNDER_DEVELOPMENT);
selected(games);
}
/** Handles navigation to the mission view. */
@FXML
void mission(MouseEvent event) {
- loadPage("underDevelopment");
+ loadPage(PAGE_UNDER_DEVELOPMENT);
selected(mission);
}
/** Handles navigation to the wiki view. */
@FXML
void wiki(MouseEvent event) {
- loadPage("underDevelopment");
+ loadPage(PAGE_UNDER_DEVELOPMENT);
selected(wiki);
}
@@ -160,7 +160,7 @@ void wiki(MouseEvent event) {
@FXML
void recipeManager(MouseEvent event) {
// This should only be called if button is visible (admin user logged in)
- loadPage("recipeManager");
+ loadPage(PAGE_RECIPE_MANAGER);
selected(recipeManager);
}
@@ -168,7 +168,7 @@ void recipeManager(MouseEvent event) {
@FXML
void userManager(MouseEvent event) {
// This should only be called if button is visible (admin user logged in)
- loadPage("userManager");
+ loadPage(PAGE_USER_MANAGER);
selected(userManager);
}
@@ -179,9 +179,9 @@ private boolean isCurrentUserAdmin() {
if (Session.getCurrentUser() == null) {
return false;
}
-
- String username = Session.getCurrentUser().username;
- return username != null && username.toLowerCase().equals("admin");
+
+ String username = Session.getCurrentUser().getUsername();
+ return username != null && username.equalsIgnoreCase("admin");
}
/**
@@ -224,7 +224,7 @@ public void refreshNavigationState() {
*/
public void refreshCommunityPage() {
if (bp.getCenter() != null) {
- Object controller = bp.getCenter().getProperties().get("controller");
+ Object controller = bp.getCenter().getProperties().get(PROP_CONTROLLER);
if (controller instanceof CommunityController) {
((CommunityController) controller).refreshPosts();
}
@@ -234,55 +234,31 @@ public void refreshCommunityPage() {
/**
* Loads the given FXML page into the grid pane container.
*/
- void loadPage(String page) {
+ private static final String PAGE_UNDER_DEVELOPMENT = "underDevelopment";
+ private static final String PAGE_COMMUNITY = "community";
+ private static final String PAGE_CONSULT = "consult";
+ private static final String PAGE_RECIPE_MANAGER = "recipeManager";
+ private static final String PAGE_USER_MANAGER = "userManager";
+ private static final String PROP_CONTROLLER = "controller";
+ private static final String STYLE_ACTIVE = "button-active";
+ private static final String STYLE_SIDEBAR = "button-sidebar";
+
+ void loadPage(String page) { loadPage(page, null); }
+
+ private void loadPage(String page, Consumer