implemented a 'Share Vault' button in vault_detail_locked and added a new window for vault sharing options

This commit is contained in:
Jan-Peter Klein
2024-01-16 17:21:07 +01:00
parent 8f32b46b30
commit 4ca190cfb9
15 changed files with 283 additions and 2 deletions

View File

@@ -44,6 +44,7 @@ public enum FxmlFile {
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
SHARE_VAULT("/fxml/share_vault.fxml"), //
UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
UNLOCK_REQUIRES_RESTART("/fxml/unlock_requires_restart.fxml"), //

View File

@@ -47,6 +47,7 @@ public enum FontAwesome5Icon {
QUESTION_CIRCLE("\uf059"), //
REDO("\uF01E"), //
SEARCH("\uF002"), //
SHARE("\uF064"), //
SPINNER("\uF110"), //
STETHOSCOPE("\uF0f1"), //
SYNC("\uF021"), //

View File

@@ -13,6 +13,7 @@ import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.sharevault.ShareVaultComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
@@ -22,7 +23,17 @@ import javafx.scene.image.Image;
import java.io.IOException;
import java.io.InputStream;
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, VaultOptionsComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class, HealthCheckComponent.class, UpdateReminderComponent.class})
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, //
MainWindowComponent.class, //
PreferencesComponent.class, //
VaultOptionsComponent.class, //
UnlockComponent.class, //
LockComponent.class, //
QuitComponent.class, //
ErrorComponent.class, //
HealthCheckComponent.class, //
UpdateReminderComponent.class, //
ShareVaultComponent.class})
abstract class FxApplicationModule {
private static Image createImageFromResource(String resourceName) throws IOException {

View File

@@ -11,6 +11,7 @@ import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.sharevault.ShareVaultComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.unlock.UnlockWorkflow;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
@@ -51,6 +52,7 @@ public class FxApplicationWindows {
private final ErrorComponent.Factory errorWindowFactory;
private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final ShareVaultComponent.Factory shareVaultWindow;
private final FilteredList<Window> visibleWindows;
@Inject
@@ -64,6 +66,7 @@ public class FxApplicationWindows {
LockComponent.Factory lockWorkflowFactory, //
ErrorComponent.Factory errorWindowFactory, //
VaultOptionsComponent.Factory vaultOptionsWindow, //
ShareVaultComponent.Factory shareVaultWindow, //
ExecutorService executor) {
this.primaryStage = primaryStage;
this.trayIntegration = trayIntegration;
@@ -76,6 +79,7 @@ public class FxApplicationWindows {
this.errorWindowFactory = errorWindowFactory;
this.executor = executor;
this.vaultOptionsWindow = vaultOptionsWindow;
this.shareVaultWindow = shareVaultWindow;
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
}
@@ -122,6 +126,10 @@ public class FxApplicationWindows {
return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater).whenComplete(this::reportErrors);
}
public void showShareVaultWindow(Vault vault) {
CompletableFuture.runAsync(() -> shareVaultWindow.create(vault).showShareVaultWindow(), Platform::runLater);
}
public CompletionStage<Stage> showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) {
return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors);
}

View File

@@ -44,6 +44,11 @@ public class VaultDetailLockedController implements FxController {
appWindows.startUnlockWorkflow(vault.get(), mainWindow);
}
@FXML
public void share() {
appWindows.showShareVaultWindow(vault.get());
}
@FXML
public void showVaultOptions() {
vaultOptionsWindow.create(vault.get()).showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);

View File

@@ -0,0 +1,34 @@
package org.cryptomator.ui.sharevault;
import dagger.BindsInstance;
import dagger.Lazy;
import dagger.Subcomponent;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javafx.scene.Scene;
import javafx.stage.Stage;
@ShareVaultScoped
@Subcomponent(modules = {ShareVaultModule.class})
public interface ShareVaultComponent {
@ShareVaultWindow
Stage window();
@FxmlScene(FxmlFile.SHARE_VAULT)
Lazy<Scene> scene();
default void showShareVaultWindow(){
Stage stage = window();
stage.setScene(scene().get());
stage.show();
}
@Subcomponent.Factory
interface Factory {
ShareVaultComponent create(@BindsInstance @ShareVaultWindow Vault vault);
}
}

View File

@@ -0,0 +1,50 @@
package org.cryptomator.ui.sharevault;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@ShareVaultScoped
public class ShareVaultController implements FxController {
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/";
private static final String OPEN_HUB_URL = "https://cryptomator.org/hub/";
private final Stage window;
private final Lazy<Application> application;
private final Boolean hubVault;
@Inject
ShareVaultController(@ShareVaultWindow Stage window, //
Lazy<Application> application, //
@ShareVaultWindow Vault vault){
this.window = window;
this.application = application;
var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
this.hubVault = (vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS));
}
@FXML
public void close(){
window.close();
}
@FXML
public void visitHub() {
application.get().getHostServices().showDocument(VISIT_HUB_URL);
}
@FXML
public void openHub() {
application.get().getHostServices().showDocument(OPEN_HUB_URL);
}
public boolean isHubVault() {
return hubVault;
}
}

View File

@@ -0,0 +1,54 @@
package org.cryptomator.ui.sharevault;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
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 javax.inject.Provider;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
import java.util.ResourceBundle;
@Module
abstract class ShareVaultModule {
@Provides
@ShareVaultWindow
@ShareVaultScoped
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@ShareVaultWindow
@ShareVaultScoped
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setResizable(false);
stage.initModality(Modality.APPLICATION_MODAL);
stage.setTitle(resourceBundle.getString("shareVault.title"));
return stage;
}
@Provides
@FxmlScene(FxmlFile.SHARE_VAULT)
@ShareVaultScoped
static Scene provideShareVaultScene(@ShareVaultWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.SHARE_VAULT);
}
@Binds
@IntoMap
@FxControllerKey(ShareVaultController.class)
abstract FxController bindShareVaultController(ShareVaultController controller);
}

View File

@@ -0,0 +1,13 @@
package org.cryptomator.ui.sharevault;
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 ShareVaultScoped {
}

View File

@@ -0,0 +1,14 @@
package org.cryptomator.ui.sharevault;
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 ShareVaultWindow {
}

View File

@@ -0,0 +1,64 @@
<?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.Region?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.shape.Circle?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.sharevault.ShareVaultController"
prefWidth="600"
spacing="12"
alignment="CENTER_LEFT">
<padding>
<Insets topRightBottomLeft="24"/>
</padding>
<HBox spacing="12" VBox.vgrow="NEVER">
<StackPane HBox.hgrow="NEVER">
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="INFO" glyphSize="24"/>
</StackPane>
<VBox spacing="6" HBox.hgrow="ALWAYS" visible="${controller.hubVault}" managed="${controller.hubVault}">
<Label text="%shareVault.hub.message" styleClass="label-extra-large" wrapText="true"/>
<Label text="%shareVault.hub.instruction.0" wrapText="true"/>
<Label text="%shareVault.hub.instruction.1" wrapText="true"/>
<Label text="%shareVault.hub.instruction.2" wrapText="true"/>
</VBox>
<VBox spacing="12" HBox.hgrow="ALWAYS" visible="${!controller.hubVault}" managed="${!controller.hubVault}">
<Label text="%shareVault.message" styleClass="label-extra-large" wrapText="true"/>
<Label text="%shareVault.description" wrapText="true"/>
<Label text="%shareVault.instruction.0" wrapText="true"/>
<Label text="%shareVault.instruction.1" wrapText="true"/>
<Label text="%shareVault.more" wrapText="true"/>
<HBox spacing="6" style="-fx-padding: 10px; -fx-background-color: white; -fx-border-color: #dddddd; -fx-border-width: 2px; -fx-border-radius: 5px;">
<VBox spacing="6" alignment="CENTER_LEFT">
<Label text="%shareVault.info.1" style="-fx-font-weight: bold;" wrapText="true"/>
<Label text="%shareVault.info.2" wrapText="true"/>
<Label text="%shareVault.info.3" wrapText="true"/>
<Label text="%shareVault.info.4" wrapText="true"/>
<Label text="%shareVault.info.5" wrapText="true"/>
</VBox>
<Region HBox.hgrow="ALWAYS"/>
<ImageView HBox.hgrow="ALWAYS" fitWidth="200" preserveRatio="true" cache="true">
<Image url="@../img/share-hub-logo.png"/>
</ImageView>
</HBox>
</VBox>
</HBox>
<Region VBox.vgrow="ALWAYS"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<buttons>
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" onAction="#close"/>
<Button text="%shareVault.hub.openHub" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#openHub" visible="${controller.hubVault}" managed="${controller.hubVault}"/>
<Button text="%shareVault.visitHub" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#visitHub" visible="${!controller.hubVault}" managed="${!controller.hubVault}"/>
</buttons>
</ButtonBar>
</VBox>

View File

@@ -24,6 +24,11 @@
<FontAwesome5IconView glyph="KEY" glyphSize="15"/>
</graphic>
</Button>
<Button text="%main.vaultDetail.share" minWidth="120" onAction="#share">
<graphic>
<FontAwesome5IconView glyph="SHARE" glyphSize="15"/>
</graphic>
</Button>
<Hyperlink text="%main.vaultDetail.passwordSavedInKeychain" visible="${controller.passwordSaved}" onAction="#showKeyVaultOptions">
<graphic>
<FontAwesome5IconView glyph="LOCK"/>

View File

@@ -387,6 +387,7 @@ main.vaultDetail.unlockBtn=Unlock…
main.vaultDetail.unlockNowBtn=Unlock Now
main.vaultDetail.optionsBtn=Vault Options
main.vaultDetail.passwordSavedInKeychain=Password saved
main.vaultDetail.share=Share...
### Unlocked
main.vaultDetail.unlockedStatus=UNLOCKED
main.vaultDetail.accessLocation=Your vault's contents are accessible here:
@@ -524,4 +525,24 @@ updateReminder.message=Check for Updates?
updateReminder.description=Stay updated with new features, bug fixes, and security improvements. We recommend to automatically check for updates.
updateReminder.notNow=Not Now
updateReminder.yesOnce=Yes, Once
updateReminder.yesAutomatically=Yes, Automatically
updateReminder.yesAutomatically=Yes, Automatically
# Share Vault
shareVault.title=Share Vault
shareVault.message=Would you like to share your vault with others?
shareVault.description=Always be careful when sharing your vault with other people. In short, follow these steps:
shareVault.instruction.0=To encrypt files, follow these steps:
shareVault.instruction.1=1. Share access of the encrypted vault folder via cloud storage.\n2. Share vault password in a secure way.
shareVault.more=For more information, check out our best practices.
shareVault.info.1=The secure way to work in teams
shareVault.info.2=Use Cryptomator Hub!
shareVault.info.3=• Zero-knowledge key management
shareVault.info.4=• Strong authentication
shareVault.info.5=• ...
shareVault.visitHub=Visit Cryptomator Hub
shareVault.hub.message=How to share a Hub vault
shareVault.hub.instruction.0=In order to share the vault content with another team member, you have to perform two steps:
shareVault.hub.instruction.1=1. Share access of the encrypted vault folder via cloud storage.
shareVault.hub.instruction.2=2. Grant access to team member in Cryptomator Hub.
shareVault.hub.openHub=Open Cryptomator Hub

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB