Add forceLock Dialogue:

* integrate it in workflow if normal lock throws exception
* add stubs if also forced lock fails
This commit is contained in:
Armin Schrenk
2020-11-19 12:52:16 +01:00
parent 57bfa3276d
commit 432a9a27f1
7 changed files with 245 additions and 14 deletions

View File

@@ -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"),

View File

@@ -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() {
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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>