add ui to import a vault from template

add scaffolding, controller are stubs for now

Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
This commit is contained in:
Armin Schrenk
2026-07-14 17:00:25 +02:00
parent fbdf511f05
commit 40bb0079ac
11 changed files with 289 additions and 0 deletions
@@ -13,6 +13,8 @@ public enum FxmlFile {
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
CONVERTVAULT_HUBTOPASSWORD_SUCCESS("/fxml/convertvault_hubtopassword_success.fxml"), //
DECRYPTNAMES("/fxml/decryptnames.fxml"), //
IMPORT_TEMPLATE_LOCATION("/fxml/import_template_location.fxml"), //
IMPORT_TEMPLATE_SUCCESS("/fxml/import_template_success.fxml"), //
ERROR("/fxml/error.fxml"), //
EVENT_VIEW("/fxml/eventview.fxml"), //
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
@@ -12,6 +12,7 @@ import org.cryptomator.ui.decryptname.DecryptNameComponent;
import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.importtemplate.ImportTemplateComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.notification.NotificationComponent;
@@ -31,6 +32,7 @@ import java.util.Optional;
@Module(subcomponents = {TrayMenuComponent.class, //
DecryptNameComponent.class, //
ImportTemplateComponent.class, //
MainWindowComponent.class, //
PreferencesComponent.class, //
VaultOptionsComponent.class, //
@@ -0,0 +1,34 @@
package org.cryptomator.ui.importtemplate;
import dagger.BindsInstance;
import dagger.Lazy;
import dagger.Subcomponent;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javax.inject.Named;
import javafx.scene.Scene;
import javafx.stage.Stage;
@ImportTemplateScoped
@Subcomponent(modules = {ImportTemplateModule.class})
public interface ImportTemplateComponent {
@ImportTemplateWindow
Stage window();
@FxmlScene(FxmlFile.IMPORT_TEMPLATE_LOCATION)
Lazy<Scene> scene();
default void showImportTemplateWindow() {
Stage stage = window();
stage.setScene(scene().get());
stage.show();
}
@Subcomponent.Factory
interface Factory {
ImportTemplateComponent create(@BindsInstance @Named("vaultName") String name, @BindsInstance @Named("vaultTemplate") byte[] template);
}
}
@@ -0,0 +1,40 @@
package org.cryptomator.ui.importtemplate;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.nio.file.Path;
@ImportTemplateScoped
public class ImportTemplateLocationController implements FxController {
private final Stage window;
private final String vaultName;
private final byte[] template;
private final ObjectProperty<Path> vaultPath;
@Inject
ImportTemplateLocationController(@ImportTemplateWindow Stage window, //
@Named("vaultName") String vaultName, //
@Named("vaultTemplate") byte[] template, //
ObjectProperty<Path> vaultPath) {
this.window = window;
this.vaultName = vaultName;
this.template = template;
this.vaultPath = vaultPath;
}
@FXML
public void initialize() {
// TODO (import-template step 4b): location presets, path validation, finish() with spinner + Tasks.runOnce
}
public String getVaultName() {
return vaultName;
}
}
@@ -0,0 +1,90 @@
package org.cryptomator.ui.importtemplate;
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.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.fxapp.PrimaryStage;
import javax.inject.Provider;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.nio.file.Path;
import java.util.Map;
import java.util.ResourceBundle;
@Module
abstract class ImportTemplateModule {
@Provides
@ImportTemplateWindow
@ImportTemplateScoped
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@ImportTemplateWindow
@ImportTemplateScoped
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setResizable(false);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(primaryStage);
stage.setTitle(resourceBundle.getString("importTemplate.title"));
return stage;
}
@Provides
@ImportTemplateScoped
static ObjectProperty<Path> provideVaultPath() {
return new SimpleObjectProperty<>();
}
@Provides
@ImportTemplateWindow
@ImportTemplateScoped
static ObjectProperty<Vault> provideVault() {
return new SimpleObjectProperty<>();
}
// ------------------
@Provides
@FxmlScene(FxmlFile.IMPORT_TEMPLATE_LOCATION)
@ImportTemplateScoped
static Scene provideImportTemplateLocationScene(@ImportTemplateWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.IMPORT_TEMPLATE_LOCATION);
}
@Provides
@FxmlScene(FxmlFile.IMPORT_TEMPLATE_SUCCESS)
@ImportTemplateScoped
static Scene provideImportTemplateSuccessScene(@ImportTemplateWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.IMPORT_TEMPLATE_SUCCESS);
}
// ------------------
@Binds
@IntoMap
@FxControllerKey(ImportTemplateLocationController.class)
abstract FxController bindImportTemplateLocationController(ImportTemplateLocationController controller);
@Binds
@IntoMap
@FxControllerKey(ImportTemplateSuccessController.class)
abstract FxController bindImportTemplateSuccessController(ImportTemplateSuccessController controller);
}
@@ -0,0 +1,13 @@
package org.cryptomator.ui.importtemplate;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface ImportTemplateScoped {
}
@@ -0,0 +1,39 @@
package org.cryptomator.ui.importtemplate;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@ImportTemplateScoped
public class ImportTemplateSuccessController implements FxController {
private final Stage window;
private final ReadOnlyObjectProperty<Vault> vault;
@Inject
ImportTemplateSuccessController(@ImportTemplateWindow Stage window, @ImportTemplateWindow ObjectProperty<Vault> vault) {
this.window = window;
this.vault = vault;
}
@FXML
public void close() {
window.close();
}
// TODO (import-template step 4b): unlockAndClose() -> appWindows.startUnlockWorkflow(vault.get(), window)
public ReadOnlyObjectProperty<Vault> vaultProperty() {
return vault;
}
public Vault getVault() {
return vault.get();
}
}
@@ -0,0 +1,14 @@
package org.cryptomator.ui.importtemplate;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
@interface ImportTemplateWindow {
}
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.importtemplate.ImportTemplateLocationController"
prefWidth="450"
prefHeight="450"
spacing="12"
alignment="CENTER_LEFT"
accessibleRole="DIALOG">
<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}"/>
</children>
</VBox>
@@ -0,0 +1,28 @@
<?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.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.importtemplate.ImportTemplateSuccessController"
prefWidth="450"
prefHeight="450"
spacing="12"
alignment="TOP_CENTER"
accessibleRole="DIALOG">
<padding>
<Insets topRightBottomLeft="24"/>
</padding>
<children>
<!-- TODO (import-template step 4b): success glyph, instructions, unlock-now button -->
<Label text="%importTemplate.success.message"/>
<ButtonBar buttonMinWidth="120">
<buttons>
<Button text="%generic.button.done" ButtonBar.buttonData="FINISH" onAction="#close"/>
</buttons>
</ButtonBar>
</children>
</VBox>
@@ -667,6 +667,11 @@ retryIfReadonly.message=No write access to vault directory
retryIfReadonly.description=Cryptomator cannot write to the vault directory. You can change the vault to be read-only and try again. This option can be disabled in the vault options.
retryIfReadonly.retry=Change and Retry
# Import Template
importTemplate.title=Initialize Vault
importTemplate.nameLabel=Vault name
importTemplate.success.message=Your vault has been created.
# Share Vault
shareVault.title=Share Vault
shareVault.message=Would you like to share your vault with others?