Parcourir la source

split up check view:
- checkListController is responsible for selecting and (batch)running checks
- checkDetailController is responsible for displaying results

Sebastian Stenzel il y a 4 ans
Parent
commit
64d54133f8

+ 1 - 1
main/pom.xml

@@ -25,7 +25,7 @@
 		<project.jdk.version>16</project.jdk.version>
 
 		<!-- cryptomator dependencies -->
-		<cryptomator.cryptofs.version>2.1.0-beta4</cryptomator.cryptofs.version>
+		<cryptomator.cryptofs.version>2.1.0-beta5</cryptomator.cryptofs.version>
 		<cryptomator.integrations.version>1.0.0-beta2</cryptomator.integrations.version>
 		<cryptomator.integrations.win.version>1.0.0-beta2</cryptomator.integrations.win.version>
 		<cryptomator.integrations.mac.version>1.0.0-beta2</cryptomator.integrations.mac.version>

+ 1 - 1
main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java

@@ -12,7 +12,7 @@ public enum FxmlFile {
 	ERROR("/fxml/error.fxml"), //
 	FORGET_PASSWORD("/fxml/forget_password.fxml"), //
 	HEALTH_START("/fxml/health_start.fxml"), //
-	HEALTH_CHECK("/fxml/health_check.fxml"), //
+	HEALTH_CHECK("/fxml/health_check_list.fxml"), //
 	LOCK_FORCED("/fxml/lock_forced.fxml"), //
 	LOCK_FAILED("/fxml/lock_failed.fxml"), //
 	MAIN_WINDOW("/fxml/main_window.fxml"), //

+ 36 - 0
main/ui/src/main/java/org/cryptomator/ui/health/BatchService.java

@@ -0,0 +1,36 @@
+package org.cryptomator.ui.health;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Suppliers;
+import dagger.Lazy;
+
+import javax.inject.Inject;
+import javafx.concurrent.Service;
+import javafx.concurrent.Task;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Supplier;
+
+public class BatchService extends Service<Void> {
+
+	private final Iterator<HealthCheckTask> remainingTasks;
+
+	@Inject
+	public BatchService(Iterable<HealthCheckTask> tasks) {
+		this.remainingTasks = tasks.iterator();
+	}
+
+	@Override
+	protected Task<Void> createTask() {
+		Preconditions.checkState(remainingTasks.hasNext(), "No remaining tasks");
+		return remainingTasks.next();
+	}
+
+	@Override
+	protected void succeeded() {
+		if (remainingTasks.hasNext()) {
+			this.restart();
+		}
+	}
+}

+ 7 - 50
main/ui/src/main/java/org/cryptomator/ui/health/CheckController.java

@@ -8,99 +8,56 @@ import org.cryptomator.ui.common.FxController;
 import javax.inject.Inject;
 import javafx.beans.binding.Binding;
 import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.ReadOnlyBooleanProperty;
-import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.property.SimpleStringProperty;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
 import javafx.concurrent.Worker;
 import javafx.fxml.FXML;
-import javafx.scene.control.ListView;
 import javafx.scene.control.TableColumn;
 import javafx.scene.control.TableView;
-import java.util.concurrent.ExecutorService;
 
-@HealthCheckScoped
-public class CheckController implements FxController {
+public class CheckDetailController implements FxController {
 
-	private final HealthCheckSupervisor supervisor;
-	private final HealthReportWriteTask reportWriter;
-	private final ExecutorService executorService;
-	private final ObjectProperty<HealthCheckTask> selectedTask;
 	private final Binding<ObservableList<DiagnosticResult>> selectedResults;
 	private final OptionalBinding<Worker.State> selectedTaskState;
 	private final Binding<String> selectedTaskName;
 	private final Binding<String> selectedTaskDescription;
-	private final ReadOnlyBooleanProperty running;
 
-	/* FXML */
-	public ListView<HealthCheckTask> checksListView;
 	public TableView<DiagnosticResult> resultsTableView;
 	public TableColumn<DiagnosticResult, String> resultDescriptionColumn;
 	public TableColumn<DiagnosticResult, String> resultSeverityColumn;
 
-
 	@Inject
-	public CheckController(HealthCheckSupervisor supervisor, HealthReportWriteTask reportWriteTask, ExecutorService executorService) {
-		this.supervisor = supervisor;
-		this.reportWriter = reportWriteTask;
-		this.executorService = executorService;
-		this.selectedTask = new SimpleObjectProperty<>();
+	public CheckDetailController(ObjectProperty<HealthCheckTask> selectedTask) {
 		this.selectedResults = EasyBind.wrapNullable(selectedTask).map(HealthCheckTask::results).orElse(FXCollections.emptyObservableList());
 		this.selectedTaskState = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::stateProperty);
 		this.selectedTaskName = EasyBind.wrapNullable(selectedTask).map(HealthCheckTask::getTitle).orElse("");
 		this.selectedTaskDescription = EasyBind.wrapNullable(selectedTask).map(task -> task.getCheck().toString()).orElse("");
-		this.running = supervisor.runningProperty();
 	}
 
 	@FXML
 	public void initialize() {
-		checksListView.setItems(FXCollections.observableArrayList(supervisor.getTasks()));
-		checksListView.setCellFactory(ignored -> new CheckListCell());
-		checksListView.getSelectionModel().select(0);
-		selectedTask.bind(checksListView.getSelectionModel().selectedItemProperty());
-
 		resultsTableView.itemsProperty().bind(selectedResults);
 		resultDescriptionColumn.setCellValueFactory(cellFeatures -> new SimpleStringProperty(cellFeatures.getValue().toString()));
 		resultSeverityColumn.setCellValueFactory(cellFeatures -> new SimpleStringProperty(cellFeatures.getValue().getServerity().name()));
-		executorService.execute(supervisor);
-	}
-
-	@FXML
-	public void cancelCheck() {
-		assert running.get();
-		supervisor.cancel(true);
-	}
-
-
-	@FXML
-	public void exportResults() {
-		executorService.execute(reportWriter);
 	}
 
-	/* Getter&Setter */
-
-	public boolean isRunning() {
-		return running.get();
-	}
+	/* Getter/Setter */
 
-	public ReadOnlyBooleanProperty runningProperty() {
-		return running;
+	public String getSelectedTaskName() {
+		return selectedTaskName.getValue();
 	}
 
 	public Binding<String> selectedTaskNameProperty() {
 		return selectedTaskName;
 	}
 
-	public String isSelectedTaskName() {
-		return selectedTaskName.getValue();
+	public String getSelectedTaskDescription() {
+		return selectedTaskDescription.getValue();
 	}
 
 	public Binding<String> selectedTaskDescriptionProperty() {
 		return selectedTaskDescription;
 	}
 
-	public String isSelectedTaskDescription() {
-		return selectedTaskDescription.getValue();
-	}
 }

+ 105 - 0
main/ui/src/main/java/org/cryptomator/ui/health/CheckListController.java

@@ -0,0 +1,105 @@
+package org.cryptomator.ui.health;
+
+import com.google.common.base.Preconditions;
+import com.tobiasdiez.easybind.EasyBind;
+import com.tobiasdiez.easybind.optional.OptionalBinding;
+import dagger.Lazy;
+import org.cryptomator.cryptofs.health.api.DiagnosticResult;
+import org.cryptomator.ui.common.FxController;
+
+import javax.inject.Inject;
+import javafx.beans.binding.Binding;
+import javafx.beans.binding.BooleanBinding;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.concurrent.Worker;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListView;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+
+@HealthCheckScoped
+public class CheckListController implements FxController {
+
+	private final ObservableList<HealthCheckTask> tasks;
+	private final HealthReportWriteTask reportWriter;
+	private final ExecutorService executorService;
+	private final ObjectProperty<HealthCheckTask> selectedTask;
+	private final SimpleObjectProperty<Worker<?>> runningTask;
+	private final Binding<Boolean> running;
+	private final BooleanBinding anyCheckSelected;
+
+	/* FXML */
+	public ListView<HealthCheckTask> checksListView;
+
+	@Inject
+	public CheckListController(Lazy<Collection<HealthCheckTask>> tasks, HealthReportWriteTask reportWriteTask, ObjectProperty<HealthCheckTask> selectedTask, ExecutorService executorService) {
+		this.tasks = FXCollections.observableArrayList(tasks.get());
+		this.reportWriter = reportWriteTask;
+		this.executorService = executorService;
+		this.selectedTask = selectedTask;
+		this.runningTask = new SimpleObjectProperty<>();
+		this.running = EasyBind.wrapNullable(runningTask).map(Worker::isRunning).orElse(false);
+		this.anyCheckSelected = selectedTask.isNotNull();
+	}
+
+	@FXML
+	public void initialize() {
+		checksListView.setItems(tasks);
+		checksListView.setCellFactory(ignored -> new CheckListCell());
+		selectedTask.bind(checksListView.getSelectionModel().selectedItemProperty());
+	}
+
+	@FXML
+	public synchronized void runSelectedChecks() {
+		Preconditions.checkState(runningTask.get() == null);
+		var batch = new BatchService(checksListView.getSelectionModel().getSelectedItems());
+		batch.setExecutor(executorService);
+		batch.start();
+		runningTask.set(batch);
+	}
+
+	@FXML
+	public synchronized void runAllChecks() {
+		Preconditions.checkState(runningTask.get() == null);
+		var batch = new BatchService(checksListView.getItems());
+		batch.setExecutor(executorService);
+		batch.start();
+		runningTask.set(batch);
+	}
+
+	@FXML
+	public synchronized void cancelCheck() {
+		Preconditions.checkState(runningTask.get() != null);
+		runningTask.get().cancel();
+	}
+
+	@FXML
+	public void exportResults() {
+		executorService.execute(reportWriter);
+	}
+
+	/* Getter/Setter */
+
+	public boolean isRunning() {
+		return running.getValue();
+	}
+
+	public Binding<Boolean> runningProperty() {
+		return running;
+	}
+
+	public boolean isAnyCheckSelected() {
+		return anyCheckSelected.get();
+	}
+
+	public BooleanBinding anyCheckSelectedProperty() {
+		return anyCheckSelected;
+	}
+
+}

+ 30 - 16
main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java

@@ -20,11 +20,13 @@ import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
 import org.cryptomator.ui.mainwindow.MainWindow;
 
 import javax.inject.Provider;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ChangeListener;
 import javafx.scene.Scene;
 import javafx.stage.Modality;
 import javafx.stage.Stage;
 import java.security.SecureRandom;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Optional;
@@ -42,27 +44,27 @@ abstract class HealthCheckModule {
 
 	@Provides
 	@HealthCheckScoped
-	static Runnable provideMasterkeyDestructor(AtomicReference<Masterkey> masterkeyRef) {
-		return () -> Optional.ofNullable(masterkeyRef.getAndSet(null)).ifPresent(Masterkey::destroy);
+	static AtomicReference<VaultConfig> provideVaultConfigRef() {
+		return new AtomicReference<>();
 	}
 
 	@Provides
 	@HealthCheckScoped
-	static AtomicReference<VaultConfig> provideVaultConfigRef() {
-		return new AtomicReference<>();
+	static Collection<HealthCheck> provideAvailableHealthChecks() {
+		return HealthCheck.allChecks();
 	}
 
 	@Provides
 	@HealthCheckScoped
-	static Collection<HealthCheck> provideSelectedHealthChecks() {
-		return new ArrayList<>();
+	static ObjectProperty<HealthCheckTask> provideSelectedHealthCheckTask() {
+		return new SimpleObjectProperty<>();
 	}
 
 	/* Only inject with Lazy-Wrapper!*/
 	@Provides
 	@HealthCheckScoped
-	static Collection<HealthCheckTask> provideSelectedHealthCheckTasks(Collection<HealthCheck> selectedHealthChecks, @HealthCheckWindow Vault vault, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, SecureRandom csprng) {
-		return selectedHealthChecks.stream().map(check -> new HealthCheckTask(vault.getPath(), vaultConfigRef.get(), masterkeyRef.get(), csprng, check)).toList();
+	static Collection<HealthCheckTask> provideAvailableHealthCheckTasks(Collection<HealthCheck> availableHealthChecks, @HealthCheckWindow Vault vault, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, SecureRandom csprng) {
+		return availableHealthChecks.stream().map(check -> new HealthCheckTask(vault.getPath(), vaultConfigRef.get(), masterkeyRef.get(), csprng, check)).toList();
 	}
 
 	@Provides
@@ -82,18 +84,24 @@ abstract class HealthCheckModule {
 	@Provides
 	@HealthCheckWindow
 	@HealthCheckScoped
-	static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle, Runnable masterkeyDestructor) {
+	static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle, ChangeListener<Boolean> showingListener) {
 		Stage stage = factory.create();
 		stage.setTitle(resourceBundle.getString("health.title"));
 		stage.setResizable(true);
 		stage.initModality(Modality.WINDOW_MODAL);
 		stage.initOwner(owner);
-		stage.showingProperty().addListener((observable, wasShowing, isShowing) -> {  //TODO: should we use showingProperty or onCloseRequest
+		stage.showingProperty().addListener(showingListener); // bind masterkey lifecycle to window
+		return stage;
+	}
+
+	@Provides
+	@HealthCheckScoped
+	static ChangeListener<Boolean> provideWindowShowingChangeListener(AtomicReference<Masterkey> masterkey) {
+		return (observable, wasShowing, isShowing) -> {
 			if (!isShowing) {
-				masterkeyDestructor.run();
+				Optional.ofNullable(masterkey.getAndSet(null)).ifPresent(Masterkey::destroy);
 			}
-		});
-		return stage;
+		};
 	}
 
 	@Provides
@@ -117,7 +125,13 @@ abstract class HealthCheckModule {
 
 	@Binds
 	@IntoMap
-	@FxControllerKey(CheckController.class)
-	abstract FxController bindCheckController(CheckController controller);
+	@FxControllerKey(CheckListController.class)
+	abstract FxController bindCheckController(CheckListController controller);
+
+	@Binds
+	@IntoMap
+	@FxControllerKey(CheckDetailController.class)
+	abstract FxController bindCheckDetailController(CheckDetailController controller);
+
 
 }

+ 0 - 80
main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckSupervisor.java

@@ -1,80 +0,0 @@
-package org.cryptomator.ui.health;
-
-import dagger.Lazy;
-
-import javax.inject.Inject;
-import javafx.concurrent.Task;
-import java.util.Collection;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicReference;
-
-@HealthCheckScoped
-public class HealthCheckSupervisor extends Task<Void> {
-
-	private final ExecutorService executor;
-	private final Lazy<Collection<HealthCheckTask>> lazyTasks;
-	private final Runnable masterkeyDestructor;
-	private final AtomicReference<HealthCheckTask> currentTask;
-	private final ConcurrentLinkedQueue<HealthCheckTask> remainingTasks;
-
-	@Inject
-	public HealthCheckSupervisor(Lazy<Collection<HealthCheckTask>> lazyTasks, Runnable masterkeyDestructor) {
-		this.lazyTasks = lazyTasks;
-		this.masterkeyDestructor = masterkeyDestructor;
-		this.currentTask = new AtomicReference<>(null);
-		this.executor = Executors.newSingleThreadExecutor();
-		this.remainingTasks = new ConcurrentLinkedQueue<>();
-	}
-
-	public Void call() {
-		remainingTasks.addAll(lazyTasks.get());
-		while (!remainingTasks.isEmpty()) {
-			final var task = remainingTasks.remove();
-			currentTask.set(task);
-			executor.execute(task); //TODO: think about if we set the scheduled property for all tasks?
-			try {
-				task.get();
-			} catch (InterruptedException e) {
-				executor.shutdownNow();
-				cleanup();
-				;
-				Thread.currentThread().interrupt(); //TODO: do we need this?
-			} catch (ExecutionException e) {
-				e.printStackTrace();
-			} catch (CancellationException e) {
-				// ok
-			}
-		}
-		return null;
-	}
-
-	@Override
-	public boolean cancel(boolean mayInterruptIfRunning) {
-		cleanup();
-		currentTask.get().cancel(mayInterruptIfRunning);
-		if (mayInterruptIfRunning) {
-			executor.shutdownNow();
-		} else {
-			executor.shutdown();
-		}
-		return super.cancel(mayInterruptIfRunning);
-	}
-
-	private void cleanup() {
-		remainingTasks.forEach(task -> task.cancel(false));
-		remainingTasks.clear();
-	}
-
-	@Override
-	protected void done() {
-		masterkeyDestructor.run(); //TODO: if we destroy it, no check can rerun
-	}
-
-	public Collection<HealthCheckTask> getTasks() {
-		return lazyTasks.get();
-	}
-}

+ 0 - 64
main/ui/src/main/java/org/cryptomator/ui/health/StartController.java

@@ -44,8 +44,6 @@ public class StartController implements FxController {
 	private static final Logger LOG = LoggerFactory.getLogger(StartController.class);
 
 	private final Stage window;
-	private final Collection<HealthCheck> availableChecks;
-	private final Map<HealthCheck, BooleanProperty> availableCheckListSelectProperties;
 	private final Optional<VaultConfig.UnverifiedVaultConfig> unverifiedVaultConfig;
 	private final KeyLoadingStrategy keyLoadingStrategy;
 	private final ExecutorService executor;
@@ -55,11 +53,6 @@ public class StartController implements FxController {
 	private final Lazy<Scene> checkScene;
 
 	/* FXML */
-	public ListView availableChecksList;
-	public RadioButton customCheckSetButton;
-	public RadioButton quickCheckSetButton;
-	public RadioButton fullCheckSetButton;
-	public ToggleGroup checksSetToggleGroup;
 
 	@Inject
 	public StartController(@HealthCheckWindow Vault vault, @HealthCheckWindow Stage window, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, Collection<HealthCheck> selectedChecks, @FxmlScene(FxmlFile.HEALTH_CHECK) Lazy<Scene> checkScene) {
@@ -71,31 +64,6 @@ public class StartController implements FxController {
 		this.vaultConfigRef = vaultConfigRef;
 		this.selectedChecks = selectedChecks;
 		this.checkScene = checkScene;
-		this.availableChecks = HealthCheck.allChecks();
-		this.availableCheckListSelectProperties = new HashMap<>();
-
-		availableChecks.forEach(check -> availableCheckListSelectProperties.put(check, new SimpleBooleanProperty(false)));
-	}
-
-	public void initialize() {
-		availableChecksList.setItems(FXCollections.observableList(availableChecks.stream().toList()));
-		availableChecksList.setCellFactory(CheckBoxListCell.forListView(availableCheckListSelectProperties::get));
-		availableChecksList.setEditable(false);
-
-		var availableCheckIds = availableChecks.stream().map(HealthCheck::identifier).toList();
-
-		if (PredefinedCheckSet.QUICK.getCheckIds().stream().anyMatch(checkId -> !availableCheckIds.contains(checkId))) {
-			quickCheckSetButton.setVisible(false);
-			quickCheckSetButton.setManaged(false);
-		}
-		if (PredefinedCheckSet.FULL.getCheckIds().stream().anyMatch(checkId -> !availableCheckIds.contains(checkId))) {
-			fullCheckSetButton.setVisible(false);
-			fullCheckSetButton.setManaged(false);
-		}
-
-		quickCheckSetButton.setUserData(PredefinedCheckSet.QUICK);
-		fullCheckSetButton.setUserData(PredefinedCheckSet.FULL);
-		customCheckSetButton.setUserData(PredefinedCheckSet.CUSTOM);
 	}
 
 	@FXML
@@ -106,15 +74,6 @@ public class StartController implements FxController {
 
 	@FXML
 	public void next() {
-		switch ((PredefinedCheckSet) checksSetToggleGroup.getSelectedToggle().getUserData()) {
-			case FULL -> selectedChecks.addAll(availableChecks);
-			case QUICK -> selectedChecks.addAll(availableChecks.stream().filter(check -> PredefinedCheckSet.QUICK.getCheckIds().contains(check.identifier())).toList());
-			case CUSTOM -> availableCheckListSelectProperties.forEach((check, selected) -> {
-				if (selected.get()) {
-					selectedChecks.add(check);
-				}
-			});
-		}
 		LOG.trace("StartController.next()");
 		executor.submit(this::loadKey);
 	}
@@ -166,27 +125,4 @@ public class StartController implements FxController {
 		return unverifiedVaultConfig.isEmpty();
 	}
 
-	public BooleanBinding anyCheckSetSelectedProperty() {
-		return checksSetToggleGroup.selectedToggleProperty().isNotNull();
-	}
-
-	public boolean isAnyCheckSetSelected() {
-		return anyCheckSetSelectedProperty().get();
-	}
-
-	enum PredefinedCheckSet {
-		QUICK(DirIdCheck.class.getCanonicalName()), //TODO: get identifier via static method?
-		FULL(DirIdCheck.class.getCanonicalName()),
-		CUSTOM();
-
-		private Collection<String> checkIds;
-
-		PredefinedCheckSet(String... checkIds) {
-			this.checkIds = Set.of(checkIds);
-		}
-
-		Collection<String> getCheckIds() {
-			return checkIds;
-		}
-	}
 }

+ 0 - 41
main/ui/src/main/resources/fxml/health_check.fxml

@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<?import javafx.geometry.Insets?>
-<?import javafx.scene.control.Button?>
-<?import javafx.scene.control.Label?>
-<?import javafx.scene.control.ListView?>
-<?import javafx.scene.control.TableColumn?>
-<?import javafx.scene.control.TableView?>
-<?import javafx.scene.layout.HBox?>
-<?import javafx.scene.layout.VBox?>
-<?import javafx.scene.text.Text?>
-<HBox xmlns:fx="http://javafx.com/fxml"
-	  xmlns="http://javafx.com/javafx"
-	  fx:controller="org.cryptomator.ui.health.CheckController"
-	  minWidth="400"
-	  maxWidth="400"
-	  minHeight="145"
-	  spacing="12">
-	<padding>
-		<Insets topRightBottomLeft="12"/>
-	</padding>
-	<children>
-		<VBox minWidth="80">
-			<Label fx:id="listHeading" text="Health checks"/>
-			<ListView fx:id="checksListView"/>
-			<Button text="%generic.button.cancel" onAction="#cancelCheck" disable="${!controller.running}" maxWidth="Infinity"/>
-			<Button text="Export Results" onAction="#exportResults" disable="${controller.running}" maxWidth="Infinity"/>
-		</VBox>
-		<VBox spacing="6">
-			<Label fx:id="checkTitle" styleClass="label-large" text="${controller.selectedTaskName}" />
-			<Text fx:id="checkDescription" styleClass="label" text="${controller.selectedTaskDescription}" />
-			<TableView fx:id="resultsTableView" >
-				<columns>
-					<TableColumn fx:id="resultDescriptionColumn" text="Info" editable="false"/>
-					<TableColumn fx:id="resultSeverityColumn" text="Severity" editable="false"/>
-					<!--TableColumn fx:id="resultAction" text="Action" /-->
-				</columns>
-			</TableView>
-		</VBox>
-	</children>
-</HBox>

+ 22 - 0
main/ui/src/main/resources/fxml/health_check_details.fxml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.text.Text?>
+<VBox xmlns:fx="http://javafx.com/fxml"
+	  xmlns="http://javafx.com/javafx"
+	  fx:controller="org.cryptomator.ui.health.CheckDetailController"
+	  spacing="6">
+	<Label fx:id="checkTitle" styleClass="label-large" text="${controller.selectedTaskName}"/>
+	<Text fx:id="checkDescription" styleClass="label" text="${controller.selectedTaskDescription}"/>
+	<!-- TODO hide if state != succeeded: -->
+	<TableView fx:id="resultsTableView">
+		<columns>
+			<TableColumn fx:id="resultDescriptionColumn" text="Info" editable="false"/>
+			<TableColumn fx:id="resultSeverityColumn" text="Severity" editable="false"/>
+			<!--TableColumn fx:id="resultAction" text="Action" /-->
+		</columns>
+	</TableView>
+</VBox>

+ 39 - 0
main/ui/src/main/resources/fxml/health_check_list.fxml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.ButtonBar?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.ListView?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.VBox?>
+<VBox xmlns:fx="http://javafx.com/fxml"
+	  xmlns="http://javafx.com/javafx"
+	  fx:controller="org.cryptomator.ui.health.CheckListController"
+	  minWidth="400"
+	  maxWidth="400"
+	  minHeight="145"
+	  spacing="12">
+	<padding>
+		<Insets topRightBottomLeft="12"/>
+	</padding>
+	<children>
+		<HBox>
+			<VBox minWidth="80">
+				<Label fx:id="listHeading" text="Health checks"/>
+				<ListView fx:id="checksListView"/>
+				<!-- TODO: clean up button states: -->
+				<Button text="TODO: run selected" onAction="#runSelectedChecks" disable="${controller.running || !controller.anyCheckSelected}" maxWidth="Infinity"/>
+				<Button text="TODO: run all" onAction="#runAllChecks" disable="${controller.running}"  maxWidth="Infinity"/>
+				<Button text="%generic.button.cancel" onAction="#cancelCheck" disable="${!controller.running}" maxWidth="Infinity"/>
+			</VBox>
+			<fx:include source="/fxml/health_check_details.fxml" visible="${controller.anyCheckSelected}" managed="${controller.anyCheckSelected}"/>
+		</HBox>
+		<ButtonBar buttonMinWidth="120" buttonOrder="+X">
+			<buttons>
+				<!-- TODO: hide/disable if no results yet -->
+				<Button text="TODO Export Results" ButtonBar.buttonData="NEXT_FORWARD" disable="${controller.running}" onAction="#exportResults"/>
+			</buttons>
+		</ButtonBar>
+	</children>
+</VBox>

+ 2 - 13
main/ui/src/main/resources/fxml/health_start.fxml

@@ -16,29 +16,18 @@
 	  maxWidth="400"
 	  minHeight="145"
 	  spacing="12">
-	<fx:define>
-		<ToggleGroup fx:id="checksSetToggleGroup"/>
-	</fx:define>
 	<padding>
 		<Insets topRightBottomLeft="12"/>
 	</padding>
 	<children>
 		<Label text="TODO: Invalid vault config" visible="${controller.invalidConfig}" managed="${controller.invalidConfig}"/>
 
-		<Label text="Please select, which check(s) should be performed:"/>
-		<VBox spacing="6" disable="${controller.invalidConfig}">
-			<RadioButton fx:id="quickCheckSetButton" toggleGroup="${checksSetToggleGroup}" text="Quick Check"/>
-			<RadioButton fx:id="fullCheckSetButton" toggleGroup="${checksSetToggleGroup}" text="Full Check"/>
-			<AnchorPane >
-				<RadioButton fx:id="customCheckSetButton" toggleGroup="${checksSetToggleGroup}" text="Custom Check"/>
-				<ListView fx:id="availableChecksList" AnchorPane.leftAnchor="20.0" AnchorPane.topAnchor="20.0" maxHeight="100" visible="${customCheckSetButton.selected}"/>
-			</AnchorPane>
-		</VBox>
+		<Label text="TODO: Blabla going to health check now...:"/>
 
 		<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
 			<buttons>
 				<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
-				<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" disable="${controller.invalidConfig || !controller.anyCheckSetSelected}" defaultButton="true" onAction="#next"/>
+				<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" disable="${controller.invalidConfig}" defaultButton="true" onAction="#next"/>
 			</buttons>
 		</ButtonBar>
 	</children>

+ 5 - 5
main/ui/src/main/resources/license/THIRD-PARTY.txt

@@ -19,10 +19,10 @@ Cryptomator uses 45 third-party dependencies under the following licenses:
 			- jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - http://github.com/jnr/jnr-ffi)
 			- FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/)
 			- Gson (com.google.code.gson:gson:2.8.6 - https://github.com/google/gson/gson)
-			- Dagger (com.google.dagger:dagger:2.32 - https://github.com/google/dagger)
-			- error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations)
+			- Dagger (com.google.dagger:dagger:2.35.1 - https://github.com/google/dagger)
+			- error-prone annotations (com.google.errorprone:error_prone_annotations:2.5.1 - http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations)
 			- Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - https://github.com/google/guava/failureaccess)
-			- Guava: Google Core Libraries for Java (com.google.guava:guava:30.1-jre - https://github.com/google/guava/guava)
+			- Guava: Google Core Libraries for Java (com.google.guava:guava:30.1.1-jre - https://github.com/google/guava/guava)
 			- Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture)
 			- J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - https://github.com/google/j2objc/)
 			- Apache Commons CLI (commons-cli:commons-cli:1.4 - http://commons.apache.org/proper/commons-cli/)
@@ -70,11 +70,11 @@ Cryptomator uses 45 third-party dependencies under the following licenses:
 			- Java Native Access (net.java.dev.jna:jna:5.7.0 - https://github.com/java-native-access/jna)
 			- Java Native Access Platform (net.java.dev.jna:jna-platform:5.7.0 - https://github.com/java-native-access/jna)
         MIT License:
-			- java jwt (com.auth0:java-jwt:3.13.0 - https://github.com/auth0/java-jwt)
+			- java jwt (com.auth0:java-jwt:3.15.0 - https://github.com/auth0/java-jwt)
 			- jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - http://github.com/jnr/jnr-x86asm)
 			- jnr-fuse (com.github.serceman:jnr-fuse:0.5.5 - https://github.com/SerCeMan/jnr-fuse)
 			- zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - https://github.com/nulab/zxcvbn4j)
-			- Checker Qual (org.checkerframework:checker-qual:3.5.0 - https://checkerframework.org)
+			- Checker Qual (org.checkerframework:checker-qual:3.8.0 - https://checkerframework.org)
 			- SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - http://www.slf4j.org)
         The BSD 2-Clause License:
 			- EasyBind (com.tobiasdiez:easybind:2.1.0 - https://github.com/tobiasdiez/EasyBind)