Skip to content
Merged
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
9 changes: 7 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import org.panteleyev.jpackage.ImageType
import org.springframework.boot.gradle.plugin.SpringBootPlugin

plugins {
id 'java'
id 'org.springframework.boot' version '3.4.5'
Expand Down Expand Up @@ -29,7 +32,11 @@ repositories {
}

dependencies {
implementation(platform(SpringBootPlugin.BOM_COORDINATES))
implementation 'org.springframework.boot:spring-boot-starter'
implementation('org.springframework.boot:spring-boot-starter-webflux') {
exclude group: 'io.netty', module: 'netty-transport-native-epoll'
}
implementation 'commons-io:commons-io:2.19.0'
implementation 'com.google.code.gson:gson:2.13.1'
implementation 'org.apache.commons:commons-lang3:3.17.0'
Expand Down Expand Up @@ -63,8 +70,6 @@ tasks.register('copyInstaller', Copy) {
doNotTrackState('Copying installation file need to re-run every time')
}

import org.panteleyev.jpackage.ImageType

jpackage {
dependsOn deleteLibs, cleanJpackageWorkdir, bootJar, copyInstaller
mustRunAfter deleteLibs
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.synops.replayreader.core;

import com.synops.replayreader.common.i18n.I18nUtils;
import com.synops.replayreader.ui.util.UiUtil;
import java.io.IOException;
import java.util.Objects;
import java.util.ResourceBundle;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
Expand Down Expand Up @@ -45,7 +44,7 @@ public void onApplicationEvent(StageReadyEvent event) {
stage.setScene(new Scene(parent, applicationWidth, applicationHeight));
stage.setResizable(false);
stage.setTitle(resourceBundle.getString("main.title"));
stage.getIcons().add(new Image(Objects.requireNonNull(stage.getClass().getResourceAsStream("/image/icon.png"))));
UiUtil.setDefaultIcon(stage);
stage.show();
} catch (IOException e) {
throw new RuntimeException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.synops.replayreader.core.configuration;

import com.synops.replayreader.clan.comparator.ClanListComparator;
import com.synops.replayreader.core.service.DialogService;
import com.synops.replayreader.core.service.DialogServiceImpl;
import com.synops.replayreader.maps.comparator.MapsComparator;
import com.synops.replayreader.common.comparator.SortingComparators;
import com.synops.replayreader.replay.controller.ReplayController;
Expand All @@ -21,6 +23,11 @@ public ReplayService replayService(ReplayController replayController) {
return new ReplayServiceImpl(replayController);
}

@Bean
public DialogService dialogService() {
return new DialogServiceImpl();
}

@Bean
public ClanStringConverter clanStringConverter() {
return new ClanStringConverter();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.synops.replayreader.core.service;

import javafx.scene.control.Alert.AlertType;

public interface DialogService {

void showAlertError(Throwable throwable);

void alert(AlertType alertType, String message);

void alertConfirm(String message, Runnable runnable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.synops.replayreader.core.service;

import static javafx.scene.control.Alert.AlertType.ERROR;

import com.synops.replayreader.ui.util.UiUtil;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class DialogServiceImpl implements DialogService {

private static final Logger LOGGER = LoggerFactory.getLogger(DialogServiceImpl.class);

public void showAlertError(Throwable throwable) {
Platform.runLater(() -> buildAlert(ERROR, "Error",
throwable.getClass().getSimpleName() + ": " + throwable.getMessage(),
ExceptionUtils.getStackTrace(throwable)).showAndWait());
LOGGER.error("Error {}", throwable.getMessage(), throwable);
}

public void alert(AlertType alertType, String message) {
var alert = buildAlert(alertType, null, message, null);
alert.showAndWait();
}

public void alertConfirm(String message, Runnable runnable) {
var alert = buildAlert(AlertType.CONFIRMATION, null, message, null);
alert.showAndWait().filter(response -> response == ButtonType.OK)
.ifPresent(response -> runnable.run());
}

private Alert buildAlert(AlertType alertType, String title, String message, String stackTrace) {
var alert = new Alert(alertType);
var stage = (Stage) alert.getDialogPane().getScene().getWindow();
UiUtil.setDefaultIcon(stage);

if (title != null) {
alert.setTitle(title);
}
alert.setHeaderText(null);

var vbox = new VBox();
var hbox = new HBox();
hbox.setAlignment(Pos.TOP_RIGHT);

var textArea = new TextArea();
textArea.setWrapText(true);
textArea.setEditable(false);
textArea.setText(message);
textArea.setPrefHeight(StringUtils.defaultString(message).length() < 120 ? 60 : 100);
vbox.setPadding(new Insets(14.0));
vbox.getChildren().addAll(hbox, textArea);
alert.getDialogPane().setContent(vbox);

if (stackTrace != null) {
var stackTraceTextArea = new TextArea(stackTrace);
stackTraceTextArea.setWrapText(false);
stackTraceTextArea.setEditable(false);
stackTraceTextArea.setMaxWidth(Double.MAX_VALUE);
stackTraceTextArea.setMaxHeight(Double.MAX_VALUE);
GridPane.setHgrow(stackTraceTextArea, Priority.ALWAYS);
GridPane.setVgrow(stackTraceTextArea, Priority.ALWAYS);

var content = new GridPane();
content.setMaxWidth(Double.MAX_VALUE);
content.add(new Label("Full stacktrace:"), 0, 0);
content.add(stackTraceTextArea, 0, 1);

alert.getDialogPane().setExpandableContent(content);
}

alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE);

return alert;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.synops.replayreader.common.comparator.SortingComparators;
import com.synops.replayreader.common.util.LogUtil;
import com.synops.replayreader.core.event.ReplayProgressEvent;
import com.synops.replayreader.core.service.DialogService;
import com.synops.replayreader.maps.comparator.MapsComparator;
import com.synops.replayreader.maps.ui.MapListCell;
import com.synops.replayreader.player.comparator.PlayerListComparatorLong;
Expand All @@ -16,9 +17,12 @@
import com.synops.replayreader.replay.service.ReplayService;
import com.synops.replayreader.ui.model.MainModel;
import com.synops.replayreader.ui.util.DragDropSupport;
import com.synops.replayreader.update.UpdateClient;
import com.synops.replayreader.update.VersionChecker;
import com.synops.replayreader.vehicle.ui.VehicleListCell;
import com.synops.replayreader.vehicle.util.TanksUtil;
import java.io.File;
import java.text.MessageFormat;
import java.util.Comparator;
import java.util.List;
import java.util.ResourceBundle;
Expand All @@ -36,6 +40,7 @@
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
Expand All @@ -46,15 +51,19 @@
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.info.BuildProperties;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

@Component
public class MainViewController {

private static final String REPLAY_READER_BUGS_URL = "https://github.com/synopss/replay-reader/issues/new/choose";
private static final String REPLAY_RELEASES_URL = "https://github.com/synopss/replay-reader/releases/latest";
private final ReplayService replayService;
private final HostServices hostServices;
private final DialogService dialogService;
private final UpdateClient updateClient;
private final ResourceBundle resourceBundle;
private final ClanStringConverter clanStringConverter;
private final ClanListComparator clanListComparator;
Expand All @@ -64,6 +73,7 @@ public class MainViewController {
private final StringProperty selectedPlayer = new SimpleStringProperty("");
private final StringProperty selectedVehicle = new SimpleStringProperty("");
private final StringProperty selectedClan = new SimpleStringProperty("");
private final BuildProperties buildProperties;
@Value("${replay-reader.config.max-players}")
private int MAX_PLAYERS;
@Value("${replay-reader.config.max-clans}")
Expand All @@ -83,6 +93,8 @@ public class MainViewController {
@FXML
private MenuItem showAboutWindow;
@FXML
private MenuItem versionCheck;
@FXML
private ListView<String> playersList;
private Comparator<String> playersComparator;
@FXML
Expand Down Expand Up @@ -140,19 +152,24 @@ public class MainViewController {
@FXML
private Label progressLabel;
private MainModel mainModel;
private VersionChecker versionChecker;

public MainViewController(ReplayService replayService, @Nullable HostServices hostServices,
ResourceBundle resourceBundle, ClanStringConverter clanStringConverter,
ClanListComparator clanListComparator, SortingComparators sortingComparators,
MapsComparator mapsComparator, DragDropSupport dragDropSupport) {
DialogService dialogService, UpdateClient updateClient, ResourceBundle resourceBundle,
ClanStringConverter clanStringConverter, ClanListComparator clanListComparator,
SortingComparators sortingComparators, MapsComparator mapsComparator,
DragDropSupport dragDropSupport, BuildProperties buildProperties) {
this.replayService = replayService;
this.hostServices = hostServices;
this.dialogService = dialogService;
this.updateClient = updateClient;
this.resourceBundle = resourceBundle;
this.clanStringConverter = clanStringConverter;
this.clanListComparator = clanListComparator;
this.sortingComparators = sortingComparators;
this.mapsComparator = mapsComparator;
this.dragDropSupport = dragDropSupport;
this.buildProperties = buildProperties;
}

@FXML
Expand All @@ -166,12 +183,14 @@ public void initialize() {
initClanChoiceBox();
bindingSelectedElements();
progressBar.setVisible(false);
versionChecker = new VersionChecker();
}

private void initMenu() {
exitApplication.setOnAction(_ -> Platform.exit());
reportBug.setOnAction(_ -> openUrl(REPLAY_READER_BUGS_URL));
showAboutWindow.setOnAction(_ -> (new AboutWindowController(resourceBundle)).showAndWait());
versionCheck.setOnAction(_ -> checkForUpdate());
}

private void initLists() {
Expand Down Expand Up @@ -418,4 +437,18 @@ private void openUrl(String url) {
}
hostServices.showDocument(url);
}

private void checkForUpdate() {
updateClient.getLatestVersion().doOnSuccess(versionResponse -> Platform.runLater(() -> {
if (versionChecker.isNewVersionAvailable(versionResponse.tagName(),
buildProperties.getVersion())) {
dialogService.alertConfirm(
MessageFormat.format(resourceBundle.getString("update.new-version"),
versionResponse.tagName().substring(1)), () -> openUrl(REPLAY_RELEASES_URL));
} else {
dialogService.alert(AlertType.INFORMATION,
resourceBundle.getString("update.latest-already"));
}
})).doOnError(dialogService::showAlertError).subscribe();
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/synops/replayreader/ui/util/UiUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.synops.replayreader.ui.util;

import java.util.Objects;
import javafx.scene.image.Image;
import javafx.stage.Stage;

public final class UiUtil {

public static void setDefaultIcon(Stage stage) {
stage.getIcons().add(
new Image(Objects.requireNonNull(stage.getClass().getResourceAsStream("/image/icon.png"))));
}
}
29 changes: 29 additions & 0 deletions src/main/java/com/synops/replayreader/update/UpdateClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.synops.replayreader.update;

import com.synops.replayreader.core.StageReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Component
public class UpdateClient {

private final WebClient.Builder webClientBuilder;
private WebClient webClient;

public UpdateClient(WebClient.Builder webClientBuilder) {
this.webClientBuilder = webClientBuilder;
}

@EventListener
public void init(StageReadyEvent event) {
webClient = webClientBuilder.baseUrl("https://api.github.com/repos/synopss/replay-reader/")
.defaultHeaders(HttpHeaders::clear).build();
}

public Mono<VersionResponse> getLatestVersion() {
return webClient.get().uri("releases/latest").retrieve().bodyToMono(VersionResponse.class);
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/synops/replayreader/update/Version.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.synops.replayreader.update;

import org.jspecify.annotations.NonNull;

public record Version(int major, int minor, int patch) implements Comparable<Version> {

@Override
public int compareTo(Version version) {
if (major < version.major) {
return -1;
} else if (major > version.major) {
return 1;
} else {
if (minor < version.minor) {
return -1;
} else if (minor > version.minor) {
return 1;
} else {
return Integer.compare(patch, version.patch);
}
}
}

@Override
@NonNull
public String toString() {
return major + "." + minor + "." + patch;
}

public boolean isNotARelease() {
return major == 0 && minor == 0 && patch == 0;
}
}
Loading