From 4dc1d59305ae972e9f708c5f88c441460e78bb0b Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 25 Nov 2022 17:17:55 +0100 Subject: [PATCH] Implement fix all button --- .../ui/health/CheckDetailController.java | 38 ++++++++++++++-- .../ui/health/ResultFixApplier.java | 24 ++++++++--- .../ui/health/ResultListCellController.java | 31 ++++++------- .../resources/fxml/health_check_details.fxml | 43 ++++++++++++------- src/main/resources/i18n/strings.properties | 1 + 5 files changed, 95 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java index c467a5328..83cb4ea7d 100644 --- a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java +++ b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java @@ -8,8 +8,12 @@ import org.cryptomator.ui.common.FxController; import javax.inject.Inject; import javafx.beans.binding.Binding; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.BooleanExpression; +import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.fxml.FXML; @@ -35,30 +39,42 @@ public class CheckDetailController implements FxController { private final Binding countOfCritSeverity; private final Binding warnOrCritsExist; private final ResultListCellFactory resultListCellFactory; + private final ResultFixApplier resultFixApplier; + + private final BooleanProperty fixAllInfoResultsExecuted; + private final BooleanBinding fixAllInfoResultsPossible; public ListView resultsListView; private Subscription resultSubscription; @Inject - public CheckDetailController(ObjectProperty selectedTask, ResultListCellFactory resultListCellFactory) { + public CheckDetailController(ObjectProperty selectedTask, ResultListCellFactory resultListCellFactory, ResultFixApplier resultFixApplier) { this.resultListCellFactory = resultListCellFactory; + this.resultFixApplier = resultFixApplier; this.results = EasyBind.wrapList(FXCollections.observableArrayList()); this.check = selectedTask; this.checkState = selectedTask.flatMap(Check::stateProperty); this.checkName = selectedTask.map(Check::getName).orElse(""); this.checkRunning = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.RUNNING::equals).orElse(false)); this.checkScheduled = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SCHEDULED::equals).orElse(false)); - this.checkSkipped =BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SKIPPED::equals).orElse(false)); + this.checkSkipped = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SKIPPED::equals).orElse(false)); this.checkSucceeded = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SUCCEEDED::equals).orElse(false)); this.checkFailed = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.ERROR::equals).orElse(false)); this.checkCancelled = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.CANCELLED::equals).orElse(false)); this.checkFinished = checkSucceeded.or(checkFailed).or(checkCancelled); this.countOfWarnSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.WARN)); this.countOfCritSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.CRITICAL)); - this.warnOrCritsExist = EasyBind.combine(checkSucceeded, countOfWarnSeverity, countOfCritSeverity, (suceeded, warns, crits) -> suceeded && (warns.longValue() > 0 || crits.longValue() > 0) ); + this.warnOrCritsExist = EasyBind.combine(checkSucceeded, countOfWarnSeverity, countOfCritSeverity, (suceeded, warns, crits) -> suceeded && (warns.longValue() > 0 || crits.longValue() > 0)); + this.fixAllInfoResultsExecuted = new SimpleBooleanProperty(false); + this.fixAllInfoResultsPossible = Bindings.createBooleanBinding(() -> results.stream().anyMatch(this::isFixableInfoResult), results) // + .and(fixAllInfoResultsExecuted.not()); selectedTask.addListener(this::selectedTaskChanged); } + private boolean isFixableInfoResult(Result r) { + return r.diagnosis().getSeverity() == DiagnosticResult.Severity.INFO && r.getState() == Result.FixState.FIXABLE; + } + private void selectedTaskChanged(ObservableValue observable, Check oldValue, Check newValue) { if (resultSubscription != null) { resultSubscription.unsubscribe(); @@ -78,6 +94,13 @@ public class CheckDetailController implements FxController { resultsListView.setCellFactory(resultListCellFactory); } + @FXML + public void fixAllInfoResults() { + fixAllInfoResultsExecuted.setValue(true); + results.stream().filter(this::isFixableInfoResult).forEach(resultFixApplier::fix); + } + + /* Getter/Setter */ public String getCheckName() { @@ -175,4 +198,13 @@ public class CheckDetailController implements FxController { public Check getCheck() { return check.get(); } + + public ObservableValue fixAllInfoResultsPossibleProperty() { + return fixAllInfoResultsPossible; + } + + public boolean getFixAllInfoResultsPossible() { + return fixAllInfoResultsPossible.getValue(); + } + } diff --git a/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java b/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java index 4a71730a0..2654da221 100644 --- a/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java +++ b/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java @@ -23,6 +23,8 @@ import java.util.concurrent.atomic.AtomicReference; @HealthCheckScoped class ResultFixApplier { + private static final Logger LOG = LoggerFactory.getLogger(ResultFixApplier.class); + private final Path vaultPath; private final SecureRandom csprng; private final Masterkey masterkey; @@ -40,19 +42,26 @@ class ResultFixApplier { public CompletionStage fix(Result result) { Preconditions.checkArgument(result.getState() == Result.FixState.FIXABLE); - result.setState(Result.FixState.FIXING); - return CompletableFuture.runAsync(() -> fix(result.diagnosis()), sequentialExecutor) + return CompletableFuture.runAsync(() -> result.setState(Result.FixState.FIXING), Platform::runLater) // + .thenRunAsync(() -> fix(result.diagnosis()), sequentialExecutor) // .whenCompleteAsync((unused, throwable) -> { - var fixed = throwable == null ? Result.FixState.FIXED : Result.FixState.FIX_FAILED; - result.setState(fixed); + final Result.FixState s; + if (throwable == null) { + LOG.debug("Fix for {} applied successful.", result.diagnosis().getClass().getName()); + s = Result.FixState.FIXED; + } else { + LOG.error("Failed to apply fix for {}", result.diagnosis().getClass().getName(), throwable); + s = Result.FixState.FIX_FAILED; + } + result.setState(s); }, Platform::runLater); } - public void fix(DiagnosticResult diagnosis) { + private void fix(DiagnosticResult diagnosis) { try (var masterkeyClone = masterkey.copy(); // var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) { - diagnosis.getFix(vaultPath, vaultConfig, masterkeyClone, cryptor) - .orElseThrow(() -> new IllegalStateException("No fix for diagnosis "+diagnosis.getClass().getName() +" implemented.")) + diagnosis.getFix(vaultPath, vaultConfig, masterkeyClone, cryptor) // + .orElseThrow(() -> new IllegalStateException("No fix for diagnosis " + diagnosis.getClass().getName() + " implemented.")) // .apply(); } catch (Exception e) { throw new FixFailedException(e); @@ -60,6 +69,7 @@ class ResultFixApplier { } public static class FixFailedException extends CompletionException { + private FixFailedException(Throwable cause) { super(cause); } diff --git a/src/main/java/org/cryptomator/ui/health/ResultListCellController.java b/src/main/java/org/cryptomator/ui/health/ResultListCellController.java index d655d0058..03c225d57 100644 --- a/src/main/java/org/cryptomator/ui/health/ResultListCellController.java +++ b/src/main/java/org/cryptomator/ui/health/ResultListCellController.java @@ -12,7 +12,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.ObjectBinding; @@ -49,8 +48,8 @@ public class ResultListCellController implements FxController { private final BooleanBinding fixFailed; private final BooleanBinding fixRunningOrDone; private final List subscriptions; - private final Tooltip fixSuccess; - private final Tooltip fixFail; + private final Tooltip fixSuccessTip; + private final Tooltip fixFailTip; private AutoAnimator fixRunningRotator; @@ -73,10 +72,10 @@ public class ResultListCellController implements FxController { this.fixFailed = Bindings.createBooleanBinding(this::isFixFailed, fixState); this.fixRunningOrDone = fixing.or(fixed).or(fixFailed); this.subscriptions = new ArrayList<>(); - this.fixSuccess = new Tooltip(resourceBundle.getString("health.fix.successTip")); - this.fixFail = new Tooltip(resourceBundle.getString("health.fix.failTip")); - fixSuccess.setShowDelay(Duration.millis(100)); - fixFail.setShowDelay(Duration.millis(100)); + this.fixSuccessTip = new Tooltip(resourceBundle.getString("health.fix.successTip")); + this.fixFailTip = new Tooltip(resourceBundle.getString("health.fix.failTip")); + fixSuccessTip.setShowDelay(Duration.millis(100)); + fixFailTip.setShowDelay(Duration.millis(100)); } @FXML @@ -93,22 +92,20 @@ public class ResultListCellController implements FxController { .onCondition(fixing) // .afterStop(() -> fixView.setRotate(0)) // .build(); + fixState.addListener(((observable, oldValue, newValue) -> { + if (newValue == Result.FixState.FIXED) { + Tooltip.install(fixView, fixSuccessTip); + } else if (newValue == Result.FixState.FIX_FAILED) { + Tooltip.install(fixView, fixFailTip); + } + })); } @FXML public void fix() { Result r = result.get(); if (r != null) { - fixApplier.fix(r).whenCompleteAsync(this::fixFinished, Platform::runLater); - } - } - - private void fixFinished(Void unused, Throwable exception) { - if (exception != null) { - LOG.error("Failed to apply fix", exception); - Tooltip.install(fixView, fixFail); - } else { - Tooltip.install(fixView, fixSuccess); + fixApplier.fix(r); } } diff --git a/src/main/resources/fxml/health_check_details.fxml b/src/main/resources/fxml/health_check_details.fxml index 6dd7d224e..d4564a852 100644 --- a/src/main/resources/fxml/health_check_details.fxml +++ b/src/main/resources/fxml/health_check_details.fxml @@ -5,25 +5,38 @@ + + + - + spacing="12"> + + + + + + - \ No newline at end of file diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index 460b56476..d3ae3a0e4 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -219,6 +219,7 @@ health.check.detail.checkFinished=The check finished successfully. health.check.detail.checkFinishedAndFound=The check finished running. Please review the results. health.check.detail.checkFailed=The check exited due to an error. health.check.detail.checkCancelled=The check was cancelled. +health.check.detail.fixAllSpecificBtn=Fix all of type health.check.exportBtn=Export Report ## Fix Application health.fix.fixBtn=Fix