mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 10:41:26 +00:00
Added migration workflow
This commit is contained in:
@@ -25,13 +25,14 @@ public class AddVaultSuccessController implements FxController {
|
||||
this.vault = vault;
|
||||
}
|
||||
|
||||
public void unlockAndClose(ActionEvent actionEvent) {
|
||||
close(actionEvent);
|
||||
@FXML
|
||||
public void unlockAndClose() {
|
||||
close();
|
||||
fxApplication.showUnlockWindow(vault.get());
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close(ActionEvent actionEvent) {
|
||||
public void close() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ public enum FxmlFile {
|
||||
ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), //
|
||||
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
||||
MAIN_WINDOW("/fxml/main_window.fxml"), //
|
||||
MIGRATION_RUN("/fxml/migration_run.fxml"), //
|
||||
MIGRATION_START("/fxml/migration_start.fxml"), //
|
||||
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
|
||||
PREFERENCES("/fxml/preferences.fxml"), //
|
||||
QUIT("/fxml/quit.fxml"), //
|
||||
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.cryptomator.ui.migration;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Lazy;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.IntoSet;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.input.KeyCode;
|
||||
@@ -11,6 +13,7 @@ import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.common.FXMLLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
@@ -23,6 +26,7 @@ import javax.inject.Provider;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
@Module
|
||||
abstract class MigrationModule {
|
||||
@@ -33,16 +37,26 @@ abstract class MigrationModule {
|
||||
static FXMLLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, ResourceBundle resourceBundle) {
|
||||
return new FXMLLoaderFactory(factories, resourceBundle);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@MigrationWindow
|
||||
@MigrationScoped
|
||||
static Map<KeyCodeCombination, Runnable> provideDefaultAccellerators(@MigrationWindow Set<Map.Entry<KeyCombination, Runnable>> accelerators) {
|
||||
return Map.ofEntries(accelerators.toArray(Map.Entry[]::new));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@MigrationWindow
|
||||
@MigrationScoped
|
||||
static Stage provideStage(@MainWindow Stage owner, ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon) {
|
||||
static Stage provideStage(@MainWindow Stage owner, ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon, @MigrationWindow Lazy<Map<KeyCodeCombination, Runnable>> accelerators) {
|
||||
Stage stage = new Stage();
|
||||
stage.setTitle(resourceBundle.getString("unlock.title"));
|
||||
stage.setTitle(resourceBundle.getString("migration.title"));
|
||||
stage.setResizable(false);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.initOwner(owner);
|
||||
stage.sceneProperty().addListener(observable -> {
|
||||
stage.getScene().getAccelerators().putAll(accelerators.get());
|
||||
});
|
||||
windowIcon.ifPresent(stage.getIcons()::add);
|
||||
return stage;
|
||||
}
|
||||
@@ -50,15 +64,36 @@ abstract class MigrationModule {
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.MIGRATION_START)
|
||||
@MigrationScoped
|
||||
static Scene provideMigrationStartScene(@MigrationWindow FXMLLoaderFactory fxmlLoaders, @MigrationWindow Stage window) {
|
||||
Scene scene = fxmlLoaders.createScene("/fxml/migration_start.fxml"); // TODO rename fxml file
|
||||
|
||||
KeyCombination cmdW = new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN);
|
||||
scene.getAccelerators().put(cmdW, window::close);
|
||||
|
||||
return scene;
|
||||
static Scene provideMigrationStartScene(@MigrationWindow FXMLLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene("/fxml/migration_start.fxml");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.MIGRATION_RUN)
|
||||
@MigrationScoped
|
||||
static Scene provideMigrationRunScene(@MigrationWindow FXMLLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene("/fxml/migration_run.fxml");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.MIGRATION_SUCCESS)
|
||||
@MigrationScoped
|
||||
static Scene provideMigrationSuccessScene(@MigrationWindow FXMLLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene("/fxml/migration_success.fxml");
|
||||
}
|
||||
|
||||
// ------------------
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
@MigrationWindow
|
||||
static Map.Entry<KeyCombination, Runnable> provideCloseWindowShortcut(@MigrationWindow Stage window) {
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
return Map.entry(new KeyCodeCombination(KeyCode.F4, KeyCombination.ALT_DOWN), window::close);
|
||||
} else {
|
||||
return Map.entry(new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN), window::close);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------
|
||||
|
||||
@@ -67,5 +102,14 @@ abstract class MigrationModule {
|
||||
@FxControllerKey(MigrationStartController.class)
|
||||
abstract FxController bindMigrationStartController(MigrationStartController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(MigrationRunController.class)
|
||||
abstract FxController bindMigrationRunController(MigrationRunController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(MigrationSuccessController.class)
|
||||
abstract FxController bindMigrationSuccessController(MigrationSuccessController controller);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package org.cryptomator.ui.migration;
|
||||
|
||||
import dagger.Lazy;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.WritableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Duration;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.cryptofs.migration.Migrators;
|
||||
import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.keychain.KeychainAccess;
|
||||
import org.cryptomator.keychain.KeychainAccessException;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.Tasks;
|
||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@MigrationScoped
|
||||
public class MigrationRunController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MigrationRunController.class);
|
||||
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes
|
||||
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final ExecutorService executor;
|
||||
private final Optional<KeychainAccess> keychainAccess;
|
||||
private final Lazy<Scene> startScene;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final ObjectBinding<ContentDisplay> migrateButtonContentDisplay;
|
||||
private final BooleanProperty migrationButtonDisabled;
|
||||
public NiceSecurePasswordField passwordField;
|
||||
|
||||
@Inject
|
||||
public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, Optional<KeychainAccess> keychainAccess, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy<Scene> successScene) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.executor = executor;
|
||||
this.keychainAccess = keychainAccess;
|
||||
this.startScene = startScene;
|
||||
this.successScene = successScene;
|
||||
this.migrateButtonContentDisplay = Bindings.createObjectBinding(this::getMigrateButtonContentDisplay, vault.stateProperty());
|
||||
this.migrationButtonDisabled = new SimpleBooleanProperty();
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
if (keychainAccess.isPresent()) {
|
||||
loadStoredPassword();
|
||||
}
|
||||
migrationButtonDisabled.bind(vault.stateProperty().isNotEqualTo(VaultState.NEEDS_MIGRATION).or(passwordField.textProperty().isEmpty()));
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void back() {
|
||||
window.setScene(startScene.get());
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void migrate() {
|
||||
LOG.info("Migrating vault {}", vault.getPath());
|
||||
CharSequence password = passwordField.getCharacters();
|
||||
vault.setState(VaultState.PROCESSING);
|
||||
Tasks.create(() -> {
|
||||
Migrators migrators = Migrators.get();
|
||||
migrators.migrate(vault.getPath(), MASTERKEY_FILENAME, password);
|
||||
return migrators.needsMigration(vault.getPath(), MASTERKEY_FILENAME);
|
||||
}).onSuccess(needsAnotherMigration -> {
|
||||
if (needsAnotherMigration) {
|
||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||
} else {
|
||||
vault.setState(VaultState.LOCKED);
|
||||
passwordField.swipe();
|
||||
LOG.info("Migration of '{}' succeeded.", vault.getDisplayableName());
|
||||
window.setScene(successScene.get());
|
||||
}
|
||||
}).onError(InvalidPassphraseException.class, e -> {
|
||||
shakeWindow();
|
||||
passwordField.selectAll();
|
||||
passwordField.requestFocus();
|
||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||
}).onError(NoApplicableMigratorException.class, e -> {
|
||||
LOG.error("Can not migrate vault.", e);
|
||||
vault.setState(VaultState.ERROR);
|
||||
// TODO show specific error screen
|
||||
}).onError(Exception.class, e -> { // including RuntimeExceptions
|
||||
LOG.error("Migration failed for technical reasons.", e);
|
||||
vault.setState(VaultState.ERROR);
|
||||
// TODO show generic error screen
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
private void loadStoredPassword() {
|
||||
assert keychainAccess.isPresent();
|
||||
char[] storedPw = null;
|
||||
try {
|
||||
storedPw = keychainAccess.get().loadPassphrase(vault.getId());
|
||||
if (storedPw != null) {
|
||||
passwordField.setPassword(storedPw);
|
||||
passwordField.selectRange(storedPw.length, storedPw.length);
|
||||
}
|
||||
} catch (KeychainAccessException e) {
|
||||
LOG.error("Failed to load entry from system keychain.", e);
|
||||
} finally {
|
||||
if (storedPw != null) {
|
||||
Arrays.fill(storedPw, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
||||
private void shakeWindow() {
|
||||
WritableValue<Double> writableWindowX = new WritableValue<>() {
|
||||
@Override
|
||||
public Double getValue() {
|
||||
return window.getX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Double value) {
|
||||
window.setX(value);
|
||||
}
|
||||
};
|
||||
Timeline timeline = new Timeline( //
|
||||
new KeyFrame(Duration.ZERO, new KeyValue(writableWindowX, window.getX())), //
|
||||
new KeyFrame(new Duration(100), new KeyValue(writableWindowX, window.getX() - 22.0)), //
|
||||
new KeyFrame(new Duration(200), new KeyValue(writableWindowX, window.getX() + 18.0)), //
|
||||
new KeyFrame(new Duration(300), new KeyValue(writableWindowX, window.getX() - 14.0)), //
|
||||
new KeyFrame(new Duration(400), new KeyValue(writableWindowX, window.getX() + 10.0)), //
|
||||
new KeyFrame(new Duration(500), new KeyValue(writableWindowX, window.getX() - 6.0)), //
|
||||
new KeyFrame(new Duration(600), new KeyValue(writableWindowX, window.getX() + 2.0)), //
|
||||
new KeyFrame(new Duration(700), new KeyValue(writableWindowX, window.getX())) //
|
||||
);
|
||||
timeline.play();
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
public Vault getVault() {
|
||||
return vault;
|
||||
}
|
||||
|
||||
public BooleanProperty migrationButtonDisabledProperty() {
|
||||
return migrationButtonDisabled;
|
||||
}
|
||||
|
||||
public boolean isMigrationButtonDisabled() {
|
||||
return migrationButtonDisabled.get();
|
||||
}
|
||||
|
||||
public ObjectBinding<ContentDisplay> migrateButtonContentDisplayProperty() {
|
||||
return migrateButtonContentDisplay;
|
||||
}
|
||||
|
||||
public ContentDisplay getMigrateButtonContentDisplay() {
|
||||
switch (vault.getState()) {
|
||||
case PROCESSING:
|
||||
return ContentDisplay.LEFT;
|
||||
default:
|
||||
return ContentDisplay.TEXT_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,13 +16,13 @@ public class MigrationStartController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final Lazy<Scene> runMigrationScene;
|
||||
|
||||
@Inject
|
||||
public MigrationStartController(@MigrationWindow Stage window, @MigrationWindow Vault vault, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> successScene) {
|
||||
public MigrationStartController(@MigrationWindow Stage window, @MigrationWindow Vault vault, @FxmlScene(FxmlFile.MIGRATION_RUN) Lazy<Scene> runMigrationScene) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.successScene = successScene;
|
||||
this.runMigrationScene = runMigrationScene;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
@@ -35,7 +35,7 @@ public class MigrationStartController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void proceed() {
|
||||
|
||||
window.setScene(runMigrationScene.get());
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.cryptomator.ui.migration;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@MigrationScoped
|
||||
public class MigrationSuccessController implements FxController {
|
||||
|
||||
private final FxApplication fxApplication;
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
|
||||
@Inject
|
||||
MigrationSuccessController(FxApplication fxApplication, @MigrationWindow Stage window, @MigrationWindow Vault vault) {
|
||||
this.fxApplication = fxApplication;
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void unlockAndClose() {
|
||||
close();
|
||||
fxApplication.showUnlockWindow(vault);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
/* Getter/Setters */
|
||||
|
||||
public Vault getVault() {
|
||||
return vault;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.stage.Stage;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
@@ -37,12 +38,13 @@ public class UnlockSuccessController implements FxController {
|
||||
this.revealButtonDisabled = new SimpleBooleanProperty();
|
||||
}
|
||||
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
LOG.trace("UnlockSuccessController.close()");
|
||||
window.close();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void revealAndClose() {
|
||||
LOG.trace("UnlockSuccessController.revealAndClose()");
|
||||
revealButtonState.set(ContentDisplay.LEFT);
|
||||
|
||||
40
main/ui/src/main/resources/fxml/migration_run.fxml
Normal file
40
main/ui/src/main/resources/fxml/migration_run.fxml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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.ProgressIndicator?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import org.cryptomator.ui.controls.FormattedLabel?>
|
||||
<?import org.cryptomator.ui.controls.NiceSecurePasswordField?>
|
||||
<VBox xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="org.cryptomator.ui.migration.MigrationRunController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<VBox spacing="6">
|
||||
<FormattedLabel format="%migration.run.enterPassword" arg1="${controller.vault.displayableName}" wrapText="true"/>
|
||||
<NiceSecurePasswordField fx:id="passwordField"/>
|
||||
</VBox>
|
||||
|
||||
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="B+X">
|
||||
<buttons>
|
||||
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" cancelButton="true" onAction="#back"/>
|
||||
<Button text="%migration.run.startMigrationBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#migrate" contentDisplay="${controller.migrateButtonContentDisplay}"
|
||||
disable="${controller.migrationButtonDisabled}">
|
||||
<graphic>
|
||||
<ProgressIndicator progress="-1" prefWidth="12" prefHeight="12"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</VBox>
|
||||
@@ -28,15 +28,15 @@
|
||||
</StackPane>
|
||||
|
||||
<VBox spacing="6" HBox.hgrow="ALWAYS">
|
||||
<FormattedLabel format="TODO Your vault "%s" needs to be updated to a newer format. Before proceeding, make sure there is no pending synchronization affecting this vault." arg1="${controller.vault.displayableName}" wrapText="true" />
|
||||
<CheckBox fx:id="confirmSyncDone" text="TODO Yes, my vault is fully synced"/>
|
||||
<FormattedLabel format="%migration.start.prompt" arg1="${controller.vault.displayableName}" wrapText="true" />
|
||||
<CheckBox fx:id="confirmSyncDone" text="%migration.start.confirm"/>
|
||||
</VBox>
|
||||
</HBox>
|
||||
|
||||
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="C+X">
|
||||
<buttons>
|
||||
<Button text="%generic.button.done" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#cancel"/>
|
||||
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#cancel"/>
|
||||
<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#proceed" disable="${!confirmSyncDone.selected}"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
|
||||
40
main/ui/src/main/resources/fxml/migration_success.fxml
Normal file
40
main/ui/src/main/resources/fxml/migration_success.fxml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import org.cryptomator.ui.controls.FormattedLabel?>
|
||||
<VBox xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="org.cryptomator.ui.migration.MigrationSuccessController"
|
||||
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-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="CHECK" glyphSize="24"/>
|
||||
</StackPane>
|
||||
<FormattedLabel format="%migration.success.nextStepsInstructions" arg1="${controller.vault.displayableName}" wrapText="true" HBox.hgrow="ALWAYS"/>
|
||||
</HBox>
|
||||
|
||||
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
|
||||
<buttons>
|
||||
<Button text="%generic.button.done" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
|
||||
<Button text="%migration.success.unlockNow" ButtonBar.buttonData="FINISH" onAction="#unlockAndClose" defaultButton="true"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</VBox>
|
||||
@@ -81,6 +81,18 @@ unlock.deleteSavedPasswordDialog.title=Delete Saved Password
|
||||
unlock.deleteSavedPasswordDialog.header=Do you really want to delete the saved password of this vault?
|
||||
unlock.deleteSavedPasswordDialog.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.
|
||||
|
||||
# Migration
|
||||
migration.title=Upgrade Vault
|
||||
## Start
|
||||
migration.start.prompt=Your vault "%s" needs to be updated to a newer format. Before proceeding, make sure there is no pending synchronization affecting this vault.
|
||||
migration.start.confirm=Yes, my vault is fully synced
|
||||
## Run
|
||||
migration.run.enterPassword=Enter the password for "%s"
|
||||
migration.run.startMigrationBtn=Migrate Vault
|
||||
## Sucess
|
||||
migration.success.nextStepsInstructions=Migrated "%s" successfully.\nYou can now unlock your vault.
|
||||
migration.success.unlockNow=Unlock Now
|
||||
|
||||
# Preferences
|
||||
preferences.title=Preferences
|
||||
## General
|
||||
|
||||
Reference in New Issue
Block a user