diff --git a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java index 68a74c4a0..7ccc9e09c 100644 --- a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java +++ b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java @@ -23,7 +23,7 @@ import java.util.stream.Stream; @HealthCheckScoped public class CheckDetailController implements FxController { - private final EasyObservableList results; + private final EasyObservableList results; private final OptionalBinding taskState; private final Binding taskName; private final Binding taskDuration; @@ -39,7 +39,7 @@ public class CheckDetailController implements FxController { private final ResultListCellFactory resultListCellFactory; private final ResourceBundle resourceBundle; - public ListView resultsListView; + public ListView resultsListView; private Subscription resultSubscription; @Inject @@ -71,8 +71,8 @@ public class CheckDetailController implements FxController { } } - private Function, Long> countSeverity(DiagnosticResult.Severity severity) { - return stream -> stream.filter(item -> severity.equals(item.getSeverity())).count(); + private Function, Long> countSeverity(DiagnosticResult.Severity severity) { + return stream -> stream.filter(item -> severity.equals(item.diagnosis().getSeverity())).count(); } @FXML diff --git a/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java b/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java index 03d67ebef..3efe333b3 100644 --- a/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java +++ b/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java @@ -35,7 +35,7 @@ class HealthCheckTask extends Task { private final Masterkey masterkey; private final SecureRandom csprng; private final HealthCheck check; - private final ObservableList results; + private final ObservableList results; private final LongProperty durationInMillis; private final BooleanProperty chosenForExecution; @@ -45,7 +45,7 @@ class HealthCheckTask extends Task { this.masterkey = Objects.requireNonNull(masterkey); this.csprng = Objects.requireNonNull(csprng); this.check = Objects.requireNonNull(check); - this.results = FXCollections.observableArrayList(); + this.results = FXCollections.observableArrayList(Result::observables); try { updateTitle(resourceBundle.getString("health." + check.identifier())); } catch (MissingResourceException e) { @@ -61,11 +61,11 @@ class HealthCheckTask extends Task { Instant start = Instant.now(); try (var masterkeyClone = masterkey.clone(); // var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) { - check.check(vaultPath, vaultConfig, masterkeyClone, cryptor, result -> { + check.check(vaultPath, vaultConfig, masterkeyClone, cryptor, diagnosis -> { if (isCancelled()) { throw new CancellationException(); } - Platform.runLater(() -> results.add(result)); + Platform.runLater(() -> results.add(Result.create(diagnosis))); }); } Platform.runLater(() -> durationInMillis.set(Duration.between(start, Instant.now()).toMillis())); @@ -88,7 +88,7 @@ class HealthCheckTask extends Task { return new Observable[]{results, chosenForExecution}; } - public ObservableList results() { + public ObservableList results() { return results; } diff --git a/src/main/java/org/cryptomator/ui/health/ReportWriter.java b/src/main/java/org/cryptomator/ui/health/ReportWriter.java index 2bc4c28d9..6039901ef 100644 --- a/src/main/java/org/cryptomator/ui/health/ReportWriter.java +++ b/src/main/java/org/cryptomator/ui/health/ReportWriter.java @@ -72,7 +72,7 @@ public class ReportWriter { case SUCCEEDED -> { writer.write("STATUS: SUCCESS\nRESULTS:\n"); for (var result : task.results()) { - writer.write(REPORT_CHECK_RESULT.formatted(result.getSeverity(), result.toString())); + writer.write(REPORT_CHECK_RESULT.formatted(result.diagnosis().getSeverity(), result.getDescription())); } } case CANCELLED -> writer.write("STATUS: CANCELED\n"); diff --git a/src/main/java/org/cryptomator/ui/health/Result.java b/src/main/java/org/cryptomator/ui/health/Result.java new file mode 100644 index 000000000..23d812f50 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/health/Result.java @@ -0,0 +1,43 @@ +package org.cryptomator.ui.health; + +import org.cryptomator.cryptofs.health.api.DiagnosticResult; + +import javafx.beans.Observable; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + +record Result(DiagnosticResult diagnosis, ObjectProperty fixState) { + + enum FixState { + NOT_FIXABLE, + FIXABLE, + FIXING, + FIXED, + FIX_FAILED + } + + public static Result create(DiagnosticResult diagnosis) { + FixState initialState = switch (diagnosis.getSeverity()) { + case WARN -> FixState.FIXABLE; + default -> FixState.NOT_FIXABLE; + }; + return new Result(diagnosis, new SimpleObjectProperty<>(initialState)); + } + + public Observable[] observables() { + return new Observable[]{fixState}; + } + + public String getDescription() { + return diagnosis.toString(); + } + + public FixState getState() { + return fixState.get(); + } + + public void setState(FixState state) { + this.fixState.set(state); + } + +} diff --git a/src/main/java/org/cryptomator/ui/health/ResultListCellController.java b/src/main/java/org/cryptomator/ui/health/ResultListCellController.java index 4506bc602..cf9f03a73 100644 --- a/src/main/java/org/cryptomator/ui/health/ResultListCellController.java +++ b/src/main/java/org/cryptomator/ui/health/ResultListCellController.java @@ -1,84 +1,82 @@ package org.cryptomator.ui.health; import com.tobiasdiez.easybind.EasyBind; -import org.cryptomator.cryptofs.health.api.DiagnosticResult; +import com.tobiasdiez.easybind.optional.OptionalBinding; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.controls.FontAwesome5Icon; import org.cryptomator.ui.controls.FontAwesome5IconView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javafx.application.Platform; import javafx.beans.binding.Binding; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.binding.ObjectBinding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.value.ObservableValue; +import javafx.beans.value.ObservableObjectValue; import javafx.fxml.FXML; import javafx.scene.control.Button; // unscoped because each cell needs its own controller public class ResultListCellController implements FxController { - private final ResultFixApplier fixApplier; - private final ObjectProperty result; + private final Logger LOG = LoggerFactory.getLogger(ResultListCellController.class); + + private final ObjectProperty result; private final Binding description; + private final ResultFixApplier fixApplier; + private final OptionalBinding fixState; + private final ObjectBinding glyph; + private final BooleanBinding fixable; + private final BooleanBinding fixing; + private final BooleanBinding fixed; public FontAwesome5IconView iconView; - public Button actionButton; + public Button fixButton; @Inject public ResultListCellController(ResultFixApplier fixApplier) { this.result = new SimpleObjectProperty<>(null); - this.description = EasyBind.wrapNullable(result).map(DiagnosticResult::toString).orElse(""); + this.description = EasyBind.wrapNullable(result).map(Result::getDescription).orElse(""); this.fixApplier = fixApplier; - result.addListener(this::updateCellContent); - } - - private void updateCellContent(ObservableValue observable, DiagnosticResult oldVal, DiagnosticResult newVal) { - iconView.getStyleClass().clear(); - actionButton.setVisible(false); - //TODO: see comment in case WARN - actionButton.setManaged(false); - switch (newVal.getSeverity()) { - case INFO -> { - iconView.setGlyph(FontAwesome5Icon.INFO_CIRCLE); - iconView.getStyleClass().add("glyph-icon-muted"); - } - case GOOD -> { - iconView.setGlyph(FontAwesome5Icon.CHECK); - iconView.getStyleClass().add("glyph-icon-primary"); - } - case WARN -> { - iconView.setGlyph(FontAwesome5Icon.EXCLAMATION_TRIANGLE); - iconView.getStyleClass().add("glyph-icon-orange"); - //TODO: Neither is any fix implemented, nor it is ensured, that only fix is executed at a time with good ui indication - // before both are not fix, do not show the button - //actionButton.setVisible(true); - } - case CRITICAL -> { - iconView.setGlyph(FontAwesome5Icon.TIMES); - iconView.getStyleClass().add("glyph-icon-red"); - } - } + this.fixState = EasyBind.wrapNullable(result).mapObservable(Result::fixState); + this.glyph = Bindings.createObjectBinding(this::getGlyph, result); + this.fixable = Bindings.createBooleanBinding(this::isFixable, fixState); + this.fixing = Bindings.createBooleanBinding(this::isFixing, fixState); + this.fixed = Bindings.createBooleanBinding(this::isFixed, fixState); } @FXML - public void runResultAction() { + public void fix() { final var realResult = result.get(); if (realResult != null) { fixApplier.fix(realResult); } } + + @FXML + public void initialize() { + // see getGlyph() for relevant glyphs: + EasyBind.includeWhen(iconView.getStyleClass(), "glyph-icon-muted", iconView.glyphProperty().isEqualTo(FontAwesome5Icon.INFO_CIRCLE)); + EasyBind.includeWhen(iconView.getStyleClass(), "glyph-icon-primary", iconView.glyphProperty().isEqualTo(FontAwesome5Icon.CHECK)); + EasyBind.includeWhen(iconView.getStyleClass(), "glyph-icon-orange", iconView.glyphProperty().isEqualTo(FontAwesome5Icon.EXCLAMATION_TRIANGLE)); + EasyBind.includeWhen(iconView.getStyleClass(), "glyph-icon-red", iconView.glyphProperty().isEqualTo(FontAwesome5Icon.TIMES)); + } + /* Getter & Setter */ - - public DiagnosticResult getResult() { + public Result getResult() { return result.get(); } - public void setResult(DiagnosticResult result) { + public void setResult(Result result) { this.result.set(result); } - public ObjectProperty resultProperty() { + public ObjectProperty resultProperty() { return result; } @@ -86,7 +84,49 @@ public class ResultListCellController implements FxController { return description.getValue(); } + public ObjectBinding glyphProperty() { + return glyph; + } + + public FontAwesome5Icon getGlyph() { + var r = result.get(); + if (r == null) { + return null; + } + return switch (r.diagnosis().getSeverity()) { + case INFO -> FontAwesome5Icon.INFO_CIRCLE; + case GOOD -> FontAwesome5Icon.CHECK; + case WARN -> FontAwesome5Icon.EXCLAMATION_TRIANGLE; + case CRITICAL -> FontAwesome5Icon.TIMES; + }; + } + public Binding descriptionProperty() { return description; } + + public BooleanBinding fixableProperty() { + return fixable; + } + + public boolean isFixable() { + return fixState.get().map(Result.FixState.FIXABLE::equals).orElse(false); + } + + public BooleanBinding fixingProperty() { + return fixing; + } + + public boolean isFixing() { + return fixState.get().map(Result.FixState.FIXING::equals).orElse(false); + } + + public BooleanBinding fixedProperty() { + return fixed; + } + + public boolean isFixed() { + return fixState.get().map(Result.FixState.FIXED::equals).orElse(false); + } + } diff --git a/src/main/java/org/cryptomator/ui/health/ResultListCellFactory.java b/src/main/java/org/cryptomator/ui/health/ResultListCellFactory.java index 7acada487..86c793bf7 100644 --- a/src/main/java/org/cryptomator/ui/health/ResultListCellFactory.java +++ b/src/main/java/org/cryptomator/ui/health/ResultListCellFactory.java @@ -1,7 +1,6 @@ package org.cryptomator.ui.health; -import org.cryptomator.cryptofs.health.api.DiagnosticResult; import org.cryptomator.ui.common.FxmlLoaderFactory; import javax.inject.Inject; @@ -15,7 +14,7 @@ import java.io.IOException; import java.io.UncheckedIOException; @HealthCheckScoped -public class ResultListCellFactory implements Callback, ListCell> { +public class ResultListCellFactory implements Callback, ListCell> { private final FxmlLoaderFactory fxmlLoaders; @@ -25,7 +24,7 @@ public class ResultListCellFactory implements Callback call(ListView param) { + public ListCell call(ListView param) { try { FXMLLoader fxmlLoader = fxmlLoaders.load("/fxml/health_result_listcell.fxml"); return new ResultListCellFactory.Cell(fxmlLoader.getRoot(), fxmlLoader.getController()); @@ -34,7 +33,7 @@ public class ResultListCellFactory implements Callback { + private static class Cell extends ListCell { private final Parent node; private final ResultListCellController controller; @@ -45,7 +44,7 @@ public class ResultListCellFactory implements Callback + + + + - +