Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .sonarcloud.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ sonar.tests=src/test/java
sonar.java.source=21

# Exclusions
sonar.exclusions=**/generated/**,**/target/**
sonar.exclusions=**/generated/**,**/target/**,**/src/main/resources/styles/*.css
sonar.test.exclusions=**/test/**

# Source encoding
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.dinosaur.dinosaurexploder.model;

/**
* Model class representing a GitHub contributor. Immutable data object following clean architecture
* principles.
*/
public class GitHubContributor {
private final String login;
private final String avatarUrl;
private final int contributions;
private final String htmlUrl;

public GitHubContributor(String login, String avatarUrl, int contributions, String htmlUrl) {
this.login = login;
this.avatarUrl = avatarUrl;
this.contributions = contributions;
this.htmlUrl = htmlUrl;
}

public String getLogin() {
return login;
}

public String getAvatarUrl() {
return avatarUrl;
}

public int getContributions() {
return contributions;
}

public String getHtmlUrl() {
return htmlUrl;
}

@Override
public String toString() {
return "GitHubContributor{"
+ "login='"
+ login
+ '\''
+ ", contributions="
+ contributions
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.dinosaur.dinosaurexploder.utils;

import com.dinosaur.dinosaurexploder.model.GitHubContributor;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Service provider for fetching GitHub repository contributors. Handles asynchronous API
* communication with proper error handling.
*/
public class GitHubProvider {
private static final Logger LOGGER = Logger.getLogger(GitHubProvider.class.getName());
private static final String GITHUB_API_URL =
"https://api.github.com/repos/jvondermarck/dinosaur-exploder/contributors";
private static final int TIMEOUT_MS = 10000;

private final ObjectMapper objectMapper;

public GitHubProvider() {
this.objectMapper = new ObjectMapper();
}

public CompletableFuture<List<GitHubContributor>> fetchContributorsAsync() {
return CompletableFuture.supplyAsync(this::fetchContributors);
}

private List<GitHubContributor> fetchContributors() {
List<GitHubContributor> contributors = new ArrayList<>();

try {
URL url = new URL(GITHUB_API_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(TIMEOUT_MS);
connection.setReadTimeout(TIMEOUT_MS);
connection.setRequestProperty("Accept", "application/vnd.github.v3+json");
connection.setRequestProperty("User-Agent", "Dinosaur-Exploder-Game");

int responseCode = connection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}

contributors = parseContributors(response.toString());
LOGGER.log(Level.INFO, "Successfully fetched {0} contributors", contributors.size());
}
} else {
LOGGER.log(Level.WARNING, "GitHub API returned response code: {0}", responseCode);
}

connection.disconnect();

} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error fetching contributors from GitHub API", e);
}

return contributors;
}

private List<GitHubContributor> parseContributors(String jsonResponse) {
List<GitHubContributor> contributors = new ArrayList<>();

try {
JsonNode rootNode = objectMapper.readTree(jsonResponse);

for (JsonNode node : rootNode) {
String login = node.get("login").asText();
String avatarUrl = node.get("avatar_url").asText();
int contributions = node.get("contributions").asInt();
String htmlUrl = node.get("html_url").asText();

contributors.add(new GitHubContributor(login, avatarUrl, contributions, htmlUrl));
}

} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error parsing contributor JSON", e);
}

return contributors;
}
}
46 changes: 46 additions & 0 deletions src/main/java/com/dinosaur/dinosaurexploder/utils/MenuHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
import com.dinosaur.dinosaurexploder.constants.GameConstants;
import java.io.InputStream;
import java.util.Objects;
import javafx.animation.FadeTransition;
import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.util.Duration;

Expand Down Expand Up @@ -117,4 +121,46 @@ public static void setupSelectionMenu(

menu.getContentRoot().getChildren().addAll(background, container);
}

public static Text createSubtitle(String text, double wrapWidth, boolean animated) {
Text subtitle =
getUIFactoryService()
.newText(text, Color.rgb(0, 255, 100), FontType.MONO, GameConstants.TEXT_SUB_DETAILS);

subtitle.setWrappingWidth(wrapWidth);
subtitle.setTextAlignment(javafx.scene.text.TextAlignment.CENTER);

DropShadow glow = new DropShadow();
glow.setColor(Color.rgb(0, 255, 100));
glow.setRadius(6);
glow.setSpread(0.25);
subtitle.setEffect(glow);

if (animated) {
FadeTransition pulse = new FadeTransition(Duration.seconds(2.2), subtitle);
pulse.setFromValue(0.8);
pulse.setToValue(1.0);
pulse.setCycleCount(FadeTransition.INDEFINITE);
pulse.setAutoReverse(true);
pulse.setInterpolator(Interpolator.EASE_BOTH);
pulse.play();
}

return subtitle;
}

public static Button createStyledButton(String text) {
Button button = new Button(text.toUpperCase());
button.setMinSize(140, 60);
applyButtonStyle(button);
return button;
}

public static void applyButtonStyle(Button button) {
button
.getStylesheets()
.add(
Objects.requireNonNull(MenuHelper.class.getResource(GameConstants.STYLESHEET_PATH))
.toExternalForm());
}
}
Loading