mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-19 19:21:27 +00:00
fixes #511
This commit is contained in:
@@ -50,6 +50,7 @@ import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.binding.BooleanExpression;
|
||||
import javafx.beans.binding.ObjectExpression;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
@@ -92,9 +93,9 @@ public class MainController implements ViewController {
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final BooleanBinding areAllVaultsLocked;
|
||||
private final ObjectProperty<Vault> selectedVault = new SimpleObjectProperty<>();
|
||||
private final BooleanExpression isSelectedVaultUnlocked = BooleanExpression.booleanExpression(EasyBind.select(selectedVault).selectObject(Vault::unlockedProperty).orElse(false));
|
||||
private final ObjectExpression<Vault.State> selectedVaultState = ObjectExpression.objectExpression(EasyBind.select(selectedVault).selectObject(Vault::stateProperty));
|
||||
private final BooleanExpression isSelectedVaultValid = BooleanExpression.booleanExpression(EasyBind.monadic(selectedVault).map(Vault::isValidVaultDirectory).orElse(false));
|
||||
private final BooleanExpression canEditSelectedVault = selectedVault.isNotNull().and(isSelectedVaultUnlocked.not());
|
||||
private final BooleanExpression canEditSelectedVault = selectedVaultState.isEqualTo(Vault.State.LOCKED);
|
||||
private final MonadicBinding<UpgradeStrategy> upgradeStrategyForSelectedVault;
|
||||
private final BooleanBinding isShowingSettings;
|
||||
private final Map<Vault, UnlockedController> unlockedVaults = new HashMap<>();
|
||||
@@ -116,7 +117,7 @@ public class MainController implements ViewController {
|
||||
// derived bindings:
|
||||
this.isShowingSettings = Bindings.equal(SettingsController.class, EasyBind.monadic(activeController).map(ViewController::getClass));
|
||||
this.upgradeStrategyForSelectedVault = EasyBind.monadic(selectedVault).map(upgradeStrategies::getUpgradeStrategy);
|
||||
this.areAllVaultsLocked = Bindings.isEmpty(FXCollections.observableList(vaults, Vault::observables).filtered(Vault::isUnlocked));
|
||||
this.areAllVaultsLocked = Bindings.isEmpty(FXCollections.observableList(vaults, Vault::observables).filtered(Vault.NOT_LOCKED));
|
||||
|
||||
EasyBind.subscribe(areAllVaultsLocked, Platform::setImplicitExit);
|
||||
autoUnlocker.unlockAllSilently();
|
||||
@@ -201,7 +202,7 @@ public class MainController implements ViewController {
|
||||
}
|
||||
|
||||
private void gracefulShutdown() {
|
||||
vaults.filtered(Vault::isUnlocked).forEach(Vault::prepareForShutdown);
|
||||
vaults.filtered(Vault.NOT_LOCKED).forEach(Vault::prepareForShutdown);
|
||||
Platform.runLater(Platform::exit);
|
||||
}
|
||||
|
||||
@@ -361,7 +362,7 @@ public class MainController implements ViewController {
|
||||
if (newValue == null) {
|
||||
return;
|
||||
}
|
||||
if (newValue.isUnlocked()) {
|
||||
if (newValue.getState() != Vault.State.LOCKED) {
|
||||
this.showUnlockedView(newValue);
|
||||
} else if (!newValue.doesVaultDirectoryExist()) {
|
||||
this.showNotFoundView();
|
||||
|
||||
@@ -30,7 +30,7 @@ import com.google.common.util.concurrent.Runnables;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.beans.binding.BooleanExpression;
|
||||
import javafx.beans.binding.ObjectExpression;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
@@ -64,7 +64,7 @@ public class UnlockedController implements ViewController {
|
||||
private final Localization localization;
|
||||
private final AsyncTaskService asyncTaskService;
|
||||
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||
private final BooleanExpression vaultMounted = BooleanExpression.booleanExpression(EasyBind.select(vault).selectObject(Vault::mountedProperty).orElse(false));
|
||||
private final ObjectExpression<Vault.State> vaultState = ObjectExpression.objectExpression(EasyBind.select(vault).selectObject(Vault::stateProperty));
|
||||
private Optional<LockListener> listener = Optional.empty();
|
||||
private Timeline ioAnimation;
|
||||
|
||||
@@ -103,9 +103,9 @@ public class UnlockedController implements ViewController {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
mountVaultMenuItem.disableProperty().bind(vaultMounted);
|
||||
unmountVaultMenuItem.disableProperty().bind(vaultMounted.not());
|
||||
revealVaultMenuItem.disableProperty().bind(vaultMounted.not());
|
||||
mountVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.UNLOCKED).not()); // enable when unlocked
|
||||
unmountVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.MOUNTED).not()); // enable when mounted
|
||||
revealVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.MOUNTED).not()); // enable when mounted
|
||||
|
||||
EasyBind.subscribe(vault, this::vaultChanged);
|
||||
EasyBind.subscribe(moreOptionsMenu.showingProperty(), moreOptionsButton::setSelected);
|
||||
@@ -121,7 +121,7 @@ public class UnlockedController implements ViewController {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newVault.getVaultSettings().mountAfterUnlock().get()) {
|
||||
if (newVault.getState() == Vault.State.UNLOCKED && newVault.getVaultSettings().mountAfterUnlock().get()) {
|
||||
mountVault(newVault);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ package org.cryptomator.ui.controls;
|
||||
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.monadic.MonadicBinding;
|
||||
|
||||
import javafx.beans.binding.ObjectExpression;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
@@ -31,9 +31,13 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
|
||||
private final Label pathText = new Label();
|
||||
private final VBox vbox = new VBox(4.0, nameText, pathText);
|
||||
private final HBox hbox = new HBox(6.0, statusText, vbox);
|
||||
private final ObjectExpression<Vault.State> vaultState;
|
||||
|
||||
private ContextMenu vaultContextMenu;
|
||||
|
||||
public DirectoryListCell() {
|
||||
vaultState = ObjectExpression.objectExpression(EasyBind.select(itemProperty()).selectObject(Vault::stateProperty));
|
||||
|
||||
hbox.setAlignment(Pos.CENTER_LEFT);
|
||||
hbox.setPrefWidth(1);
|
||||
vbox.setFillWidth(true);
|
||||
@@ -46,36 +50,57 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
|
||||
pathText.setTextOverrun(OverrunStyle.ELLIPSIS);
|
||||
pathText.getStyleClass().add("detail-label");
|
||||
|
||||
MonadicBinding<Boolean> optionalItemIsUnlocked = EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty);
|
||||
statusText.textProperty().bind(optionalItemIsUnlocked.map(this::getStatusIconText));
|
||||
statusText.textFillProperty().bind(EasyBind.combine(optionalItemIsUnlocked, textFillProperty(), this::getStatusIconColor));
|
||||
statusText.textProperty().bind(EasyBind.map(vaultState, this::getStatusIconText));
|
||||
statusText.textFillProperty().bind(EasyBind.combine(vaultState, textFillProperty(), this::getStatusIconColor));
|
||||
statusText.setMinSize(16.0, 16.0);
|
||||
statusText.setAlignment(Pos.CENTER);
|
||||
statusText.getStyleClass().add("fontawesome");
|
||||
|
||||
tooltipProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::displayablePath).map(p -> new Tooltip(p.toString())));
|
||||
contextMenuProperty().bind(optionalItemIsUnlocked.map(unlocked -> unlocked ? null : vaultContextMenu));
|
||||
contextMenuProperty().bind(EasyBind.map(vaultState, this::getContextMenu));
|
||||
|
||||
setGraphic(hbox);
|
||||
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
|
||||
}
|
||||
|
||||
private String getStatusIconText(Boolean unlockedOrNull) {
|
||||
if (Boolean.TRUE.equals(unlockedOrNull)) {
|
||||
return "\uf09c";
|
||||
} else if (Boolean.FALSE.equals(unlockedOrNull)) {
|
||||
return "\uf023";
|
||||
} else {
|
||||
private String getStatusIconText(Vault.State state) {
|
||||
if (state == null) {
|
||||
return "";
|
||||
}
|
||||
switch (state) {
|
||||
case UNLOCKED:
|
||||
case MOUNTED:
|
||||
case MOUNTING:
|
||||
case UNMOUNTING:
|
||||
return "\uf09c";
|
||||
case LOCKED:
|
||||
default:
|
||||
return "\uf023";
|
||||
}
|
||||
}
|
||||
|
||||
private Paint getStatusIconColor(Boolean unlockedOrNull, Paint lockedValue) {
|
||||
if (Boolean.TRUE.equals(unlockedOrNull)) {
|
||||
return UNLOCKED_ICON_COLOR;
|
||||
} else {
|
||||
private Paint getStatusIconColor(Vault.State state, Paint lockedValue) {
|
||||
if (state == null) {
|
||||
return lockedValue;
|
||||
}
|
||||
switch (state) {
|
||||
case UNLOCKED:
|
||||
case MOUNTED:
|
||||
case MOUNTING:
|
||||
case UNMOUNTING:
|
||||
return UNLOCKED_ICON_COLOR;
|
||||
case LOCKED:
|
||||
default:
|
||||
return lockedValue;
|
||||
}
|
||||
}
|
||||
|
||||
private ContextMenu getContextMenu(Vault.State state) {
|
||||
if (state == Vault.State.LOCKED) {
|
||||
return vaultContextMenu;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setVaultContextMenu(ContextMenu contextMenu) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -50,25 +51,30 @@ import org.slf4j.LoggerFactory;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
@PerVault
|
||||
public class Vault {
|
||||
|
||||
public static final Predicate<Vault> NOT_LOCKED = hasState(State.LOCKED).negate();
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Vault.class);
|
||||
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator";
|
||||
|
||||
private final Settings settings;
|
||||
private final VaultSettings vaultSettings;
|
||||
private final WebDavServer server;
|
||||
private final BooleanProperty unlocked = new SimpleBooleanProperty();
|
||||
private final BooleanProperty mounted = new SimpleBooleanProperty();
|
||||
private final AtomicReference<CryptoFileSystem> cryptoFileSystem = new AtomicReference<>();
|
||||
private final ObjectProperty<State> state = new SimpleObjectProperty<State>(State.LOCKED);
|
||||
|
||||
private WebDavServletController servlet;
|
||||
private Mount mount;
|
||||
|
||||
public enum State {
|
||||
LOCKED, UNLOCKED, MOUNTING, MOUNTED, UNMOUNTING
|
||||
};
|
||||
|
||||
@Inject
|
||||
Vault(Settings settings, VaultSettings vaultSettings, WebDavServer server) {
|
||||
this.settings = settings;
|
||||
@@ -121,7 +127,7 @@ public class Vault {
|
||||
servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.mountName().get());
|
||||
servlet.start();
|
||||
Platform.runLater(() -> {
|
||||
unlocked.set(true);
|
||||
state.set(State.UNLOCKED);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LOG.error("Unable to provide filesystem", e);
|
||||
@@ -138,9 +144,12 @@ public class Vault {
|
||||
.withPreferredGvfsScheme(settings.preferredGvfsScheme().get()) //
|
||||
.build();
|
||||
|
||||
mount = servlet.mount(mountParams);
|
||||
Platform.runLater(() -> {
|
||||
mounted.set(true);
|
||||
state.set(State.MOUNTING);
|
||||
});
|
||||
mount = servlet.mount(mountParams); // might block this thread for a while
|
||||
Platform.runLater(() -> {
|
||||
state.set(State.MOUNTED);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -153,11 +162,14 @@ public class Vault {
|
||||
}
|
||||
|
||||
private synchronized void unmount(Function<Mount, ? extends UnmountOperation> unmountOperationChooser) throws CommandFailedException {
|
||||
Platform.runLater(() -> {
|
||||
state.set(State.UNMOUNTING);
|
||||
});
|
||||
if (mount != null) {
|
||||
unmountOperationChooser.apply(mount).unmount();
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
mounted.set(false);
|
||||
state.set(State.UNLOCKED);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -174,7 +186,7 @@ public class Vault {
|
||||
fs.close();
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
unlocked.set(false);
|
||||
state.set(State.LOCKED);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -212,8 +224,22 @@ public class Vault {
|
||||
// Getter/Setter
|
||||
// *******************************************************************************/
|
||||
|
||||
public State getState() {
|
||||
return state.get();
|
||||
}
|
||||
|
||||
public ReadOnlyObjectProperty<State> stateProperty() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public static Predicate<Vault> hasState(State state) {
|
||||
return vault -> {
|
||||
return vault.getState() == state;
|
||||
};
|
||||
}
|
||||
|
||||
public Observable[] observables() {
|
||||
return new Observable[] {unlocked, mounted};
|
||||
return new Observable[] {state};
|
||||
}
|
||||
|
||||
public VaultSettings getVaultSettings() {
|
||||
@@ -256,22 +282,6 @@ public class Vault {
|
||||
return CryptoFileSystemProvider.containsVault(getPath(), MASTERKEY_FILENAME);
|
||||
}
|
||||
|
||||
public BooleanProperty unlockedProperty() {
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
public BooleanProperty mountedProperty() {
|
||||
return mounted;
|
||||
}
|
||||
|
||||
public boolean isUnlocked() {
|
||||
return unlocked.get();
|
||||
}
|
||||
|
||||
public boolean isMounted() {
|
||||
return mounted.get();
|
||||
}
|
||||
|
||||
public long pollBytesRead() {
|
||||
CryptoFileSystem fs = cryptoFileSystem.get();
|
||||
if (fs != null) {
|
||||
|
||||
Reference in New Issue
Block a user