mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-08-25 00:26:03 +00:00
Replace ImportTemplate stubs with impls
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
This commit is contained in:
+290
-2
@@ -1,40 +1,328 @@
|
||||
package org.cryptomator.ui.importtemplate;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.ObservableUtil;
|
||||
import org.cryptomator.common.locationpresets.LocationPreset;
|
||||
import org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.launcher.VaultTemplateExtractor;
|
||||
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.FontAwesome5IconView;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.RadioButton;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@ImportTemplateScoped
|
||||
public class ImportTemplateLocationController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ImportTemplateLocationController.class);
|
||||
private static final Path DEFAULT_CUSTOM_VAULT_PATH = Path.of(System.getProperty("user.home"));
|
||||
private static final String TEMP_FILE_PREFIX = ".locationTest.cryptomator";
|
||||
|
||||
private final Stage window;
|
||||
private final String vaultName;
|
||||
private final byte[] template;
|
||||
private final ObjectProperty<Path> vaultPath;
|
||||
private final ObjectProperty<Vault> vault;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final VaultListManager vaultListManager;
|
||||
private final ExecutorService executor;
|
||||
private final Settings settings;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final ObservableValue<VaultPathStatus> vaultPathStatus;
|
||||
private final ObservableValue<Boolean> validVaultPath;
|
||||
private final BooleanProperty usePresetPath;
|
||||
private final BooleanProperty loadingPresetLocations = new SimpleBooleanProperty(false);
|
||||
private final BooleanProperty processing = new SimpleBooleanProperty(false);
|
||||
private final ObservableValue<Boolean> readyToImport;
|
||||
private final ObjectBinding<ContentDisplay> createButtonState;
|
||||
private final ObservableList<Node> radioButtons;
|
||||
private final ObservableList<Node> sortedRadioButtons;
|
||||
|
||||
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
|
||||
|
||||
//FXML
|
||||
public ToggleGroup locationPresetsToggler;
|
||||
public VBox radioButtonVBox;
|
||||
public HBox customLocationRadioBtn;
|
||||
public RadioButton customRadioButton;
|
||||
public Label locationStatusLabel;
|
||||
public FontAwesome5IconView goodLocation;
|
||||
public FontAwesome5IconView badLocation;
|
||||
|
||||
@Inject
|
||||
ImportTemplateLocationController(@ImportTemplateWindow Stage window, //
|
||||
@Named("vaultName") String vaultName, //
|
||||
@Named("vaultTemplate") byte[] template, //
|
||||
ObjectProperty<Path> vaultPath) {
|
||||
ObjectProperty<Path> vaultPath, //
|
||||
@ImportTemplateWindow ObjectProperty<Vault> vault, //
|
||||
@FxmlScene(FxmlFile.IMPORT_TEMPLATE_SUCCESS) Lazy<Scene> successScene, //
|
||||
FxApplicationWindows appWindows, //
|
||||
VaultListManager vaultListManager, //
|
||||
ExecutorService executor, //
|
||||
Settings settings, //
|
||||
ResourceBundle resourceBundle) {
|
||||
this.window = window;
|
||||
this.vaultName = vaultName;
|
||||
this.template = template;
|
||||
this.vaultPath = vaultPath;
|
||||
this.vault = vault;
|
||||
this.successScene = successScene;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultListManager = vaultListManager;
|
||||
this.executor = executor;
|
||||
this.settings = settings;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.vaultPathStatus = ObservableUtil.mapWithDefault(vaultPath, this::validatePath, new VaultPathStatus(false, "error.message"));
|
||||
this.validVaultPath = ObservableUtil.mapWithDefault(vaultPathStatus, VaultPathStatus::valid, false);
|
||||
this.vaultPathStatus.addListener(this::updateStatusLabel);
|
||||
this.usePresetPath = new SimpleBooleanProperty();
|
||||
this.readyToImport = Bindings.createBooleanBinding(() -> validVaultPath.getValue() && !processing.get(), validVaultPath, processing);
|
||||
this.createButtonState = Bindings.when(processing).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY);
|
||||
this.radioButtons = FXCollections.observableArrayList();
|
||||
this.sortedRadioButtons = radioButtons.sorted(this::compareLocationPresets);
|
||||
|
||||
Path previouslyUsedDirectory = settings.previouslyUsedVaultDirectory.get();
|
||||
if (previouslyUsedDirectory != null) {
|
||||
try {
|
||||
if (Files.exists(previouslyUsedDirectory) && Files.isDirectory(previouslyUsedDirectory) && isActuallyWritable(previouslyUsedDirectory)) {
|
||||
this.customVaultPath = previouslyUsedDirectory;
|
||||
}
|
||||
} catch (InvalidPathException | NullPointerException e) {
|
||||
LOG.warn("Invalid previously used vault directory path: {}", previouslyUsedDirectory, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private VaultPathStatus validatePath(Path p) throws NullPointerException {
|
||||
if (!Files.exists(p.getParent())) {
|
||||
return new VaultPathStatus(false, "addvaultwizard.new.locationDoesNotExist");
|
||||
} else if (!isActuallyWritable(p.getParent())) {
|
||||
return new VaultPathStatus(false, "addvaultwizard.new.locationIsNotWritable");
|
||||
} else if (!Files.notExists(p)) {
|
||||
return new VaultPathStatus(false, "addvaultwizard.new.fileAlreadyExists");
|
||||
} else {
|
||||
return new VaultPathStatus(true, "addvaultwizard.new.locationIsOk");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStatusLabel(ObservableValue<? extends VaultPathStatus> observable, VaultPathStatus oldValue, VaultPathStatus newValue) {
|
||||
if (newValue.valid()) {
|
||||
locationStatusLabel.setGraphic(goodLocation);
|
||||
locationStatusLabel.getStyleClass().remove("label-red");
|
||||
locationStatusLabel.getStyleClass().add("label-muted");
|
||||
} else {
|
||||
locationStatusLabel.setGraphic(badLocation);
|
||||
locationStatusLabel.getStyleClass().remove("label-muted");
|
||||
locationStatusLabel.getStyleClass().add("label-red");
|
||||
}
|
||||
this.locationStatusLabel.setText(resourceBundle.getString(newValue.localizationKey()));
|
||||
}
|
||||
|
||||
private boolean isActuallyWritable(Path p) {
|
||||
Path tmpDir = null;
|
||||
try {
|
||||
tmpDir = Files.createTempDirectory(p, TEMP_FILE_PREFIX);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (tmpDir != null) {
|
||||
try {
|
||||
Files.deleteIfExists(tmpDir);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Unable to delete temporary directory {}. Needs to be deleted manually.", tmpDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
// TODO (import-template step 4b): location presets, path validation, finish() with spinner + Tasks.runOnce
|
||||
var task = executor.submit(this::loadLocationPresets);
|
||||
window.addEventHandler(WindowEvent.WINDOW_HIDING, _ -> task.cancel(true));
|
||||
locationPresetsToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
|
||||
usePresetPath.bind(locationPresetsToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
|
||||
radioButtons.add(customLocationRadioBtn);
|
||||
Bindings.bindContent(radioButtonVBox.getChildren(), sortedRadioButtons); //to prevent garbage collection of the binding, we bind explicitly to the sorted list
|
||||
}
|
||||
|
||||
private void loadLocationPresets() {
|
||||
Platform.runLater(() -> loadingPresetLocations.set(true));
|
||||
try {
|
||||
LocationPresetsProvider.loadAll(LocationPresetsProvider.class) //
|
||||
.flatMap(LocationPresetsProvider::getLocations) //we do not use sorted(), because it evaluates the stream elements, blocking until all elements are gathered
|
||||
.forEach(this::createRadioButtonFor);
|
||||
} finally {
|
||||
Platform.runLater(() -> loadingPresetLocations.set(false));
|
||||
}
|
||||
}
|
||||
|
||||
private void createRadioButtonFor(LocationPreset preset) {
|
||||
Platform.runLater(() -> {
|
||||
var btn = new RadioButton(preset.name());
|
||||
btn.setUserData(preset.path());
|
||||
radioButtons.add(btn);
|
||||
locationPresetsToggler.getToggles().add(btn);
|
||||
});
|
||||
}
|
||||
|
||||
private int compareLocationPresets(Node left, Node right) {
|
||||
if (customLocationRadioBtn.getId().equals(left.getId())) {
|
||||
return 1;
|
||||
} else if (customLocationRadioBtn.getId().equals(right.getId())) {
|
||||
return -1;
|
||||
} else {
|
||||
return ((RadioButton) left).getText().compareToIgnoreCase(((RadioButton) right).getText());
|
||||
}
|
||||
}
|
||||
|
||||
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
|
||||
var storagePath = Optional.ofNullable((Path) newValue.getUserData()).orElse(customVaultPath);
|
||||
vaultPath.set(storagePath.resolve(vaultName));
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void chooseCustomVaultPath() {
|
||||
DirectoryChooser directoryChooser = new DirectoryChooser();
|
||||
directoryChooser.setTitle(resourceBundle.getString("addvaultwizard.new.directoryPickerTitle"));
|
||||
if (Files.exists(customVaultPath)) {
|
||||
directoryChooser.setInitialDirectory(customVaultPath.toFile());
|
||||
} else {
|
||||
directoryChooser.setInitialDirectory(DEFAULT_CUSTOM_VAULT_PATH.toFile());
|
||||
}
|
||||
final File file = directoryChooser.showDialog(window);
|
||||
if (file != null) {
|
||||
customVaultPath = file.toPath().toAbsolutePath();
|
||||
vaultPath.set(customVaultPath.resolve(vaultName));
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void finish() {
|
||||
if (!isReadyToImport()) {
|
||||
return;
|
||||
}
|
||||
Path destination = vaultPath.get();
|
||||
processing.set(true);
|
||||
Tasks.create(() -> {
|
||||
VaultTemplateExtractor.extractAndMove(template, destination);
|
||||
return vaultListManager.add(destination);
|
||||
}).onSuccess(newVault -> {
|
||||
vault.set(newVault);
|
||||
rememberParentDirectory(destination);
|
||||
window.setScene(successScene.get());
|
||||
}).onError(IOException.class, e -> {
|
||||
LOG.error("Failed to import vault template.", e);
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}).andFinally(() -> processing.set(false)).runOnce(executor);
|
||||
}
|
||||
|
||||
private void rememberParentDirectory(Path destination) {
|
||||
Path parentPath = destination.getParent();
|
||||
if (parentPath != null) {
|
||||
settings.previouslyUsedVaultDirectory.setValue(parentPath);
|
||||
}
|
||||
}
|
||||
|
||||
/* Internal classes */
|
||||
|
||||
private record VaultPathStatus(boolean valid, String localizationKey) {
|
||||
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
public String getVaultName() {
|
||||
return vaultName;
|
||||
}
|
||||
|
||||
public Path getVaultPath() {
|
||||
return vaultPath.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<Path> vaultPathProperty() {
|
||||
return vaultPath;
|
||||
}
|
||||
|
||||
public boolean isLoadingPresetLocations() {
|
||||
return loadingPresetLocations.getValue();
|
||||
}
|
||||
|
||||
public BooleanProperty loadingPresetLocationsProperty() {
|
||||
return loadingPresetLocations;
|
||||
}
|
||||
|
||||
public BooleanProperty usePresetPathProperty() {
|
||||
return usePresetPath;
|
||||
}
|
||||
|
||||
public boolean isUsePresetPath() {
|
||||
return usePresetPath.get();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> readyToImportProperty() {
|
||||
return readyToImport;
|
||||
}
|
||||
|
||||
public boolean isReadyToImport() {
|
||||
return readyToImport.getValue();
|
||||
}
|
||||
|
||||
public ObjectBinding<ContentDisplay> createButtonStateProperty() {
|
||||
return createButtonState;
|
||||
}
|
||||
|
||||
public ContentDisplay getCreateButtonState() {
|
||||
return createButtonState.get();
|
||||
}
|
||||
|
||||
public BooleanBinding anyRadioButtonSelectedProperty() {
|
||||
return locationPresetsToggler.selectedToggleProperty().isNotNull();
|
||||
}
|
||||
|
||||
public boolean isAnyRadioButtonSelected() {
|
||||
return anyRadioButtonSelectedProperty().get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-3
@@ -2,6 +2,7 @@ package org.cryptomator.ui.importtemplate;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
@@ -12,22 +13,28 @@ import javafx.stage.Stage;
|
||||
@ImportTemplateScoped
|
||||
public class ImportTemplateSuccessController implements FxController {
|
||||
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final Stage window;
|
||||
private final ReadOnlyObjectProperty<Vault> vault;
|
||||
|
||||
@Inject
|
||||
ImportTemplateSuccessController(@ImportTemplateWindow Stage window, @ImportTemplateWindow ObjectProperty<Vault> vault) {
|
||||
ImportTemplateSuccessController(FxApplicationWindows appWindows, @ImportTemplateWindow Stage window, @ImportTemplateWindow ObjectProperty<Vault> vault) {
|
||||
this.appWindows = appWindows;
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void unlockAndClose() {
|
||||
close();
|
||||
appWindows.startUnlockWorkflow(vault.get(), window);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
// TODO (import-template step 4b): unlockAndClose() -> appWindows.startUnlockWorkflow(vault.get(), window)
|
||||
|
||||
public ReadOnlyObjectProperty<Vault> vaultProperty() {
|
||||
return vault;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.RadioButton?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.ToggleGroup?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
@@ -11,12 +21,61 @@
|
||||
spacing="12"
|
||||
alignment="CENTER_LEFT"
|
||||
accessibleRole="DIALOG">
|
||||
<fx:define>
|
||||
<ToggleGroup fx:id="locationPresetsToggler"/>
|
||||
<FontAwesome5IconView fx:id="badLocation" styleClass="glyph-icon-red" glyph="TIMES"/>
|
||||
<FontAwesome5IconView fx:id="goodLocation" styleClass="glyph-icon-primary" glyph="CHECK"/>
|
||||
</fx:define>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="24"/>
|
||||
</padding>
|
||||
<children>
|
||||
<!-- TODO (import-template step 4b): fixed-name label, location presets, path field, finish button with spinner -->
|
||||
<Label text="%importTemplate.nameLabel"/>
|
||||
<Label text="${controller.vaultName}"/>
|
||||
<VBox spacing="6">
|
||||
<Label text="%importTemplate.nameLabel"/>
|
||||
<TextField text="${controller.vaultName}" editable="false" disable="true"/>
|
||||
</VBox>
|
||||
|
||||
<Region prefHeight="6" VBox.vgrow="NEVER"/>
|
||||
|
||||
<Label wrapText="true" text="%addvaultwizard.new.locationInstruction"/>
|
||||
<ScrollPane hbarPolicy="NEVER">
|
||||
<VBox fx:id="radioButtonVBox" spacing="6">
|
||||
<!-- PLACEHOLDER, more radio buttons are added programmatically via controller -->
|
||||
<HBox fx:id="customLocationRadioBtn" spacing="12" alignment="CENTER_LEFT">
|
||||
<RadioButton fx:id="customRadioButton" toggleGroup="${locationPresetsToggler}" text="%addvaultwizard.new.directoryPickerLabel"/>
|
||||
<Button contentDisplay="LEFT" text="%addvaultwizard.new.directoryPickerButton" onAction="#chooseCustomVaultPath" disable="${controller.usePresetPath}">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="FOLDER_OPEN"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
</HBox>
|
||||
</VBox>
|
||||
</ScrollPane>
|
||||
<Label wrapText="true" text="%addvaultwizard.new.locationLoading" visible="${controller.loadingPresetLocations}" managed="${controller.loadingPresetLocations}" graphicTextGap="8">
|
||||
<graphic>
|
||||
<FontAwesome5Spinner/>
|
||||
</graphic>
|
||||
</Label>
|
||||
|
||||
<Region prefHeight="12" VBox.vgrow="NEVER"/>
|
||||
|
||||
<VBox spacing="6">
|
||||
<Label text="%addvaultwizard.new.locationLabel" labelFor="$locationTextField"/>
|
||||
<TextField fx:id="locationTextField" promptText="%addvaultwizard.new.locationPrompt" text="${controller.vaultPath}" editable="false" disable="${!controller.anyRadioButtonSelected}" HBox.hgrow="ALWAYS"/>
|
||||
<Label fx:id="locationStatusLabel" alignment="CENTER_RIGHT" wrapText="true" visible="${controller.anyRadioButtonSelected}" maxWidth="Infinity" graphicTextGap="6"/>
|
||||
</VBox>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+X">
|
||||
<buttons>
|
||||
<Button text="%importTemplate.createVaultBtn" ButtonBar.buttonData="NEXT_FORWARD" onAction="#finish" defaultButton="true" disable="${!controller.readyToImport}"
|
||||
contentDisplay="${controller.createButtonState}">
|
||||
<graphic>
|
||||
<FontAwesome5Spinner glyphSize="12"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</children>
|
||||
</VBox>
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import org.cryptomator.ui.controls.FormattedLabel?>
|
||||
<?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.Region?>
|
||||
<?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.importtemplate.ImportTemplateSuccessController"
|
||||
@@ -17,11 +22,23 @@
|
||||
<Insets topRightBottomLeft="24"/>
|
||||
</padding>
|
||||
<children>
|
||||
<!-- TODO (import-template step 4b): success glyph, instructions, unlock-now button -->
|
||||
<Label text="%importTemplate.success.message"/>
|
||||
<ButtonBar buttonMinWidth="120">
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<StackPane alignment="CENTER" HBox.hgrow="NEVER">
|
||||
<Circle styleClass="glyph-icon-primary" radius="36"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="CHECK" glyphSize="36"/>
|
||||
</StackPane>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<FormattedLabel format="%importTemplate.success.message" arg1="${controller.vault.displayName}" wrapText="true" HBox.hgrow="ALWAYS"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+IU">
|
||||
<buttons>
|
||||
<Button text="%generic.button.done" ButtonBar.buttonData="FINISH" onAction="#close"/>
|
||||
<Button text="%generic.button.done" ButtonBar.buttonData="FINISH" onAction="#close" defaultButton="${!controller.vault.locked}"/>
|
||||
<Button text="%importTemplate.success.unlockNow" ButtonBar.buttonData="OTHER" onAction="#unlockAndClose" defaultButton="${controller.vault.locked}" visible="${controller.vault.locked}"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</children>
|
||||
|
||||
@@ -668,9 +668,11 @@ retryIfReadonly.description=Cryptomator cannot write to the vault directory. You
|
||||
retryIfReadonly.retry=Change and Retry
|
||||
|
||||
# Import Template
|
||||
importTemplate.title=Initialize Vault
|
||||
importTemplate.title=Setup Vault
|
||||
importTemplate.nameLabel=Vault name
|
||||
importTemplate.success.message=Your vault has been created.
|
||||
importTemplate.createVaultBtn=Create Vault
|
||||
importTemplate.success.message=Vault %s has been setup.
|
||||
importTemplate.success.unlockNow=Unlock Now
|
||||
|
||||
# Share Vault
|
||||
shareVault.title=Share Vault
|
||||
|
||||
Reference in New Issue
Block a user