mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-08-22 07:06:03 +00:00
moved mount + reveal from UnlockController to UnlockedController
This commit is contained in:
@@ -21,7 +21,6 @@ import org.cryptomator.common.settings.VaultSettings;
|
|||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||||
import org.cryptomator.frontend.webdav.ServerLifecycleException;
|
import org.cryptomator.frontend.webdav.ServerLifecycleException;
|
||||||
import org.cryptomator.frontend.webdav.mount.Mounter.CommandFailedException;
|
|
||||||
import org.cryptomator.keychain.KeychainAccess;
|
import org.cryptomator.keychain.KeychainAccess;
|
||||||
import org.cryptomator.ui.controls.SecPasswordField;
|
import org.cryptomator.ui.controls.SecPasswordField;
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
@@ -38,7 +37,6 @@ import com.google.common.base.CharMatcher;
|
|||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -345,59 +343,42 @@ public class UnlockController implements ViewController {
|
|||||||
private void didClickUnlockButton(ActionEvent event) {
|
private void didClickUnlockButton(ActionEvent event) {
|
||||||
advancedOptions.setDisable(true);
|
advancedOptions.setDisable(true);
|
||||||
progressIndicator.setVisible(true);
|
progressIndicator.setVisible(true);
|
||||||
downloadsPageLink.setVisible(false);
|
|
||||||
CharSequence password = passwordField.getCharacters();
|
|
||||||
asyncTaskService.asyncTaskOf(() -> this.unlock(password)).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unlock(CharSequence password) {
|
CharSequence password = passwordField.getCharacters();
|
||||||
try {
|
asyncTaskService.asyncTaskOf(() -> {
|
||||||
vault.unlock(password);
|
vault.unlock(password);
|
||||||
if (mountAfterUnlock.isSelected()) {
|
|
||||||
vault.mount();
|
|
||||||
if (revealAfterMount.isSelected()) {
|
|
||||||
vault.reveal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
messageText.setText(null);
|
|
||||||
listener.ifPresent(lstnr -> lstnr.didUnlock(vault));
|
|
||||||
});
|
|
||||||
if (keychainAccess.isPresent() && savePassword.isSelected()) {
|
if (keychainAccess.isPresent() && savePassword.isSelected()) {
|
||||||
keychainAccess.get().storePassphrase(vault.getId(), password);
|
keychainAccess.get().storePassphrase(vault.getId(), password);
|
||||||
} else {
|
|
||||||
Platform.runLater(passwordField::swipe);
|
|
||||||
}
|
}
|
||||||
} catch (InvalidPassphraseException e) {
|
}).onSuccess(() -> {
|
||||||
Platform.runLater(() -> {
|
messageText.setText(null);
|
||||||
messageText.setText(localization.getString("unlock.errorMessage.wrongPassword"));
|
downloadsPageLink.setVisible(false);
|
||||||
passwordField.selectAll();
|
listener.ifPresent(lstnr -> lstnr.didUnlock(vault));
|
||||||
passwordField.requestFocus();
|
}).onError(InvalidPassphraseException.class, e -> {
|
||||||
});
|
messageText.setText(localization.getString("unlock.errorMessage.wrongPassword"));
|
||||||
} catch (UnsupportedVaultFormatException e) {
|
passwordField.selectAll();
|
||||||
Platform.runLater(() -> {
|
passwordField.requestFocus();
|
||||||
if (e.isVaultOlderThanSoftware()) {
|
}).onError(UnsupportedVaultFormatException.class, e -> {
|
||||||
// whitespace after localized text used as separator between text and hyperlink
|
if (e.isVaultOlderThanSoftware()) {
|
||||||
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
|
// whitespace after localized text used as separator between text and hyperlink
|
||||||
downloadsPageLink.setVisible(true);
|
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
|
||||||
} else if (e.isSoftwareOlderThanVault()) {
|
downloadsPageLink.setVisible(true);
|
||||||
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
|
} else if (e.isSoftwareOlderThanVault()) {
|
||||||
downloadsPageLink.setVisible(true);
|
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
|
||||||
} else if (e.getDetectedVersion() == Integer.MAX_VALUE) {
|
downloadsPageLink.setVisible(true);
|
||||||
messageText.setText(localization.getString("unlock.errorMessage.unauthenticVersionMac"));
|
} else if (e.getDetectedVersion() == Integer.MAX_VALUE) {
|
||||||
}
|
messageText.setText(localization.getString("unlock.errorMessage.unauthenticVersionMac"));
|
||||||
});
|
}
|
||||||
} catch (ServerLifecycleException | CommandFailedException e) {
|
}).onError(ServerLifecycleException.class, e -> {
|
||||||
LOG.error("Unlock failed for technical reasons.", e);
|
LOG.error("Unlock failed for technical reasons.", e);
|
||||||
Platform.runLater(() -> {
|
messageText.setText(localization.getString("unlock.errorMessage.unlockFailed"));
|
||||||
messageText.setText(localization.getString("unlock.errorMessage.mountingFailed"));
|
}).andFinally(() -> {
|
||||||
});
|
if (!savePassword.isSelected()) {
|
||||||
} finally {
|
passwordField.swipe();
|
||||||
Platform.runLater(() -> {
|
}
|
||||||
advancedOptions.setDisable(false);
|
advancedOptions.setDisable(false);
|
||||||
progressIndicator.setVisible(false);
|
progressIndicator.setVisible(false);
|
||||||
});
|
}).run();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* callback */
|
/* callback */
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.cryptomator.frontend.webdav.mount.Mounter.CommandFailedException;
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
import org.cryptomator.ui.model.Vault;
|
import org.cryptomator.ui.model.Vault;
|
||||||
import org.cryptomator.ui.util.AsyncTaskService;
|
import org.cryptomator.ui.util.AsyncTaskService;
|
||||||
@@ -25,6 +26,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import javafx.animation.Animation;
|
import javafx.animation.Animation;
|
||||||
import javafx.animation.KeyFrame;
|
import javafx.animation.KeyFrame;
|
||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
|
import javafx.beans.binding.BooleanExpression;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -58,6 +60,7 @@ public class UnlockedController implements ViewController {
|
|||||||
private final Localization localization;
|
private final Localization localization;
|
||||||
private final AsyncTaskService asyncTaskService;
|
private final AsyncTaskService asyncTaskService;
|
||||||
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||||
|
private final BooleanExpression vaultMounted = BooleanExpression.booleanExpression(EasyBind.select(vault).selectObject(Vault::mountedProperty).orElse(false));
|
||||||
private Optional<LockListener> listener = Optional.empty();
|
private Optional<LockListener> listener = Optional.empty();
|
||||||
private Timeline ioAnimation;
|
private Timeline ioAnimation;
|
||||||
|
|
||||||
@@ -76,6 +79,9 @@ public class UnlockedController implements ViewController {
|
|||||||
@FXML
|
@FXML
|
||||||
private ContextMenu moreOptionsMenu;
|
private ContextMenu moreOptionsMenu;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private MenuItem mountVaultMenuItem;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private MenuItem revealVaultMenuItem;
|
private MenuItem revealVaultMenuItem;
|
||||||
|
|
||||||
@@ -90,7 +96,8 @@ public class UnlockedController implements ViewController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
revealVaultMenuItem.disableProperty().bind(EasyBind.map(vault, vault -> vault != null && !vault.isMounted()));
|
revealVaultMenuItem.disableProperty().bind(vaultMounted.not());
|
||||||
|
mountVaultMenuItem.disableProperty().bind(vaultMounted);
|
||||||
|
|
||||||
EasyBind.subscribe(vault, this::vaultChanged);
|
EasyBind.subscribe(vault, this::vaultChanged);
|
||||||
EasyBind.subscribe(moreOptionsMenu.showingProperty(), moreOptionsButton::setSelected);
|
EasyBind.subscribe(moreOptionsMenu.showingProperty(), moreOptionsButton::setSelected);
|
||||||
@@ -106,9 +113,8 @@ public class UnlockedController implements ViewController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newVault.getVaultSettings().mountAfterUnlock().get() && !newVault.isMounted()) {
|
if (newVault.getVaultSettings().mountAfterUnlock().get()) {
|
||||||
// TODO Markus Kreusch #393: hyperlink auf FAQ oder sowas?
|
mountVault(newVault);
|
||||||
messageLabel.setText(localization.getString("unlocked.label.mountFailed"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// (re)start throughput statistics:
|
// (re)start throughput statistics:
|
||||||
@@ -186,11 +192,35 @@ public class UnlockedController implements ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void didClickRevealVault(ActionEvent event) {
|
public void didClickMountVault(ActionEvent event) {
|
||||||
|
mountVault(vault.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mountVault(Vault vault) {
|
||||||
asyncTaskService.asyncTaskOf(() -> {
|
asyncTaskService.asyncTaskOf(() -> {
|
||||||
vault.get().reveal();
|
vault.mount();
|
||||||
}).onError(RuntimeException.class, () -> {
|
}).onSuccess(() -> {
|
||||||
// TODO overheadhunter catch more specific exception type thrown by reveal()
|
messageLabel.setText(null);
|
||||||
|
if (vault.getVaultSettings().revealAfterMount().get()) {
|
||||||
|
revealVault(vault);
|
||||||
|
}
|
||||||
|
}).onError(CommandFailedException.class, e -> {
|
||||||
|
// TODO Markus Kreusch #393: hyperlink auf FAQ oder sowas?
|
||||||
|
messageLabel.setText(localization.getString("unlocked.label.mountFailed"));
|
||||||
|
}).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void didClickRevealVault(ActionEvent event) {
|
||||||
|
revealVault(vault.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void revealVault(Vault vault) {
|
||||||
|
asyncTaskService.asyncTaskOf(() -> {
|
||||||
|
vault.reveal();
|
||||||
|
}).onSuccess(() -> {
|
||||||
|
messageLabel.setText(null);
|
||||||
|
}).onError(CommandFailedException.class, () -> {
|
||||||
messageLabel.setText(localization.getString("unlocked.label.revealFailed"));
|
messageLabel.setText(localization.getString("unlocked.label.revealFailed"));
|
||||||
}).run();
|
}).run();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
|||||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||||
import org.cryptomator.cryptolib.api.CryptoException;
|
import org.cryptomator.cryptolib.api.CryptoException;
|
||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
|
import org.cryptomator.frontend.webdav.ServerLifecycleException;
|
||||||
import org.cryptomator.frontend.webdav.WebDavServer;
|
import org.cryptomator.frontend.webdav.WebDavServer;
|
||||||
import org.cryptomator.frontend.webdav.mount.MountParams;
|
import org.cryptomator.frontend.webdav.mount.MountParams;
|
||||||
import org.cryptomator.frontend.webdav.mount.Mounter.CommandFailedException;
|
import org.cryptomator.frontend.webdav.mount.Mounter.CommandFailedException;
|
||||||
@@ -108,7 +109,7 @@ public class Vault {
|
|||||||
CryptoFileSystemProvider.changePassphrase(getPath(), MASTERKEY_FILENAME, oldPassphrase, newPassphrase);
|
CryptoFileSystemProvider.changePassphrase(getPath(), MASTERKEY_FILENAME, oldPassphrase, newPassphrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void unlock(CharSequence passphrase) {
|
public synchronized void unlock(CharSequence passphrase) throws ServerLifecycleException {
|
||||||
try {
|
try {
|
||||||
FileSystem fs = getCryptoFileSystem(passphrase);
|
FileSystem fs = getCryptoFileSystem(passphrase);
|
||||||
if (!server.isRunning()) {
|
if (!server.isRunning()) {
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ public class AsyncTaskService {
|
|||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new async task
|
||||||
|
*
|
||||||
|
* @param task Tasks to be invoked in a background thread.
|
||||||
|
* @return The async task
|
||||||
|
*/
|
||||||
public AsyncTaskWithoutSuccessHandler<Void> asyncTaskOf(RunnableThrowingException<?> task) {
|
public AsyncTaskWithoutSuccessHandler<Void> asyncTaskOf(RunnableThrowingException<?> task) {
|
||||||
return new AsyncTaskImpl<>(() -> {
|
return new AsyncTaskImpl<>(() -> {
|
||||||
task.run();
|
task.run();
|
||||||
@@ -39,6 +45,12 @@ public class AsyncTaskService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new async task
|
||||||
|
*
|
||||||
|
* @param task Tasks to be invoked in a background thread.
|
||||||
|
* @return The async task
|
||||||
|
*/
|
||||||
public <ResultType> AsyncTaskWithoutSuccessHandler<ResultType> asyncTaskOf(SupplierThrowingException<ResultType, ?> task) {
|
public <ResultType> AsyncTaskWithoutSuccessHandler<ResultType> asyncTaskOf(SupplierThrowingException<ResultType, ?> task) {
|
||||||
return new AsyncTaskImpl<>(task);
|
return new AsyncTaskImpl<>(task);
|
||||||
}
|
}
|
||||||
@@ -153,27 +165,55 @@ public class AsyncTaskService {
|
|||||||
|
|
||||||
public interface AsyncTaskWithoutSuccessHandler<ResultType> extends AsyncTaskWithoutErrorHandler {
|
public interface AsyncTaskWithoutSuccessHandler<ResultType> extends AsyncTaskWithoutErrorHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||||
|
* @return The async task
|
||||||
|
*/
|
||||||
AsyncTaskWithoutErrorHandler onSuccess(ConsumerThrowingException<ResultType, ?> handler);
|
AsyncTaskWithoutErrorHandler onSuccess(ConsumerThrowingException<ResultType, ?> handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||||
|
* @return The async task
|
||||||
|
*/
|
||||||
AsyncTaskWithoutErrorHandler onSuccess(RunnableThrowingException<?> handler);
|
AsyncTaskWithoutErrorHandler onSuccess(RunnableThrowingException<?> handler);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AsyncTaskWithoutErrorHandler extends AsyncTaskWithoutFinallyHandler {
|
public interface AsyncTaskWithoutErrorHandler extends AsyncTaskWithoutFinallyHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param type Exception type to catch
|
||||||
|
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||||
|
* @return The async task
|
||||||
|
*/
|
||||||
<ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, ConsumerThrowingException<ErrorType, ?> handler);
|
<ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, ConsumerThrowingException<ErrorType, ?> handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param type Exception type to catch
|
||||||
|
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||||
|
* @return The async task
|
||||||
|
*/
|
||||||
<ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, RunnableThrowingException<?> handler);
|
<ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, RunnableThrowingException<?> handler);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AsyncTaskWithoutFinallyHandler extends AsyncTask {
|
public interface AsyncTaskWithoutFinallyHandler extends AsyncTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||||
|
* @return The async task
|
||||||
|
*/
|
||||||
AsyncTask andFinally(RunnableThrowingException<?> handler);
|
AsyncTask andFinally(RunnableThrowingException<?> handler);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AsyncTask extends Runnable {
|
public interface AsyncTask extends Runnable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the async task.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
void run();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@
|
|||||||
<fx:define>
|
<fx:define>
|
||||||
<ContextMenu fx:id="moreOptionsMenu">
|
<ContextMenu fx:id="moreOptionsMenu">
|
||||||
<items>
|
<items>
|
||||||
|
<MenuItem fx:id="mountVaultMenuItem" text="%unlocked.moreOptions.mount" onAction="#didClickMountVault">
|
||||||
|
<graphic><Label text="" styleClass="ionicons"/></graphic>
|
||||||
|
</MenuItem>
|
||||||
<MenuItem fx:id="revealVaultMenuItem" text="%unlocked.moreOptions.reveal" onAction="#didClickRevealVault">
|
<MenuItem fx:id="revealVaultMenuItem" text="%unlocked.moreOptions.reveal" onAction="#didClickRevealVault">
|
||||||
<graphic><Label text="" styleClass="ionicons"/></graphic>
|
<graphic><Label text="" styleClass="ionicons"/></graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ unlock.savePassword.delete.confirmation.header=Do you really want to delete the
|
|||||||
unlock.savePassword.delete.confirmation.content=The saved password of this vault will be immediately deleted from your system keychain. If you'd like to save your password again, you'd have to unlock your vault with the "Save Password" option enabled.
|
unlock.savePassword.delete.confirmation.content=The saved password of this vault will be immediately deleted from your system keychain. If you'd like to save your password again, you'd have to unlock your vault with the "Save Password" option enabled.
|
||||||
unlock.choicebox.winDriveLetter.auto=Assign automatically
|
unlock.choicebox.winDriveLetter.auto=Assign automatically
|
||||||
unlock.errorMessage.wrongPassword=Wrong password
|
unlock.errorMessage.wrongPassword=Wrong password
|
||||||
unlock.errorMessage.mountingFailed=Mounting failed. See log file for details.
|
unlock.errorMessage.unlockFailed=Unlock failed. See log file for details.
|
||||||
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware=Unsupported vault. This vault has been created with an older version of Cryptomator.
|
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware=Unsupported vault. This vault has been created with an older version of Cryptomator.
|
||||||
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault=Unsupported vault. This vault has been created with a newer version of Cryptomator.
|
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault=Unsupported vault. This vault has been created with a newer version of Cryptomator.
|
||||||
unlock.errorMessage.unauthenticVersionMac=Could not authenticate version MAC.
|
unlock.errorMessage.unauthenticVersionMac=Could not authenticate version MAC.
|
||||||
@@ -89,6 +89,7 @@ changePassword.errorMessage.decryptionFailed=Decryption failed
|
|||||||
|
|
||||||
# unlocked.fxml
|
# unlocked.fxml
|
||||||
unlocked.button.lock=Lock Vault
|
unlocked.button.lock=Lock Vault
|
||||||
|
unlocked.moreOptions.mount=Mount Drive
|
||||||
unlocked.moreOptions.reveal=Reveal Drive
|
unlocked.moreOptions.reveal=Reveal Drive
|
||||||
unlocked.moreOptions.copyUrl=Copy WebDAV URL
|
unlocked.moreOptions.copyUrl=Copy WebDAV URL
|
||||||
unlocked.label.mountFailed=Connecting drive failed
|
unlocked.label.mountFailed=Connecting drive failed
|
||||||
|
|||||||
Reference in New Issue
Block a user