mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-14 08:41:28 +00:00
Replace stub check detail description with info about check duration and count of warnings and critical errors
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package org.cryptomator.ui.controls;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
public class FormattedLabel2 extends FormattedLabel {
|
||||
|
||||
private final ObjectProperty<Object> arg2 = new SimpleObjectProperty<>();
|
||||
|
||||
public FormattedLabel2() {
|
||||
textProperty().unbind();
|
||||
textProperty().bind(createStringBinding2());
|
||||
}
|
||||
|
||||
private StringBinding createStringBinding2() {
|
||||
return Bindings.createStringBinding(this::updateText, formatProperty(), arg1Property(), arg2);
|
||||
}
|
||||
|
||||
private String updateText() {
|
||||
return String.format(getFormat(), getArg1(), arg2.get());
|
||||
}
|
||||
|
||||
/* Getter & Setter */
|
||||
|
||||
public ObjectProperty<Object> arg2Property() {
|
||||
return arg2;
|
||||
}
|
||||
|
||||
public Object getArg2() {
|
||||
return arg2.get();
|
||||
}
|
||||
|
||||
public void setArg2(Object arg2) {
|
||||
this.arg2.set(arg2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,37 +8,85 @@ import org.cryptomator.ui.common.FxController;
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.LongProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleLongProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ListView;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@HealthCheckScoped
|
||||
public class CheckDetailController implements FxController {
|
||||
|
||||
private final Map<ObservableList<DiagnosticResult>, WarnAndErrorEntry> cachedWarnAndErrorCounts;
|
||||
private final Binding<ObservableList<DiagnosticResult>> results;
|
||||
private final OptionalBinding<Worker.State> taskState;
|
||||
private final Binding<String> taskName;
|
||||
private final Binding<Number> taskDuration;
|
||||
private final ResultListCellFactory resultListCellFactory;
|
||||
private final BooleanBinding producingResults;
|
||||
private final LongProperty numOfWarnings;
|
||||
private final LongProperty numOfErrors;
|
||||
|
||||
public ListView<DiagnosticResult> resultsListView;
|
||||
|
||||
@Inject
|
||||
public CheckDetailController(ObjectProperty<HealthCheckTask> selectedTask, ResultListCellFactory resultListCellFactory) {
|
||||
selectedTask.addListener(this::rebindWarnAndErrorCount);
|
||||
this.results = EasyBind.wrapNullable(selectedTask).map(HealthCheckTask::results).orElse(FXCollections.emptyObservableList());
|
||||
this.taskState = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::stateProperty);
|
||||
this.taskName = EasyBind.wrapNullable(selectedTask).map(HealthCheckTask::getTitle).orElse("");
|
||||
this.taskDuration = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::durationInMillisProperty).orElse(-1L);
|
||||
this.resultListCellFactory = resultListCellFactory;
|
||||
this.producingResults = taskState.filter(this::producesResults).isPresent();
|
||||
this.numOfWarnings = new SimpleLongProperty(0);
|
||||
this.numOfErrors = new SimpleLongProperty(0);
|
||||
this.cachedWarnAndErrorCounts = new IdentityHashMap<>(); //important to use an identity hashmap, because collections violate the immnutable hashkey contract
|
||||
}
|
||||
|
||||
private synchronized void rebindWarnAndErrorCount(ObservableValue<? extends HealthCheckTask> observable, HealthCheckTask oldVal, HealthCheckTask newVal) {
|
||||
//create and cache properites for the newList, if not already present
|
||||
final var listToUpdate = newVal.results();
|
||||
cachedWarnAndErrorCounts.computeIfAbsent(listToUpdate, key -> {
|
||||
var warnProperty = new SimpleLongProperty(countSeverityInList(listToUpdate, DiagnosticResult.Severity.WARN));
|
||||
var errProperty = new SimpleLongProperty(countSeverityInList(listToUpdate, DiagnosticResult.Severity.CRITICAL));
|
||||
return new WarnAndErrorEntry(warnProperty, errProperty);
|
||||
});
|
||||
listToUpdate.addListener(this::updateListSpecificWarnAndErrorCount);
|
||||
|
||||
//updateBindings
|
||||
numOfErrors.bind(cachedWarnAndErrorCounts.get(listToUpdate).errorCount);
|
||||
numOfWarnings.bind(cachedWarnAndErrorCounts.get(listToUpdate).warningCount);
|
||||
}
|
||||
|
||||
private synchronized void updateListSpecificWarnAndErrorCount(ListChangeListener.Change<? extends DiagnosticResult> c) {
|
||||
long tmpErr = cachedWarnAndErrorCounts.get(c.getList()).errorCount.get();
|
||||
long tmpWarn = cachedWarnAndErrorCounts.get(c.getList()).warningCount.get();
|
||||
while (c.next()) {
|
||||
if (c.wasAdded()) {
|
||||
tmpWarn += countSeverityInList(c.getAddedSubList(), DiagnosticResult.Severity.WARN);
|
||||
tmpErr += countSeverityInList(c.getAddedSubList(), DiagnosticResult.Severity.CRITICAL);
|
||||
}
|
||||
}
|
||||
cachedWarnAndErrorCounts.get(c.getList()).errorCount.set(tmpErr);
|
||||
cachedWarnAndErrorCounts.get(c.getList()).warningCount.set(tmpWarn);
|
||||
}
|
||||
|
||||
private long countSeverityInList(List<? extends DiagnosticResult> list, DiagnosticResult.Severity severityToCount) {
|
||||
return list.stream().map(DiagnosticResult::getServerity).filter(severityToCount::equals).count();
|
||||
}
|
||||
|
||||
private boolean producesResults(Worker.State state) {
|
||||
return switch (state) {
|
||||
case SCHEDULED, RUNNING, SUCCEEDED -> true;
|
||||
case READY, CANCELLED, FAILED -> false;
|
||||
case SCHEDULED, RUNNING -> true;
|
||||
case READY, SUCCEEDED, CANCELLED, FAILED -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,4 +113,40 @@ public class CheckDetailController implements FxController {
|
||||
public BooleanBinding producingResultsProperty() {
|
||||
return producingResults;
|
||||
}
|
||||
|
||||
public Number getTaskDuration() {
|
||||
return taskDuration.getValue();
|
||||
}
|
||||
|
||||
public Binding<Number> taskDurationProperty() {
|
||||
return taskDuration;
|
||||
}
|
||||
|
||||
public long getNumOfWarnings() {
|
||||
return numOfWarnings.get();
|
||||
}
|
||||
|
||||
public LongProperty numOfWarningsProperty() {
|
||||
return numOfWarnings;
|
||||
}
|
||||
|
||||
public long getNumOfErrors() {
|
||||
return numOfErrors.get();
|
||||
}
|
||||
|
||||
public LongProperty numOfErrorsProperty() {
|
||||
return numOfErrors;
|
||||
}
|
||||
|
||||
private static class WarnAndErrorEntry {
|
||||
|
||||
WarnAndErrorEntry(LongProperty warningCount, LongProperty errorCount) {
|
||||
this.warningCount = warningCount;
|
||||
this.errorCount = errorCount;
|
||||
}
|
||||
|
||||
final LongProperty warningCount;
|
||||
final LongProperty errorCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,11 +8,15 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.LongProperty;
|
||||
import javafx.beans.property.SimpleLongProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Task;
|
||||
import java.nio.file.Path;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
@@ -28,6 +32,7 @@ class HealthCheckTask extends Task<Void> {
|
||||
private final SecureRandom csprng;
|
||||
private final HealthCheck check;
|
||||
private final ObservableList<DiagnosticResult> results;
|
||||
private final LongProperty durationInMillis;
|
||||
|
||||
public HealthCheckTask(Path vaultPath, VaultConfig vaultConfig, Masterkey masterkey, SecureRandom csprng, HealthCheck check, ResourceBundle resourceBundle) {
|
||||
this.vaultPath = Objects.requireNonNull(vaultPath);
|
||||
@@ -42,10 +47,12 @@ class HealthCheckTask extends Task<Void> {
|
||||
LOG.warn("Missing proper name for health check {}, falling back to default.", check.identifier());
|
||||
updateTitle(check.identifier());
|
||||
}
|
||||
this.durationInMillis = new SimpleLongProperty(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void call() {
|
||||
Instant start = Instant.now();
|
||||
try (var masterkeyClone = masterkey.clone(); //
|
||||
var cryptor = vaultConfig.getCipherCombo().getCryptorProvider(csprng).withKey(masterkeyClone)) {
|
||||
check.check(vaultPath, vaultConfig, masterkeyClone, cryptor, result -> {
|
||||
@@ -66,6 +73,7 @@ class HealthCheckTask extends Task<Void> {
|
||||
Platform.runLater(() -> results.add(result));
|
||||
});
|
||||
}
|
||||
Platform.runLater(() ->durationInMillis.set(Duration.between(start, Instant.now()).toMillis()));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -89,4 +97,12 @@ class HealthCheckTask extends Task<Void> {
|
||||
return check;
|
||||
}
|
||||
|
||||
public LongProperty durationInMillisProperty() {
|
||||
return durationInMillis;
|
||||
}
|
||||
|
||||
public long getDurationInMillis() {
|
||||
return durationInMillis.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FormattedLabel?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
<?import org.cryptomator.ui.controls.FormattedLabel?>
|
||||
<?import org.cryptomator.ui.controls.FormattedLabel2?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.health.CheckDetailController"
|
||||
spacing="6">
|
||||
<FormattedLabel fx:id="checkTitle" styleClass="label-large" format="%health.check.result.header" arg1="${controller.taskName}"/>
|
||||
<Text fx:id="checkDescription" styleClass="label" text="FIXME: Here could be a small description."/>
|
||||
<ListView fx:id="resultsListView"/>
|
||||
<Label text="The check is currently running..." visible="${controller.producingResults}" managed="${controller.producingResults}"/>
|
||||
<!-- TODO: what if the check was canceled? Should be indicated in ui -->
|
||||
<FormattedLabel fx:id="checkDescription" styleClass="label" format="The check finished after %d milliseconds." arg1="${controller.taskDuration}" visible="${!controller.producingResults}" managed="${!controller.producingResults}"/>
|
||||
<FormattedLabel2 styleClass="label" format="Found %d Warnings and %d unfixable Errors." arg1="${controller.numOfWarnings}" arg2="${controller.numOfErrors}" />
|
||||
<ListView fx:id="resultsListView" VBox.vgrow="ALWAYS"/>
|
||||
</VBox>
|
||||
Reference in New Issue
Block a user