package com.faforever.client.map; import com.faforever.client.api.ApiException; import com.faforever.client.i18n.I18n; import com.faforever.client.map.event.MapUploadedEvent; import com.faforever.client.notification.Action; import com.faforever.client.notification.DismissAction; import com.faforever.client.notification.ImmediateNotification; import com.faforever.client.notification.NotificationService; import com.faforever.client.notification.ReportAction; import com.faforever.client.reporting.ReportingService; import com.faforever.client.task.CompletableTask; import com.google.common.eventbus.EventBus; import javafx.beans.binding.Bindings; import javafx.fxml.FXML; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.control.ProgressBar; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Resource; import java.lang.invoke.MethodHandles; import java.nio.file.Path; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadPoolExecutor; import static com.faforever.client.notification.Severity.ERROR; import static java.util.Arrays.asList; public class MapUploadController { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @FXML Label rankedLabel; @FXML Label uploadTaskMessageLabel; @FXML Label uploadTaskTitleLabel; @FXML Label sizeLabel; @FXML Label playersLabel; @FXML Pane parseProgressPane; @FXML Pane uploadProgressPane; @FXML Pane uploadCompletePane; @FXML ProgressBar uploadProgressBar; @FXML Pane mapInfoPane; @FXML Label mapNameLabel; @FXML Label descriptionLabel; @FXML Label versionLabel; @FXML ImageView thumbnailImageView; @FXML Region mapUploadRoot; @FXML CheckBox rankedCheckbox; @Resource MapService mapService; @Resource ThreadPoolExecutor threadPoolExecutor; @Resource NotificationService notificationService; @Resource ReportingService reportingService; @Resource I18n i18n; @Resource EventBus eventBus; private Path mapPath; private MapBean mapInfo; private CompletableTask<Void> uploadMapTask; @FXML void initialize() { mapInfoPane.managedProperty().bind(mapInfoPane.visibleProperty()); uploadProgressPane.managedProperty().bind(uploadProgressPane.visibleProperty()); parseProgressPane.managedProperty().bind(parseProgressPane.visibleProperty()); uploadCompletePane.managedProperty().bind(uploadCompletePane.visibleProperty()); mapInfoPane.setVisible(false); uploadProgressPane.setVisible(false); parseProgressPane.setVisible(false); uploadCompletePane.setVisible(false); rankedLabel.setLabelFor(rankedCheckbox); } public void setMapPath(Path mapPath) { this.mapPath = mapPath; enterParsingState(); CompletableFuture.supplyAsync(() -> mapService.readMap(mapPath), threadPoolExecutor) .thenAccept(this::setMapInfo) .exceptionally(throwable -> { logger.warn("Map could not be read", throwable); return null; }); } private void enterParsingState() { mapInfoPane.setVisible(false); uploadProgressPane.setVisible(false); parseProgressPane.setVisible(true); uploadCompletePane.setVisible(false); } private void setMapInfo(MapBean mapInfo) { this.mapInfo = mapInfo; enterMapInfoState(); mapNameLabel.textProperty().bind(mapInfo.displayNameProperty()); descriptionLabel.textProperty().bind(mapInfo.descriptionProperty()); versionLabel.textProperty().bind(mapInfo.versionProperty().asString()); sizeLabel.textProperty().bind(Bindings.createStringBinding( () -> { MapSize mapSize = mapInfo.getSize(); return i18n.get("mapVault.upload.sizeFormat", mapSize.getWidth(), mapSize.getHeight()); }, mapInfo.sizeProperty()) ); playersLabel.textProperty().bind(Bindings.createStringBinding( () -> i18n.get("mapVault.upload.playersFormat", mapInfo.getPlayers()), mapInfo.playersProperty()) ); Image image = PreviewGenerator.generatePreview(mapPath, 256, 256); thumbnailImageView.setImage(image); } private void enterMapInfoState() { mapInfoPane.setVisible(true); uploadProgressPane.setVisible(false); parseProgressPane.setVisible(false); uploadCompletePane.setVisible(false); } @FXML void onCancelUploadClicked() { uploadMapTask.cancel(true); enterMapInfoState(); } private void onUploadFailed(Throwable throwable) { enterMapInfoState(); if (throwable instanceof ApiException) { notificationService.addNotification(new ImmediateNotification( i18n.get("errorTitle"), i18n.get("mapVault.upload.failed", throwable.getLocalizedMessage()), ERROR, asList( new Action(i18n.get("mapVault.upload.retry"), event -> onUploadClicked()), new DismissAction(i18n) ) )); } else { notificationService.addNotification(new ImmediateNotification( i18n.get("errorTitle"), i18n.get("mapVault.upload.failed", throwable.getLocalizedMessage()), ERROR, throwable, asList( new Action(i18n.get("mapVault.upload.retry"), event -> onUploadClicked()), new ReportAction(i18n, reportingService, throwable), new DismissAction(i18n) ) )); } } @FXML void onUploadClicked() { enterUploadingState(); uploadProgressPane.setVisible(true); uploadMapTask = mapService.uploadMap(mapPath, rankedCheckbox.isSelected()); uploadTaskTitleLabel.textProperty().bind(uploadMapTask.titleProperty()); uploadTaskMessageLabel.textProperty().bind(uploadMapTask.messageProperty()); uploadProgressBar.progressProperty().bind(uploadMapTask.progressProperty()); uploadMapTask.getFuture() .thenAccept(v -> eventBus.post(new MapUploadedEvent(mapInfo))) .thenAccept(aVoid -> enterUploadCompleteState()) .exceptionally(throwable -> { if (!(throwable instanceof CancellationException)) { onUploadFailed(throwable.getCause()); } return null; }); } private void enterUploadingState() { mapInfoPane.setVisible(false); uploadProgressPane.setVisible(true); parseProgressPane.setVisible(false); uploadCompletePane.setVisible(false); } private void enterUploadCompleteState() { mapInfoPane.setVisible(false); uploadProgressPane.setVisible(false); parseProgressPane.setVisible(false); uploadCompletePane.setVisible(true); } @FXML void onCancelClicked() { getRoot().getScene().getWindow().hide(); } public Region getRoot() { return mapUploadRoot; } }