package com.faforever.client.mod; import com.faforever.client.api.ApiException; import com.faforever.client.i18n.I18n; import com.faforever.client.mod.event.ModUploadedEvent; 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.faforever.client.util.IdenticonUtil; import com.google.common.eventbus.EventBus; import javafx.beans.binding.Bindings; import javafx.fxml.FXML; 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.Files; 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 ModUploadController { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @FXML Label uploadTaskMessageLabel; @FXML Label uploadTaskTitleLabel; @FXML Pane parseProgressPane; @FXML Pane uploadProgressPane; @FXML Pane uploadCompletePane; @FXML ProgressBar uploadProgressBar; @FXML Pane modInfoPane; @FXML Label modNameLabel; @FXML Label descriptionLabel; @FXML Label versionLabel; @FXML Label uidLabel; @FXML ImageView thumbnailImageView; @FXML Region modUploadRoot; @Resource ModService modService; @Resource ThreadPoolExecutor threadPoolExecutor; @Resource NotificationService notificationService; @Resource ReportingService reportingService; @Resource I18n i18n; @Resource EventBus eventBus; private Path modPath; private CompletableTask<Void> modUploadTask; private ModInfoBean modInfo; @FXML void initialize() { modInfoPane.managedProperty().bind(modInfoPane.visibleProperty()); uploadProgressPane.managedProperty().bind(uploadProgressPane.visibleProperty()); parseProgressPane.managedProperty().bind(parseProgressPane.visibleProperty()); uploadCompletePane.managedProperty().bind(uploadCompletePane.visibleProperty()); modInfoPane.setVisible(false); uploadProgressPane.setVisible(false); parseProgressPane.setVisible(false); uploadCompletePane.setVisible(false); } public void setModPath(Path modPath) { this.modPath = modPath; enterParsingState(); CompletableFuture.supplyAsync(() -> modService.extractModInfo(modPath), threadPoolExecutor) .thenAccept(this::setModInfo) .exceptionally(throwable -> { logger.warn("Mod could not be read", throwable); return null; }); } private void enterParsingState() { modInfoPane.setVisible(false); uploadProgressPane.setVisible(false); parseProgressPane.setVisible(true); uploadCompletePane.setVisible(false); } private void setModInfo(ModInfoBean modInfo) { this.modInfo = modInfo; enterModInfoState(); modNameLabel.textProperty().bind(modInfo.nameProperty()); descriptionLabel.textProperty().bind(modInfo.descriptionProperty()); versionLabel.textProperty().bind(modInfo.versionProperty()); uidLabel.textProperty().bind(modInfo.idProperty()); thumbnailImageView.imageProperty().bind( Bindings.createObjectBinding(() -> { if (modInfo.getImagePath() != null && Files.isRegularFile(modInfo.getImagePath())) { return new Image(modInfo.getImagePath().toUri().toString(), true); } return IdenticonUtil.createIdenticon(modInfo.getId()); }, modInfo.idProperty(), modInfo.imagePathProperty()) ); } private void enterModInfoState() { modInfoPane.setVisible(true); uploadProgressPane.setVisible(false); parseProgressPane.setVisible(false); uploadCompletePane.setVisible(false); } @FXML void onCancelUploadClicked() { modUploadTask.cancel(true); enterModInfoState(); } private void onUploadFailed(Throwable throwable) { enterModInfoState(); if (throwable instanceof ApiException) { notificationService.addNotification(new ImmediateNotification( i18n.get("errorTitle"), i18n.get("modVault.upload.failed", throwable.getLocalizedMessage()), ERROR, asList( new Action(i18n.get("modVault.upload.retry"), event -> onUploadClicked()), new DismissAction(i18n) ) )); } else { notificationService.addNotification(new ImmediateNotification( i18n.get("errorTitle"), i18n.get("modVault.upload.failed", throwable.getLocalizedMessage()), ERROR, throwable, asList( new Action(i18n.get("modVault.upload.retry"), event -> onUploadClicked()), new ReportAction(i18n, reportingService, throwable), new DismissAction(i18n) ) )); } } @FXML void onUploadClicked() { enterUploadingState(); uploadProgressPane.setVisible(true); modUploadTask = modService.uploadMod(modPath); uploadTaskTitleLabel.textProperty().bind(modUploadTask.titleProperty()); uploadTaskMessageLabel.textProperty().bind(modUploadTask.messageProperty()); uploadProgressBar.progressProperty().bind(modUploadTask.progressProperty()); modUploadTask.getFuture() .thenAccept(v -> eventBus.post(new ModUploadedEvent(modInfo))) .thenAccept(aVoid -> enterUploadCompleteState()) .exceptionally(throwable -> { if (!(throwable instanceof CancellationException)) { onUploadFailed(throwable.getCause()); } return null; }); } private void enterUploadingState() { modInfoPane.setVisible(false); uploadProgressPane.setVisible(true); parseProgressPane.setVisible(false); uploadCompletePane.setVisible(false); } private void enterUploadCompleteState() { modInfoPane.setVisible(false); uploadProgressPane.setVisible(false); parseProgressPane.setVisible(false); uploadCompletePane.setVisible(true); } @FXML void onCancelClicked() { getRoot().getScene().getWindow().hide(); } public Region getRoot() { return modUploadRoot; } }