Parcourir la source

Add forceLock Dialogue:
* integrate it in workflow if normal lock throws exception
* add stubs if also forced lock fails

Armin Schrenk il y a 4 ans
Parent
commit
432a9a27f1

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

@@ -11,6 +11,8 @@ public enum FxmlFile {
 	CHANGEPASSWORD("/fxml/changepassword.fxml"), //
 	ERROR("/fxml/error.fxml"), //
 	FORGET_PASSWORD("/fxml/forget_password.fxml"), //
+	LOCK_FORCED("/fxml/lock_forced.fxml"), //
+	LOCK_FAILED("/fxml/lock_failed.fxml"), //
 	MAIN_WINDOW("/fxml/main_window.fxml"), //
 	MIGRATION_CAPABILITY_ERROR("/fxml/migration_capability_error.fxml"), //
 	MIGRATION_IMPOSSIBLE("/fxml/migration_impossible.fxml"),

+ 14 - 0
main/ui/src/main/java/org/cryptomator/ui/lock/LockFailedController.java

@@ -0,0 +1,14 @@
+package org.cryptomator.ui.lock;
+
+import org.cryptomator.ui.common.FxController;
+
+import javax.inject.Inject;
+
+public class LockFailedController implements FxController {
+
+	@Inject
+	public LockFailedController() {
+
+	}
+
+}

+ 44 - 1
main/ui/src/main/java/org/cryptomator/ui/lock/LockForcedController.java

@@ -1,5 +1,48 @@
 package org.cryptomator.ui.lock;
 
-public class LockForcedController {
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.common.UserInteractionLock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javafx.event.ActionEvent;
+import javafx.stage.Stage;
+import javafx.stage.WindowEvent;
+
+public class LockForcedController implements FxController {
+
+	private static final Logger LOG = LoggerFactory.getLogger(LockForcedController.class);
+
+	private final Stage window;
+	private final Vault vault;
+	private final UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock;
+
+	@Inject
+	public LockForcedController(@LockWindow Stage window, @LockWindow Vault vault, UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock) {
+		this.window = window;
+		this.vault = vault;
+		this.forceLockDecisionLock = forceLockDecisionLock;
+		this.window.setOnHiding(this::windowClosed);
+	}
+
+	public void cancel() {
+		forceLockDecisionLock.interacted(LockModule.ForceLockDecision.CANCEL);
+		window.close();
+	}
+
+	public void confirmForcedLock(ActionEvent actionEvent) {
+		forceLockDecisionLock.interacted(LockModule.ForceLockDecision.FORCE);
+		window.close();
+	}
+
+	private void windowClosed(WindowEvent windowEvent) {
+		// if not already interacted, mark this workflow as cancelled:
+		if (forceLockDecisionLock.awaitingInteraction().get()) {
+			LOG.debug("Lock canceled in force-lock-phase by user.");
+			forceLockDecisionLock.interacted(LockModule.ForceLockDecision.CANCEL);
+		}
+	}
 
 }

+ 82 - 1
main/ui/src/main/java/org/cryptomator/ui/lock/LockModule.java

@@ -1,8 +1,89 @@
 package org.cryptomator.ui.lock;
 
+import dagger.Binds;
 import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoMap;
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.ui.common.DefaultSceneFactory;
+import org.cryptomator.ui.common.FXMLLoaderFactory;
+import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.common.FxControllerKey;
+import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlScene;
+import org.cryptomator.ui.common.StageFactory;
+import org.cryptomator.ui.common.UserInteractionLock;
+
+import javax.inject.Named;
+import javax.inject.Provider;
+import javafx.scene.Scene;
+import javafx.stage.Modality;
+import javafx.stage.Stage;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ResourceBundle;
 
 @Module
-public class LockModule {
+abstract class LockModule {
+
+	enum ForceLockDecision {
+		CANCEL,
+		FORCE;
+	}
+
+	@Provides
+	@LockScoped
+	static UserInteractionLock<LockModule.ForceLockDecision> provideForceLockDecisionLock() {
+		return new UserInteractionLock<>(null);
+	}
+
+	@Provides
+	@LockWindow
+	@LockScoped
+	static FXMLLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
+		return new FXMLLoaderFactory(factories, sceneFactory, resourceBundle);
+	}
+
+	@Provides
+	@LockWindow
+	@LockScoped
+	static Stage provideWindow(StageFactory factory, @LockWindow Vault vault, @Named("lockWindowOwner") Optional<Stage> owner) {
+		Stage stage = factory.create();
+		stage.setTitle(vault.getDisplayName());
+		stage.setResizable(false);
+		if (owner.isPresent()) {
+			stage.initOwner(owner.get());
+			stage.initModality(Modality.WINDOW_MODAL);
+		} else {
+			stage.initModality(Modality.APPLICATION_MODAL);
+		}
+		return stage;
+	}
+
+	@Provides
+	@FxmlScene(FxmlFile.LOCK_FORCED)
+	@LockScoped
+	static Scene provideForceLockScene(@LockWindow FXMLLoaderFactory fxmlLoaders) {
+		return fxmlLoaders.createScene("/fxml/lock_forced.fxml");
+	}
+
+	@Provides
+	@FxmlScene(FxmlFile.LOCK_FAILED)
+	@LockScoped
+	static Scene provideLockFailedScene(@LockWindow FXMLLoaderFactory fxmlLoaders) {
+		return fxmlLoaders.createScene("/fxml/lock_failed.fxml");
+	}
+
+	// ------------------
+
+	@Binds
+	@IntoMap
+	@FxControllerKey(LockForcedController.class)
+	abstract FxController bindLockForcedController(LockForcedController controller);
+
+	@Binds
+	@IntoMap
+	@FxControllerKey(LockFailedController.class)
+	abstract FxController bindLockFailedController(LockFailedController controller);
 
 }

+ 43 - 12
main/ui/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java

@@ -1,27 +1,43 @@
 package org.cryptomator.ui.lock;
 
+import dagger.Lazy;
 import org.cryptomator.common.vaults.Vault;
 import org.cryptomator.common.vaults.VaultState;
 import org.cryptomator.common.vaults.Volume;
+import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlScene;
+import org.cryptomator.ui.common.UserInteractionLock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.inject.Inject;
+import javafx.application.Platform;
 import javafx.concurrent.Task;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+import javafx.stage.Window;
 
 public class LockWorkflow extends Task<Boolean> {
 
 	private static final Logger LOG = LoggerFactory.getLogger(LockWorkflow.class);
 
+	private final Stage lockWindow;
 	private final Vault vault;
+	private final UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock;
+	private final Lazy<Scene> lockForcedScene;
+	private final Lazy<Scene> lockFailedScene;
 
 	@Inject
-	public LockWorkflow(@LockWindow Vault vault) {
+	public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy<Scene> lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy<Scene> lockFailedScene) {
+		this.lockWindow = lockWindow;
 		this.vault = vault;
+		this.forceLockDecisionLock = forceLockDecisionLock;
+		this.lockForcedScene = lockForcedScene;
+		this.lockFailedScene = lockFailedScene;
 	}
 
 	@Override
-	protected Boolean call() throws Exception {
+	protected Boolean call() throws Volume.VolumeException, InterruptedException {
 		// change vault state to processing -- done by overriding scheduled method of Task
 		if (attemptLock() || attemptForcedLock()) {
 			handleSuccess();
@@ -42,19 +58,34 @@ public class LockWorkflow extends Task<Boolean> {
 		}
 	}
 
-	private boolean attemptForcedLock() {
-		// show forcedLock dialogue
-		// wait for answer
-		// depening on answer do one of two things
-		// a) force Lock -> needs to throw exception on failure
-		// b) cancel
-		// if lock was performed over main window, show it again
-		return false;
+	private boolean attemptForcedLock() throws Volume.VolumeException, InterruptedException {
+		// show forcedLock dialogue ...
+		Platform.runLater(() -> {
+			lockWindow.setScene(lockForcedScene.get());
+			lockWindow.show();
+			Window owner = lockWindow.getOwner();
+			if (owner != null) {
+				lockWindow.setX(owner.getX() + (owner.getWidth() - lockWindow.getWidth()) / 2);
+				lockWindow.setY(owner.getY() + (owner.getHeight() - lockWindow.getHeight()) / 2);
+			} else {
+				lockWindow.centerOnScreen();
+			}
+		});
+		// ... and wait for answer
+		switch (forceLockDecisionLock.awaitInteraction()) {
+			case FORCE:
+				vault.lock(true);
+				return true;
+			case CANCEL:
+				// if lock was performed over main window, show it again
+				return false;
+			default:
+				return false;
+		}
 	}
 
 	private void handleSuccess() {
-		LOG.info("Lock of  {} succeeded.", vault.getDisplayName());
-		// set vault state to locked
+		LOG.info("Lock of {} succeeded.", vault.getDisplayName());
 	}
 
 	@Override

+ 20 - 0
main/ui/src/main/resources/fxml/lock_failed.fxml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Label?>
+<?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.lock.LockFailedController"
+	  minWidth="400"
+	  maxWidth="400"
+	  minHeight="145"
+	  spacing="12">
+	<padding>
+		<Insets topRightBottomLeft="12"/>
+	</padding>
+	<children>
+		<Label text="This is a stub." wrapText="true" textAlignment="LEFT" HBox.hgrow="ALWAYS"/>
+	</children>
+</VBox>

+ 40 - 0
main/ui/src/main/resources/fxml/lock_forced.fxml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.ButtonBar?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.StackPane?>
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.shape.Circle?>
+<VBox xmlns:fx="http://javafx.com/fxml"
+	  xmlns="http://javafx.com/javafx"
+	  fx:controller="org.cryptomator.ui.lock.LockForcedController"
+	  minWidth="400"
+	  maxWidth="400"
+	  minHeight="145"
+	  spacing="12">
+	<padding>
+		<Insets topRightBottomLeft="12"/>
+	</padding>
+	<children>
+		<HBox spacing="12" alignment="CENTER_LEFT" VBox.vgrow="ALWAYS">
+			<StackPane alignment="CENTER" HBox.hgrow="NEVER">
+				<Circle styleClass="glyph-icon-orange" radius="24"/>
+				<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
+			</StackPane>
+			<Label text="Yadda yadda" wrapText="true" textAlignment="LEFT" HBox.hgrow="ALWAYS"/>
+		</HBox>
+
+		<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
+			<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
+				<buttons>
+					<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" cancelButton="true" onAction="#cancel"/>
+					<Button text="Use the FORCE, Luke!" ButtonBar.buttonData="FINISH" onAction="#confirmForcedLock"/>
+				</buttons>
+			</ButtonBar>
+		</VBox>
+	</children>
+</VBox>